5

在尝试压缩/修复线性分支时,我怎么可能仍然需要手动合并?该 repo 已从 Subversion 转换。每个冲突都是“自动挑选失败”或“由于提交消息为空而中止提交”。后者我可以理解,但一个--fixup-empty或一些东西会很有用。

典型输出:

user@machine:/path (master|REBASE-i)$ git add * && git rebase --continue 
[detached HEAD c536940] fixup!
 Author: John Doe <John.doe@example.com>
 2 files changed, 57 insertions(+), 4 deletions(-)
Automatic cherry-pick failed.  After resolving the conflicts,
mark the corrected paths with 'git add <paths>', and
run 'git rebase --continue'
Could not apply 8854a54... >6d5f180 foo
user@machine:/path (master|REBASE-i)$ git st
# Not currently on any branch.
# Unmerged paths:
#   (use "git reset HEAD <file>..." to unstage)
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
#   both modified:      filename.ics
#
no changes added to commit (use "git add" and/or "git commit -a")
4

3 回答 3

2

这些工作:

git mergetool
git rebase --continue
于 2011-10-19T09:20:32.067 回答
2

这是我的建议,以实现您拥有某种--fixup-empty功能的想法:

git filter-branch --msg-filter "sed 's/^$/Unknown/'"

这会将空提交消息替换为“未知”,如果您想在使用git-svn转换具有一些空提交消息但不能因为“由于空提交消息而中止提交”而失败的 Subversion 存储库后进行变基,这将特别有用.

于 2011-05-03T20:37:10.450 回答
0

我遇到了同样令人费解的问题。我想将一大堆顺序提交的更改转换为单个提交,但是使用带有一系列 squash 的交互式 rebase 会令人沮丧地产生合并冲突。这是很久以前的事了,我刚才无法在一个最小的例子中重现它。无论如何,我确实知道如何解决它,所以你去:

我将介绍两种不同的方式来完成帖子所要求的工作——一种笨重但“不那么可怕”,因为它很清楚你在做什么,另一种很优雅,只使用git命令,但需要你相信你的理解git多一点。

设置

假设您有一个提交 IDidA作为初始状态,提交 IDidB作为最终状态,中间还有一堆其他提交。您想要压缩所有中间提交的事实表明您不再关心您是如何从初始状态到最终状态的——您只需要一个将您从一个点移动idA到另一个点的提交idB

让我们假设它idB对应于当前HEAD并说它也是你的master分支。我们将创建一个名为的新分支squashed,它具有与 相同的工作树内容master,但在提交 ID 之后只有一个提交idA

解决方案 1

  1. git checkout master(你可能已经在master
  2. 使用bash或您的文件资源管理器将整个工作树复制到其他地方。只需确保您忽略了.git目录——这是大多数操作系统的默认行为。所以,要明确一点——打开 repo 文件夹,全选,复制并粘贴到某个新文件夹中,在您的桌面或任何您想要的地方。
  3. git checkout -b squashed idA创建一个新分支作为其最新提交,并将squashedidA设为当前分支。
  4. master将(您在步骤 2 中放在其他位置)的内容粘贴回 repo 文件夹。如果您的文件资源管理器询问,请告诉它替换所有已更改的文件。
  5. 在你的 repo 的顶层,git add .然后git commit. 您的新提交将包含将您从idA带到的所有更改idB

解决方案 2

git branch squashed idA # make a new branch called `squash` with `idA` as its latest commit 
git checkout master # master is "idB" in this case.
git symbolic-ref HEAD refs/heads/squashed
git commit

瞧。当您使用symbolic-ref将 HEAD 移动到squash分支的顶端时,工作树状态和新 HEAD 位置之间的差异集将被计算和暂存。

我想回应一些人表示这是一种“危险”、“低级”的git黑客攻击的担忧。我会尽量解释它是如何工作的,你可以放心。您的.git文件夹是所有版本化文件内容所在的位置。其中的两个文件夹是objectsrefs。该refs文件夹包含的文件告诉 git 你的分支的名称以及它们对应的提交。因此,例如,如果您打开.git/refs/heads/master,您将看到最新提交到master. 该objects文件夹包含一堆文件,这些文件位于具有两个字符的十六进制名称的子文件夹中。对象可以是几个不同的东西,包括补丁、提交和整个文件。.git文件夹的顶层还有一个index文件。这是一篇关于索引文件内容的好文章。对于当前的讨论,您需要知道的是索引文件告诉您哪个对象(在objects文件夹中)对应于当前分支上每个文件的最新提交版本并提交。

在这种背景下,解决方案的作用如下:该symbolic-ref命令只是git“突然”告诉您您在squash分支而不是master分支上,而无需触及工作树。这意味着您的文件都对应于master您所知道和喜爱的状态,但是git将其视为idA一堆未提交的更改(即,index文件指定所有文件的当前签出版本是 的idA,并且这是衡量工作树变化的参考)。事实上,确切的变化会让你明白idB. 这正是解决方案 1 所做的。在这种情况下,由于方式symbolic-ref实施后,所有上述更改都已上演(即git add-ed),因此您所要做的就是git commit. 这并没有什么“危险”,因为它不会改变master或任何objects正在git跟踪的东西。您刚刚在refs名为 的文件夹中创建了一个新文件squashed,并在文件夹中创建了一个objects与该新组合提交相对应的新文件。你master就在你离开它的地方,你只是有一个新的分支squashed,它的内容相同,但提交历史更短。如果您不确定发生了什么,请放心,您可以放心git checkout master,它就在您离开的地方。

如果你想领养squashed你的新人master,你可以继续

git branch -m master old-master
git branch -m squashed master

然后您将拥有所有多余提交的旧分支保存为old-mastermaster这正是您想要的。

希望这可以帮助!

于 2016-08-14T00:44:07.927 回答