Change direction of git merge (after the merge is done) -
i have branch (e.g. feature-x) in git repo.
what did following
git checkout master git merge feature-x
there quite few conflicts resolved. haven't committed changes yet.
but, turns out wanted reverse merge (i.e. merge master feature-x) because branch still unstable.
is there command can salvage work did in resolving conflicts or need resolution once more, time in branch feature-x?
i'm thinking whether there way current patch apply in branch feature-x in "reverse" order. e.g. when patch says line x changed y, relative feature-x going master. if want opposite, patch should line y changed x, right? thoughts. solution ok.
tl;dr: see end
there "recipe" @ end; scroll down find (and bit find description , explanation , safer method).
good news , bad news
in significant sense, merges don't have "direction". (the direction "see" when @ merge product of imagination, plus merge commit's message, plus 1 more important thing our "bad news"—but it's not fatally bad, in end.)
consider diagram of pair of branches commit *
merge base:
o--o--...--o <-- branch1 / ...--o--* \ o--o--...--o <-- branch2
the process of merging ("merge verb") branch1
, branch2
achieved by:
- comparing, i.e.,
git diff
, merge base commit*
tip commit ofbranch1
; - comparing merge base tip of
branch2
; - then, starting base, combining both sets of changes.
hence actual merge should this:
o--o--...--o / \ ...--o--* m \ / o--o--...--o
(i named merge commit m
since don't know, or care, crazy git hash id deadbeef
or badc0ffee
or whatever have.) eventual merge commit m
is merge ("merge noun"); stores final source tree, based on merge-as-a-verb combining process.
which "direction" merge? well, depends, @ least in part, on labels stick on it, doesn't it? :-) note stripped away both branch1
, branch2
. let's put them back:
o--o--...--o <-- branch1? / \ ...--o--* m <-- branch1? branch2? \ / o--o--...--o <-- branch2?
this might confusing. is confusing. it's big part of whole issue. the bad news is, it's not whole thing. there's cannot quite capture in these drawings. may not matter you, , if does, it's not big deal anyway; we'll fix making 1 more merge commit. now, let's press on.
making merge commit
once make merge commit m
itself, have choose—or have git choose-which branch it's on. in simple, unconflicted merge, end having git choose this. git does simple: whatever branch on, when start git merge
command, branch gets merge commit. so:
git checkout branch1 git merge branch2
means final picture is:
o--o--...--o / \ ...--o--* m <-- branch1 \ / o--o--...--o <-- branch2
which can re-draw as:
...--o--*--o--...--o--m <-- branch1 \ / o---...---o <-- branch2
which makes obvious merged branch2 branch1, not vice versa. nonetheless, source code attached commit m
not depend on "merge direction".
sidebar: conflicted merges
conflicted merges unconflicted merges in terms of final result. it's git can't merge on own. stops , makes you resolve conflicts, , run git commit
make merge commit. final git commit
step makes merge commit m
.
the new merge commit goes on current branch, other commit. that's how m
winds on branch1
. note merge commit's message says branch name merged other branch name—but have chance edit while make commit, can change suit whatever whim may have. :-)
(in fact, no different unconflicted case: both run git commit
make final merge commit, , both give chance edit message.)
the bad news
the bad news these diagrams omit may matter you. git has notion of first parent: merge commit m
has 2 parents, 1 of these first parent , 1 not.1 first parent how tell branch on when made merge.
hence, while these diagrams, , merge-as-a-verb process, show there isn't "merge direction", first-parent notion proves there is. if ever use first-parent notion, care this, , want right. fortunately, there solution. (in fact, there multiple solutions, i'll show klunky-but-straightforward ones first.)
1obviously, other 1 second parent: there 2 parents. git allows, however, merge commits 3 or more parents. can enumerate parents whenever have to; git considers first important, , has --first-parent
flags various commands, follow "the original branch".
getting merge want
let's go ahead , make "wrong" merge commit:
# git add ... (if needed) git commit
now have:
o--o--...--o <-- [old branch1 tip] / \ ...--o--* m <-- branch1 \ / o--o--...--o <-- branch2
where m
's first parent old branch1
tip.
what want make new merge commit (with different id) that's same m
except that:
branch2
points it- its first parent tip of
branch2
- its second parent previous tip of
branch1
the trick save commit m
somehow—that's easy enough, we'll use temporary name don't have copy down m
's hash id—and reset branch1
:
git branch temp # or git tag temp git reset --hard head~1 # move branch1 back, dropping m
(note same brehonia's answer, far). have graph, quite unchanged except labels attached each commit:
o--o--...--o <-- branch1 / \ ...--o--* m <-- temp \ / o--o--...--o <-- branch2
now check out branch2
, run merge on again; fail conflicts before:
git checkout branch2 git merge branch1 # use --no-commit if git commit merge
the commit have not yet made same, in sense, merge commit m
—but once make it, first parent current tip of branch2
, , second parent current tip of branch1
. need right source go commit we're going make.
now use merge result saved name temp
. assumes you're in top level of tree, .
names everything:
git rm -rf -- . # remove git checkout temp -- . # temp
we using merge result committed earlier. not have git add
form of git checkout
updates index, so:
git commit
and have our new merge m2
, on branch branch2
, desired:
o--o--...--o__ <-- branch1 / \ \ ...--o--* m \ <-- temp \ / \ o--o--...--o-----m2 <-- branch2
now can delete temporary branch or tag:
git branch -d temp # or git tag -d temp
and done. temporary name gone, can't see original merge m
more.
sneaky plumbing method
there's shorter way this, using git's "plumbing" commands, it's bit tricky. once have git add
-ed , have run git commit
, have right tree, have commit has wrong first , second parent ids. may wish edit commit message well, looks you're merging branch1 branch2 in message. then:
# git add ... (if / needed, above) # git commit (make merge) git branch -f branch2 $(git log --pretty=format:%b --no-walk head | git commit-tree -p head^2 -p head^1 -f -) git reset --hard head^1
the git commit-tree
step makes new merge commit that's copy of 1 made, 2 parents swapped. make branch2
point commit, using git branch -f
. be sure name (branch2
) right @ step. head^1
, head^2
names refer 2 (first , second) parents of current commit, of course merge commit made. has right tree, , right commit message text; has wrong first , second parent hashes.
once have new commit safely added branch2
, reset our current (branch1
) branch remove merge commit made.
Comments
Post a Comment