我怎么告诉git总是选择我的本地版本来解决特定文件上的冲突合并?

时间:2020-11-30 07:08:52

Say I'm collaborating with someone via a git repository, and there is a particular file that I never want to accept any external changes to.

假设我通过git存储库与某人合作,并且有一个特定的文件,我永远不想接受任何外部更改。

Is there any way for me to set up my local repo to not complain about a conflicted merge every time I git pull? I'd like to always select my local version when merging this file.

有没有办法让我设置我的本地仓库,以便每当我git pull时不抱怨冲突的合并?我想在合并此文件时始终选择我的本地版本。

2 个解决方案

#1


On the specific instance of a config file, I would agree with Ron's answer:
a config should be "private" to your workspace (hence "ignored", as in "declared in a .gitignore file").
You may have a config file template with tokenized values in it, and a script transforming that config.template file into a private (and ignored) config file.

在配置文件的特定实例上,我同意Ron的回答:配置应该对您的工作区是“私有的”(因此“被忽略”,如“在.gitignore文件中声明”)。您可能有一个带有标记化值的配置文件模板,以及一个将该config.template文件转换为私有(并忽略)配置文件的脚本。


However, that specific remark does not answer what is a broader more general question, i.e. your question(!):

但是,这个具体的评论并没有回答更广泛的更普遍的问题,即你的问题(!):

How do I tell git to always select my local version for conflicted merges on a specific file ? (for any file or group of file)

我怎么告诉git总是选择我的本地版本来解决特定文件上的冲突合并? (对于任何文件或文件组)

This kind of merge is a "copy merge", in which you will always copy 'ours' or 'theirs' version of a file whenever there is a conflict.

这种合并是一种“复制合并”,在发生冲突时,您将始终复制文件的“我们的”或“他们的”版本。

(as Brian Vandenberg notes in the comments, 'ours' and 'theirs' are here used for a merge.
They are reversed for a rebase: see "Why is the meaning of “ours” and “theirs” reversed with git-svn", which uses a rebase, "git rebase, keeping track of 'local' and 'remote'")

(正如Brian Vandenberg在评论中指出的那样,“我们的”和“他们的”在这里被用于合并。他们被反转为一个变化:看“为什么”我们的“和”他们的“的含义与git-svn相反” ,使用rebase,“git rebase,跟踪'本地'和'远程'”)

For "a file" (a file in general, not speaking of a "config" file, since it is a bad example), you would achieve that with a custom script called through merges.
Git will call that script because you will have define a gitattributes value, which defines a custom merge driver.

对于“一个文件”(一般来说,不是说“配置”文件,因为它是一个不好的例子),你可以通过合并调用自定义脚本来实现。 Git将调用该脚本,因为您将定义一个gitattributes值,该值定义自定义合并驱动程序。

The "custom merge driver" is, in this case, a very simple script which basically will keep unchanged the current version, hence allowing you to always select your local version.

在这种情况下,“自定义合并驱动程序”是一个非常简单的脚本,它基本上将保持当前版本不变,因此允许您始终选择本地版本。


Let's test that in a simple scenario, with a msysgit 1.6.3 on Windows, in a mere DOS session:

让我们在一个简单的场景中测试,在Windows上使用msysgit 1.6.3,在一个简单的DOS会话中:

cd f:\prog\git\test
mkdir copyMerge\dirWithConflicts
mkdir copyMerge\dirWithCopyMerge
cd copyMerge
git init
Initialized empty Git repository in F:/prog/git/test/copyMerge/.git/

Now, let's make two files, which will both have conflicts, but which will be merged differently.

现在,让我们制作两个文件,这两个文件都会有冲突,但会以不同的方式合并。

echo a > dirWithConflicts\a.txt
echo b > dirWithCopyMerge\b.txt
git add -A
git commit -m "first commit with 2 directories and 2 files"
[master (root-commit) 0adaf8e] first commit with 2 directories and 2 files

