我有一个功能分支featureBranch
,它尚未合并到main
。我现在需要根据main
的最新提交重新调整我的功能分支,所以我这样做了:
git log
# commit <commitID-3>
# commit <commitID-2>
# commit <commitID-1>
git status
# Your branch is up to date with 'origin/featureBranch'
git checkout main
git pull
git checkout featureBranch
git rebase main
# error: could not apply <commitID-1>... initial first commit
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
Could not apply <commitID-1>... initial first commit
git status
# shows ALL the files that haven't been changed since the latest commit and rather changed across different commits within featureBranch
因此,由于需要手动解决的文件中的冲突,rebase失败了。但是,在运行git status
之后,我看到了在featureBranch
中更改/创建的所有文件。它们也是旧的修订版。
这是意料之中的事吗?如果我执行git rebase --continue
,我是否提交文件的旧版本?
git-rebase应该应用第一次提交吗?!
首先:这个问题最常见的根源是行尾格式的更改。为了避免这种情况,请使用--ignore-cr-at-eol
或类似产品。同时,回答您的最后一个问题:
git-rebase应该应用第一次提交吗?!
有时,是的。
理解git rebase
:有七个左右的关键
- 提交本质上是所有文件的快照
- 提交的哈希ID是它的真名:Git需要哈希ID才能找到提交
- 分支名称只存储一个哈希ID。无论该分支名称中的哈希ID是什么,都是该分支上的最后一次提交
- 每个提交都记录其父级,特别是其哈希ID
- 没有任何承诺,一旦做出,就永远无法更改,所以Git不会尝试这样做
git cherry-pick
命令可以通过将提交与其父提交区分开来,将提交(快照)转换为更改。这是假设只有一个家长;请参阅下面的一些特殊情况。把承诺变成了";对这些文件进行这些改变";,Git现在可以使用这些指令对一些其他提交中的相同文件进行相同的更改。从技术上讲,这是一个完全的三方合并,只是Git没有将结果保存为合并提交(有两个父级),而是保存为一个新的单亲提交。最终结果是复制一个提交,有点像- 由于更改提交(包括它们的父代)是不可能的,
git rebase
并没有这样做:相反,它列出了一个提交列表,大概除了它们的祖先之外,你最喜欢这些提交。然后,它检查出一些其他提交,并从之前的列表中运行一系列git cherry-pick
操作,每个提交一个。然后git rebase
取找到要复制的提交的分支名称,并将其拉来拉去,以便现在找到刚刚复制的提交
在大多数情况下,这一切的最终结果正是你想要的。您从例如:开始
C--D--E--F <-- feature (HEAD)
/
...--A--B--G--H <-- mainline-or-develop
例如,其中每个大写字母代表一个丑陋的大散列ID。您喜欢feature
上的提交,除了一件事:它们从提交B
开始。如果它们构建在提交H
上,您会更希望它们,如下所示:
C--D--E--F [abandoned: old and lousy]
/
...--A--B--G--H <-- mainline-or-develop
C'-D'-E'-F' <-- feature (HEAD)
通过列出原始C-D-E-F
提交的哈希ID,然后签出提交H
并运行四个git cherry-pick
操作,最后拖动名称feature
,使其找到提交F'
——过去是F
的新的改进副本——git rebase
最终会得到您想要的。
但假设你有这个:
A--B--C--D <-- br1 (HEAD)
E--F--G--H <-- br2
这里,在"提交"上的提交之间不存在父/子或祖先/后代关系;顶行";(通过br1
找到)和";底部行";(通过br2
找到)。如果运行:
git rebase br2
现在,Git将列出四个提交哈希IDA-B-C-D
中的每一个。这包括根提交A
,它没有父级。
这就是cherry-pick的特殊情况。每当我们有单亲以外的东西时,cherry-tick都需要一些帮助,将提交转化为一组更改。对于合并提交,Git需要告诉Git将哪个父级视为";";暂时成为家长。但对于根提交——一个没有父级的提交——Git可以假设前面有某种ur提交ε
,其中根本没有文件,并与空树进行比较。与空的提交ε
相比,提交A
中的所有文件都是新添加的。
如果提交A
所在的提交H
与提交A
具有相同的文件集,则会在每个文件上添加错误。这并不罕见:例如,当有人使用历史改写工具时,就会发生这种情况。
请注意,git rebase
命令有更高级的形式来处理其中的一些问题。例如,假设我们想在br1
和-br2
设置中将复制为提交C-D
到H
之上。我们可以运行:
git rebase --onto br2 br1~2 # or HEAD~2
--onto
参数将重载的必需参数(git rebase
将其称为上游,这可能不是这里的最佳单词选择)分离出来,以便它可以用于列出哈希ID或其他标识符,这些标识符定位第一个(或最后一个,如果您向前而不是向后读取历史记录)提交而不是进行复制。