我不想要一个可视化合并工具,我也不想要 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 是否认为合并发生了,所以我认为这不一定有效。
反过来说,“接受他们的”同样是一团糟。我能弄清楚的唯一方法是:
git show test-branch:Makefile > Makefile; git add Makefile;
这也给了我一个混乱的提交消息,其中包含 Conflicts: Makefile 两次。
有人可以指出如何以更简单的方式执行上述两个操作吗?谢谢
解决方案非常简单。 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)
您还需要运行“添加”以将其标记为已解决:
git add <filename>
尝试这个:
接受他们的更改: git merge --strategy-option theirs
接受您的更改: git merge --strategy-option ours
theirs
的目录,但要手动解决冲突?例如:git merge --strategy-option theirs some/dir
。因此,如果路径 some/dir
的文件中存在冲突,则接受他们的冲突,但如果不是,那么问我?
fatal: No current branch.
根据 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'
~.gitconfig
的 [alias]
部分或使用 git config --global accept-ours "..."
。已编辑我的答案。
~/.gitconfig
。
!f() { git checkout --ours -- "${@:-.}" 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"
解决冲突的理想情况是您提前知道要以哪种方式解决冲突,并且可以通过 -Xours
或 -Xtheirs
递归合并策略选项。除此之外,我可以看到三个场景:
您只想保留文件的单个版本(这可能应该只用于不可合并的二进制文件,因为否则冲突和非冲突文件可能会彼此不同步)。你想简单地决定一个特定方向的所有冲突。您需要手动解决一些冲突,然后在特定方向解决所有其余冲突。
要解决这三种情况,您可以将以下行添加到您的 .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)
工具从文件的本地、基本和远程版本重新执行三路合并,选择解决给定方向的冲突。这有一些警告,特别是:它忽略了传递给合并命令的差异选项(例如算法和空白处理);是否从原始文件中干净地合并(因此对文件的任何手动更改都将被丢弃,这可能是好是坏);并且具有不会被文件中应该存在的差异标记混淆的优点。
keep(ours|theirs)
工具只需编辑差异标记和封闭部分,通过正则表达式检测它们。这样做的好处是它保留了合并命令中的差异选项,并允许您手动解决一些冲突,然后自动解决其余的冲突。它的缺点是如果文件中有其他冲突标记,它可能会混淆。
这些都由运行 git mergetool -t (get|merge|keep)(ours|theirs) [<filename>]
使用,如果未提供 <filename>
,它将处理所有冲突的文件。
一般来说,假设您知道没有混淆正则表达式的差异标记,那么命令的 keep*
变体是最强大的。如果您保留 mergetool.keepBackup
选项未设置或为 true,则在合并后您可以将 *.orig
文件与合并结果进行比较,以检查它是否有意义。例如,我在 mergetool
之后运行以下命令,只是为了在提交前检查更改:
for f in `find . -name '*.orig'`; do vimdiff $f ${f%.orig}; done
注意:如果 merge.conflictstyle
不是 diff3
,则 sed
规则中的 /^|||||||/
模式需要改为 /^=======/
。
sed -i ''
。它只允许就地编辑。无论如何我应该使用小写,所以我会更新帖子。
--ours
和--theirs
的含义与我在尝试此命令时的直觉想法完全相反...git show
时要小心——这会跳过换行规范化。--
将修订(分支名称等)与路径名称(文件名、目录)分开。如果 Git 无法确定名称是分支名称还是文件名称,这一点很重要。这遵循使用双破折号将选项与参数(文件名)分开的 POSIX(或 GNU)约定。theirs
/ours
可能会出现交换。因为 rebase 通过检查目标分支然后从“你的”分支中挑选提交到目标上来工作,所以传入的更改(“他们的”)来自“你的”分支,而当前分支是目标分支(“我们的” )。