Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Git - Same file with different versions on two branches, when merge do not want to overwrite

Tags:

We have two branches -- 'master' and 'release'

And we have one file, like fileA, we want to keep different version on these two branches.

But each time, we have to merge 'release' into 'master', how can we achieve, the fileA in 'master' will not be overwritten by the fileA in branch 'release'.

like image 964
LongYang0806 Avatar asked Feb 24 '15 08:02

LongYang0806


People also ask

Does git merge overwrite changes?

Usually git does not overwrite anything during merge.

What happens when the same line was modified in two branches and then we try to Marge those branches?

If you change same line in 2 different branches, git asks you to manually solve the conflict because git can not decide which one to select. It is up to you to how to solve conflicts. If you change same line in 2 different branches but the changes are exactly the same, git doesn't create a conflict.

Does merging affect both branches?

So merging one branch into another has a secondary effect: it moves the merge base of those two branches. In particular, the new merge base of the two branches is now the second parent of the merge commit. In many situations, that fact will not matter to you.

Does merging two branches delete?

The more the branches and master diverge away from each other the farther away their “common ancestor” commit becomes. When you're done with a branch and it has been merged into master, delete it. A new branch can be made off of the most recent commit on the master branch.


1 Answers

Pro Git describes how to get this effect in the “Merge Strategies” section of 8.2 Customizing Git — Git Attributes.

Merge Strategies

You can also use Git attributes to tell Git to use different merge strategies for specific files in your project. One very useful option is to tell Git to not try to merge specific files when they have conflicts, but rather to use your side of the merge over someone else’s.

This is helpful if a branch in your project has diverged or is specialized, but you want to be able to merge changes back in from it, and you want to ignore certain files. Say you have a database settings file called database.xml that is different in two branches, and you want to merge in your other branch without messing up the database file. You can set up an attribute like this:

database.xml merge=ours 

And then define a dummy ours merge strategy with:

$ git config --global merge.ours.driver true 

If you merge in the other branch, instead of having merge conflicts with the database.xml file, you see something like this:

$ git merge topic Auto-merging database.xml Merge made by recursive. 

In this case, database.xml stays at whatever version you originally had.

Applying it to your situation, first create fileA

$ echo 'master fileA' > fileA $ git add fileA ; git commit -m "master fileA" [master (root-commit) fba9f1a] master fileA  1 files changed, 1 insertions(+), 0 deletions(-)  create mode 100644 fileA 

and make it special.

$ echo fileA merge=ours > .gitattributes $ git add .gitattributes ; git commit -m 'fileA merge=ours' [master 98e056f] fileA merge=ours  1 files changed, 1 insertions(+), 0 deletions(-)  create mode 100644 .gitattributes $ git config --global merge.ours.driver true 

Now we create a representative release branch.

$ git checkout -b release Switched to a new branch 'release' $ echo 'release fileA' > fileA $ git add fileA ; git commit -m 'release fileA' [release 53f3564] release fileA  1 files changed, 1 insertions(+), 1 deletions(-) 

Nothing special has happened yet: version control is merely working as it’s supposed to at this point.

Now, we implement Feature B back on master.

$ git checkout master Switched to branch 'master' $ touch featureB ; echo 'With Feature B' >> fileA $ git add featureB fileA ; git commit -m 'Feature B' [master 443030f] Feature B  1 files changed, 1 insertions(+), 0 deletions(-)  create mode 100644 featureB 

Try to contain your excitement.

Here is where our special merge driver comes into play. Our hero wants to merge the new code from master into release. First switch branches.

$ git checkout release Switched to branch 'release' 

Sanity check that fileA contains what we expect.

$ cat fileA release fileA 

Merge Feature B from master.

$ git merge master Auto-merging fileA Merge made by recursive.  0 files changed, 0 insertions(+), 0 deletions(-)  create mode 100644 featureB 

The line Auto-merging fileA is a clue that something special happened. Indeed:

$ cat fileA release fileA 

How It Works

The section “Defining a custom merge driver” in the gitattributes documentation explains.

The merge.*.driver variable’s value is used to construct a command to run to merge ancestor’s version (%O), current version (%A) and the other branches' version (%B). These three tokens are replaced with the names of temporary files that hold the contents of these versions when the command line is built …

The merge driver is expected to leave the result of the merge in the file named with %A by overwriting it, and exit with zero status if it managed to merge them cleanly, or non-zero if there were conflicts.

The custom ours driver uses almost none of this machinery, only the true command to exit with zero status. This achieves the desired effect because it starts with fileA from whichever branch we’re currently on — which is the result we want — then does nothing in the merge-overwrite phase (i.e., ignores the other branch’s version named by %B), and finally tells git all is well with a successful exit status.

like image 93
Greg Bacon Avatar answered Oct 08 '22 14:10

Greg Bacon