我有3个分支:master,feature,bugfix ...并且提交看起来像这样:
4-5-6(feature)
|
1-2-3(master)
|
7(bugfix)
我做了" git rebase bugfix功能"用bugfix测试我的功能
1-2-3(master)
|
7(bugfix)-4-5-6(feature)
现在我需要为我的功能分支创建一个不带错误修复的拉取请求,所以我做了#34; git rebase master feature"期待:
1-2-3(master)-4-5-6(feature)
|
7(bugfix)
相反,它说功能是与主人最新的。这是真的,但我不想在那里合并提交7。 我可以做rebase交互并删除提交,但我想知道是否有更好的方法来做到这一点。 我认为rebase只能将1个分支中的提交带到另一个分支,但看起来并非如此。
答案 0 :(得分:2)
要意识到的是,rebase不会重写历史记录或移动提交,Git中的提交无法更改。相反,它创造了新的历史,并说它一直都是这样。例如,当你开始:
4-5-6(feature)
|
1-2-3(master)
|
7(bugfix)
然后git rebase bugfix feature
真正发生的是:
4-5-6
|
1-2-3(master)
|
7(bugfix)-4A-5A-6A(feature)
提交了三个新提交,4A,5A和6A。原始提交仍然存在,但没有任何指向他们。他们最终会被清理干净,但他们会留在那里好几天。
这意味着你可以撤消一个rebase,这正是你想要做的。你需要在rebase之前找到feature
的位置。这可以通过每次git reflog
移动时跟踪的HEAD
来完成。这种情况发生在checkout
,commit
,reset
和rebase
。 git reflog
可能类似于:
65e93ca (HEAD -> feature) HEAD@{0}: rebase finished: returning to refs/heads/feature
65e93ca (HEAD -> feature) HEAD@{1}: rebase: 3 feature
6d539a3 HEAD@{2}: rebase: 2 feature
3cd634f HEAD@{3}: rebase: 1 feature
b84924b (bugfix) HEAD@{4}: rebase: checkout bugfix
a9fd2f1 HEAD@{5}: commit: 3 feature
29136bc HEAD@{6}: commit: 2 feature
60543b0 HEAD@{7}: commit: 1 feature
c487530 (master) HEAD@{8}: checkout: moving from master to feature
这告诉我a9fd2f1是在重新定位之前最后一次提交功能。我可以重新移动功能,而不是重做rebase。
git checkout feature
git reset --hard a9fd2f1
将来,如果在执行rebase之前git tag
功能的原始位置,这种事情会变得容易多了。然后,您可以git reset
返回该标记,而无需搜索reflog。
关于您的具体问题,问题是在rebase之后您的存储库现在看起来像这样:
6A [feature]
|
5A
|
4A
|
7 [bugfix]
|
3 [master]
|
2
|
1
当你问git rebase master feature
时,Git注意到master已经是一个功能的祖先,什么都不做。 bugfix介于两者之间并不重要。
相反,你需要告诉Git你想要仅修改4A,5A和6A并忽略7.这是使用--onto
语法完成的。
git rebase --onto master bugfix feature
这就是说要将错误修正,但不包括在主人身上。
我建议使用git reset
而不是尝试重做rebase。不能保证第二个篮板会出现同样的情况,特别是如果有冲突的话。而使用git reset
,您将明确地移回存储库的旧状态。
答案 1 :(得分:2)
我认为rebase只会将1个分支中的提交带到另一个分支但看起来不是。
这是关键:您的图表中的提交7
在分支feature
中。它也在分支bugfix
中。提交1-2-3
在所有三个分支中。
Git的分支与大多数其他版本控制系统截然不同。分支“包含”提交只是因为能够“到达”从分支名称指向的提交提交。像master
,bugfix
和feature
这样的分支名称只是指向一个特定的提交,Git调用该分支的提示 。通过让每个提交“回头”到它的前身,它就是构成链的提交本身。
因此,git rebase
实际副本提交:您来自:
4--5--6 <-- feature
/
1--2--3 <-- master
\
7 <-- bugfix
为:
4--5--6 [abandoned - used to be feature]
/
1--2--3 <-- master
\
7 <-- bugfix
\
D--E--F <-- feature
其中D
是原始4
的副本,E
是5
的副本,而F
是6
的副本(我在这里使用了第4,第5和第6个字母,所以我们可以将7复制到G
,例如,如果我们想要,但这种技术即将耗尽。)
但你仍然可以得到你想要的东西。你只需要复制 D-E-F
再次,或者 - 对于这个特殊情况,这可能更好 - 只需回到被遗弃的原始4-5-6
当您使用git rebase
复制提交时,原件会一直存在。您可以通过两个名称找到它们:ORIG_HEAD
和 reflog 名称。名称ORIG_HEAD
被其他各种命令覆盖,但您可以检查它是否仍然指向提交6
:
git log ORIG_HEAD
你可能会认出你的原件。
reflog名称的格式为name@{number}
,例如feature@{1}
。每次更改 number
部分指向的提交时, name
部分都会递增,因为Git只会保存的当前值在reflog中name
,将其余部分推到一个档次。
因此:
git log feature@{1}
应该向您显示与git log ORIG_HEAD
相同的提交,但feature@{1}
更长时间(可能会变成feature@{2}
,feature@{3}
等等,随着时间推移)。默认情况下,每个名称的先前值至少保存30天,因此应该有足够的时间将其恢复。
要将其恢复,请使用git reflog feature
查看@{...}
部分中的哪个号码,然后 on feature
(git checkout feature
),运行:
git reset --hard feature@{1}
或任何数字(虽然首先用git log
再次验证是个好主意)。
(这假设您没有要检查的内容,即git status
表示一切都很干净,因为git reset --hard
消除了尚未检入的索引和工作树更改。)