自动在重新基础操作期间解决文件已删除的冲突。

huangapple go评论45阅读模式
英文:

Automatically resolve conflicts during rebase when files are deleted

问题

在大型代码库中进行了大规模的搜索和替换,创建了许多新的提交。在我开始的时候和尝试完成 PR 的时候,一些文件已经在目标分支(我们称之为 main 目标分支)上被删除了。通常情况下,我可能会像这样做:

git fetch
git rebase origin/main my-feature-branch
# 现在我在所有已删除文件上有冲突
git rm <list-of-deleted-files-here>
git rebase --continue

但在这种情况下,我必须为许多提交重复这个过程,而且这需要时间。尽管我喜欢将我的功能分支进行变基,但我可能会选择放弃,而是执行合并操作:

git fetch
git switch my-feature-branch
git merge origin/main
# 现在我在所有已删除文件上有冲突
git rm <list-of-deleted-files-here>
git merge --continue

通过合并,我只需要执行额外的 git rm 步骤一次,而不是对于每个提交都需要 N 次,如果选择变基的话。这是一个可以接受的解决方案,但我很固执,我真的不想在我的功能分支中添加合并提交,只要可能的话。(而且我确信可以自动化变基)。实际上,我正在寻找类似于 git rebase origin/main -X ours 这样的选项,不过它也可以在其中一方删除文件的情况下工作。(请注意,-X ours/theirs 的自动冲突解决仅在两侧都更改文件时才有效;当一侧删除文件时它不起作用。)

附注: 我觉得类似于 -X ours --include-deleted 的选项可能会很有用。

英文:

