可能性
我能想到这可能发生的几种方式。
快进合并。 我认为这是最可能的原因。这就是我认为发生的事情:
release-1.3
被合并到develop
- 紧接着,
develop
被快进合并到release-1.3
就这样。 release-1.3
被合并到develop
,然后在对release-1.3
某人进行任何其他提交之前合并develop
到release-1.3
. 如果可能,git
默认情况下会进行快进合并(一个“功能”,一半时间做错事),这就是图表看起来令人困惑的原因。
请注意,快进合并不会在结果图中留下直接证据。与常规合并不同,快进合并不会创建新的提交对象,也不会修改现有的提交。快进合并只是将分支引用调整为指向现有提交。
尽管没有直接证据表明快进合并,但您可以通过遵循每个分支的第一个父路径到达该合并提交这一事实强烈表明这是快进合并的结果。
偶然git branch
而不是git checkout
。在我正在处理的一个项目中,另一位刚接触 Git 的开发人员犯了git branch foo
一个错误,试图切换到 branch foo
。这是一个非常自然的错误,即使是专家也会在疲倦时犯。不管怎样,这个错误最终导致了一些看起来就像快进合并的东西,即使git merge
从未输入过。类似的事情也可能发生在这里。剧情是这样发展的:
- 用户
develop
签出了分支,并且develop
恰好指向位于这个谜团中心的合并提交。
- 用户希望切换到
release-1.3
分支做一些工作,但不小心输入git branch release-1.3
了git checkout release-1.3
.
- 因为它是一个全新的克隆,所以还没有本地
release-1.3
分支(只有origin/release-1.3
)。因此,Git 愉快地创建了一个新的本地分支,命名release-1.3
为指向同一个合并提交。
- 用户编辑某些文件时会经过一段时间。
- 准备提交,用户运行
git status
. 因为当前分支仍然develop
不是release-1.3
,Git 会打印“On branch develop”。
- 用户大吃一惊,心想:“我不是很久以前切换到release-1.3了吗?哦,我一定忘记切换分支了。”
- 用户运行
git checkout release-1.3
,这次记住了正确的命令。
- 用户创建提交并运行
git push
.
- Git 的默认推送行为(参见 参考资料
push.default
)git help config
是matching
,因此即使release-1.3
没有配置上游分支,也会git push
选择release-1.3
要推送到的上游分支。
- 新版本
release-1.3
是前一个的后代release-1.3
,所以远程仓库很乐意接受推送。
这就是生成您在问题中提供的图表所需的全部内容。
假设develop
被故意合并到release-1.3
如果 into 的合并develop
是release-1.3
故意的(有人做出了一个develop
足以发货的有意识的决定),这是完全正常和正确的。尽管有视觉上的差异,但蓝色和黑色的线条都在release-1.3
树枝上;蓝线恰好也在develop
分支上。
唯一错误的是,回顾历史以弄清楚发生了什么有点尴尬(否则你不会有这个问题)。为防止这种情况再次发生,请遵循以下经验法则:
- 如果您要合并两个不同名称的分支(例如,
develop
和release-1.3
,那么总是做git merge --no-ff
.
- 如果您要合并同一分支的两个版本(例如,
develop
和origin/develop
),那么总是这样做git merge --ff-only
。如果因为您无法快进而失败,那么是时候使用git rebase
.
如果您遵循上述经验法则,那么图表将如下所示:
* (develop)
| * (release-1.3)
* | Merge...
|\|
| * Added...
| * using ...
* | adding...
| * Hide s...
* | Date ...
* | updati...
* | Candi...
| * Locali...
| * <---- merge commit that would have been created by
|/| 'git merge' had you used the '--no-ff' option
* | Merge...
|\|
| * Un-ign...
| * Added...
* | Merge...
|\|
| * Remov...
| * Move...
* | Fixed...
请注意额外的合并提交如何使历史更具可读性。
如果 into 的合并develop
是release-1.3
一个错误
嗬!看起来你有一些变基和强制推送要做。这不会使该存储库的其他用户满意。
以下是您可以解决的方法:
- 运行
git checkout release-1.3
。
- 找到那个中间提交的 sha1(两个分支汇合的地方)。让我们称之为
X
。
运行git rebase --onto X^2 X
。生成的图表将如下所示:
* (develop)
| * (release-1.3)
* | Merge...
|\ |
| * | Added...
| | * Added...
| * | using ...
| | * using ...
* | | adding...
| * | Hide s...
| | * Hide s...
* | | Date ...
* | | updati...
* | | Candi...
| * | Locali...
|/ * Locali...
* / Merge...
|\|
| * Un-ign...
| * Added...
* | Merge...
|\|
| * Remov...
| * Move...
* | Fixed...
这修复了release-1.3
分支,但请注意您现在有两个版本的release-1.3
提交。下一步将从develop
分支中删除这些重复项。
- 运行
git checkout develop
。
- 运行
git branch temp
以充当此提交的临时占位符。
- 运行
git reset --hard HEAD^^
以从develop
分支中删除两个提交:提示develop
提交和合并旧版本release-1.3
into的提交develop
。我们稍后会恢复该提示提交。
- 运行
git merge --no-ff release-1.3^
以将新分支上的第二个提交release-1.3
及其祖先合并到develop
.
- 运行
git cherry-pick temp
以恢复在步骤 #6 中删除的提示提交。
运行git branch -D temp
以摆脱临时占位符分支。您的图表现在应该如下所示:
* (develop)
| * (release-1.3)
* | Merge...
|\|
| * Added...
| * using ...
* | adding...
| * Hide s...
* | Date ...
* | updati...
* | Candi...
| * Locali...
* | Merge...
|\|
| * Un-ign...
| * Added...
* | Merge...
|\|
| * Remov...
| * Move...
* | Fixed...
运行git push -f origin release-1.3 develop
以强制更新上游分支。
防止这种情况在未来再次发生
如果您可以控制上游存储库并且可以安装一些钩子,您可以创建一个钩子,通过从新版本的分支开始并走第一个父路径来拒绝无法访问旧版本分支的任何推送. 这还具有拒绝由git pull
.