我如何告诉 git 总是为特定文件上的冲突合并选择我的本地版本? [英] How do I tell git to always select my local version for conflicted merges on a specific file?

查看:37
本文介绍了我如何告诉 git 总是为特定文件上的冲突合并选择我的本地版本?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

有什么方法可以让我设置本地存储库,以免每次 git pull 时都抱怨合并冲突?我想在合并这个文件时总是选择我的本地版本.

解决方案

关于配置文件的特定实例,我同意 罗恩的回答:
配置对于您的工作区应该是私有的"(因此被忽略",如在 .gitignore 文件中声明").
您可能有一个包含 标记化值的配置文件 template,以及一个将该 config.template 文件转换为私有(并被忽略)的脚本配置文件.

<小时>

但是,该特定评论并没有回答更广泛更一般的问题,即您的问题(!):

<块引用>

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

这种合并是复制合并",在这种合并中,只要出现冲突,您将始终复制文件的我们的"或他们的"版本.

<块引用>

(如Brian Vandenberg 注释在评论中'ours' 和 'theirs' 在这里用于合并.
它们被反转用于rebase:参见为什么是这个意思我们的"和他们的"用 git-svn 反转",它使用了 rebase,"git rebase,跟踪本地"和远程"")

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

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

IE.,作为 注意到 西罗桑蒂利:

echo 'path/to/file merge=ours' >>.git 属性git config --global merge.ours.driver true

<小时>

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

cd f:proggit	estmkdir copyMergedirWithConflictsmkdir copyMergedirWithCopyMerge光盘复制合并混帐初始化在 F:/prog/git/test/copyMerge/.git/中初始化空 Git 存储库

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

echo a >dirWithConflictsa.txt回声 b >dirWithCopyMerge.txtgit add -Agit commit -m "第一次提交 2 个目录和 2 个文件"[master (root-commit) 0adaf8e] 首先提交 2 个目录和 2 个文件

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

git checkout -b myBranch切换到新分支myBranch"echo myLineForA >>dirWithConflictsa.txtecho myLineForB >>dirWithCopyMerge.txtgit add -Agit commit -m "在 myBranch 中添加修改"[myBranch 97eac61] 在myBranch中添加修改git 结帐大师切换到分支master"git checkout -b hisBranch切换到一个新的分支 'hisBranch'echo hisLineForA >>dirWithConflictsa.txtecho hisLineForB >>dirWithCopyMerge.txtgit add -Agit commit -m "在 hisBranch 中添加修改"[hisBranch 658c31c]在hisBranch中添加修改

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

  • 手动解决合并冲突
  • 除了对于dirWithCopyMerge.txt,我总是想保留我的版本的b.txt.

由于合并发生在MyBranch"中,我们将切换回它,并添加将自定义合并行为的gitattributes"指令.

git checkout myBranch切换到分支myBranch"echo b.txt merge=keepMine >dirWithCopyMerge.gitattributesgit config merge.keepMine.name 在合并期间始终保留我的"git config merge.keepMine.driver "keepMine.sh %O %A %B"git add -Agit commit -m "用 .gitattributes 合并策略准备 myBranch"[myBranch ec202aa] 准备 myBranch 与 .gitattributes 合并策略

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

[合并keepMine"]名称 = 在合并期间始终保留我的名称驱动程序 = keepMine.sh %O %A %B

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

git merge hisBranchsh:keepMine.sh:找不到命令致命:无法执行内部合并混帐# 在分支 myBranch# 已更改但未更新:#(使用git add ..."来更新将提交的内容)# (使用 "git checkout -- <file>..." 放弃工作目录中的更改)## 修改:dirWithConflicts/a.txt#没有更改提交(使用git add"和/或git commit -a")输入 dirWithConflictsa.txt一种<<<<<<<HEAD:dirWithConflicts/a.txtmyLineForA========hisLineForA>>>>>>hisBranch:dirWithConflicts/a.txt

没关系:

  • a.txt 已准备好合并但存在冲突
  • b.txt 仍然没有改变,因为合并驱动程序应该处理它(由于其目录中的 .gitattributes 文件中的指令).

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

作为 lrkwz 评论,并在合并策略 部分 Customizing Git - Git Attributes,可以用shell命令替换shell脚本true.

git config merge.keepMine.driver true

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

keepMine.sh

# 有冲突时我想保留我的版本# 无事可做:%A(第二个参数)已经包含我的版本# 只用退出状态表示合并已成功解决"退出 0

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

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

git reset --hardHEAD 现在在 ec202aa 准备 myBranch 与 .gitattributes 合并策略git合并他的分支自动合并 dirWithConflicts/a.txtCONFLICT(内容):在 dirWithConflicts/a.txt 中合并冲突自动合并 dirWithCopyMerge/b.txt自动合并失败;修复冲突,然后提交结果.

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

git add -Agit commit -m "通过接受 hisBranch 版本来解析 a.txt"[myBranch 77bc81f] 通过接受 hisBranch 版本解析 a.txt

让我们检查一下 b.txt 在这次合并过程中是否被保留了

type dirWithCopyMerge.txt乙myLineForB

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

git show -v 77bc81f5e提交 77bc81f5ed585f90fc1ca5e2e1ddef24a6913a1d合并:ec202aa 658c31cgit合并他的分支已经是最新的.

(以 Merge 开头的行确实证明了这一点)

<小时>

考虑您可以像 Git 一样定义、组合和/或覆盖合并驱动程序:

  • 检查<dir>/.gitattributes(与所讨论的路径在同一目录中):将优先于目录中的其他.gitattributes莉>
  • 然后它检查 .gitattributes(在父目录中),如果还没有设置,只会设置指令
  • 最后检查$GIT_DIR/info/attributes.此文件用于覆盖树内设置.它将覆盖 /.gitattributes 指令.

通过组合",我的意思是聚合"多个合并驱动程序.
尼克格林尝试,在评论中,实际结合合并驱动程序:见通过 python git 驱动程序合并 pom .
但是,正如他的另一个问题所述,它仅在发生冲突(两个分支中的并发修改)时才有效.>

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.

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.

解决方案

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.


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)

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'")

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.

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.

IE., As noted by Ciro Santilli:

echo 'path/to/file merge=ours' >> .gitattributes
git config --global merge.ours.driver true


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

cd f:proggit	est
mkdir copyMergedirWithConflicts
mkdir copyMergedirWithCopyMerge
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 > dirWithConflictsa.txt
echo b > dirWithCopyMerge.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 checkout -b myBranch
Switched to a new branch 'myBranch'
echo myLineForA >> dirWithConflictsa.txt
echo myLineForB >> dirWithCopyMerge.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 >> dirWithConflictsa.txt
echo hisLineForB >> dirWithCopyMerge.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:

  • manual resolution for conflicting merges
  • except for dirWithCopyMerge.txt where I always want to keep my version of 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.

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 .gitconfig file which now contains a merge driver.

[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.

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 dirWithConflictsa.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
  • 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).

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)

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.

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)

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:

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

type dirWithCopyMerge.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)


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

  • examine <dir>/.gitattributes (which is in the same directory as the path in question): will prevail upon the other .gitattributes in directories
  • Then it examines .gitattributes (which is in the parent directory), will only set directives if not already set
  • Finally it examines $GIT_DIR/info/attributes. This file is used to override the in-tree settings. It will overwrite <dir>/.gitattributes directives.

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).

这篇关于我如何告诉 git 总是为特定文件上的冲突合并选择我的本地版本?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