We will introduce a "conflict" in the content of both those files in two different git branches:

我们将在两个不同的git分支中的这两个文件的内容中引入“冲突”:

git checkout -b myBranch
Switched to a new branch 'myBranch'
echo myLineForA >> dirWithConflicts\a.txt
echo myLineForB >> dirWithCopyMerge\b.txt
git add -A
git commit -m "add modification in myBranch"
[myBranch 97eac61] add modification in myBranch

git checkout master
Switched to branch 'master'
git checkout -b hisBranch
Switched to a new branch 'hisBranch'
echo hisLineForA >> dirWithConflicts\a.txt
echo hisLineForB >> dirWithCopyMerge\b.txt
git add -A
git commit -m "add modification in hisBranch"
[hisBranch 658c31c] add modification in hisBranch

Now, let's try to merge "hisBranch" upon "myBranch", with:

现在,让我们尝试将“hisBranch”合并到“myBranch”上,并:

  • manual resolution for conflicting merges
  • 冲突合并的手动解决方案

  • except for dirWithCopyMerge\b.txt where I always want to keep my version of b.txt.
  • 除了dirWithCopyMerge \ b.txt,我总是想保留我的b.txt版本。

Since the merge occurs in 'MyBranch', we will switch back to it, and add the 'gitattributes' directives which will customize the merge behavior.

由于合并发生在'MyBranch'中,我们将切换回它,并添加'gitattributes'指令,这将指定合并行为。

git checkout myBranch
Switched to branch 'myBranch'
echo b.txt merge=keepMine > dirWithCopyMerge\.gitattributes
git config merge.keepMine.name "always keep mine during merge"
git config merge.keepMine.driver "keepMine.sh %O %A %B"
git add -A
git commit -m "prepare myBranch with .gitattributes merge strategy"
[myBranch ec202aa] prepare myBranch with .gitattributes merge strategy

We have a .gitattributes file defined in the dirWithCopyMerge directory (defined only in the branch where the merge will occurs: myBranch), and we have a .git\config file which now contains a merge driver.

我们在dirWithCopyMerge目录中定义了一个.gitattributes文件(仅在发生合并的分支中定义:myBranch),我们有一个.git \ config文件,现在包含一个合并驱动程序。

[merge "keepMine"]
        name = always keep mine during merge
        driver = keepMine.sh %O %A %B

If you do not yet define keepMine.sh, and launch the merge anyway, here is what you get.

如果你还没有定义keepMine.sh,并且无论如何都要启动合并,这就是你得到的。

git merge hisBranch
sh: keepMine.sh: command not found
fatal: Failed to execute internal merge
git st
# On branch myBranch
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   dirWithConflicts/a.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

type dirWithConflicts\a.txt
a
<<<<<<< HEAD:dirWithConflicts/a.txt
myLineForA
=======
hisLineForA
>>>>>>> hisBranch:dirWithConflicts/a.txt

That is fine:

那样就好:

  • a.txt is ready to be merged and has conflict in it
  • a.txt已准备好合并,并且存在冲突

  • b.txt is still untouched, since the merge driver is supposed to take care of it (due to the directive in the .gitattributes file in its directory).
  • b.txt仍未触及,因为合并驱动程序应该处理它(由于其目录中的.gitattributes文件中的指令)。

Define a keepMine.sh anywhere in your %PATH% (or $PATH for our Unix friend. I do both of course: I have an Ubuntu session in a VirtualBox session)

在%PATH%(或者我们的Unix好友的$ PATH)中的任何位置定义keepMine.sh。我当然都这样做:我在VirtualBox会话中有一个Ubuntu会话)

As commented by lrkwz, and described in the "Merge Strategies" section of Customizing Git - Git Attributes, you can replace the shell script with the shell command true.

如lrkwz所评论,并在Customizing Git - Git Attributes的“Merge Strategies”部分中描述,您可以使用shell命令true替换shell脚本。