I did a massive search and replace in a large codebase of multiple strings, and created many new commits. Between the time when I started, and when I tried to complete the PR, some of the files had been deleted on the target branch (let's call that target branch main). Normally I might do something like this:

git fetch
git rebase origin/main my-feature-branch
# I now have conflicts on all deleted files
git rm &lt;list-of-deleted-files-here&gt;
git rebase --continue

But in this case, I have to repeat that process for many commits, and it's time consuming. As much as I like rebasing my feature branch, I could just throw in the towel and merge instead:

git fetch
git switch my-feature-branch
git merge origin/main
# I now have conflicts on all deleted files
git rm &lt;list-of-deleted-files-here&gt;
git merge --continue

With the merge I only have to do that extra git rm step one time instead of N times for each commit if I choose rebase. This is an acceptable solution, however, I am stubborn and I really want to avoid adding merge commits to my feature branch, whenever possible. (And I'm convinced it's possible to automate the rebase.) Essentially I am looking for something like git rebase origin/main -X ours, except which will also work when one side is deleted. (Note the automatic conflict resolution of -X ours/theirs only works when both sides change the file; it doesn't work when one side deletes the file.)

Side Note: I feel like an option similar to -X ours --include-deleted might be nice.

答案1

得分: 1

I did something similar by iteratively doing grep CONFLICT in the rebase output, and doing git rm on that, using xargs. Here is how I did it:

git rebase --merge -s recursive -Xtheirs the_other_branch|tee reb1.log
grep CONFLICT reb1.log|cut -f 2 -d &quot;:&quot; | cut -f 2 -d &quot; &quot;|xargs git rm
git rebase --continue|tee reb2.log
grep CONFLICT reb2.log|cut -f 2 -d &quot;:&quot; | cut -f 2 -d &quot; &quot;|xargs git rm
git rebase --continue|tee reb3.log
grep CONFLICT reb3.log|cut -f 2 -d &quot;:&quot; | cut -f 2 -d &quot; &quot;|xargs git rm
git rebase --continue|tee reb4.log
grep CONFLICT reb4.log|cut -f 2 -d &quot;:&quot; | cut -f 2 -d &quot; &quot;|xargs git rm
git rebase --continue|tee reb5.log

I had lots of file removals on the_other_branch, and changes on them in my current branch. I wanted to delete them anyway, and this worked fine. Maybe you need to modify the way cut is used, etc, according to the situation, and maybe you are not interested in looking at the rebN.log files, and then you can have the same name, of course.

This can certainly be done more elegantly and more automatized. To be sure not to destroy things, create extra branches and try there first.

英文:

I did something similar by iteratively doing grep CONFLICT in the rebase output, and doing git rm on that, using xargs. Here is how i did it:

git rebase --merge -s recursive -Xtheirs the_other_branch|tee reb1.log
grep CONFLICT reb1.log|cut -f 2 -d &quot;:&quot; | cut -f 2 -d &quot; &quot;|xargs git rm
git rebase --continue|tee reb2.log
grep CONFLICT reb2.log|cut -f 2 -d &quot;:&quot; | cut -f 2 -d &quot; &quot;|xargs git rm
git rebase --continue|tee reb3.log
grep CONFLICT reb3.log|cut -f 2 -d &quot;:&quot; | cut -f 2 -d &quot; &quot;|xargs git rm
git rebase --continue|tee reb4.log
grep CONFLICT reb4.log|cut -f 2 -d &quot;:&quot; | cut -f 2 -d &quot; &quot;|xargs git rm
git rebase --continue|tee reb5.log

I had lots of file removals on the_other_branch, and changes on them in my current branch. I wanted to delete them anyway, and this worked fine. Maybe you need to modify the way cut is used, etc, according to the situation, and maybe you are not interested in looking at the rebN.log files, and then you can have the same name, of course.

This can certainly be done more elegantly and more automatized. To be sure not to destroy things, create extra branches and try there first.

答案2

得分: 0

这是我最终采用的方法,实现了我自动化重定基的目标,时间复杂度为 O(1)(只需执行一组操作,而不是重复多次)。请注意,我在下面的命令中使用 bash

  1. 合并目标分支。请注意,我不会保留此合并,但我使用它来获取已删除文件的列表:
git fetch
git switch my-feature-branch
git merge origin/main

此时,我会有一堆文件冲突。

  1. 使用 git status 查看已删除文件的列表(或者如果唯一的冲突是删除的文件,可以使用 git diff 更容易解析)。将此列表保存到文件中:
git diff --name-only --diff-filter=U > deleted-files.txt
  1. 中止合并(git merge --abort)。
  2. 从我的分支的父提交处创建一个新分支:
git switch -c rewrite-deleted-files $(git merge-base @ origin/main)
  1. 使用一些随机文本重写每个已删除文件:
cat deleted-files.txt | while read file; do echo deleted > $file; done
  1. 提交对这些重写文件的更改:
git commit -am "wip: rewrite deleted files"

现在,我有一个与我的特性分支具有相同合并基的分支,只有一个提交,将已删除文件的整个内容替换为单词 "deleted"。

  1. 这里是很酷的部分。现在,将我的特性分支重定基到已删除文件的提交上,使用 -X ours 策略仅保留临时提交的更改:
git rebase origin/main my-feature-branch --onto rewrite-deleted-files -X ours
# 当重定基处理每个提交时,非常令人满意
  1. 现在,将所有我的好提交重定基到 main 上,不包括临时提交:
git rebase rewrite-deleted-files my-feature-branch --onto origin/main
# 再次,这非常令人满意...
  1. 删除包含文件列表的临时文件:
rm deleted-files.txt

注意: 我基于我去年写的类似答案,该答案是用于自动重写特定文件更改的。

英文:

Here's what I ended up doing, which achieves my goal of automating the rebase in O(1) time. (Meaning a set of operations I need to do just once, instead of repeating over many commits.) Note I'm using bash for my commands below.

  1. Merge in the target branch. Note I won't keep this merge, but I'm using this to get the list of files that were deleted:
git fetch
git switch my-feature-branch
git merge origin/main

At this point I have a bunch of files as conflicts.

  1. Use git status to see the list of files that were deleted. (Or if the only conflicts are deleted files, git diff is slightly easier to parse.) Save this list to a file:
git diff --name-only --diff-filter=U &gt; deleted-files.txt
  1. Abort the merge. (git merge --abort)
  2. Check out a new branch from the parent commit of my branch:
git switch -c rewrite-deleted-files $(git merge-base @ origin/main)
  1. Rewrite each of the deleted files with some random text:
cat deleted-files.txt | while read file; do echo deleted &gt; $file; done
  1. Commit the change to those re-written files:
git commit -am &quot;wip: rewrite deleted files&quot;

Now I have a branch with the same merge-base as my feature branch, with exactly one commit that replaces the entire contents of the deleted files with the word "deleted".

  1. Here's the cool part. Now rebase my feature branch onto the deleted files commit, using the -X ours strategy to only keep the change from the temporary commit:
git rebase origin/main my-feature-branch --onto rewrite-deleted-files -X ours
# It&#39;s super satisfying as the rebase goes through every commit without stopping
  1. Now rebase all of my good commits onto main without the temporary commit:
git rebase rewrite-deleted-files my-feature-branch --onto origin/main
# Once again, this is super satisfying...
  1. Delete the temporary file with the list of files in it:
rm deleted-files.txt

Note: I based this answer on another similar answer I wrote last year, which was for automating rewriting of changes to specific files.

huangapple
  • 本文由 发表于 2023年2月7日 03:08:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/75365576.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定