ChatGPT解决这个技术问题 Extra ChatGPT

Git 在合并特定文件的冲突时选择本地版本吗?

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

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

只是通过 .gitattributes 添加了一个简单的解决方案和一个非常基本的“合并驱动程序”
TD;LR:echo 'path/to/file merge=ours' >> .gitattributes && git config --global merge.ours.driver true
@CiroSantilli:在 Linux 上就像一个魅力。这个驱动程序很简单,可以内置到 Git 中......
您希望将更改推送到文件吗?或者它是例如一个配置文件,其中默认存储在 git.xml 中。
@CiroSantilli新疆改造中心六四事件轮法功的评论是正确的,但会使系统上带有 --global 标签的每个 repo 发生这种行为。如果您只希望单个 repo 具有此行为,请忽略 --global 标志:echo 'path/to/file merge=ours' >> .gitattributes && git config merge.ours.driver true

V
VonC

在配置文件的特定实例上,我同意 Ron's answer
配置应该是您工作区的“私有”(因此“忽略”,如“在 .gitignore 文件中声明”)。< br> 您可能有一个配置文件 template,其中包含 tokenized values,以及一个将该 config.template 文件转换为私有(且被忽略)配置文件的脚本。

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

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

这种合并是“复制合并”,只要有冲突,您总是会复制“我们的”或“他们的”版本的文件。

(正如 Brian Vandenberg 在评论中指出的那样,'ours' 和 'theirs' 在这里用于合并。它们被反转用于变基:请参阅“为什么用 git-svn 反转“ours”和“theirs”的含义” ,它使用一个变基,“git变基,跟踪'本地'和'远程'”)

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

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

即,作为 Ciro Santillinoted

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

让我们在一个简单的场景中测试它,在 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/

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

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

我们在 dirWithCopyMerge 目录中定义了一个 .gitattributes 文件(仅在将发生合并的分支中定义: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 文件中的指令)。

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

正如 lrkwz 中的 commentedCustomizing Git - Git Attributes 的“Merge Strategies”部分所述,您可以用 shell 命令 true 替换 shell 脚本。

git config merge.keepMine.driver true

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

保持矿井.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 将:

检查

/.gitattributes (与相关路径位于同一目录中):将优先于目录中的其他 .gitattributes

然后它检查 .gitattributes (在父目录中),如果尚未设置,则只会设置指令

最后它检查 $GIT_DIR/info/attributes。此文件用于覆盖树内设置。它将覆盖

/.gitattributes 指令。

“组合”是指“聚合”多个合并驱动程序。
Nick Green in the comments 尝试实际组合合并驱动程序:请参阅“Merge pom's via python git driver”。
但是,如 his other question 中所述,它仅在发生冲突的情况下有效(两个分支中的并发修改)。


谢谢你的详细解答!我知道版本控制配置文件没有意义,但我追求的是一个简单的激励示例。事实上,我感兴趣的是更广泛的问题。我以前从未听说过 git merge 驱动程序,所以感谢您启发我。
cp -f $3 $2 应该被引用,即cp -f "$3" "$2"
@VonC 感谢您的详细回答!我遇到的问题是它取决于人们在他们的 .git/config 文件中设置驱动程序。我想将驱动程序信息添加到项目本身,所以它会是自动的并且需要完成的设置工作更少。任何指针?
@ulmangt:您也可以将该脚本存储在 git repo 中,...只要您找到一种方法将其父目录添加到 PATH(Unix 或 Windows PATH)。由于该脚本将通过 Unix bash shell 或 MingWin bash MsysGit Windows shell 进行解释,因此它将是可移植的。
@VonC 谢谢。又一题。在某些情况下(如果没有对要合并的本地分支进行任何更改),似乎甚至从未调用合并驱动程序,这导致本地文件被修改(应该使用自定义合并驱动程序)以防止它们在合并期间更改)。有什么方法可以强制 git 始终使用合并驱动程序?
2
2 revs, 2 users 88%

正如@ciro-santilli 评论的那样,使用 .gitattributes 设置它的简单方法:

path/to/file merge=ours

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

git config --global merge.ours.driver true

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


(除此之外:如果有人将答案作为评论给出并且没有添加答案,那么写一个非 CW 答案并获得学分是完全可以的。如果他们仍然是活跃成员,您可以 ping 他们以添加答案,如果您希望,但他们在技术上已经有机会:-))。
C
Community

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