git config merge.keepMine.driver true

But in the general case, you can define a script file:

但在一般情况下,您可以定义脚本文件:

keepMine.sh

# I want to keep MY version when there is a conflict
# Nothing to do: %A (the second parameter) already contains my version
# Just indicate the merge has been successfully "resolved" with the exit status
exit 0

(that was one simple merge driver ;) (Even simpler in that case, use true)
(If you wanted to keep the other version, just add before the exit 0 line:
cp -f $3 $2.
That's it. You merge driver would aways keep the version coming from the other branch, overriding any local change)

(这是一个简单的合并驱动程序;)(在这种情况下甚至更简单,使用true)(如果你想保留其他版本,只需在退出0行之前添加:cp -f $ 3 $ 2.就是这样。你合并驱动程序会aways保持版本来自其他分支,覆盖任何本地更改)

Now, let's retry the merge from the beginning:

现在,让我们从头开始重试合并:

git reset --hard
HEAD is now at ec202aa prepare myBranch with .gitattributes merge strategy

git merge hisBranch
Auto-merging dirWithConflicts/a.txt
CONFLICT (content): Merge conflict in dirWithConflicts/a.txt
Auto-merging dirWithCopyMerge/b.txt
Automatic merge failed; fix conflicts and then commit the result.

The merge fails... only for a.txt.
Edit a.txt and leave the line from 'hisBranch', then:

合并失败...仅适用于a.txt。编辑a.txt并从'hisBranch'中保留该行,然后:

git add -A
git commit -m "resolve a.txt by accepting hisBranch version"
[myBranch 77bc81f] resolve a.txt by accepting hisBranch version

Let's check that b.txt has been preserved during this merge

让我们检查一下这个合并期间是否保留了b.txt

type dirWithCopyMerge\b.txt
b
myLineForB

The last commit does represent the full merge:

最后一次提交确实代表完全合并:

git show -v 77bc81f5e
commit 77bc81f5ed585f90fc1ca5e2e1ddef24a6913a1d
Merge: ec202aa 658c31c
git merge hisBranch
Already up-to-date.

(The line beginning with Merge does prove that)

(从Merge开始的行证明了这一点)


Consider you can define, combine and/or overwrite merge driver, as Git will:

考虑你可以定义,组合和/或覆盖合并驱动程序,因为Git将:

  • examine <dir>/.gitattributes (which is in the same directory as the path in question): will prevail upon the other .gitattributes in directories
  • 检查

    / .gitattributes(与相关路径位于同一目录中):将占用目录中的另一个.gitattributes

  • Then it examines .gitattributes (which is in the parent directory), will only set directives if not already set
  • 然后它检查.gitattributes(在父目录中),只会设置指令(如果尚未设置)

  • Finally it examines $GIT_DIR/info/attributes. This file is used to override the in-tree settings. It will overwrite <dir>/.gitattributes directives.
  • 最后,它检查了$ GIT_DIR / info / attributes。此文件用于覆盖树内设置。它将覆盖

    / .gitattributes指令。

By "combining", I mean "aggregate" multiple merge driver.
Nick Green tries, in the comments, to actually combine merge drivers: see "Merge pom's via python git driver".
However, as mentioned in his other question, it only works in case of conflicts (concurrent modification in both branches).

通过“组合”,我的意思是“聚合”多个合并驱动程序。 Nick Green在评论中尝试实际组合合并驱动程序:请参阅“通过python git驱动程序合并pom”。但是,正如他在另一个问题中提到的,它只适用于冲突(两个分支中的并发修改)。

#2


We have multiple config files that we never want overwritten. However .gitignore and .gitattributes did not work in our situation. Our solution was to store the config files in a configs branch. Then, allow the files to be changed during the git merge, but immediately following the merge use the "git checkout branch -- ." to copy our config files from the configs branch after every merge. Detailed * answer here

我们有多个配置文件,我们永远不会想要覆盖。但是.gitignore和.gitattributes在我们的情况下不起作用。我们的解决方案是将配置文件存储在configs分支中。然后,允许在git合并期间更改文件,但在合并之后立即使用“git checkout branch - ”。每次合并后从configs分支复制我们的配置文件。详细的*回答这里

#1


On the specific instance of a config file, I would agree with Ron's answer:
a config should be "private" to your workspace (hence "ignored", as in "declared in a .gitignore file").
You may have a config file template with tokenized values in it, and a script transforming that config.template file into a private (and ignored) config file.

在配置文件的特定实例上,我同意Ron的回答:配置应该对您的工作区是“私有的”(因此“被忽略”,如“在.gitignore文件中声明”)。您可能有一个带有标记化值的配置文件模板,以及一个将该config.template文件转换为私有(并忽略)配置文件的脚本。


However, that specific remark does not answer what is a broader more general question, i.e. your question(!):

但是,这个具体的评论并没有回答更广泛的更普遍的问题,即你的问题(!):

How do I tell git to always select my local version for conflicted merges on a specific file ? (for any file or group of file)

我怎么告诉git总是选择我的本地版本来解决特定文件上的冲突合并? (对于任何文件或文件组)

This kind of merge is a "copy merge", in which you will always copy 'ours' or 'theirs' version of a file whenever there is a conflict.

这种合并是一种“复制合并”,在发生冲突时,您将始终复制文件的“我们的”或“他们的”版本。

(as Brian Vandenberg notes in the comments, 'ours' and 'theirs' are here used for a merge.
They are reversed for a rebase: see "Why is the meaning of “ours” and “theirs” reversed with git-svn", which uses a rebase, "git rebase, keeping track of 'local' and 'remote'")

(正如Brian Vandenberg在评论中指出的那样,“我们的”和“他们的”在这里被用于合并。他们被反转为一个变化:看“为什么”我们的“和”他们的“的含义与git-svn相反” ,使用rebase,“git rebase,跟踪'本地'和'远程'”)

For "a file" (a file in general, not speaking of a "config" file, since it is a bad example), you would achieve that with a custom script called through merges.
Git will call that script because you will have define a gitattributes value, which defines a custom merge driver.

对于“一个文件”(一般来说,不是说“配置”文件,因为它是一个不好的例子),你可以通过合并调用自定义脚本来实现。 Git将调用该脚本,因为您将定义一个gitattributes值,该值定义自定义合并驱动程序。

The "custom merge driver" is, in this case, a very simple script which basically will keep unchanged the current version, hence allowing you to always select your local version.

在这种情况下,“自定义合并驱动程序”是一个非常简单的脚本,它基本上将保持当前版本不变,因此允许您始终选择本地版本。


Let's test that in a simple scenario, with a msysgit 1.6.3 on Windows, in a mere DOS session:

让我们在一个简单的场景中测试,在Windows上使用msysgit 1.6.3,在一个简单的DOS会话中:

cd f:\prog\git\test
mkdir copyMerge\dirWithConflicts
mkdir copyMerge\dirWithCopyMerge
cd copyMerge
git init
Initialized empty Git repository in F:/prog/git/test/copyMerge/.git/

Now, let's make two files, which will both have conflicts, but which will be merged differently.

现在,让我们制作两个文件,这两个文件都会有冲突,但会以不同的方式合并。

echo a > dirWithConflicts\a.txt
echo b > dirWithCopyMerge\b.txt
git add -A
git commit -m "first commit with 2 directories and 2 files"
[master (root-commit) 0adaf8e] first commit with 2 directories and 2 files

We will introduce a "conflict" in the content of both those files in two different git branches:

我们将在两个不同的git分支中的这两个文件的内容中引入“冲突”:

git checkout -b myBranch
Switched to a new branch 'myBranch'
echo myLineForA >> dirWithConflicts\a.txt
echo myLineForB >> dirWithCopyMerge\b.txt
git add -A
git commit -m "add modification in myBranch"
[myBranch 97eac61] add modification in myBranch

git checkout master
Switched to branch 'master'
git checkout -b hisBranch
Switched to a new branch 'hisBranch'
echo hisLineForA >> dirWithConflicts\a.txt
echo hisLineForB >> dirWithCopyMerge\b.txt
git add -A
git commit -m "add modification in hisBranch"
[hisBranch 658c31c] add modification in hisBranch

Now, let's try to merge "hisBranch" upon "myBranch", with:

现在,让我们尝试将“hisBranch”合并到“myBranch”上,并:

  • manual resolution for conflicting merges
  • 冲突合并的手动解决方案

  • except for dirWithCopyMerge\b.txt where I always want to keep my version of b.txt.
  • 除了dirWithCopyMerge \ b.txt,我总是想保留我的b.txt版本。

Since the merge occurs in 'MyBranch', we will switch back to it, and add the 'gitattributes' directives which will customize the merge behavior.

由于合并发生在'MyBranch'中,我们将切换回它,并添加'gitattributes'指令,这将指定合并行为。

git checkout myBranch
Switched to branch 'myBranch'
echo b.txt merge=keepMine > dirWithCopyMerge\.gitattributes
git config merge.keepMine.name "always keep mine during merge"
git config merge.keepMine.driver "keepMine.sh %O %A %B"
git add -A
git commit -m "prepare myBranch with .gitattributes merge strategy"
[myBranch ec202aa] prepare myBranch with .gitattributes merge strategy

We have a .gitattributes file defined in the dirWithCopyMerge directory (defined only in the branch where the merge will occurs: myBranch), and we have a .git\config file which now contains a merge driver.

我们在dirWithCopyMerge目录中定义了一个.gitattributes文件(仅在发生合并的分支中定义:myBranch),我们有一个.git \ config文件,现在包含一个合并驱动程序。

[merge "keepMine"]
        name = always keep mine during merge
        driver = keepMine.sh %O %A %B

If you do not yet define keepMine.sh, and launch the merge anyway, here is what you get.

如果你还没有定义keepMine.sh,并且无论如何都要启动合并,这就是你得到的。

git merge hisBranch
sh: keepMine.sh: command not found
fatal: Failed to execute internal merge
git st
# On branch myBranch
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   dirWithConflicts/a.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

type dirWithConflicts\a.txt
a
<<<<<<< HEAD:dirWithConflicts/a.txt
myLineForA
=======
hisLineForA
>>>>>>> hisBranch:dirWithConflicts/a.txt

That is fine:

那样就好:

  • a.txt is ready to be merged and has conflict in it
  • a.txt已准备好合并,并且存在冲突

  • b.txt is still untouched, since the merge driver is supposed to take care of it (due to the directive in the .gitattributes file in its directory).
  • b.txt仍未触及,因为合并驱动程序应该处理它(由于其目录中的.gitattributes文件中的指令)。

Define a keepMine.sh anywhere in your %PATH% (or $PATH for our Unix friend. I do both of course: I have an Ubuntu session in a VirtualBox session)

在%PATH%(或者我们的Unix好友的$ PATH)中的任何位置定义keepMine.sh。我当然都这样做:我在VirtualBox会话中有一个Ubuntu会话)

