Setup and Config
Getting and Creating Projects
Basic Snapshotting
Branching and Merging
Sharing and Updating Projects
Inspection and Comparison
Patching
Debugging
External Systems
Server Admin
Guides
- gitattributes
- Command-line interface conventions
- Everyday Git
- Frequently Asked Questions (FAQ)
- Glossary
- Hooks
- gitignore
- gitmodules
- Revisions
- Submodules
- Tutorial
- Workflows
- All guides...
Administration
Plumbing Commands
- 2.43.0 11/20/23
- 2.42.1 11/02/23
- 2.42.0 no changes
- 2.41.0 06/01/23
- 2.40.1 no changes
- 2.40.0 03/12/23
- 2.39.3 04/17/23
- 2.39.1 → 2.39.2 no changes
- 2.39.0 12/12/22
- 2.38.1 → 2.38.5 no changes
- 2.38.0 10/02/22
- 2.37.3 → 2.37.7 no changes
- 2.37.2 08/11/22
- 2.36.3 → 2.37.1 no changes
- 2.36.2 06/23/22
- 2.35.1 → 2.36.1 no changes
- 2.35.0 01/24/22
- 2.34.1 → 2.34.8 no changes
- 2.34.0 11/15/21
- 2.33.2 → 2.33.8 no changes
- 2.33.1 10/12/21
- 2.32.1 → 2.33.0 no changes
- 2.32.0 06/06/21
- 2.31.1 → 2.31.8 no changes
- 2.31.0 03/15/21
- 2.30.1 → 2.30.9 no changes
- 2.30.0 12/27/20
- 2.29.1 → 2.29.3 no changes
- 2.29.0 10/19/20
- 2.28.1 no changes
- 2.28.0 07/27/20
- 2.27.1 no changes
- 2.27.0 06/01/20
- 2.26.1 → 2.26.3 no changes
- 2.26.0 03/22/20
概述
git rebase [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase> | --keep-base] [<upstream> [<branch>]] git rebase [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>] --root [<branch>] git rebase (--continue | --skip | --abort | --quit | --edit-todo | --show-current-patch)
描述
如果指定了 <branch>
,git rebase
会自动执行 git switch <branch>
,然后再做其他事情。 否则,它会保留在当前分支上。
如果未指定 <upstream>
,将使用 branch.<name>.remote
和 branch.<name>.merge
选项中配置的上游(详见 git-config[1]),并假定使用 `-fork-point`选项。 如果您当前不在任何分支上,或者当前分支没有配置上游,变基操作会中止。
当前分支中的所有提交所做的改动,但不在 <upstream>
中的,都会被保存到一个临时区域。 这与 git log <upstream>..HEAD
或 git log 'fork_point'..HEAD
所显示的提交相同(如果启用了 -fork-point
,请参阅下文关于 -fork-point`的描述);或者,如果指定了 `--root
选项,则与 git log HEAD
所显示的提交相同。
如果提供了 --onto
选项,当前分支会重置为 <upstream>
或 <newbase>
。 这与 git reset --hard <upstream>
(或 <newbase>
)的效果完全相同。ORIG_HEAD
设置为指向重置前分支的顶端。
Note
|
如果在重置过程中使用了其他写入伪引用的命令(例如 git reset ),就不能保证 ORIG_HEAD 在重置结束时仍然指向前一个分支的分支提示。不过,使用当前分支的 引用日志(即 @{1} ,参见 gitrevisions[7])可以访问前一个分支的分支提示。
|
然后,先前保存在临时区域的提交将按顺序逐一重新应用到当前分支。请注意,HEAD
中任何与 HEAD...<upstream>
中的提交有相同文字改动的提交都会被省略(也就是说,上游已经接受的补丁,如果提交信息或时间戳不同,就会被跳过)。
合并失败有可能导致该过程无法完全自动进行。 您必须解决合并失败问题,然后运行 git rebase --continue
。 另一种方法是使用 git rebase --skip
绕过导致合并失败的提交。 要检查原始的 <branch>
并移除 .git/rebase-apply
工作文件,请使用 git rebase --abort
命令。
假设存在以下历史记录,当前分支为 "topic":
A---B---C topic / D---E---F---G master
从这一点来看,以下任一命令的结果都是如此:
git rebase master git rebase master topic
那就是 :
A'--B'--C' topic / D---E---F---G master
注: 后一种形式只是 git checkout topic
的简写。
后接 git rebase master
。当 rebase 退出时,topic
将
仍是已签出的分支。
如果上游分支已经包含了您所做的修改(例如,因为您邮寄的补丁被应用到了上游),那么该提交将被跳过,并发出警告(如果使用了 “合并” 后台)。 例如,在以下历史记录上运行 git rebase master
(其中 A'
和 A
引入了相同的变更集,但提交者信息不同):
A---B---C topic / D---E---A'---F master
将导致 :
B'---C' topic / D---E---A'---F master
下面是如何使用 rebase --onto
将基于一个分支的特性分支移植到另一个分支,以假装特性分支是从后一个分支分叉而来。
首先,我们假设 topic 基于 next 分支。 例如,topic 中开发的功能依赖于 next 中的某些功能。
o---o---o---o---o master \ o---o---o---o---o next \ o---o---o topic
我们想让 topic 从分支 master 中分叉出来;例如,因为 topic 所依赖的功能已经合并到了更稳定的分支 master 中。我们希望我们的提交树看起来像这样 :
o---o---o---o---o master | \ | o'--o'--o' topic \ o---o---o---o---o next
我们可以使用以下命令来获取 :
git rebase --onto master next topic
Another example of --onto option is to rebase part of a branch. If we have the following situation:
H---I---J topicB / E---F---G topicA / A---B---C---D master
那么命令
git rebase --onto master topicA topicB
将导致 :
H'--I'--J' topicB / | E---F---G topicA |/ A---B---C---D master
这在主题 B 不依赖于主题 A 的情况下非常有用。
也可以用变基删除一系列提交。 如果我们遇到以下情况 :
E---F---G---H---I---J topicA
那么命令
git rebase --onto topicA~5 topicA~3 topicA
将导致删除 F 和 G 项:
E---H'---I'---J' topicA
如果 F 和 G 在某些方面有缺陷,或者不应该是 topicA 的一部分,这一点就很有用。 请注意,--onto
参数和 <upstream>
参数可以是任何有效的提交。
如果出现冲突,git rebase
会在第一个有问题的提交处停止,并在树中留下冲突标记。 你可以用 git diff
找到这些标记 (<<<<<<),并进行编辑以解决冲突。 每编辑一个文件,都需要告诉 Git 冲突已被解决,通常可以用
git add <filename>
手动解决冲突并以所需的分辨率更新索引后,您可以使用
git rebase --continue
或者,您也可以通过以下方法撤销 git rebase 操作
git rebase --abort
MODE OPTIONS
本节中的选项不能与任何其他选项一起使用,包括不能相互使用:
- --continue
-
解决合并冲突后,重新启动重新分区进程。
- --skip
-
跳过当前补丁,重新启动重新分区进程。
- --abort
-
终止变基操作并将 HEAD 重置为原始分支。如果在启动变基操作时提供了
<分支>
,那么HEAD
将被重置为<分支>
。否则,HEAD
将被重置为启动变基操作时的位置。 - --quit
-
放弃变基操作,但
HEAD
不会重置回原始分支。索引和工作树也会因此保持不变。如果使用--autostash
创建了临时储藏条目,它将被保存到储藏列表中。 - --edit-todo
-
在交互式变基过程中编辑待办事项列表。
- --show-current-patch
-
在交互式变基或因冲突而停止变基时显示当前补丁。相当于
git show REBASE_HEAD
。
选项
- --onto <newbase>
-
创建新提交的起点。如果未指定
--onto
选项,则起点为<upstream>
。 可以是任何有效的提交,而不仅仅是现有的分支名称。作为一种特殊情况,如果 A 和 B 的合并库只有一个,则可以使用 "A...B "作为合并库的快捷方式。您最多可以省略 A 和 B 中的一个,在这种情况下,它默认为 HEAD。
- --keep-base
-
将创建新提交的起点设为
<upstream>
和<branch>
的合并库。运行git rebase --keep-base <upstream> <branch>
相当于运行git rebase --reapply-cherry-picks --no-fork-point --onto <upstream>...<branch> <upstream> <branch>
。该选项适用于在上游分支上开发功能的情况。在开发功能的过程中,上游分支可能会前进,这时最好的办法可能不是继续在上游分支上重新加载,而是保持基本提交不变。由于基本提交保持不变,该选项意味着
--reapply-cherry-picks
,以避免丢失提交。虽然该选项和
-fork-point
都能找到<upstream>
和<branch>
之间的合并基数,但该选项使用合并基数作为创建新提交的_起点_,而 `-fork-point`则使用合并基数来确定将被重定向的_提交集_。另请参阅下面的不兼容选项。
- <upstream>
-
要与之比较的上游分支。 可以是任何有效的提交,而不仅仅是现有的分支名称。默认为当前分支配置的上游分支。
- <branch>
-
工作分支;默认为
HEAD
。 - --apply
-
使用应用策略来变基(内部调用
git-am
)。 一旦合并后端处理了应用后端所做的一切,这个选项将来可能就不再适用了。另请参阅下面的不兼容选项。
- --empty={drop,keep,ask}
-
如何处理开始时不是空的提交,也不是对上游提交的纯粹撷取,但在重排序后变为空的提交(因为它们包含了上游修改的子集)。 如果使用 drop(默认),则会丢弃变为空的提交。 如果使用 keep,则会保留此类提交。 如果使用 ask(隐含
--interactive
),当应用空提交时,变基停止,允许你选择是丢弃它、编辑更多文件,还是只提交空改动。 其他选项,如--exec
,除非明确指定了-i
/-interactive
,否则将使用默认的 drop。需要注意的是,开始时为空的提交会被保留(除非指定了
--no-keep-empty
),而干净的 cherry-picks 提交(由git log --cherry-mark ...
)会被检测到并作为第一步被丢弃(除非通过了--reapply-cherry-picks`或 `--keep-base
)。另请参阅下面的不兼容选项。
- --no-keep-empty
- --keep-empty
-
不在结果中保留重置前开始为空的提交(即不改变父提交的任何内容)。 默认情况下保留开始时为空的提交,因为创建此类提交需要向
git commit
传递--allow-empty
覆盖标志,表明用户非常有意地创建此类提交,因此希望保留它。使用这个标记的情况可能很少,因为你只需启动交互式变基,并删除与你不想要的提交对应的行,就能删除开始时为空的提交。 该标记是一种方便的快捷方式,比如当外部工具生成许多空提交,而你希望将它们全部删除时。
如果提交开始时不是空的,但重定向后变成空的,参阅
--empty
标志。另请参阅下面的不兼容选项。
- --reapply-cherry-picks
- --no-reapply-cherry-picks
-
重新应用任何上游提交中所有干净的拣选,而不是先发制人地丢弃它们。(如果这些提交在重排后成为空提交,因为它们包含了上游修改的子集,则对它们的行为由
--empty
标志控制)。如果没有
--keep-base
(或给出了--no-reapply-cherry-picks
),这些提交将被自动放弃。 由于这需要读取所有上游提交,对于需要读取大量上游提交的仓库来说,代价可能会很高。使用 merge 后端时,每次丢弃提交都会发出警告(除非给出--quiet
)。除非将advice.skippedCherryPicks
设为 false,否则也会发出警告(参见 git-config[1])。--reapply-cherry-picks
允许变基操作放弃读取所有上游提交,从而可能提高性能。另请参阅下面的不兼容选项。
- --allow-empty-message
-
无操作。 过去,重写带空信息的提交会失败,而该选项会覆盖这一行为,允许重写带空信息的提交。 现在,带空信息的提交不会导致重定向失败。
另请参阅下面的不兼容选项。
- -m
- --merge
-
使用合并策略重定向(默认)。
请注意,变基合并是通过在
<上游>
分支上重放工作分支的每次提交来实现的。 因此,当发生合并冲突时,被报告为 ours 的一方是迄今为止以<上游>
为起点的重定向系列,而 theirs 则是工作分支。 换句话说,双方是对调的。另请参阅下面的不兼容选项。
- -s <strategy>
- --strategy=<strategy>
-
使用给定的合并策略,而不是默认的
ort
。暗指--merge
。由于
git rebase
会使用给定的策略在<上游>
分支之上重复工作分支的每次提交,因此使用ours
策略只会清空<分支>
中的所有补丁,这没有什么意义。另请参阅下面的不兼容选项。
- -X <strategy-option>
- --strategy-option=<strategy-option>
-
将 <策略选项> 传递给合并策略。 这意味着
--merge
,如果没有指定策略,则是--s ort
。 请注意 ours 和 theirs 的颠倒,就像上面的 `-m`选项一样。另请参阅下面的不兼容选项。
- --rerere-autoupdate
- --no-rerere-autoupdate
-
在 rerere 机制重用当前冲突的记录解析来更新工作树中的文件后,允许它也用解析的结果来更新索引。
--no-rerere-auto-oupdate`是一个很好的方法,在用单独的 `git add
提交结果到索引之前,可以反复检查rerere
所做的事情,并抓住潜在的错误合并。
- -S[<keyid>]
- --gpg-sign[=<keyid>]
- --no-gpg-sign
-
GPG 签名提交。
keyid
参数是可选的,默认为提交者身份;如果指定了,则必须与选项相连,不加空格。--no-gpg-sign
用于还原commit.gpgSign
配置变量和先前的--gpg-sign
。 - -q
- --quiet
-
Be quiet. Implies
--no-stat
. - -v
- --verbose
-
Be verbose. Implies
--stat
. - --stat
-
显示上次变基后上游变化的差异状态。差异状态也由配置选项 rebase.stat 控制。
- -n
- --no-stat
-
不要将差异状态作为变基过程的一部分。
- --no-verify
-
该选项会绕过 pre-rebase 钩子。 另请参阅 githooks[5]。
- --verify
-
允许运行 pre-rebase 钩子,这是默认选项。 该选项可用于覆盖
--no-verify
。 另请参见 githooks[5]。 - -C<n>
-
确保每次更改前后至少有
<n>
行周围上下文匹配。 如果周围的上下文行数较少,则必须全部匹配。 默认情况下,不会忽略任何上下文。 暗指--apply
。另请参阅下面的不兼容选项。
- --no-ff
- --force-rebase
- -f
-
单独重放所有重定向的提交,而不是快进不变的提交。 这样就能确保重建分支的整个历史都是由新提交组成的。
在还原主题分支合并后,你可能会发现这很有帮助,因为该选项会用新提交的内容重新创建主题分支,这样就可以成功地重新合并,而无需 “恢复原状”(详见如何还原故障合并)。
- --fork-point
- --no-fork-point
-
在计算哪些提交由
<分支>
引入时,使用引用日志在<上游仓库>
和<分支>
之间找到更好的共同祖先。当启用
--fork-point
时,将使用 fork_point 而不是<上游仓库>
来计算要重置的提交集,其中 fork_point 是git merge-base --fork-point <上游仓库> <分支>
命令的结果(参见 git-merge-base[1])。 如果 fork_point 最终为空,<上游仓库>
将作为备用。如果在命令行中提供了
<上游仓库> ` 或 `--keep-base ` 参数,则默认为 `--no-fork-point
,否则默认为--fork-point
。另请参阅 git-config[1] 中的rebase.forkpoint
。如果你的分支基于
<上游仓库>
,但<上游仓库>
被回退了,而你的分支包含了被删除的提交,那么可以使用--keep-base
选项来从你的分支中删除那些提交。另请参阅下面的不兼容选项。
- --ignore-whitespace
-
在尝试协调差异时空格的差异。目前,每个后端都实现了对此行为的近似处理:
- --whitespace=<option>
-
这个标志被传递给应用补丁的
git apply
程序(参阅 git-apply[1])。它隐含了--apply
。另请参阅下面的不兼容选项。
- --committer-date-is-author-date
-
不使用当前时间作为提交者日期,而是使用被重定向的提交的作者日期作为提交者日期。该选项暗含
--force-rebase
选项。 - --ignore-date
- --reset-author-date
-
不使用原始提交的作者日期,而使用当前时间作为重建提交的作者日期。 此选项暗含
--force-rebase
选项。另请参阅下面的不兼容选项。
- --signoff
-
在所有重写的提交中添加
Signed-off-by
尾注。请注意,如果给定了--interactive
,则只有标记为被选中、编辑或重写的提交才会添加尾注。另请参阅下面的不兼容选项。
- -i
- --interactive
-
列出即将重写的提交列表。 让用户在重写前编辑该列表。 这种模式也可用于拆分提交(见下文的拆分提交)。
提交列表格式可通过设置配置选项 rebase.instructionFormat 进行更改。 自定义的指令格式会自动在格式前加上长提交哈希值。
另请参阅下面的不兼容选项。
- -r
- --rebase-merges[=(rebase-cousins|no-rebase-cousins)]
- --no-rebase-merges
-
默认情况下,变基操作会简单地从待办事项列表中删除合并提交,并将被重置的提交放入一个单一的线性分支中。 使用
--rebase-merges
后,变基操作会通过重新创建合并提交,尝试保留要被重置的提交中的分支结构。在这些合并提交中,任何已解决的合并冲突或手动修正都必须手动解决/重新应用。--no--rebase-merges
可以用来抵消rebase.rebaseMerges
配置选项和之前的 `--rebase-merges 选项。重定向合并时,有两种模式:
rebase-cousins
和no-rebase-cousins
。如果未指定模式,则默认为no-rebase-cousins
。在no-rebase-cousins
模式下,没有<上游仓库>
作为直接祖先的提交将保留其原始分支点,也就是说,被 git-log[1] 的--ancestry-path
选项排除在外的提交将默认保留其原始祖先。在rebase-cousins
模式下,此类提交会被重定向到<上游仓库>
(或是`<onto>`,如果指定了)。目前只能使用
ort
合并策略重新创建合并提交;只有通过明确的exec git merge -s <strategy> [...]
命令才能使用不同的合并策略。另请参阅下面的重置合并和不兼容选项。
- -x <命令>
- --exec <cmd>
-
在最终历史记录中创建提交的每一行后添加 "exec <命令>"。
<命令>
将被解释为一个或多个 shell 命令。任何失败的命令都会中断重置,退出代码为 1。您可以通过使用一个包含多个命令的
--exec
实例来执行多个命令:git rebase -i --exec "cmd1 && cmd2 && ..."
或给出多个
--exec
:git rebase -i --exec "cmd1" --exec "cmd2" --exec ...
如果使用了
--autosquash
,则不会为中间提交附加exec
行,而只会出现在每个 squash/fixup 系列的末尾。它在内部使用
--interactive
机制,但也可以在没有显式--interactive
的情况下运行。另请参阅下面的不兼容选项。
- --root
-
重置从
<分支>
到的所有提交,而不是用<上游分支>
来限制它们。 这样就可以重定向分支上的根提交。另请参阅下面的不兼容选项。
- --autosquash
- --no-autosquash
-
当提交日志信息以 "squash!…" 或 "fixup!…" 或 "amend!……",并且待办事项列表中已经有一个与相同的
...
匹配的提交时,就会自动修改rebase -i
的待办事项列表,使标记为压扁的提交紧跟在要修改的提交之后,并将移动提交的操作从pick
分别改为squash
或fixup
或fixup -C
。如果提交主题匹配...
,或者...
指的是提交的哈希值,则该提交匹配...
。作为后备,提交主题的部分匹配也会起作用。建议使用 git-commit[1] 的--fixup
、--fixup=amend:
或--fixup=reword:
、--squash
选项来创建 fixup/amend/squash 提交。如果默认情况下使用配置变量
rebase.autoSquash
启用了--autosquash
选项,则可以使用此选项覆盖并禁用此设置。另请参阅下面的不兼容选项。
- --autostash
- --no-autostash
-
在操作开始前自动创建临时存储条目,并在操作结束后应用它。 这意味着你可以在脏工作树上运行变基操作。 不过,请谨慎使用:成功重储后的最终储藏应用可能会导致非实质性冲突。
- --reschedule-failed-exec
- --no-reschedule-failed-exec
-
自动重新安排执行失败的
exec
命令。这只有在交互模式下(或提供了--exec
选项时)才有意义。尽管该选项在启动一个变基操作时就会应用,但它会在启动时根据
rebase.rescheduleFailedExec
配置(参见 git-config[1] 或下面的 “配置”)或是否提供了该选项为整个变基的设置。否则,如果存在rebase.rescheduleFailedExec=true
配置,一开始明确的--no-reschedule-failed-exec
就会被覆盖。 - --update-refs
- --no-update-refs
-
自动强制更新任何指向正在重定向的提交的分支。任何在工作区中签出的分支都不会以这种方式更新。
如果设置了配置变量
rebase.updateRefs
, 则可使用此选项覆盖并禁用此设置。另请参阅下面的不兼容选项。
不兼容选项
下列选项:
-
--apply
-
--whitespace
-
-C
与下列选项不兼容:
-
--merge
-
--strategy
-
--strategy-option
-
--autosquash
-
--rebase-merges
-
--interactive
-
--exec
-
--no-keep-empty
-
--empty=
-
--[no-]reapply-cherry-picks when used without --keep-base
-
--update-refs
-
--root when used without --onto
此外,以下几对选项是不兼容的:
-
--keep-base and --onto
-
--keep-base and --root
-
--fork-point and --root
行为差异
git rebase
有两个主要的后台:‘应用’ 和 ‘合并’(‘应用’ 后台曾被称为 am 后台,但这个名字看起来像动词而非名词,因此引起了混淆)。 此外,‘合并’ 后台曾被称为交互式后台,但现在也用于非交互式情况。 两者都是根据各自的底层功能重新命名的)。这两个后台在行为方式上有一些微妙的区别:
Empty commits
不幸的是,‘应用’ 后台会丢弃故意为空的提交,即开始时为空的提交,不过这种情况在实践中并不多见。 它还会丢弃变为空的提交,而且没有控制这种行为的选项。
默认情况下,‘合并’ 后台会保留故意为空的提交(不过,如果使用 -i
选项,这些提交会在待办事项列表编辑器中标记为空,或者使用 --no-keep-empty
自动删除)。
与应用后端类似,合并后端默认情况下也会丢弃变为空的提交,除非指定了 -i
或 --interactive
选项(在这种情况下,合并会停止并询问用户该怎么做)。 合并后端还有一个 --empty={drop,keep,ask}
选项,用于改变处理变为空的提交的行为。
目录重命名检测
由于缺乏准确的目录树信息(利用补丁中的有限信息构建假祖先),‘应用’ 后台禁用了目录重命名检测。 禁用目录重命名检测意味着,如果历史记录的一方重命名了一个目录,而另一方在旧目录中添加了新文件,那么新文件就会被留在旧目录中,而不会在重新编排时发出任何警告,提醒您可能需要将这些文件移到新目录中。
目录重命名检测与 ‘合并’ 后台协同工作,在这种情况下会向你发出警告。
Context
‘应用’ 后台的工作方式是创建一系列补丁(在内部调用 format-patch
),然后依次应用这些补丁(在内部调用 am
)。 补丁由多个块组成,每个块包含行号、上下文区域和实际更改。 行号的获取必须谨慎,因为另一方很可能在文件中插入或删除了更早的行。 上下文区域的目的是帮助找到如何调整行号,以便将更改应用到正确的行上。 但是,如果代码的多个区域有相同的上下文行,就可能选错。 在现实世界中,这种情况曾导致提交被错误地重新应用,而没有报告任何冲突。 将 diff.context
设置为更大的值可能会避免此类问题,但会增加发生虚假冲突的几率(因为需要更多行匹配的上下文才能应用)。
‘合并’ 后台使用每个相关文件的完整副本,从而避免了此类问题。
Labelling of conflicts markers
当出现内容冲突时,合并机制会尝试在每一方的冲突标记上标注内容来源的提交。 由于 ‘应用’ 后台丢弃了关于重建的提交及其父提交的原始信息(而是根据生成的补丁中的有限信息生成新的假提交),因此无法识别这些提交,而只能使用提交摘要。 此外,当 merge.conflictStyle
设置为 diff3
或 zdiff3
时,‘应用’ 后端将使用 "constructed merge base"(构建的合并基础)来标注来自合并基础的内容,因此不会提供任何关于合并基础提交的信息。
‘合并’ 后台使用历史上双方的完整提交,因此没有这些限制。
Hooks
传统上,‘应用’ 后台并不调用提交后(post-commit)钩子,而 ‘合并’ 后台却调用了。 虽然 ‘合并’ 后台已取消了其输出,但两者都调用了检出后(post-checkout)钩子。 此外,这两个后端都只调用了变基操作的起点提交,而不是中间提交或最终提交的检出后(post-checkout)钩子。 在每种情况下,调用这些钩子都是偶然的,而不是设计出来的(两个后台最初都是以 shell 脚本的形式实现的,碰巧调用了其他会调用钩子的命令,如 git checkout
或 git commit
)。 这两个后台应该具有相同的行为,但目前还不完全清楚哪一个才是正确的。 我们将来可能会让变基操作停止调用这些钩子。
可中断性
如果用户在错误的时间按下 Ctrl-C 试图终止重置,重置就会进入无法通过后续的 git rebase --abort
终止的状态。 ‘合并’ 后台似乎不存在同样的缺陷。 (详见 https://lore.kernel.org/git/20200207132152.GC2868@szeder.dev/)
合并战略
合并机制(git merge`和`git pull`命令)允许用`s`选项来选择后端'合并策略'。 一些策略也可以采取自己的选项,可以通过给`git merge`和/或`git pull`的
-X<选项>`参数来传递。
- ort
-
这是拉取或合并一个分支时的默认合并策略。 这个策略只能使用三方合并算法解决两个头。 当有一个以上的共同祖先可用于三方合并时,它会创建一个共同祖先的合并树,并将其作为三方合并的参考树。 据报道,通过对Linux 2.6内核开发历史中的实际合并提交的测试,这导致了较少的合并冲突,而不会引起错误的合并。 此外,这个策略可以检测并处理涉及重命名的合并。 它并不使用检测到的副本。 这个算法的名字是一个缩写("Ostensibly Recursive’s Twin"),来自于它是作为以前的默认算法`recursive`的替代而编写的。
ort 策略可以采取以下选项:
- ours
-
这个选项通过倾向于 "我们" 的版本,迫使冲突的猎物被自动解决。 另一棵目录树上与我们这边不冲突的变化会反映在合并结果中。 对于一个二进制文件,整个内容都来自我们这边。
这不应该与 "我们的" 合并策略相混淆,后者甚至根本不看另一棵目录树包含了什么。 它抛弃了其他树所做的一切,宣布 "我们的" 历史包含了其中所发生的一切。
- theirs
-
这与 "我们的" 相反;注意,与 "我们的" 不同,没有 "他们的" 合并策略来混淆这个合并选项。
- ignore-space-change
- ignore-all-space
- ignore-space-at-eol
- ignore-cr-at-eol
-
为了进行三方合并,将具有指定类型的空白变化的行视为没有变化。 但混合了其他改动的行的空白改动不会被忽略。 参见git-diff[1]
-b
,-w
,-ignore-space-at-eol
, 和-ignore-cr-at-eol
。-
如果 "他们的" 版本只在一行中引入了空白的变化,则使用 "我们的" 版本;
-
如果 "我们的" 版本引入了空白的变化,但 "他们的" 版本包括一个实质性的变化,则使用 "他们的" 版本;
-
否则,合并将以常规方式进行。
-
- renormalize
-
在解决三方合并时,这将对一个文件的所有三个阶段运行虚拟检出和检入。 这个选项是为了在合并具有不同清洁过滤器或行末规范化规则的分支时使用。 详情见 gitattributes[5] 中的 "合并具有不同检入/检出属性的分支"。
- no-renormalize
-
禁用
renormalize
选项。 这覆盖了merge.renormalize
配置变量。 - find-renames[=<n>]
-
开启重名检测,可选择设置相似度阈值。 这是默认的。这覆盖了 merge.renames 配置变量。 参见git-diff[1]
--find-renames
。 - rename-threshold=<n>
-
废弃的,
find-renames=<n>
的同义词。 - subtree[=<路径>]
-
这个选项是 子树 策略的更高级形式,该策略对两棵树在合并时必须如何移位以相互匹配进行猜测。 相反,指定的路径是前缀(或从开始剥离),以使两棵树的形状相匹配。
- recursive
-
这只能用三方合并算法解决两个头。 当有一个以上的共同祖先可用于三方合并时,它会创建一个共同祖先的合并树,并使用它作为三方合并的参考树。 据报道,通过对Linux 2.6内核开发历史中的实际合并提交的测试,这导致了较少的合并冲突,而不会引起错误的合并。 此外,它可以检测并处理涉及重命名的合并。 它并不使用检测到的副本。 从Git v0.99.9k到v2.33.0,这是解决双头的默认策略。
recursive 策略采用与 ort 相同的选项。 然而,有三个 ort 忽略的额外选项(上面没有记录),对 recursive 策略有潜在的作用:
- patience
-
废弃的,
diff-algorithm=patience
的同义词。 - diff-algorithm=[patience|minimal|histogram|myers]
-
在合并时使用不同的差异算法,这可以帮助避免由于不重要的匹配行(比如不同函数的大括号)而发生的错误合并。 参见git-diff[1]
--diff-algorithm
。 注意,ort
特定diff-algorithm=histogram
,而`recursive`默认为`diff.algorithm`配置设置。 - no-renames
-
关闭重名检测。这覆盖了
merge.renames
的配置变量。 参见git-diff[1]--no-renames
。
- resolve
-
这只能用三方合并算法解决两个头(即当前分支和你拉来的另一个分支)。 它试图仔细检测纵横交错的合并歧义。 它不处理重名。
- octopus
-
这可以解决有两个以上头的情况,但拒绝做复杂的合并,需要手动解决。 它主要是用于将主题分支头捆绑在一起。 当拉动或合并一个以上的分支时,这是默认的合并策略。
- ours
-
这可以解决任何数量的头,但合并的结果总是当前分支头的树,有效地忽略了所有其他分支的变化。 它是用来取代侧边分支的旧开发历史的。 注意,这与 recursive 合并策略的-Xours选项不同。
- subtree
-
这是一个修正的
ort
策略。当合并树A和B时,如果B对应于A的子树,B首先被调整为与A的树结构相匹配,而不是在同一级别读取树。这种调整也是针对共同祖先树进行的。
对于使用三方合并的策略(包括默认的 ort 策略),如果在两个分支上都做了修改,但后来在其中一个分支上被撤销,那么这个修改就会出现在合并后的结果中;有些人觉得这种行为令人困惑。 出现这种情况是因为在执行合并时只考虑头部和合并基数,而不是单个提交。 因此,合并算法认为被恢复的修改根本就没有变化,而是用被修改的版本来代替。
NOTES
You should understand the implications of using git rebase
on a repository that you share. See also RECOVERING FROM UPSTREAM REBASE below.
在运行变基命令时,如果存在 pre-rebase
(变基前)钩子,它会首先执行该钩子。 您可以使用此钩子进行正确性检查,并在不合适时拒绝重置。 有关示例,请参阅模板 pre-rebase
(变基前) 钩子脚本。
完成后,<分支>
将成为当前分支。
交互模式
交互式重定向意味着你有机会编辑被重定向的提交。 你可以重新排列提交的顺序,也可以删除它们(剔除坏的或不需要的补丁)。
互动模式就是为这种工作流程设计的:
-
灵机一动
-
敲代码
-
编写提交系列
-
submit
其中第 2 点由以下几个实例组成
a) 常规使用
-
完成值得提交的事情
-
commit
b) 独立修复
-
发现有什么东西没有用
-
fix that
-
提交
有时,b.2.中修正的内容无法修正到它所修正的并不完美的提交中,因为该提交深埋在一系列补丁中。 这正是交互式变基的用途:在大量的 "a" 和 "b" 之后使用它,重新排列和编辑提交,将多个提交合并为一个提交。
从您希望保留原样的最后一次提交开始:
git rebase -i <after-this-commit>
编辑器会弹出当前分支的所有提交(忽略合并提交),这些提交都在给定提交之后。 你可以随心所欲地调整列表中提交的顺序,也可以删除它们。 列表大致如下:
选择 deadbee ,本次提交的上线 选中 fa1afe1 ,下一次提交的上线 ...
上线描述纯粹是为了方便您查看;git rebase 不会查看这些描述,只会查看提交名称(本例中为 "deadbee" 和 "fa1afe1"),因此请勿删除或编辑这些名称。
用 "edit" 命令替换 "pick" 命令后,你可以告诉 git rebase
在应用该提交后停止,这样你就可以编辑文件和/或提交信息,修改提交,然后继续重置。
要中断重置(就像 "edit" 命令一样,但不会先选择任何提交),请使用 "break "命令。
如果只想编辑提交信息,请用 "reword" 命令替换 "pick" 命令。
要放弃提交,请用 "drop" 替换 "pick" 命令,或直接删除匹配的行。
如果要将两个或更多提交折叠为一个提交,请用 "squash" 或 "fixup" 代替命令 "pick" 来处理第二个及后续提交。 如果提交的作者不同,折叠后的提交将归属于第一个提交的作者。 除非使用了 "fixup -c" 命令,否则折叠提交的建议提交信息是第一个提交信息与 "squash" 命令确定的提交信息的合并,省略了 "fixup" 命令确定的提交信息。 在这种情况下,建议的提交信息只是 "fixup -c" 提交的信息,而且会打开一个编辑器允许你编辑信息。 "fixup -c" 提交的内容(补丁)仍然包含在折叠提交中。如果有多个 "fixup -c" 提交,则使用最后一个提交的信息。 您也可以使用 "fixup -C" 获得与 "fixup -c" 相同的效果,但不需要打开编辑器。
当 "pick" 命令被替换为 "编辑" 命令或命令因合并错误而失败时,git rebase
将停止。完成编辑和/或解决冲突后,可以使用 git rebase --continue
继续。
例如,如果您想重新排列最近 5 次提交的顺序,让原来的 HEAD~4
变成新的 HEAD
。要做到这一点,可以这样调用 git rebase
:
$ git rebase -i HEAD~5
并将第一个补丁移到列表末尾。
例如,如果您有这样的历史记录,您可能需要重新创建合并提交:
X \ A---M---B / ---o---O---P---Q
假设要将从 "A" 开始的侧分支重置为 "Q"。确保当前的 HEAD
是 "B",然后调用
$ git rebase -i -r --onto Q O
重新排序和编辑提交通常会产生未经测试的中间步骤。 您可能想通过运行测试来检查您的历史编辑是否破坏了任何东西,或者至少使用 "exec" 命令(快捷键 "x")在历史的中间点重新编译。 为此,您可以创建类似这样的待办事项列表:
选择 deadbee 执行功能 XXX 修复 f1a5c00 修复功能 XXX 执行 make 选取 c0ffeee 下一次提交的上线 编辑 deadbab 之后提交的上线 exec cd subdir; make test ...
当命令失败(即以非 0 状态退出)时,交互式变基操作就会停止,以便您有机会解决问题。您可以使用 git rebase --continue
继续。
"exec" 命令在 shell(在 $SHELL
中指定的 shell,如果未设置 $SHELL
,则使用默认 shell)中执行命令,因此可以使用 shell 功能(如 "cd"、">"、";"……)。命令从工作区的根目录运行。
$ git rebase -i --exec "make test"
该命令用于检查中间提交是否可编译。 待办事项列表就会变成这样:
选择 5928aea one exec make test 选 04d0fda 二 exec make test 选择 ba46169 三 执行制作测试 选择 f4593F9 四 exec make test
拆分提交
在交互模式下,可以用 "edit"(编辑)来标记提交。 不过,这并不一定意味着 git rebase
希望编辑的结果是一个提交。 事实上,你可以撤销提交,也可以添加其他提交。 这可以用来将一个提交一分为二:
-
使用
git rebase -i <提交>^
开始交互式变基,其中 `<提交>`是要分割的提交。 事实上,只要包含该提交,任何提交范围都可以。 -
用 "edit" 操作标记要分割的提交。
-
在编辑该提交时,执行
git reset HEAD^
。 其效果是将HEAD
重绕一圈,索引也跟着重绕一圈。 但工作区保持不变。 -
现在,将您希望在第一次提交中的改动添加到索引中。 你可以使用
git add
(可能是交互式的)或git gui
(或两者)来完成。 -
以任何合适的提交信息提交当前索引。
-
重复最后两个步骤,直到工作区干净为止。
-
使用
git rebase --continue
继续变基。
如果不能绝对确定中间修订是一致的(编译、通过 testsuite 等),则应使用 git stash
在每次提交、测试之后,将尚未提交的改动保存起来,并在必要时修改提交。
从上有仓库变基中恢复
变基(或以任何其他形式改写)他人基于其工作的分支是个坏主意:其下游的任何人都不得不手动修正自己的历史。 本节将从下游的角度解释如何进行修复。 不过,真正的修复方法是首先避免重置上游分支。
举例说明,假设有人开发了一个 ‘子系统’ 分支,而你正在开发一个依赖于该 ‘子系统’ 的 ‘主题’。 你可能会有如下的历史记录:
o---o---o---o---o---o---o---o master \ o---o---o---o---o subsystem \ *---*---* topic
如果 ‘子系统’ 针对 master 进行重定向,会出现以下情况:
o---o---o---o---o---o---o---o master \ \ o---o---o---o---o o'--o'--o'--o'--o' subsystem \ *---*---* topic
如果现在像往常一样继续开发,并最终将 ‘主题’ 合并到 ‘子系统’,那么 ‘子系统’ 中的提交将永远重复:
o---o---o---o---o---o---o---o master \ \ o---o---o---o---o o'--o'--o'--o'--o'--M subsystem \ / *---*---*-..........-*--* topic
这种重复通常是不受欢迎的,因为它们会使历史记录变得杂乱无章,难以跟踪。 要清理这些重复提交,就需要将 ‘主题’ 上的提交移植到新的 ‘子系统’ tip 上,即重置 ‘主题’。 这就会产生连锁反应:‘主题’ 的任何下游提交都会被迫重置,依此类推!
有两种修复方法,将在下面的小节中讨论:
- 简单的例子: 变化完全相同。
-
如果 ‘子系统’ 变基是简单的重置,没有冲突,就会出现这种情况。
- 较难的例子: 变化不一样。
-
如果 ‘子系统’ 变基有冲突,或者使用了
--interactive
来省略、编辑、压制或修复提交,或者上游使用了commit --amend
,reset
或完整历史重写命令(如filter-repo
),就会发生这种情况。
您将获得固定的历史记录
o---o---o---o---o---o---o---o master \ o'--o'--o'--o'--o' subsystem \ *---*---* topic
困难的例子 ~~~~~ 如果 ‘子系统’ 的变化与重置前的变化不完全一致,情况就会变得更加复杂。 NOTE: 虽然 ‘简单恢复案例’ 有时看似成功 即使在疑难案件中,也可能产生意想不到的后果。 例如,通过 `git rebase --interactive` 删除的提交将被**恢复**! 我们的想法是手动告诉 `git rebase` “旧的 ‘子系统’ 分支在哪里结束,而您的 ‘主题’ 分支在哪里开始”,也就是说,它们之间的旧合并基础是什么。 例如,你必须找到一种方法来命名旧 ‘子系统’ 分支的最后一次提交: * 使用 ‘子系统’ 引用日志:在 `git fetch` 之后,‘子系统’ 的旧 tip 位于 `subsystem@{1}`。 随后的获取将增加这个数字。 (参见 git-reflog[1]) * 与 ‘主题’ 分支的 tip 有关:由于 ‘topic’ 分支有三次提交,所以 ‘子系统’ 分支的旧提示必须是 `topic~3`。 然后,您可以将旧的 `subsystem..topic`(子系统..主题)移植到新的提示中,方法是(对于引用日志的情况,假设您已经在 ‘主题’ 分支中):
$ git rebase --onto subsystem subsystem@{1}
“困难例子” 恢复的连锁反应尤其糟糕:‘主题’ 下游的 ‘每个人’ 现在也必须执行 “困难例子” 恢复! 变基合并 ---- 交互式变基命令最初是为处理单个补丁系列而设计的。因此,将合并提交排除在待办事项列表之外是有道理的,因为开发人员在分支上工作时,可能已经合并了当时的 `master`,只是最终将所有提交重置到了 `master`上(跳过了合并提交)。 不过,开发人员想要重新创建合并提交也有合理的原因:在多个相互关联的分支上工作时,为了保持分支结构(或 “提交拓扑”)。 在下面的示例中,开发人员在一个重构了按钮定义方式的特性分支上工作,并在另一个特性分支上使用重构实现了 “报告错误” 按钮。`git log --graph --format=%s -5` 的输出可能是这样的:
-
Merge branch report-a-bug |\ | * Add the feedback button
-
| Merge branch refactor-button |\ \ | |/ | * Use the Button class for all buttons | * Extract a generic Button class from the DownloadButton one
开发人员可能希望在保留分支拓扑的同时,将这些提交重定向到更新的 `master` 分支,例如,当第一个特性分支比第二个分支更早集成到 `master` 分支时,开发人员可能希望解决与 “下载按钮” 类的合并冲突,因为 “下载按钮” 类已经集成到了 `master` 分支。 可以使用 `--rebase-merges` 选项进行重置。 它将生成如下的待办事项列表:
标签上的
分支:重构按钮
分支:报告一个错误
reset refactor-button (重构按钮)# 所有按钮都使用按钮类 pick abcdef 添加反馈按钮 label report-a-bug(报告一个错误)
重置到 merge -C a1b2c3 refactor-button # 合并 “重构按钮” merge -C 6f5e4d report-a-bug # 合并 “报告一个错误”
与常规的交互式变基不同,除了 `pick`(选取)命令外,还有 `label`(标签)、`reset`(重置) 和 `merge`(合并) 命令。 The `label` command associates a label with the current HEAD when that command is executed. These labels are created as worktree-local refs (`refs/rewritten/<label>`) that will be deleted when the rebase finishes. That way, rebase operations in multiple worktrees linked to the same repository do not interfere with one another. If the `label` command fails, it is rescheduled immediately, with a helpful message how to proceed. The `reset` command resets the HEAD, index and worktree to the specified revision. It is similar to an `exec git reset --hard <label>`, but refuses to overwrite untracked files. If the `reset` command fails, it is rescheduled immediately, with a helpful message how to edit the todo list (this typically happens when a `reset` command was inserted into the todo list manually and contains a typo). `merge`(合并)命令会将指定的版本合并到当时的 HEAD 中。使用 `-C <源提交>` 时,将使用指定合并提交的提交信息。如果将 `-C` 改为小写的 `-c`,合并成功后,提交信息将在编辑器中打开,以便用户编辑信息。 如果 `merge`(合并)命令因合并冲突以外的任何原因失败(即合并操作甚至没有开始),则会立即重新安排。 默认情况下,`merge`(合并)命令将对常规合并使用 `ort ` 合并策略,对多路合并使用 `octopus` 合并策略。 我们可以在调用变基命令时使用 `--strategy` 参数为所有合并指定一个默认策略,也可以在交互式命令列表中使用 `exec` 命令显式地调用 `git merge` 并加上 `--strategy` 参数来覆盖特定的合并策略。 需要注意的是,像这样显式调用 `git merge` 时,可以利用标签是工作区本地引用(例如,引用 `refs/rewritten/onto` 将对应标签 `onto`)这一事实来引用要合并的分支。 注意:第一条命令(`label onto`)会标注提交所基于的修订版本;`onto ` 只是一个约定俗成的名称,是对 `--onto` 选项的一种提示。 也可以通过添加 `merge <合并起点>` 命令,从头开始引入全新的合并提交。这种形式会生成暂定提交信息,并始终打开编辑器让用户编辑。例如,当一个特性分支要解决的问题不止一个,需要拆分成两个甚至更多特性分支时,这个命令就很有用。请看这个待办事项列表:
pick 192837 从 GNU Makefiles 切换到 CMake pick 5a6c7e 记录切换到 CMake 的过程 pick 918273 修复 CMake 中的 OpenSSL 检测问题 pick afbecd http:添加对 TLS v1.3 的支持 pick fdbaec 在 Windows 上修复 CMake 对 cURL 的检测
该列表中与 CMake 无关的一个提交很可能是为了修复所有因改用 CMake 而引入的错误,但它解决的是另一个问题。要把这个分支分成两个主题分支,可以这样编辑待办事项列表:
标签上的
选择 afbecd http:添加对 TLS v1.3 的支持 label tlsv1.3
reset onto pick 192837 从 GNU Makefiles 切换到 CMake pick 918273 修复 CMake 中的 OpenSSL 检测问题 pick fdbaec 在 Windows 上修复 CMake 对 cURL 的检测 pick 5a6c7e 记录切换到 CMake 的过程 label cmake
reset onto merge tlsv1.3 merge cmake
配置 -- [WARNING] ==== Missing `zh_HANS-CN/includes/cmd-config-section-all.txt` See original version for this content. ==== [] [WARNING] ==== Missing `zh_HANS-CN/config/rebase.txt` See original version for this content. ==== [] [WARNING] ==== Missing `zh_HANS-CN/config/sequencer.txt` See original version for this content. ==== [] GIT --- 属于 git[1] 文档