使用git在整个文件上“接受他们的”或“接受我的”的简单工具


399

我不需要视觉合并工具,也不想查看冲突的文件并手动选择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是否认为合并已发生,因此我认为这不一定奏效。

换一种方式,做“接受他们的”同样是混乱的。我能弄清楚的唯一方法是:

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

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

有人可以指出如何以更简单的方式执行上述两个操作吗?谢谢


4
我必须将它作为三年以上的git命令行用户提供给您,我发现从内存中很难做到这一点。确实应该默认将其内置。
Mauvis Ledford

Answers:


602

解决方案非常简单。git checkout <filename>尝试从index中检出文件,因此合并失败。

您需要做的是(即检出一个commit):

要签出自己的版本,您可以使用以下之一

git checkout HEAD -- <filename>

要么

git checkout --ours -- <filename>

要么

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)

您还需要运行“添加”以将其标记为已解决:

git add <filename>

31
我发现这有点奇怪,它--ours--theirs尝试此命令时我的直觉完全相反...
Joshua Muheim 2012年

6
使用时要小心git show-这会跳过换行规范化。
慢性的2012年

2
这对一些文件来说很好,但是当您有许多文件冲突时(因为注释中的日期已更改!),您该怎么做?
JhovaniC

4
@Santhos:--Git用于将修订(分支名称等)与路径名(文件名,目录)分开。如果Git无法确定名称是分支名称还是文件名称,这一点很重要。这遵循POSIX(或GNU)约定,即使用双破折号将选项与参数(文件名)分开。
JakubNarębski2014年

3
@Sammaron @约书亚·穆海姆; 如果您要解决的是重新配置操作的上下文中的冲突,则theirs/ ours可以交换显示。因为rebase通过签出目标分支来工作,然后从“您的”分支到目标上进行樱桃挑选,所以传入的更改(“它们的”)来自“您的”分支,而当前分支是目标分支(“我们的”) )。
RJFalconer

93

尝试这个:

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

要接受您的: git merge --strategy-option ours


5
请注意,这将保留对所有冲突文件的更改,因此,如果发生意外冲突,可能会很危险。
约翰

3
您可以将其用于其他merge-y命令,例如cherry-pick和rebase。
idbrii

50

基于Jakub的答案,为方便起见,您可以配置以下git别名:

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

它们有选择地采用一个或几个文件路径来解析,如果没有给出路径,则默认为解析当前目录下的所有内容。

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

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'

1
不适合我...这些是bash还是其他外壳?
user456584

这些是git别名,请将它们添加到[alias]您的~.gitconfig或使用中git config --global accept-ours "..."。编辑了我的答案。
kynan 2012年

2
您不知道这个别名为我节省了多少时间。竖起大拇指!
亚当·帕金

1
@hakre确保您引用了别名,否则您的shell会尝试解释它。或者只是手动编辑您的~/.gitconfig
kynan 2013年

1
默认值的Shell语法:!f() { git checkout --ours -- "${@:-.}" git add -u "${@:-.}; }; f
jthill 2014年

17

根据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"

0

解决冲突的理想情况是,当您提前知道要解决冲突的方式并可以通过-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)工具仅编辑出diff标记和封闭部分,并通过正则表达式对其进行检测。这样做的好处是它保留了merge命令中的diff选项,并允许您手动解决一些冲突,然后自动解决其余冲突。它的缺点是,如果文件中还有其他冲突标记,可能会造成混淆。

git mergetool -t (get|merge|keep)(ours|theirs) [<filename>]<filename>未提供的情况下运行这些文件(如果未处理所有冲突的文件),将全部使用这些文件。

一般来说,假设您知道没有diff标记会使正则表达式混淆keep*,那么命令的变体功能最强大。如果将mergetool.keepBackup选项设置为unset或true,则在合并之后,可以将*.orig文件与合并结果进行比较以检查其是否有意义。例如,我在mergetool公正之后运行以下命令,以在提交之前检查更改:

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

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

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.