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

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

问题描述

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



有没有对我来说,建立我的本地回购让我不会抱怨每次我混帐时发生冲突的合并?我想在合并这个文件时总是选择我的本地版本。

解决方案

在配置文件的特定实例上,会同意 Ron的答案

a config应该对您的工作空间私有(因此被忽略,如在 .gitignore file)。

您可能有一个带有标记化值的配置文件 template ,并且脚本将 config.template 文件转换为私有(并且被忽略)配置文件。




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


如何告诉git始终选择我的本地版本来处理特定文件上的冲突合并?(适用于任何文件或gr

这种合并是一种复制合并,您可以在其中复制'我们'或'他们的' '


(as Brian Vandenberg 笔记在评论'我们'和'他们的'在这里用于合并

他们是反转 :请参阅 为什么git-svn将我们和他们的的含义颠倒过来 ,其中使用了rebase, git rebase ,跟踪本地和远程

对于一个文件(一般而言,不是说config文件,因为这是一个不好的例子),您可以通过自定义脚本来实现通过合并调用。

Git将调用该脚本,因为您将定义一个 gitattributes ,它定义了一个自定义合并驱动程序

自定义合并驱动程序这种情况下,一个非常简单的脚本,基本上会保持当前版本不变,因此可以让您始终选择您的本地版本。




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

/ p>

  cd f:\prog\git\test 
mkdir copyMerge\dirWithConflicts
mkdir copyMerge \dirWithCopyMerge
cd copyMerge
git init
在F:/prog/git/test/copyMerge/.git/
中初始化的空Git仓库

现在,我们创建两个文件,这两个文件都会有冲突,但是会有不同的合并。

  echo a> dirWithConflicts\a.txt 
echo b> dirWithCopyMerge\b.txt
git add -A
git commit -m首先提交2个目录和2个文件
[master(root-commit)0adaf8e]首先提交2个目录和2个文件

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

$ $ p $ git checkout -b myBranch
切换到新分支'myBranch'
echo myLineForA> ;> dirWithConflicts\a.txt
echo myLineForB>> dirWithCopyMerge\b.txt
git add -A
git commit -m在myBranch中添加修改
[myBranch 97eac61]在myBranch中添加修改

git checkout master
切换到分支'master'
git checkout -b hisBranch
切换到新分支'hisBranch'
echo hisLineForA>> dirWithConflicts\a.txt
echo hisLineForB>> dirWithCopyMerge\b.txt
git add -A
git commit -m在他的分支中添加修改
[hisBranch 658c31c]在他的分支中添加修改



现在,我们尝试将hisBranch合并到myBranch中:


  • 手动解决冲突合并

  • dirWithCopyMerge\b.txt 我总是希望保留 版本的 b.txt



由于合并发生在' MyBranch '中,我们将切换回它,并添加' gitattributes

  git checkout myBranch 
转换到分支'myBranch'
echo b.txt merge = keepMine>
git config merge.keepMine.driverkeepMine.sh%O%A%B
git添加-A
git commit -m使用.gitattributes合并策略准备myBranch
[myBranch ec202aa]使用.gitattributes合并策略准备myBranch

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

  [mergekeepMine] 
name =
driver = keepMine.sh%O%A%B

如果您尚未定义keepMine.sh,然后启动合并,这是你得到的。

  git合并他的分支
sh:keepMine .sh:找不到命令
致命:未能找到e xecute内部合并
git st
#分支myBranch
#已更改但未更新:
#(使用git add< file> ...来更新将会更新的内容提交)
#(使用git checkout - < file> ...放弃工作目录中的更改)

#修改:dirWithConflicts / a.txt

没有更改添加到提交(使用git add和/或git commit -a)

类型dirWithConflicts\a.txt
a
< ;<<<<<< HEAD:dirWithConflicts / a.txt
myLineForA
=======
hisLineForA
>>>>>>> hisbranch:dirWithConflicts / a.txt

这很好:


  • a.txt 已准备好合并并且存在冲突

  • b.txt 仍然没有改动,因为合并驱动程序应该处理它(由于 .gitattributes 文件在其目录中)。



在任意位置定义 keepMine.sh 在我们的Unix朋友的%PATH%(或者 $ PATH >中,我做了两个:我有一个Ubuntu会话在VirtualBox会话中)



As 评论 lrkwz ,并在合并战略自定义Git - Git属性,你可以用shell命令替换shell脚本 true

  git config merge.keepMine.driver true 

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

keepMine.sh

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

(这是一个简单的合并驱动程序;)(在这种情况下更简单,使用<$ c (如果你想保留另一个版本,只需在出口0 行之前添加:$ c $ true>

cp -f $ 3 $ 2

就是这样。您合并的驱动程序会保留来自其他分支的版本,覆盖任何本地更改)



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

  git reset --hard 
HEAD现在在ec202aa使用.gitattributes合并策略准备myBranch

git合并hisBranch
自动合并dirWithConflicts / a.txt
CONFLICT(内容):合并dirWithConflicts中的冲突/ a.txt
自动合并dirWithCopyMerge / b.txt
自动合并失败;修复冲突,然后提交结果。

合并失败... 仅适用于a.txt 。 >
编辑a.txt并从'hisBranch'离开该行,然后:

  git add -A 
git commit -m通过接受他的分支版本解析a.txt
[myBranch 77bc81f]通过接受他的分支版本来解析a.txt

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

  type dirWithCopyMerge \\ b.txt 
b
myLineForB

最后一次提交代表全部合并:

  git show -v 77bc81f5e 
commit 77bc81f5ed585f90fc1ca5e2e1ddef24a6913a1d
合并:ec202aa 658c31c
git合并他的分支
已经是最新的。

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




考虑到你可以定义,合并和/或覆盖合并驱动程序,就像Git一样:


  • 检查< dir> /。gitattributes (与所讨论的路径位于同一目录中):以其他 .gitattributes 在目录中

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

  • 最后它检查 $ GIT_DIR / info / attributes 。此文件用于覆盖树中的设置。它将覆盖< dir> /。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.


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

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 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:

  • manual resolution for conflicting merges
  • except for dirWithCopyMerge\b.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 .git\config 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 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
  • 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\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)


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天全站免登陆