See the documentation for an explanation of this feature. Signed-off-by: Sverre Hvammen Johansen <hvammen@xxxxxxxxx> --- Documentation/git-merge.txt | 43 +++++++++++++++++++++++- git-merge.sh | 76 +++++++++++++++++++++++++++++-------------- 2 files changed, 93 insertions(+), 26 deletions(-) diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt index 2af33d8..e94d26b 100644 --- a/Documentation/git-merge.txt +++ b/Documentation/git-merge.txt @@ -36,7 +36,7 @@ include::merge-options.txt[] <remote>:: Other branch head merged into our branch. You need at least one <remote>. Specifying more than one <remote> - obviously means you are trying an Octopus. + usually means you are trying an Octopus. include::fast-forward-options.txt[] @@ -133,6 +133,47 @@ merge (which is typically a fraction of the whole tree), you can have local modifications in your working tree as long as they do not overlap with what the merge updates. +If more than one commit are specified for the merge, git will try to +reduce the number of commits (real parents) by eliminating commits +than can be reached from other commits. The commit message will +reflect the actual commits specified but the merge strategy will be +selected based on the real parents, but always including `HEAD`. The +real parents (only including `HEAD` if it is real) are the parents +recorded in the merge commit object. + +The following shows master and three topic branches. topicB is based +on topicA, topicA is previously branched off from master, and topicC +is based on the current `HEAD` of master: + +------------ + o---o---o topicB + / + o---o---o topicA + / + o---o---o---o---o---o master + \ + o---o topicC +------------ + +A merger of master with topicA, topicB, and topicC will select the +merge strategy based on the three branches master, topicB, and topicC +(topicA is eliminated since it can be reached from topicB). topicB +and topicC are the only real parents and are therefore the only +parents recorded in the merge commit object: + +------------ + % git checkout master + % git merge topicA topicB topicC + + o---o---o topicB + / \ + o---o---o topicA \ + / \ + o---o---o---o---o---o o master + \ / + o---o topicC +------------ + When there are conflicts, these things happen: 1. `HEAD` stays the same. diff --git a/git-merge.sh b/git-merge.sh index 2acd2cc..5398606 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -209,24 +209,41 @@ parse_config () { # Find real parents # Set the following variables as followd: -# real_parents: The parents specified on the command line +# real_parents: The real parents except fast forward of head # common: All common ancestors or not_queried # ff_head: Fast forward of head find_real_parents () { - real_parents=$(git rev-parse "$@") - real_parents=${real_parents#$LF} - if test $# = 1 + if test $fast_forward = never then - common=$(git merge-base --all $head "$@") - if test "$common" = $head + real_parents=$(git rev-parse "$@") + ff_head=$head + common=not_queried + else + if test $# = 1 then - ff_head=$1 + common=$(git merge-base --all $head "$1") + if test "$common" = $head + then + real_parents= + ff_head=$1 + elif test "$common" = "$1" + then + real_parents= + ff_head=$head + else + real_parents=$1 + ff_head=$head + + fi else - ff_head=$head + real_parents=$(git show-branch --independent $head "$@") + # Here we may actually lie about which bransh is ff of head. + # This will preserve the order the user gave. + ff_head=${real_parents%%$LF*} + real_parents=${real_parents#$ff_head} + real_parents=${real_parents#$LF} + common=not_queried fi - else - common=not_queried - ff_head=$head fi } @@ -319,6 +336,12 @@ set x $remoteheads ; shift find_real_parents "$@" +if test -n "$real_parents" +then + test $head = $ff_head || + real_parents="$ff_head$LF$real_parents" +fi + case "$use_strategies" in '') case "$real_parents" in @@ -366,13 +389,13 @@ done echo "$head" >"$GIT_DIR/ORIG_HEAD" -if true +if test -z "$real_parents" then - if test $head = $ff_head -a "$common" = "$real_parents" + if test $head = $ff_head then finish_up_to_date "Already up-to-date." exit 0 - elif test $fast_forward != never -a $ff_head = "$real_parents" + elif test $fast_forward != never then echo "Updating $(git rev-parse --short $head)..$(git rev-parse --short $ff_head)" git update-index --refresh 2>/dev/null @@ -386,6 +409,14 @@ then finish "$new_head" "$msg" || exit dropsave exit 0 + else + real_parents="$ff_head" + ff_head=$head + fi +else + if test $head != $ff_head -a $fast_forward = never + then + real_parents="$ff_head$LF$real_parents" fi fi @@ -500,17 +531,12 @@ done # auto resolved the merge cleanly. if test '' != "$result_tree" then - if test $fast_forward = allow - then - parents=$(git show-branch --independent "$head" "$@") - else - parents=$(git rev-parse "$head" "$@") - fi - parents=$(echo "$parents" | sed -e 's/^/-p /') - 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 + test $head = $ff_head && real_parents="$head$LF$real_parents" + parents=$(echo "$real_parents" | sed -e 's/^/-p /') + 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 fi # Pick the result from the best strategy and have the user fix it up. -- Sverre Hvammen Johansen -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html