当我在合并过程中与dev分支合并时,如何压缩提交记录?

3

我已经在一个功能分支上工作了几天,现在它已经准备好合并到dev中。在开发这个功能的过程中,我已经与dev合并以接收一个补丁。我的历史记录看起来像这样:

* E (feature1)
* D:merge with dev
|  \ 
* C * B:patch (dev)
  \ | 
    * A

我想将整个分支压缩为一个提交,与dev合并,然后快进dev。问题是,由于合并出现在它们之间,所以无法将EC压缩。唯一的选择似乎是将 E, BC压缩(称新提交为F),在这种情况下,压缩提交还将包括作为无关补丁的一部分的更改内容。一旦合并到dev中,就会有两个应用该补丁的提交:F(应用补丁并添加功能)和B(仅应用该补丁)。此外,F现在将进行两个不相关的更改。
是否有一种方法可以使我的历史记录保持整洁?我需要改变我的工作流程吗?

1
推荐的工作流程变更:不要合并开发分支,而是在其上进行变基。你现在只需简单地在dev上进行变基即可解决这个问题。 - undefined
同意(双方)。会尝试一下。 - undefined
@o11c - 重新定位会改变你的分支历史。虽然我是个粉丝,但有时候它可能有些危险,特别是如果你不知道自己在做什么的话。此外,当发生冲突时,重新定位会变得非常烦人,因为每次重新定位都需要重新解决冲突,而合并只需要一次解决冲突,然后继续前进。 - undefined
3个回答

1
Git不跟踪提交中的更改。每个提交包含存储库中文件的完整副本。“更改”是通过对比两个提交来确定的。
因此,简而言之,将EBC压缩成提交F,然后将其合并到dev分支上绝对没有问题。 B提交仍将存在于dev分支上。当git将FB进行比较时,只会将CE引入的更改归属于F
当然,您可以通过使用git rebase来解决这个问题,但这会带来一系列其他问题。例如,由于git rebase更改了您的分支历史记录(将您的提交移动到来自dev的最新提交之上),如果存在冲突,则每次重新基础时都必须重新解决这些冲突。如果您的问题需要一段时间才能解决,需要多次重新基础以保持与dev同步,那么这种情况很快就会变得无聊。

为了证明这一切都是真实的,你无需担心,我已经设置了一个GitHub示例存储库: https://github.com/cyborgx37/sandbox

首先,我们有dev分支,它有B提交。

B:patch
|
A

我创建了feature1分支,其中包含提交记录CDE。(请注意,因为D是一个合并提交,因此有两个父提交,B也出现在提交历史中)

E
|
D:merge with dev
|\
C \
|  B
A

最后,还有dev-with-feature1分支

F
|
B:patch
|
A

我从dev创建了这个分支,然后使用了

git merge --squash feature1
git commit -m "F"

feature1的所有提交压缩为单个提交F。如果您检查F提交的差异, 您会发现它不会"应用补丁"。由于B已经包含了这些更改, 而F只是重复了它们,git不会将它们与F关联。

从另一个角度来看,这是责任链:

initial     hello!
B:patch     patch!
F           new feature!!!

Git不跟踪提交中的更改。它存储您的完整项目状态。通过将提交与其父(或前任)提交进行比较,确定“更改”。因为B有补丁,所以git将该更改与B关联。因此,预计补丁也会在F中。唯一的例外是如果您删除了补丁,那么它就不会在F中。

就这样想一下:F的提交消息说它实现了feature1。但实际上,当你从dev中的F^移动到F时,你也会得到补丁。基本上,提交消息是在撒谎。 - undefined
也许我理解错了。你打算从开发分支中移除B吗?如果不是的话,那就没有问题。F应该包含B,因为FB之后。F将包含所有文件状态的完整副本,所以当你尝试确定F引入了哪些更改时,git会将FB进行比较并给出差异。由于补丁已经存在于B中,git不会将其视为"属于" F - undefined
@Farshid - 把git想象成一个装满压缩文件的文件夹。每个压缩文件都有一个日期标记,所以你可以追踪顺序。每个压缩文件包含了当天的完整源代码。如果你想知道在任何特定的一天引入了什么,你可以将那天的压缩文件与前一个压缩文件进行比较。这基本上就是git的工作原理...每次提交都是所有文件状态的完整副本,而不是变更记录。 - undefined
@Farshid - 如果你对此感到担心...可以克隆你的分支并进行实验。首先检出feature1,然后运行git checkout -b feature1-clone,然后检出dev并运行git checkout -b dev-clone,然后尝试一下,看看是否得到了你期望的结果。 - undefined
假设我在feature1中合并了E、B和C。那么我们会得到类似以下的结果:A--B(开发者)和A--F(feature1)(无法在评论中画出正确的树形结构)。尽管按时间顺序,F在B之后,并且包含了B引入的更改,但在某个时刻,F应该拥有B作为祖先。在我们的情况下,这并不成立。 - undefined
@Farshid - 我已经在GitHub上更新了我的回答,附有一个实际的例子。你一直认为提交(commit)会"应用"或者"改变"代码,但实际上并不是这样的。提交只是整个代码库的一个快照。Git通过比较提交与前一个提交来确定"有什么改变"。你可以从我的例子中看到这一点。 - undefined

1

如果我理解正确,你希望将提交E的整个内容作为一个单独的提交添加到B之上。

如果这是你想要实现的目标,你可以使用git checkout E .(不要忘记"."):

# go to your `dev` branch :
git checkout dev

# get the *content* of E (here comes the ".") :
git checkout E .

# all the content of E should appear as modifications staged for commit :
git status -sb

# you can double check that the content is to your liking :
git diff --cached  # --cached means 'compare with the index'
gitk --cached      #  as opposed to 'compare with what is on the disk'

# commit
git commit

这是一个非常有趣的方法。不过我至少能看到一个问题:只有在将合并结果放入开发环境后,你才能知道它是否有效,而不是先合并(或基于)开发环境,测试它,然后快进。当然,如果不起作用,你不必在开发环境中提交它。但另一个问题是你无法创建合并提交,这有助于保持良好的历史记录。 - undefined

0
这是我做的事情:我将E,B和C压缩成一个新的提交F,并为F的消息说它实现了feature1(注意到目前为止它还添加了修补程序)。然后我再基于dev进行变基。现在提交消息和历史记录是正确的:F包含了该修补程序,但它没有添加它--它是由之前的提交添加的。
最后,我与dev合并(使用--no-ff强制生成合并提交)。
将来,我将避免与dev合并,而改为变基。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接