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 of branch1;
  • 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

Popular posts from this blog

commonjs - How to write a typescript definition file for a node module that exports a function? -

openid - Okta: Failed to get authorization code through API call -

ios - Change Storyboard View using Seague -