#!/bin/sh # # Copyright (c) 2005 Junio C Hamano # setvar OPTIONS_KEEPDASHDASH = '' setvar OPTIONS_SPEC = ""\ git merge [options] ... git merge [options] HEAD -- stat show a diffstat at the end of the merge n don't show a diffstat at the end of the merge summary (synonym to --stat) log add list of one-line log to merge commit message squash create a single commit instead of doing a merge commit perform a commit if the merge succeeds (default) ff allow fast-forward (default) ff-only abort if fast-forward is not possible rerere-autoupdate update index with any reused conflict resolution s,strategy= merge strategy to use X= option for selected merge strategy m,message= message to be used for the merge commit (if any) "" setvar SUBDIRECTORY_OK = 'Yes' source git-sh-setup require_work_tree cd_to_toplevel test -z $(git ls-files -u) || die "Merge is not possible because you have unmerged files." ! test -e "$GIT_DIR/MERGE_HEAD" || die 'You have not concluded your merge (MERGE_HEAD exists).' setvar LF = '' '' setvar all_strategies = ''recur recursive octopus resolve stupid ours subtree'' setvar all_strategies = ""$all_strategies recursive-ours recursive-theirs"" setvar not_strategies = ''base file index tree'' setvar default_twohead_strategies = ''recursive'' setvar default_octopus_strategies = ''octopus'' setvar no_fast_forward_strategies = ''subtree ours'' setvar no_trivial_strategies = ''recursive recur subtree ours recursive-ours recursive-theirs'' setvar use_strategies = '' setvar xopt = '' setvar allow_fast_forward = 't' setvar fast_forward_only = '' setvar allow_trivial_merge = 't' setvar squash = '', no_commit = '', log_arg = '', rr_arg = '' proc dropsave { rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" \ "$GIT_DIR/MERGE_STASH" "$GIT_DIR/MERGE_MODE" || exit 1 } proc savestate { # Stash away any local modifications. git stash create >"$GIT_DIR/MERGE_STASH" } proc restorestate { if test -f "$GIT_DIR/MERGE_STASH" { git reset --hard $head >/dev/null git stash apply $(cat "$GIT_DIR/MERGE_STASH") git update-index --refresh >/dev/null } } proc finish_up_to_date { case (squash) { t { echo "$1 (nothing to squash)" } '' { echo $1 } } dropsave } proc squash_message { echo Squashed commit of the following: echo git log --no-merges --pretty=medium ^"$head" $remoteheads } proc finish { if test '' = $2 { setvar rlogm = "$GIT_REFLOG_ACTION" } else { echo $2 setvar rlogm = ""$GIT_REFLOG_ACTION: $2"" } case (squash) { t { echo "Squash commit -- not updating HEAD" squash_message >"$GIT_DIR/SQUASH_MSG" } '' { case (merge_msg) { '' { echo "No merge message -- not updating HEAD" } * { git update-ref -m $rlogm HEAD $1 $head || exit 1 git gc --auto } } } } case (1) { '' { } ?* { if test $show_diffstat = t {' # We want color (if set), but no pager GIT_PAGER=''' git diff --stat --summary -M $head $1 } } } # Run a post-merge hook if test -x "$GIT_DIR"/hooks/post-merge { case (squash) { t { "$GIT_DIR"/hooks/post-merge 1 } '' { "$GIT_DIR"/hooks/post-merge 0 } } } } proc merge_name { setvar remote = "$1" setvar rh = $(git rev-parse --verify "$remote^0" 2>/dev/null) || return if setvar truname = $(expr "$remote" : '\(.*\)~[0-9]*$') && git show-ref -q --verify "refs/heads/$truname" 2>/dev/null { echo "$rh branch '$truname' (early part) of ." return } if setvar found_ref = $(git rev-parse --symbolic-full-name --verify \ "$remote" 2>/dev/null) { setvar expanded = $(git check-ref-format --branch "$remote") || exit if test ${found_ref#refs/heads/} != $found_ref { echo "$rh branch '$expanded' of ." return } elif test ${found_ref#refs/remotes/} != $found_ref { echo "$rh remote branch '$expanded' of ." return } } if test $remote = "FETCH_HEAD" && test -r "$GIT_DIR/FETCH_HEAD" { sed -e 's/ not-for-merge / /' -e 1q \ "$GIT_DIR/FETCH_HEAD" return } echo "$rh commit '$remote'" } proc parse_config { while test $Argc != 0 { case (1) { -n|--no-stat|--no-summary { setvar show_diffstat = 'false' } --stat|--summary { setvar show_diffstat = 't' } --log|--no-log { setvar log_arg = "$1" } --squash { test $allow_fast_forward = t || die "You cannot combine --squash with --no-ff." setvar squash = 't', no_commit = 't' } --no-squash { setvar squash = '', no_commit = '' } --commit { setvar no_commit = '' } --no-commit { setvar no_commit = 't' } --ff { setvar allow_fast_forward = 't' } --no-ff { test $squash != t || die "You cannot combine --squash with --no-ff." test $fast_forward_only != t || die "You cannot combine --ff-only with --no-ff." setvar allow_fast_forward = 'f' } --ff-only { test $allow_fast_forward != f || die "You cannot combine --ff-only with --no-ff." setvar fast_forward_only = 't' } --rerere-autoupdate|--no-rerere-autoupdate { setvar rr_arg = "$1" } -s|--strategy { shift case{ *" $1 "* { setvar use_strategies = ""$use_strategies$1 "" } * { case{ *" $1 "* { false } } && type "git-merge-$1" >/dev/null 2>&1 || die "available strategies are: $all_strategies" setvar use_strategies = ""$use_strategies$1 "" } } } -X { shift setvar xopt = ""${xopt:+$xopt }$(git rev-parse --sq-quote "--$1")"" } -m|--message { shift setvar merge_msg = "$1" setvar have_message = 't' } -- { shift break } * { usage } } shift } setvar args_left = "$Argc" } test $Argc != 0 || usage setvar have_message = '' if setvar branch = $(git-symbolic-ref -q HEAD) { setvar mergeopts = $(git config "branch.${branch#refs/heads/}.mergeoptions") if test -n $mergeopts { parse_config $mergeopts -- } } parse_config @ARGV while test $args_left -lt $Argc { shift; } if test -z $show_diffstat { test $(git config --bool merge.diffstat) = false && setvar show_diffstat = 'false' test $(git config --bool merge.stat) = false && setvar show_diffstat = 'false' test -z $show_diffstat && setvar show_diffstat = 't' } # This could be traditional "merge HEAD ..." and the # way we can tell it is to see if the second token is HEAD, but some # people might have misused the interface and used a commit-ish that # is the same as HEAD there instead. Traditional format never would # have "-m" so it is an additional safety measure to check for it. if test -z $have_message && setvar second_token = $(git rev-parse --verify "$2^0" 2>/dev/null) && setvar head_commit = $(git rev-parse --verify "HEAD" 2>/dev/null) && test $second_token = $head_commit { setvar merge_msg = "$1" shift setvar head_arg = "$1" shift } elif ! git rev-parse --verify HEAD >/dev/null 2>&1 { # If the merged head is a valid one there is no reason to # forbid "git merge" into a branch yet to be born. We do # the same for "git pull". if test 1 -ne $Argc { echo >&2 "Can merge only exactly one commit into empty head>&2 "Can merge only exactly one commit into empty head" exit 1 } test $squash != t || die "Squash commit into empty head not supported yet" test $allow_fast_forward = t || die "Non-fast-forward into an empty head does not make sense" setvar rh = $(git rev-parse --verify "$1^0") || die "$1 - not something we can merge" git update-ref -m "initial pull" HEAD $rh "" && git read-tree --reset -u HEAD exit } else { # We are invoked directly as the first-class UI. setvar head_arg = 'HEAD' # All the rest are the commits being merged; prepare # the standard merge summary message to be appended to # the given message. If remote is invalid we will die # later in the common codepath so we discard the error # in this loop. setvar merge_msg = "$( for remote do merge_name "$remote" done | if test "$have_message" = t then git fmt-merge-msg -m "$merge_msg" $log_arg else git fmt-merge-msg $log_arg fi )" } setvar head = $(git rev-parse --verify "$head_arg"^0) || usage # All the rest are remote heads test "$Argc" = 0 && usage ;# we need at least one remote head. set_reflog_action "merge $[join(ARGV)]" setvar remoteheads = ''for remote in @ARGV { setvar remotehead = $(git rev-parse --verify "$remote"^0 2>/dev/null) || die "$remote - not something we can merge" setvar remoteheads = ""${remoteheads}$remotehead "" eval GITHEAD_$remotehead='"$remote"' export GITHEAD_$remotehead } set x $remoteheads ; shift case (use_strategies) { '' { case (#) { 1 { setvar var = "$(git config --get pull.twohead)" if test -n $var { setvar use_strategies = "$var" } else { setvar use_strategies = "$default_twohead_strategies" } } * { setvar var = "$(git config --get pull.octopus)" if test -n $var { setvar use_strategies = "$var" } else { setvar use_strategies = "$default_octopus_strategies" } } } } } for s in $use_strategies { for ss in $no_fast_forward_strategies { case{ *" $ss "* { setvar allow_fast_forward = 'f' break } } } for ss in $no_trivial_strategies { case{ *" $ss "* { setvar allow_trivial_merge = 'f' break } } } } case (#) { 1 { setvar common = $(git merge-base --all $head "$@") } * { setvar common = $(git merge-base --all --octopus $head "$@") } } echo $head >"$GIT_DIR/ORIG_HEAD" case{ ?,*,'',* { # No common ancestors found. We need a real merge. } ?,1,"$1",* { # If head can reach all the merge then we are up to date. # but first the most common case of merging one remote. finish_up_to_date "Already up-to-date." exit 0 } t,1,"$head",* { # Again the most common case of merging one remote. echo "Updating $(git rev-parse --short $head)..$(git rev-parse --short $1)" git update-index --refresh 2>/dev/null setvar msg = ""Fast-forward"" if test -n $have_message { setvar msg = ""$msg (no commit created; -m option ignored)"" } setvar new_head = $(git rev-parse --verify "$1^0") && git read-tree -v -m -u --exclude-per-directory=.gitignore $head $new_head && finish $new_head $msg || exit dropsave exit 0 } ?,1,?*"$LF"?*,* { # We are not doing octopus and not fast-forward. Need a # real merge. } ?,1,*, { # We are not doing octopus, not fast-forward, and have only # one common. git update-index --refresh 2>/dev/null case{ t, { # See if it is really trivial. git var GIT_COMMITTER_IDENT >/dev/null || exit echo "Trying really trivial in-index merge..." if git read-tree --trivial -m -u -v $common $head $1 && setvar result_tree = $(git write-tree) { echo "Wonderful." setvar result_commit = $( printf '%s\n' "$merge_msg" | git commit-tree $result_tree -p HEAD -p "$1" ) || exit finish $result_commit "In-index merge" dropsave exit 0 } echo "Nope." } } } * { # An octopus. If we can reach all the remote we are up to date. setvar up_to_date = 't'for remote in @ARGV { setvar common_one = $(git merge-base --all $head $remote) if test $common_one != $remote { setvar up_to_date = 'f' break } } if test $up_to_date = t { finish_up_to_date "Already up-to-date. Yeeah!" exit 0 } } } if test $fast_forward_only = t { die "Not possible to fast-forward, aborting." } # We are going to make a new commit. git var GIT_COMMITTER_IDENT >/dev/null || exit # At this point, we need a real merge. No matter what strategy # we use, it would operate on the index, possibly affecting the # working tree, and when resolved cleanly, have the desired tree # in the index -- this means that the index must be in sync with # the $head commit. The strategies are responsible to ensure this. case (use_strategies) { ?*' '?* { # Stash away the local changes so that we can try more than one. savestate setvar single_strategy = 'no' } * { rm -f "$GIT_DIR/MERGE_STASH" setvar single_strategy = 'yes' } } setvar result_tree = '', best_cnt = '-1', best_strategy = '', wt_strategy = '' setvar merge_was_ok = '' for strategy in $use_strategies { test $wt_strategy = '' || do { echo "Rewinding the tree to pristine..." restorestate } case (single_strategy) { no { echo "Trying merge strategy $strategy..." } } # Remember which strategy left the state in the working tree setvar wt_strategy = "$strategy" eval 'git-merge-$strategy '"$xopt"' $common -- "$head_arg" "$@"' setvar exit = ""$? if test $no_commit = t && test $exit = 0 { setvar merge_was_ok = 't' setvar exit = '1' ;# pretend it left conflicts. } test $exit = 0 || do { # The backend exits with 1 when conflicts are left to be resolved, # with 2 when it does not handle the given merge at all. if test $exit -eq 1 { setvar cnt = $({ git diff-files --name-only git ls-files --unmerged } | wc -l) if test $best_cnt -le 0 || test $cnt -le $best_cnt { setvar best_strategy = "$strategy" setvar best_cnt = "$cnt" } } continue } # Automerge succeeded. setvar result_tree = $(git write-tree) && break } # If we have a resulting tree, that means the strategy module # auto resolved the merge cleanly. if test '' != $result_tree { if test $allow_fast_forward = "t" { setvar parents = $(git merge-base --independent "$head" "$@") } else { setvar parents = $(git rev-parse "$head" "$@") } setvar parents = $(echo "$parents" | sed -e 's/^/-p /') setvar result_commit = $(printf '%s\n' "$merge_msg" | git commit-tree $result_tree $parents) || exit finish $result_commit "Merge made by $wt_strategy." dropsave exit 0 } # Pick the result from the best strategy and have the user fix it up. case (best_strategy) { '' { restorestate case (use_strategies) { ?*' '?* { echo >&2 "No merge strategy handled the merge.>&2 "No merge strategy handled the merge." } * { echo >&2 "Merge with strategy $use_strategies failed.>&2 "Merge with strategy $use_strategies failed." } } exit 2 } "$wt_strategy" { # We already have its result in the working tree. } * { echo "Rewinding the tree to pristine..." restorestate echo "Using the $best_strategy to prepare resolving by hand." git-merge-$best_strategy $common -- $head_arg @ARGV } } if test $squash = t { finish } else {for remote in @ARGV { echo $remote } >"$GIT_DIR/MERGE_HEAD" printf '%s\n' $merge_msg >"$GIT_DIR/MERGE_MSG" || die "Could not write to $GIT_DIR/MERGE_MSG" if test $allow_fast_forward != t { printf "%s" no-ff } else { : } >"$GIT_DIR/MERGE_MODE" || die "Could not write to $GIT_DIR/MERGE_MODE" } if test $merge_was_ok = t { echo >&2 \ "Automatic merge went well; stopped before committing as requested>&2 \ "Automatic merge went well; stopped before committing as requested" exit 0 } else { do { echo ' Conflicts: ' git ls-files --unmerged | sed -e 's/^[^ ]* / /' | uniq } >>"$GIT_DIR/MERGE_MSG" git rerere $rr_arg die "Automatic merge failed; fix conflicts and then commit the result." }