Home > Mobile >  How to avoid broken part when git merging branches?
How to avoid broken part when git merging branches?

Time:10-28

I imported an open Git repository (upstream/master) into my own repository (origin/master) and develop on the origin/master. Because upstream/master is active, lots of commits have been pushed into the branch since I cloned the repo; upstream/master and branch/master would look like below.

--o--o--M--...--N---...--O-----o   <- upstream/master
   \        
    A------B------C----- ... --D   <- branch/master

And if I merged the upstream into my master branch, it would look like the following:

--o--o--M--...--N---...--O-----o   <- upstream/master
   \        
    A---M--B----N-C------O--...--D <- master(upstream merged)

However, as the commits are interleaved when merging, if the commit N broke my master branch, the commits C-D would be broken. Although I could fix the branch with a new commit, the C-D part would be still broken.

How do I avoid the broken part? Or is there any best practice for this kind of situation?

CodePudding user response:

A merge doesn't rewrite any existing history, and it would actually be really difficult to create an "interleaved" history like you show in git.

If you start with this:

--X-----M-----N-----O-----P   <- upstream
   \        
    A------B------C------D   <- mybranch

Then a straight-forward merge of upstream into mybranch would create a new "merge commit", which:

  • Applies the changes both branches have made since their last common ancestor (marked X)
  • Has two "parent" commits, P and D, which is how git records history

So the result looks like this:

--X-----M-----N-----O-------P   <- upstream
   \                         \
    A------B------C------D----E   <- mybranch

From git's point of view, all the commits shown are "ancestors" of commit E, regardless of which branch they were on, but there is no direct relationship between, say C and N.

There is a way to merge code that recreates older changes, called "rebasing", which looks at each change you made, and creates a new commit pretending you made that change on top of P instead of X. It doesn't "interleave" commits, though, it puts all the new ones in order after the point you "rebase onto", giving something like this:

--X-----M-----N-----O-----P   <- upstream
                           \
                            A1------B1------C1------D1   <- mybranch

(It's tempting to think that commits A to E have "moved", but commits in git are immutable, and represent the state of the whole code, not a change, so commits A1 to E1 have to be created new by the "rebase" machinery.)

  •  Tags:  
  • git
  • Related