Git - 两个分支上不同版本的相同文件,合并时不想覆盖

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

我们有两个分支 -- 'master' 和 'release'

我们有一个文件,比如fileA,我们想在这两个分支上保持不同的版本。

但是每次都要将'release'合并到'master',如何实现'master'中的fileA不会被[=覆盖11=] 在分支 'release'.

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.

将其应用到您的情况,首先创建 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

让它变得特别。

$ 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

现在我们创建一个代表 release 分支。

$ 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(-)

还没有发生什么特别的事情:版本控制目前只是在按预期工作。

现在,我们在 master.

上实施 功能 B
$ 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

尽量克制自己的兴奋。

这是我们特殊的合并驱动程序发挥作用的地方。我们的主人公想要将 master 中的新代码合并到 release 中。首先切换分支。

$ git checkout release
Switched to branch 'release'

健全性检查 fileA 包含我们期望的内容。

$ cat fileA
release fileA

master.

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

Auto-merging fileA 行是一个线索,表明发生了一些特别的事情。确实:

$ cat fileA
release fileA

工作原理

“Defining a custom merge driver” in the gitattributes documentation 部分解释。

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.

自定义 ours 驱动程序使用了几乎 none 的这台机器,只有 true command 以零状态退出。这达到了预期的效果,因为它从我们当前所在的任何分支的 fileA 开始——这是我们想要的结果——然后在合并覆盖阶段什么都不做(, 忽略由 %B) 命名的另一个分支的版本,最后告诉 git 一切都很好,退出状态成功。