As commented by lrkwz, and described in the "Merge Strategies" section of Customizing Git - Git Attributes, you can replace the shell script with the shell command true.

如lrkwz所评论,并在Customizing Git - Git Attributes的“Merge Strategies”部分中描述,您可以使用shell命令true替换shell脚本。

git config merge.keepMine.driver true

But in the general case, you can define a script file:

但在一般情况下,您可以定义脚本文件:

keepMine.sh

# I want to keep MY version when there is a conflict
# Nothing to do: %A (the second parameter) already contains my version
# Just indicate the merge has been successfully "resolved" with the exit status
exit 0

(that was one simple merge driver ;) (Even simpler in that case, use true)
(If you wanted to keep the other version, just add before the exit 0 line:
cp -f $3 $2.
That's it. You merge driver would aways keep the version coming from the other branch, overriding any local change)

(这是一个简单的合并驱动程序;)(在这种情况下甚至更简单,使用true)(如果你想保留其他版本,只需在退出0行之前添加:cp -f $ 3 $ 2.就是这样。你合并驱动程序会aways保持版本来自其他分支,覆盖任何本地更改)

Now, let's retry the merge from the beginning:

现在,让我们从头开始重试合并:

git reset --hard
HEAD is now at ec202aa prepare myBranch with .gitattributes merge strategy

git merge hisBranch
Auto-merging dirWithConflicts/a.txt
CONFLICT (content): Merge conflict in dirWithConflicts/a.txt
Auto-merging dirWithCopyMerge/b.txt
Automatic merge failed; fix conflicts and then commit the result.

The merge fails... only for a.txt.
Edit a.txt and leave the line from 'hisBranch', then:

合并失败...仅适用于a.txt。编辑a.txt并从'hisBranch'中保留该行,然后:

git add -A
git commit -m "resolve a.txt by accepting hisBranch version"
[myBranch 77bc81f] resolve a.txt by accepting hisBranch version

Let's check that b.txt has been preserved during this merge

让我们检查一下这个合并期间是否保留了b.txt

type dirWithCopyMerge\b.txt
b
myLineForB

The last commit does represent the full merge:

最后一次提交确实代表完全合并:

git show -v 77bc81f5e
commit 77bc81f5ed585f90fc1ca5e2e1ddef24a6913a1d
Merge: ec202aa 658c31c
git merge hisBranch
Already up-to-date.

(The line beginning with Merge does prove that)

(从Merge开始的行证明了这一点)


Consider you can define, combine and/or overwrite merge driver, as Git will:

考虑你可以定义,组合和/或覆盖合并驱动程序,因为Git将:

  • examine <dir>/.gitattributes (which is in the same directory as the path in question): will prevail upon the other .gitattributes in directories
  • 检查

    / .gitattributes(与相关路径位于同一目录中):将占用目录中的另一个.gitattributes

  • Then it examines .gitattributes (which is in the parent directory), will only set directives if not already set
  • 然后它检查.gitattributes(在父目录中),只会设置指令(如果尚未设置)

  • Finally it examines $GIT_DIR/info/attributes. This file is used to override the in-tree settings. It will overwrite <dir>/.gitattributes directives.
  • 最后,它检查了$ GIT_DIR / info / attributes。此文件用于覆盖树内设置。它将覆盖

    / .gitattributes指令。

By "combining", I mean "aggregate" multiple merge driver.
Nick Green tries, in the comments, to actually combine merge drivers: see "Merge pom's via python git driver".
However, as mentioned in his other question, it only works in case of conflicts (concurrent modification in both branches).

通过“组合”,我的意思是“聚合”多个合并驱动程序。 Nick Green在评论中尝试实际组合合并驱动程序:请参阅“通过python git驱动程序合并pom”。但是,正如他在另一个问题中提到的,它只适用于冲突(两个分支中的并发修改)。

#2


We have multiple config files that we never want overwritten. However .gitignore and .gitattributes did not work in our situation. Our solution was to store the config files in a configs branch. Then, allow the files to be changed during the git merge, but immediately following the merge use the "git checkout branch -- ." to copy our config files from the configs branch after every merge. Detailed * answer here

我们有多个配置文件,我们永远不会想要覆盖。但是.gitignore和.gitattributes在我们的情况下不起作用。我们的解决方案是将配置文件存储在configs分支中。然后,允许在git合并期间更改文件,但在合并之后立即使用“git checkout branch - ”。每次合并后从configs分支复制我们的配置文件。详细的*回答这里