简单的工具'接受他们'或者'在整个文件上使用git

我不需要一个可视化的合并工具,我也不希望必须vi冲突的文件,并手动在HEAD(我的)和导入的更改(他们的)之间进行选择。大多数时候,我要么想要他们所有的更改,要么想要我所有的更改。通常情况下,这是因为我的更改是向上的,并通过拉回来给我,但可能在不同的地方略有修改。

有没有一种命令行工具可以消除冲突标记,并根据我的选择选择所有的一种方式或另一种方式?或者一组git命令,我可以别名自己做每一个。

# accept mine
alias am="some_sequence;of;commands"
alias at="some_other_sequence;of;commands"
这样做很烦人。 对于“接受我的”,我已经尝试过:

randy@sabotage ~/linus $ git merge test-branch
Auto-merging Makefile
CONFLICT (content): Merge conflict in Makefile
Automatic merge failed; fix conflicts and then commit the result.


randy@sabotage ~/linus $ git checkout Makefile
error: path 'Makefile' is unmerged


andy@sabotage ~/linus $ git reset --hard HEAD Makefile
fatal: Cannot do hard reset with paths.

我该怎么去掉这些变化标记呢?

我可以:

git reset HEAD Makefile; rm Makefile; git checkout Makefile

但这似乎有点绕,肯定有更好的办法。在这一点上,我不确定git是否认为合并发生了,所以我认为这不一定有效。

反过来说,“accept their”也同样混乱。 我能弄清楚的唯一方法是do:

git show test-branch:Makefile > Makefile; git add Makefile;

这也给了我一个混乱的提交消息,其中两次包含Conflicts: Makefile。

谁能告诉我如何用更简单的方法来做以上两个动作?谢谢

325291 次浏览

解决办法很简单。git checkout <filename>尝试从该指数签出文件,因此在合并时失败。

你需要做的是(即签出提交):

签出您自己的版本你可以使用的一个:

git checkout HEAD -- <filename>

git checkout --ours -- <filename>

(警告!:如果你正在重基--ours--theirs被交换。)

git show :2:<filename> > <filename> # (stage 2 is ours)

签出另一个版本你可以使用的一个:

git checkout test-branch -- <filename>

git checkout --theirs -- <filename>

git show :3:<filename> > <filename> # (stage 3 is theirs)

你还需要运行'add'将其标记为已解决:

git add <filename>

根据Jakub的回答,你可以为方便配置以下git别名:

accept-ours = "!f() { git checkout --ours -- \"${@:-.}\"; git add -u \"${@:-.}\"; }; f"
accept-theirs = "!f() { git checkout --theirs -- \"${@:-.}\"; git add -u \"${@:-.}\"; }; f"

它们可以选择一个或多个文件路径来解析,如果没有给出,则默认解析当前目录下的所有文件。

将它们添加到你的~/.gitconfig[alias]部分或运行

git config --global alias.accept-ours '!f() { git checkout --ours -- "${@:-.}"; git add -u "${@:-.}"; }; f'
git config --global alias.accept-theirs '!f() { git checkout --theirs -- "${@:-.}"; git add -u "${@:-.}"; }; f'

根据kynan的回答,这里是相同的别名,修改后可以处理文件名中的空格和首字符:

accept-ours = "!f() { [ -z \"$@\" ] && set - '.'; git checkout --ours -- \"$@\"; git add -u -- \"$@\"; }; f"
accept-theirs = "!f() { [ -z \"$@\" ] && set - '.'; git checkout --theirs -- \"$@\"; git add -u -- \"$@\"; }; f"

试试这个:

  • 接受他们的的更改:git merge --strategy-option theirs
  • 接受你的的更改:git merge --strategy-option ours

解决冲突的理想情况是提前知道要用哪种方式解决冲突,并且可以传递-Xours-Xtheirs递归合并策略选项。除此之外,我可以看到三个场景:

  1. 您希望只保留文件的单个版本(这应该只用于不可合并的二进制文件,否则冲突文件和非冲突文件可能会彼此不同步)。
  2. 你只想把所有的冲突都确定在一个特定的方向上。
  3. 您需要手动解决一些冲突,然后按照特定的方向解决其余所有冲突。

为了解决这三种情况,你可以在你的.gitconfig文件(或等效文件)中添加以下行:

[merge]
conflictstyle = diff3
[mergetool.getours]
cmd = git-checkout --ours ${MERGED}
trustExitCode = true
[mergetool.mergeours]
cmd = git-merge-file --ours ${LOCAL} ${BASE} ${REMOTE} -p > ${MERGED}
trustExitCode = true
[mergetool.keepours]
cmd = sed -i '' -e '/^<<<<<<</d' -e '/^|||||||/,/^>>>>>>>/d' ${MERGED}
trustExitCode = true
[mergetool.gettheirs]
cmd = git-checkout --theirs ${MERGED}
trustExitCode = true
[mergetool.mergetheirs]
cmd = git-merge-file --theirs ${LOCAL} ${BASE} ${REMOTE} -p > ${MERGED}
trustExitCode = true
[mergetool.keeptheirs]
cmd = sed -i '' -e '/^<<<<<<</,/^=======/d' -e '/^>>>>>>>/d' ${MERGED}
trustExitCode = true

get(ours|theirs)工具只保留文件的各自版本,并丢弃来自其他版本的所有更改(因此不会发生合并)。

merge(ours|theirs)工具从文件的本地、基本和远程版本重新执行三种方式合并,选择在给定方向上解决冲突。这有一些注意事项,具体来说:它忽略了传递给merge命令的diff选项(例如算法和空格处理);是否从原始文件合并干净(因此任何手动更改文件被丢弃,这可能是好的或坏的);而且它的优点是不会被文件中应该存在的差异标记所混淆。

keep(ours|theirs)工具只是编辑出差异标记和封闭的部分,通过正则表达式检测它们。这样做的好处是保留了merge命令中的diff选项,并允许您手动解决一些冲突,然后自动解决其余冲突。它的缺点是,如果文件中有其他冲突标记,它可能会混淆。

这些都是通过运行git mergetool -t (get|merge|keep)(ours|theirs) [<filename>]来使用的,如果没有提供<filename>,它将处理所有冲突的文件。

一般来说,假设你知道没有diff标记来混淆正则表达式,该命令的keep*变体是最强大的。如果你保留mergetool.keepBackup选项未设置或为true,那么在合并之后,你可以将*.orig文件与合并的结果进行区分,以检查它是否有意义。作为一个例子,我在mergetool之后运行以下命令,只是为了在提交之前检查更改:

for f in `find . -name '*.orig'`; do vimdiff $f ${f%.orig}; done

请注意:如果merge.conflictstyle不是diff3,那么sed规则中的/^|||||||/模式需要改为/^=======/