git rebase
git-rebase
命名
git-rebase - 重新申请提交另一个基本技巧
概要
git rebase [-i | --interactive] [options] [--exec <cmd>] [--onto <newbase>]
[<upstream> [<branch>]]
git rebase [-i | --interactive] [options] [--exec <cmd>] [--onto <newbase>]
--root [<branch>]
git rebase --continue | --skip | --abort | --quit | --edit-todo
描述
如果指定 <branch> ,git rebase则在执行其他任何操作之前将执行自动操作git checkout <branch>。否则它将保留在当前分支上。
如果未指定 <upstream> ,则将使用在分支。<name> .remote和分支。<name> .merge 选项中配置的上游(有关详细信息,请参阅 git-config [1]),且--fork-point假定该选项。如果您当前没有在任何分支上,或者当前分支没有配置上游,那么 rebase 会中止。
所有由当前分支提交但不在 <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 被设置为在复位之前指向分支的尖端。
之前保存到临时区域的提交按顺序依次重新应用到当前分支。请注意,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
。退出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
另一个例子 --onto 选项是重新绑定分支的一部分。如果我们有以下情况:
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
当 topicB 不依赖于 topicA 时,这很有用。
一系列的提交也可以用 rebase 去除。如果我们有以下情况:
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> 参数的参数可以是任何有效的 commit-ish 。
如果发生冲突,git rebase将停在第一个有问题的提交并在树中留下冲突标记。您可以使用git diff找到标记(<<<<<<)并进行编辑以解决冲突。对于你编辑的每一个文件,你都需要告诉 Git 冲突已经解决,通常这是可以完成的
git add <filename>
手动解决冲突并以所需的分辨率更新索引后,您可以继续使用重新绑定过程
git rebase --continue
另外,您也可以撤消git rebase
git rebase --abort
组态
rebase.stat
是否显示自上次变基期以来上游变化的差异。默认为 False 。
rebase.autoSquash
如果--autosquash
默认设置为 true 启用选项。
rebase.autoStash
如果--autostash
默认设置为 true 启用选项。
rebase.missingCommitsCheck
如果设置为 “warn” ,则在交互模式下打印有关移除提交的警告。如果设置为 “error” ,请打印警告并停止重设。如果设置为 “ignore” ,则不进行检查。默认情况下 “ignore” 。
rebase.instructionFormat
自定义提交列表格式,用于重新--interactive
绑定。
选项
--onto <newbase>
创建新提交的起点。如果没有指定 --onto 选项,起点是 <upstream> 。可以是任何有效的提交,而不仅仅是现有的分支名称。
作为特例,如果只有一个合并基础,则可以使用“A ... B”作为 A 和 B 合并基的快捷方式。您最多可以省略 A 和 B 中的一个,在这种情况下,它默认为 HEAD 。
<upstream>
上游分支进行比较。可以是任何有效的提交,而不仅仅是现有的分支名称。默认为当前分支的配置上游。
<branch>
工作分部; 默认为 HEAD 。
--continue
解决了合并冲突后重新启动重新绑定过程。
--abort
中止 rebase 操作并将 HEAD 重置为原始分支。如果 <reb> 操作开始时提供 <branch> ,则 HEAD 将重置为 <branch> 。否则 HEAD 将重置为启动 rebase 操作时的位置。
--quit
放弃 rebase 操作,但 HEAD 不会重置回原始分支。索引和工作树也因此保持不变。
--keep-empty
在结果中保留不改变父项的任何提交。
--skip
通过跳过当前补丁重新启动重新绑定过程。
--edit-todo
在交互式重新绑定期间编辑待办事项列表。
-m --merge
使用合并策略来重新分配。当使用递归(默认)合并策略时,这允许 rebase 知道上游端的重命名。
请注意,通过在 <upstream> 分支顶部重播来自工作分支的每个提交,合并索引合并工作。正因为如此,当合并冲突发生时,该方报告的ours是迄今为止重新分类的系列,从 <upstream> 开始,并且theirs是工作分支。换句话说,双方交换。
-s <strategy> --strategy=<strategy>
使用给定的合并策略。如果没有-s
选项git merge-recursive
用来代替。这意味着 - 合并。
因为git rebase使用给定的策略,重播每个都会在 <upstream> 分支之上的工作分支提交,所以使用该ours策略只会丢弃 <branch> 中的所有修补程序,这没有多大意义。
-X <strategy-option> --strategy-option=<strategy-option>
将 <strategy-option> 传递给合并策略。这意味着--merge,如果没有明确说明战略-s recursive。请注意该选项的逆转ours和theirs上面所述-m。
-S<keyid> --gpg-sign=<keyid>
GPG 标志提交。该keyid
参数是可选的,并且默认为提交者身份; 如果指定,它必须粘贴到选项没有空格。
-q --quiet
be quiet。意味着 -- 无统计。
-v --verbose
详细。意味着--要统计。
--stat
显示自上次 rebase 以来上游变化的差异。diffstat 也由配置选项 rebase.stat 控制。
-n --no-stat
不要将 diffstat 显示为 rebase 过程的一部分。
--no-verify
此选项绕过预先重新绑定钩子。另见 githooks [5]。
--verify
允许预重贴挂钩运行,这是默认设置。这个选项可以用来覆盖--no-verify 。另见 githooks [5] 。
-C<n>
确保每次更改之前和之后至少有 <n> 行周围环境匹配。当存在较少的周围环境线时,它们都必须匹配。默认情况下,不会忽略上下文。
-f --force-rebase
即使当前分支是最新的,并且--force
没有做任何事情的命令也不会返回,强制重新分配。
你可能会发现这个(或者 --no-ff 带有交互式底座)在恢复主题分支合并之后很有用,因为这个选项用新提交重新创建了主题分支,所以它可以在不需要“还原返回”的情况下成功地重新建立。在还原-A-错误的合并操作方法的详细说明)。
--fork-point --no-fork-point
计算 <branch> 引入的提交时,使用 reflog 可以在 <upstream> 和 <branch> 之间找到更好的共同祖先。
当 --fork-point 被激活时,fork_point将被用来代替<upstream>来计算 rebase 的提交集合fork_point,git merge-base --fork-point <upstream> <branch>命令的结果在哪里(参见 git-merge-base [1])。如果fork_point结果为空,<upstream> 将用作后备。
如果在命令行中给出了 <upstream> 或 --root ,则默认为--no-fork-point,否则为默认值--fork-point。
--ignore-whitespace --whitespace=<option>
这些标志被传递给git apply
应用该补丁的程序(参见 git-apply [1])。与 --interactive 选项不兼容。
--committer-date-is-author-date --ignore-date
这些标志被传递给git am
轻松更改重定义的提交的日期(请参阅 git-am [1])。与 --interactive 选项不兼容。
--signoff
这个标志被传递来git am
签署所有重新发布的提交(参见 git-am [1])。与 --interactive 选项不兼容。
-i --interactive
列出将要重新分配的提交列表。让用户在重新绑定之前编辑该列表。该模式也可用于分割提交(请参阅下面的分割提交)。
提交列表格式可以通过设置配置选项 rebase.instructionFormat 进行更改。自定义的指令格式会自动将格式中的长提交哈希值作为前缀。
-p --preserve-merges
重新创建合并提交,而不是通过重播合并提交引入的提交来平坦化历史。不保留合并冲突解决方案或手动修改合并提交。
这在--interactive
内部使用机制,但--interactive
明确地将其与选项结合通常不是一个好主意,除非您知道自己在做什么(请参阅下面的 BUGS )。
-x <cmd> --exec <cmd>
在每行在最终历史记录中创建提交后附加 “exec <cmd>” 。<cmd> 将被解释为一个或多个 shell 命令。
您可以通过使用几个命令的一个实例来--exec
执行几个命令:
git rebase -i --exec "cmd1 && cmd2 && ..."
或者通过给予多个--exec
:
git rebase -i --exec "cmd1" --exec "cmd2" --exec ...
如果--autosquash
使用,中间提交不会追加 “exec” 行,并且只会出现在 squash / fixup 系列的末尾。
这在--interactive
内部使用机制,但它可以在没有明确的情况下运行--interactive
。
--root
重新规划从 <branch> 可访问的所有提交,而不是用 <upstream> 限制它们。这允许您重新分支分支上的根提交。当与 --onto 一起使用时,它将跳过已包含在 <newbase>(而不是 <upstream> )中的更改,而不使用 - 将在每次更改时运行。当与 --onto 和 --preserve-merges 一起使用时,all根提交将被重写为具有 <newbase> 作为父代。
--autosquash --no-autosquash
当提交日志消息以“squash!...”(或“fixup!...”)开始,并且存在一个标题以相同的开头的提交时,自动修改 rebase -i 的待办事项列表,以便提交标记为压扁,在提交被修改后立即出现,并将移动的提交的操作从(或)pick
改为。在第一次之后忽略随后的“fixup!”或“squash!”,以防您提到之前的 fixup / squash 。squashfixupgit commit --fixup/--squash
该选项仅在使用该--interactive
选项时有效。
如果该--autosquash
选项默认情况下使用配置变量启用rebase.autoSquash
,则此选项可用于覆盖和禁用此设置。
--autostash --no-autostash
在操作开始之前自动创建临时存储条目,并在操作结束后应用它。这意味着你可以在肮脏的工作树上运行 rebase 。但是,谨慎使用:成功重新绑定后的最终隐藏应用程序可能会导致不平凡的冲突。
--no-ff
使用 -interactive ,cherry-pick 所有重新提交的提交,而不是对未更改的提交进行快速转发。这确保了重新分支的整个历史由新的提交组成。
Without --interactive ,这是 --force-rebase 的同义词。
在恢复主题分支合并之后,您可能会发现这很有帮助,因为此选项重新创建了具有新提交的主题分支,因此可以在不需要“还原返回”的情况下成功重新分配(请参阅恢复 - 错误 - 合并的方法细节)。
合并策略
合并机制(git merge和git pull命令)允许merge strategies使用-s选项选择后端。一些策略也可以采取他们自己的选择,可以通过给git merge和/或git pull给出-X<option>参数来传递。
resolve
这只能使用3路合并算法来解析两个头(即当前分支和您从其中取出的另一个分支)。它试图仔细检测交叉融合歧义,并被认为通常是安全和快速的。
recursive
这只能使用3路合并算法来解析两个头。当有多个可用于3路合并的共同祖先时,它将创建共同祖先的合并树并将其用作3路合并的参考树。据报道,这会导致更少的合并冲突,而不会因从 Linux 2.6内核开发历史记录中进行的实际合并提交所做的测试而导致混淆。此外,这可以检测并处理涉及重命名的合并。这是拉取或合并一个分支时的默认合并策略。
该recursive
策略可以采取以下选择:
ours
该选项强制冲突的 hunk 通过支持our
版本自动解决。来自另一棵与我们不冲突的树的变化反映到合并结果。对于二进制文件,整个内容都是从我们这边拿来的。
这不应与ours
合并策略混淆,合并策略甚至不会考虑其他树包含的内容。它丢弃了其他树的所有内容,声明our
历史包含发生在其中的所有事情。
theirs
这是与ours
相反的; 请注意,与此不同的ours
是,没有theirs
融合策略来混淆这个合并选项。
patience
使用此选项,merge-recursive
花费一点额外的时间来避免由于不重要的匹配行(例如,来自不同功能的括号)而导致的混淆。当要合并的分支疯狂地分歧时使用它。另请参阅 git-diff [1] --patience
。
diff-algorithm=patience|minimal|histogram|myers
告诉merge-recursive
使用不同的差异算法,这可以帮助避免由于不重要的匹配行(例如不同功能的花括号)而发生误合。另请参阅 git-diff [1] --diff-algorithm
。
ignore-space-change ignore-all-space ignore-space-at-eol
为了三路合并的目的,将指定类型的空白的行对待不变。空白变化与其他变化混合在一起不会被忽略。另见 GIT-DIFF [1] ,-b
,-w
和--ignore-space-at-eol
。
- 如果
their
版本只对一行引入空白变化,our
则使用版本;
renormalize
这将在解析三路合并时运行虚拟检出和检入文件的所有三个阶段。此选项用于合并具有不同干净过滤器或行结束标准化规则的分支时使用。有关详细信息,请参阅 gitattributes [5]中的“合并不同签入/签出属性的分支”。
no-renormalize
禁用该renormalize
选项。这覆盖merge.renormalize
配置变量。
no-renames
关闭重命名检测。另请参阅 git-diff [1] --no-renames
。
find-renames=<n>
打开重命名检测,可选择设置相似性阈值。这是默认设置。另请参阅 git-diff [1] --find-renames
。
rename-threshold=<n>
已弃用的同义词find-renames=<n>。
subtree=<path>
该选项是一种更高级的subtree
策略形式,该策略可以猜测两个树在合并时必须如何移动以相互匹配。相反,指定的路径是前缀(或从开始剥离)以使两棵树的形状匹配。
octopus
这解决了两个以上负责人的情况,但拒绝执行需要手动解决的复杂合并。它主要用于将主题分支主题捆绑在一起。这是拉取或合并多个分支时的默认合并策略。
ours
这可以解析任意数量的头,但合并结果树始终是当前分支头的树,有效地忽略了来自所有其他分支的所有更改。它是用来取代侧枝的旧发展历史。请注意,这与recursive
合并策略的 -Xours 选项不同。
subtree
这是一个修改后的递归策略。当合并树 A 和 B 时,如果 B 对应于 A 的子树,则首先调整 B 以匹配 A 的树结构,而不是读取处于相同级别的树。这种调整也对共同的祖先树进行。
对于使用3路合并(包括默认值recursive
)的策略,如果在两个分支上进行了更改,但稍后在其中一个分支上进行了恢复,则该更改将出现在合并结果中; 有些人觉得这种行为很混乱。这是因为在执行合并时仅考虑头部和合并基础,而不是个别提交。因此,合并算法将恢复的更改视为完全没有更改,而是替换更改的版本。
Notes
您应该了解在git rebase
共享的存储库上使用的含义。请参阅下面的“从上游重新启动”中恢复。
当 git-rebase 命令运行时,它会首先执行一个 “pre-rebase” 挂钩(如果存在)。您可以使用此挂钩进行健全性检查,并在不合适的情况下拒绝 rebase 。有关示例,请参阅模板 pre-rebase 挂钩脚本。
完成后,<branch> 将成为当前分支。
交互模式
交互式重新定位意味着您有机会编辑重新提交的提交。您可以对提交进行重新排序,并且可以将其删除(清除不良或其他不需要的修补程序)。
交互模式适用于这种类型的工作流程:
- 有一个奇妙的想法
point 2. 由几个实例组成
a)常规使用
- 完成值得一提的东西
b)独立修复
- 意识到某些事情不起作用
有时候在 b.2 中固定的东西。不能修改为它所修复的不完美的提交,因为该提交深深地埋入了补丁系列中。这正是交互式底座的用途:在大量的 “a” 和 “b” 之后使用它,重新安排和编辑提交,并将多个提交压缩成一个。
按照原样保留的最后一次提交启动它:
git rebase -i <after-this-commit>
编辑器将会触发当前分支中的所有提交(忽略合并提交),这些提交会在给定的提交之后提交。您可以将此列表中的提交重新排序,直至您的内容为准,并且可以将其删除。该列表看起来或多或少像这样:
pick deadbee The oneline of this commit
pick fa1afe1 The oneline of the next commit
...
在线描述纯粹是为了乐趣; git rebase
将不会看到它们,而是以提交名称(本例中为 “deadbee” 和 “fa1afe1” ),因此不要删除或编辑名称。
通过使用命令 “edit” 替换命令 “pick” ,您可以告诉git rebase
在应用该提交后停止,以便您可以编辑文件和/或提交消息,修改提交并继续重新绑定。
如果您只想编辑提交的提交消息,请将命令 “pick” 替换为 “reword” 命令。
要删除提交,请将 “pick” 命令替换为 “drop” ,或者只删除匹配的行。
如果要将两个或多个提交合并为一个,请将第二个提交和后续提交的命令替换为 “squash ”或 “fixup” 。如果提交具有不同的作者,则折叠提交将归于第一次提交的作者。对于已提交的提交,建议的提交消息是第一次提交的提交消息和那些使用 “squash” 命令的提交消息的连接,但省略了使用 “fixup” 命令提交的提交消息。
git rebase
将 “pick” 替换为 “edit” 或由于合并错误而导致命令失败时停止。当您完成编辑和/或解决冲突时,您可以继续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 -p --onto Q O
重新排序和编辑提交通常会创建未经测试的中间步骤。您可能需要检查历史编辑是否通过运行测试,或至少使用 “exec” 命令(快捷方式 “x” )在历史中间点重新编译来破坏任何内容。你可以通过创建一个像这样的待办事项列表来实现:
pick deadbee Implement feature XXX
fixup f1a5c00 Fix to feature XXX
exec make
pick c0ffeee The oneline of the next commit
edit deadbab The oneline of the commit after
exec cd subdir; make test
...
当命令失败时(即以非0状态退出),交互式重新分配将停止,让您有机会解决问题。你可以继续git rebase --continue
。
“exec” 命令会在 shell 中启动该命令(在中指定的命令$SHELL或默认 shell ,如果$SHELL未设置),因此您可以使用 shell 功能(如“cd”,“>”,“;”...)。该命令从工作树的根目录运行。
$ git rebase -i --exec "make test"
这个命令让你检查中间提交是否可编译。待办事项列表如下所示:
pick 5928aea one
exec make test
pick 04d0fda two
exec make test
pick ba46169 three
exec make test
pick f4593f9 four
exec make test
分割提交
在交互模式下,您可以使用“编辑”操作标记提交。但是,这并不一定意味着git rebase
期望这个编辑的结果恰好是一个提交。事实上,您可以撤销提交,也可以添加其他提交。这可以用来将提交分成两部分:
- 开始一个交互式重新绑定git rebase -i <commit>^,其中 <commit> 是您要分割的提交。实际上,只要包含该提交,任何提交范围都会执行。
如果您不确定中间修订是否一致(它们会进行编译,通过测试套件等),您应该使用git stash
它在每次提交,测试和修改提交(如果需要修复)后保留尚未提交的更改。
从上游的 rebase 恢复
其他人基于工作的分支(或任何其他形式的重写)是一个坏主意:它下游的任何人都被迫手动修改其历史记录。本节介绍如何从下游角度进行修复。然而,真正的解决办法是避免重新摆放上游。
为了说明,假设你处于某人开发subsystem
分支的情况,并且你正在研究一个topic
依赖于此的分支subsystem
。你可能会得到如下的历史记录:
o---o---o---o---o---o---o---o master
\
o---o---o---o---o subsystem
\
*---*---* topic
如果subsystem
重新发放master
,会发生以下情况:
o---o---o---o---o---o---o---o master
\ \
o---o---o---o---o o'--o'--o'--o'--o' subsystem
\
*---*---* topic
如果你现在继续开发像往常一样,并最终合并topic
到subsystem
,从提交subsystem
会永远保持复制:
o---o---o---o---o---o---o---o master
\ \
o---o---o---o---o o'--o'--o'--o'--o'--M subsystem
\ /
*---*---*-..........-*--* topic
这些重复的东西通常会被忽视,因为它们混淆了历史,使其难以遵循。为了清理,你需要将提交移植topic
到新的subsystem
提示中,即 rebase topic
。这变成了一种连锁反应:下游的任何人topic
都被迫变质,等等!
有两种修复方法,将在以下小节中讨论:
简单案例:这些变化字面上是相同的。
如果这个subsystem
rebase 是一个简单的 rebase 并且没有冲突,就会发生这种情况。
困难的情况:变化是不一样的。
如果subsystem
rebase 有冲突或者用于--interactive
忽略,编辑,挤压或修改提交,就会发生这种情况; 或者如果上游使用的一个commit --amend
,reset
或filter-branch
。
简单的情况
只有subsystem
在 rebase subsystem
之前和之后的变化(基于 diff 内容的补丁ID)字面上相同时才有效。
在这种情况下,修复很容易,因为git rebase
知道跳过已经存在于新上游的变化。所以如果你说(假设你在topic
)
$ git rebase subsystem
你会以固定的结果结束
o---o---o---o---o---o---o---o master
\
o'--o'--o'--o'--o' subsystem
\
*---*---* topic
困难的情况
如果这些subsystem
变化与 rebase 之前的变化不完全一致,事情会变得更加复杂。
Note | While an "easy case recovery" sometimes appears to be successful even in the hard case, it may have unintended consequences. For example, a commit that was removed via git rebase --interactive will be resurrected! |
---|
这个想法是手动告诉git rebase
“旧的subsystem
结局和你的topic
开始”,也就是说,他们之间的旧合并基础是什么。您必须找到一种方法来命名旧的最后一次提交subsystem
,例如:
- 随着
subsystem
reflog:之后git fetch
,旧的一角subsystem
是在subsystem@{1}
。后续提取将增加数量。(请参阅 git-reflog [1]。)
然后你可以subsystem..topic
通过说(对于 reflog 的情况,并假设你已经topic
),将旧的移植到新的提示中:
$ git rebase --onto subsystem subsystem@{1}
“硬性案例”恢复的连锁反应特别糟糕:everyone
下游topic
将不得不进行“硬性案例”恢复!
Bugs
提供的待办事项列表--preserve-merges --interactive
不代表修订图的拓扑结构。编辑提交和重新提交它们的提交消息应该可以正常工作,但尝试重新排序提交往往会产生违反直觉的结果。
例如,尝试重新排列
1 --- 2 --- 3 --- 4 --- 5
至
1 --- 2 --- 4 --- 3 --- 5
通过移动 “pick 4” 行,将导致以下历史记录:
3
/
1 --- 2 --- 4 --- 5