Re: [RFC/PATCH 3/4] Head reduction before selecting merge strategy

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux