ExternalProject: Improve handling of git remote branches

ExternalProject handles git remote branches by commit hash. Due to
this, the git repository ends in detached states, and local commits
are discarded.

Use "git rebase" for remote branches instead of "git checkout".  If
there are uncommitted changes, use "git stash save/pop" to save changes
and restore them after the rebase.  If any of these operations fails,
try to restore the original status and exit with a fatal error, asking
the user to resolve the conflicts manually.

This also makes the behaviour of ExternalProject using git more similar
to the svn version, and probably more likely to what the user expects
by setting GIT_TAG to a branch.
This commit is contained in:
Daniele E. Domenichelli 2013-11-18 11:36:18 +01:00 committed by Brad King
parent ff2451dc24
commit f2128d4c28
1 changed files with 99 additions and 7 deletions

View File

@ -473,6 +473,17 @@ else()
set(is_remote_ref 0)
endif()
# Tag is in the form <remote>/<tag> (i.e. origin/master) we must strip
# the remote from the tag.
if(\"\${show_ref_output}\" MATCHES \"refs/remotes/${git_tag}\")
string(REGEX MATCH \"^([^/]+)/(.+)$\" _unused \"${git_tag}\")
set(git_remote \"\${CMAKE_MATCH_1}\")
set(git_tag \"\${CMAKE_MATCH_2}\")
else()
set(git_remote \"origin\")
set(git_tag \"${git_tag}\")
endif()
# This will fail if the tag does not exist (it probably has not been fetched
# yet).
execute_process(
@ -494,13 +505,94 @@ if(error_code OR is_remote_ref OR NOT (\"\${tag_sha}\" STREQUAL \"\${head_sha}\"
message(FATAL_ERROR \"Failed to fetch repository '${git_repository}'\")
endif()
execute_process(
COMMAND \"${git_EXECUTABLE}\" checkout ${git_tag}
WORKING_DIRECTORY \"${work_dir}\"
RESULT_VARIABLE error_code
)
if(error_code)
message(FATAL_ERROR \"Failed to checkout tag: '${git_tag}'\")
if(is_remote_ref)
# Check if stash is needed
execute_process(
COMMAND \"${git_EXECUTABLE}\" status --porcelain
WORKING_DIRECTORY \"${work_dir}\"
RESULT_VARIABLE error_code
OUTPUT_VARIABLE repo_status
)
if(error_code)
message(FATAL_ERROR \"Failed to get the status\")
endif()
string(LENGTH \"\${repo_status}\" need_stash)
# If not in clean state, stash changes in order to be able to be able to
# perform git pull --rebase
if(need_stash)
execute_process(
COMMAND \"${git_EXECUTABLE}\" stash save --all --quiet
WORKING_DIRECTORY \"${work_dir}\"
RESULT_VARIABLE error_code
)
if(error_code)
message(FATAL_ERROR \"Failed to stash changes\")
endif()
endif()
# Pull changes from the remote branch
execute_process(
COMMAND \"${git_EXECUTABLE}\" rebase \${git_remote}/\${git_tag}
WORKING_DIRECTORY \"${work_dir}\"
RESULT_VARIABLE error_code
)
if(error_code)
# Rebase failed: Restore previous state.
execute_process(
COMMAND \"${git_EXECUTABLE}\" rebase --abort
WORKING_DIRECTORY \"${work_dir}\"
)
if(need_stash)
execute_process(
COMMAND \"${git_EXECUTABLE}\" stash pop --index --quiet
WORKING_DIRECTORY \"${work_dir}\"
)
endif()
message(FATAL_ERROR \"\\nFailed to rebase in: '${work_dir}/${src_name}'.\\nYou will have to resolve the conflicts manually\")
endif()
if(need_stash)
execute_process(
COMMAND \"${git_EXECUTABLE}\" stash pop --index --quiet
WORKING_DIRECTORY \"${work_dir}\"
RESULT_VARIABLE error_code
)
if(error_code)
# Stash pop --index failed: Try again dropping the index
execute_process(
COMMAND \"${git_EXECUTABLE}\" reset --hard --quiet
WORKING_DIRECTORY \"${work_dir}\"
RESULT_VARIABLE error_code
)
execute_process(
COMMAND \"${git_EXECUTABLE}\" stash pop --quiet
WORKING_DIRECTORY \"${work_dir}\"
RESULT_VARIABLE error_code
)
if(error_code)
# Stash pop failed: Restore previous state.
execute_process(
COMMAND \"${git_EXECUTABLE}\" reset --hard --quiet \${head_sha}
WORKING_DIRECTORY \"${work_dir}\"
)
execute_process(
COMMAND \"${git_EXECUTABLE}\" stash pop --index --quiet
WORKING_DIRECTORY \"${work_dir}\"
)
message(FATAL_ERROR \"\\nFailed to unstash changes in: '${work_dir}/${src_name}'.\\nYou will have to resolve the conflicts manually\")
endif()
endif()
endif()
else()
execute_process(
COMMAND \"${git_EXECUTABLE}\" checkout ${git_tag}
WORKING_DIRECTORY \"${work_dir}\"
RESULT_VARIABLE error_code
)
if(error_code)
message(FATAL_ERROR \"Failed to checkout tag: '${git_tag}'\")
endif()
endif()
execute_process(