我如何告诉git始终选择本地版本以对特定文件进行冲突合并?


100

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

我有什么办法可以设置本地存储库,以免每次git pull时都抱怨合并冲突?合并此文件时,我总是想选择本地版本。


1
刚刚通过.gitattributes添加了一个简单的解决方案和一个非常基本的“合并驱动程序”
VonC

12
TD; LR:echo 'path/to/file merge=ours' >> .gitattributes && git config --global merge.ours.driver true
Ciro Santilli郝海东冠状病六四事件法轮功

@CiroSantilli:在Linux上像魅惑一样工作。该驱动程序是很简单的被内置到Git的...
krlmlr

您希望将更改推送到文件吗?还是它是一个配置文件,其中默认存储在git中。
伊恩·林格罗斯

@CiroSantilli新疆改造中心六四事件法轮功的评论是正确的,但会使带有该--global标签的系统上的每个存储库都发生这种现象。如果您只希望单个回购协议具有此行为,请忽略以下--global标志:echo 'path/to/file merge=ours' >> .gitattributes && git config merge.ours.driver true
majorobot '19

Answers:


140

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


但是,该特定备注不能回答更广泛的一般性问题,即您的问题(!):

我如何告诉git始终选择本地版本以对特定文件进行冲突合并?(对于任何文件或文件组)

这种合并是“复制合并”,在这种情况下,只要有冲突,您将始终复制文件的“我们的”或“他们的”版本。

(如布赖恩·范登堡笔记中的注释ours”和“ theirs”在这里用于合并
它们是颠倒底垫:看“ Why is the meaning of “ours” and “theirs” reversed with git-svn”,它采用了变基“ git rebase随时追踪‘本地’和‘远程’ ” )

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

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

IE。,正如Ciro Santilli指出的:

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

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

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/

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

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

我们将在两个不同的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

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

  • 手动解决冲突的合并
  • 除外dirWithCopyMerge\b.txt,我一直想保持我的版本b.txt

由于合并发生在' 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

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

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

如果您还没有定义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

那样就好:

  • a.txt 准备好合并并且有冲突
  • b.txt仍然保持不变,因为合并驱动程序应该处理它(由于.gitattributes其目录中文件中的指令)。

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

正如lrkwz评论那样,并在“ 定制Git-Git属性 ” 的“ 合并策略 ”部分中进行了描述,您可以使用shell命令替换shell脚本。true

git config merge.keepMine.driver true

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

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

(这是一个简单的合并驱动器;)(即使在这种情况下,使用简单true
(如果你想保持其他的版本,只是之前添加exit 0一行:
cp -f $3 $2
这是它在合并驱动程序会跳投继续从其他未来的版本。分支,覆盖任何本地更改)

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

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.

合并失败... 仅适用于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

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

type dirWithCopyMerge\b.txt
b
myLineForB

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

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

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


考虑到您可以定义,合并和/或覆盖合并驱动程序,如Git那样:

  • 检查<dir>/.gitattributes(与所讨论的路径位于同一目录中):.gitattributes在目录中将优先于其他
  • 然后它检查.gitattributes(位于父目录中),仅在尚未设置指令的情况下设置指令
  • 最后,它进行了检查$GIT_DIR/info/attributes。该文件用于覆盖树内设置。它将覆盖<dir>/.gitattributes指令。

“合并”是指“聚合”多个合并驱动程序。
尼克·格林(Nick Green在评论中尝试实际合并合并驱动程序:请参阅“ 通过python git driver合并pom ”。
但是,正如他的另一个问题所述,它仅在发生冲突的情况下才起作用(两个分支中同时进行修改)。


1
感谢您的详细回答!我知道这对版本控制配置文件没有任何意义,但我只是追求一个简单易懂的示例。确实,这是令我感兴趣的更广泛的问题。我以前从未听说过git merge驱动程序,因此感谢您的启发。
2009年

6
cp -f $3 $2也许应该被引用,即cp -f "$3" "$2"

1
@VonC感谢您的详细回答!我遇到的问题是,这取决于人们在他们的.git / config文件中设置驱动程序。我想将驱动程序信息添加到项目本身,因此它将是自动的,并且减少了设置工作。有指针吗?
Juan Delgado

2
@ulmangt:您也可以将该脚本存储在git repo中,只要找到将其父目录添加到PATH(Unix或Windows PATH)的方法即可。由于该脚本将通过Unix bash shell或Mingming bash MsysGit Windows shell进行解释,因此它将是可移植的。
VonC 2012年

5
@VonC谢谢。再一期。在某些情况下(如果对本地分支没有任何更改,则合并驱动器似乎从未被调用过),这导致本地文件被修改(应该使用自定义合并驱动器)以防止它们在合并期间更改)。有什么方法可以强制 git始终使用合并驱动程序?
ulmangt 2012年

1

正如@ ciro-santilli所说,将其.gitattributes与设置一起使用的简单方法是:

path/to/file merge=ours

并通过以下方式启用此策略:

git config --global merge.ours.driver true

(我将其添加为答案,以使其更加可见,但将其作为社区Wiki,以免自己超越用户的信誉。请在此处在Q下对他的评论进行投票,以表扬他!)


(此外:如果有人给出答案作为评论但不添加答案,则写非CW答案并获得积分完全可以。如果他们仍然是活跃成员,则可以ping他们以添加答案,如果您愿意,但从技术上讲,他们已经有机会:-))。
半夜

0

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.