ChatGPT解决这个技术问题 Extra ChatGPT

使用 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 是否认为合并发生了,所以我认为这不一定有效。

反过来说,“接受他们的”同样是一团糟。我能弄清楚的唯一方法是:

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

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

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

作为一个三年以上的 git 命令行用户,我必须把它交给你,我发现这很难从记忆中做到。它确实应该默认内置。

T
Thomas Ahle

解决方案非常简单。 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>

我觉得有点奇怪,--ours--theirs 的含义与我在尝试此命令时的直觉想法完全相反...
使用 git show 时要小心——这会跳过换行规范化。
这对几个文件来说很好,但是当你有很多文件发生冲突时(因为评论中的日期被更改了!),你怎么做?
@Santhos:Git 使用 -- 将修订(分支名称等)与路径名称(文件名、目录)分开。如果 Git 无法确定名称是分支名称还是文件名称,这一点很重要。这遵循使用双破折号将选项与参数(文件名)分开的 POSIX(或 GNU)约定。
@Sammaron @Joshua Muheim;如果您在变基操作的上下文中解决冲突,theirs/ours 可能会出现交换。因为 rebase 通过检查目标分支然后从“你的”分支中挑选提交到目标上来工作,所以传入的更改(“他们的”)来自“你的”分支,而当前分支是目标分支(“我们的” )。
S
Sicco

尝试这个:

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

接受您的更改: git merge --strategy-option ours


请注意,这将保留您对所有冲突文件的更改,因此如果发生意外冲突可能会很危险。
您可以将其用于其他合并-y 命令,例如cherry-pick 和 rebase。
有什么方法可以指定一个只需要 theirs 的目录,但要手动解决冲突?例如:git merge --strategy-option theirs some/dir。因此,如果路径 some/dir 的文件中存在冲突,则接受他们的冲突,但如果不是,那么问我?
我有fatal: No current branch.
k
kynan

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

不适合我......这些是用于 bash 还是其他一些 shell?
这些是 git 别名,请将它们添加到 ~.gitconfig[alias] 部分或使用 git config --global accept-ours "..."。已编辑我的答案。
你不知道这个别名为我节省了多少时间。竖起大拇指!
@hakre 确保引用别名,否则您的shell 将尝试解释它。或者只是手动编辑您的 ~/.gitconfig
默认值的 Shell 语法:!f() { git checkout --ours -- "${@:-.}" git add -u "${@:-.}; }; f
D
Dar

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

请写出对其他人有帮助的答案。
P
Parakleta

解决冲突的理想情况是您提前知道要以哪种方式解决冲突,并且可以通过 -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 ''?这在我的系统上不存在。
@simohe 尝试 sed -i '' 。它只允许就地编辑。无论如何我应该使用小写,所以我会更新帖子。