Git 改变变基
Git Changing rebase
我有 3 个分支:master、feature、bugfix...提交看起来像这样:
4-5-6(feature)
|
1-2-3(master)
|
7(bugfix)
我 "git rebase bugfix feature" 是为了用错误修复测试我的功能
1-2-3(master)
|
7(bugfix)-4-5-6(feature)
现在我需要在没有错误修复的情况下为我的功能分支创建拉取请求,所以我做了 "git rebase master feature" 并期望:
1-2-3(master)-4-5-6(feature)
|
7(bugfix)
相反,它说功能是最新的。没错,但我不想在那里合并提交 7。
我可以做 rebase interactive 并删除那个提交,但我想知道是否有更好的方法来做到这一点。
我以为 rebase 只会将 1 个分支中的提交带到另一个分支,但看起来不是。
需要注意的是,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。原始提交仍然存在,但没有任何指向它们。他们最终会被清理干净,但他们会在那里呆几天。
这意味着您可以撤消变基,这正是您想要做的。您需要找到 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
将来,如果在进行变基之前 git tag
功能的原始位置,这种事情就会变得容易得多。然后您可以 git reset
返回到该标签,而无需搜索 reflog。
至于你的具体问题,问题是在变基之后你的存储库现在看起来像这样:
6A [feature]
|
5A
|
4A
|
7 [bugfix]
|
3 [master]
|
2
|
1
当您询问 git rebase master feature
时,Git 注意到 master 已经是 feature 的祖先并且什么都不做。介于两者之间的错误修复并不重要。
相反,您需要告诉 Git 您只想变基 4A、5A 和 6A 而忽略 7。这是使用 --onto
语法完成的。
git rebase --onto master bugfix feature
这表示要从但不包括错误修复改基到 master 上。
我建议使用 git reset
而不是尝试重做变基。不能保证第二次变基会产生相同的结果,尤其是在存在冲突的情况下。而使用 git reset
,您将明确返回到存储库的旧状态。
I thought rebase would only carry the commits in 1 branch to another but looks like it's not.
这是关键:您在图表 中的提交 7
是分支 feature
中的。它也在分支 bugfix
中。提交 1-2-3
在 所有三个 分支中。
Git的分支与大多数其他版本控制系统有很大不同。分支 "contains" 仅凭借能够 "reach" 从分支名称指向的提交提交。 master
、bugfix
和 feature
等分支名称仅指向 一个特定的 提交,Git 调用 tip 的分支。通过让每个提交 "point back" 到其前身,提交本身形成了一条链。
因此,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
,例如,如果我们愿意的话,但是这种技术即将 运行失去动力)。
虽然你仍然可以得到你想要的。你只需要 copy D-E-F
again,或者——这对于这种特殊情况可能更好——回到废弃的,原文4-5-6
.
当您使用 git rebase
复制提交时,原件会保留下来。您可以通过两个名称找到它们:ORIG_HEAD
和 reflog 名称。名称 ORIG_HEAD
被各种其他命令覆盖,但您可以检查它是否仍指向提交 6
:
git log ORIG_HEAD
您可能会认出您的原件。
reflog 名称的格式为 <em>name</em>@{<em>number</em>}
,例如,feature@{1}
。每次更改 name
部分指向的提交时, number
部分都会递增,如 Git 只是将 name
的当前值保存在 reflog 中,将其余值全部提高一个档次。
因此:
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
清除了尚未签入的索引和工作-树改变了。)
我有 3 个分支:master、feature、bugfix...提交看起来像这样:
4-5-6(feature)
|
1-2-3(master)
|
7(bugfix)
我 "git rebase bugfix feature" 是为了用错误修复测试我的功能
1-2-3(master)
|
7(bugfix)-4-5-6(feature)
现在我需要在没有错误修复的情况下为我的功能分支创建拉取请求,所以我做了 "git rebase master feature" 并期望:
1-2-3(master)-4-5-6(feature)
|
7(bugfix)
相反,它说功能是最新的。没错,但我不想在那里合并提交 7。 我可以做 rebase interactive 并删除那个提交,但我想知道是否有更好的方法来做到这一点。 我以为 rebase 只会将 1 个分支中的提交带到另一个分支,但看起来不是。
需要注意的是,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。原始提交仍然存在,但没有任何指向它们。他们最终会被清理干净,但他们会在那里呆几天。
这意味着您可以撤消变基,这正是您想要做的。您需要找到 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
将来,如果在进行变基之前 git tag
功能的原始位置,这种事情就会变得容易得多。然后您可以 git reset
返回到该标签,而无需搜索 reflog。
至于你的具体问题,问题是在变基之后你的存储库现在看起来像这样:
6A [feature]
|
5A
|
4A
|
7 [bugfix]
|
3 [master]
|
2
|
1
当您询问 git rebase master feature
时,Git 注意到 master 已经是 feature 的祖先并且什么都不做。介于两者之间的错误修复并不重要。
相反,您需要告诉 Git 您只想变基 4A、5A 和 6A 而忽略 7。这是使用 --onto
语法完成的。
git rebase --onto master bugfix feature
这表示要从但不包括错误修复改基到 master 上。
我建议使用 git reset
而不是尝试重做变基。不能保证第二次变基会产生相同的结果,尤其是在存在冲突的情况下。而使用 git reset
,您将明确返回到存储库的旧状态。
I thought rebase would only carry the commits in 1 branch to another but looks like it's not.
这是关键:您在图表 中的提交 7
是分支 feature
中的。它也在分支 bugfix
中。提交 1-2-3
在 所有三个 分支中。
Git的分支与大多数其他版本控制系统有很大不同。分支 "contains" 仅凭借能够 "reach" 从分支名称指向的提交提交。 master
、bugfix
和 feature
等分支名称仅指向 一个特定的 提交,Git 调用 tip 的分支。通过让每个提交 "point back" 到其前身,提交本身形成了一条链。
因此,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
,例如,如果我们愿意的话,但是这种技术即将 运行失去动力)。
虽然你仍然可以得到你想要的。你只需要 copy D-E-F
again,或者——这对于这种特殊情况可能更好——回到废弃的,原文4-5-6
.
当您使用 git rebase
复制提交时,原件会保留下来。您可以通过两个名称找到它们:ORIG_HEAD
和 reflog 名称。名称 ORIG_HEAD
被各种其他命令覆盖,但您可以检查它是否仍指向提交 6
:
git log ORIG_HEAD
您可能会认出您的原件。
reflog 名称的格式为 <em>name</em>@{<em>number</em>}
,例如,feature@{1}
。每次更改 name
部分指向的提交时, number
部分都会递增,如 Git 只是将 name
的当前值保存在 reflog 中,将其余值全部提高一个档次。
因此:
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
清除了尚未签入的索引和工作-树改变了。)