Verzeichnis suchen
Guides gitattributes giteveryday gitglossary gitignore gitmodules gitrevisions gittutorial gitworkflows Administration git archive git bundle git clean git filter-branch git fsck git gc git instaweb git reflog Basic Snapshotting git add git commit git diff git mv git reset git rm git status Branching and Merging git branch git checkout git log git merge git mergetool git stash git tag Debugging git bisect git blame git grep Email git am git format-patch git request-pull git send-email External Systems git fast-import git svn Getting and Creating Projects git clone git init Git git annotate git archimport git bisect-lk2009 git check-attr git check-mailmap git check-ref-format git checkout-index git cherry git citool git column git credential git credential-cache git credential-store git cvsexportcommit git cvsimport git cvsserver git diff-files git diff-tree git difftool git fast-export git fetch-pack git fmt-merge-msg git get-tar-commit-id git gui git http-backend git http-fetch git http-push git imap-send git index-pack git interpret-trailers git ls-remote git ls-tree git mailinfo git mailsplit git merge-file git merge-index git merge-one-file git merge-tree git mktag git mktree git name-rev git notes git p4 git pack-objects git pack-redundant git pack-refs git parse-remote git patch-id git prune git prune-packed git quiltimport git receive-pack git remote-ext git remote-fd git remote-testgit git repack git replace git rerere git send-pack git sh-i18n git sh-setup git shell git show-branch git show-index git stripspace git unpack-file git unpack-objects git upload-archive git upload-pack git var git verify-commit git verify-tag git whatchanged git worktree Inspection and Comparison git describe git shortlog git show Miscellaneous api credentials api index gitcli gitcore tutorial gitcredentials gitcvs migration gitdiffcore githooks gitk gitnamespaces gitremote helpers gitrepository layout gitsubmodules gittutorial 2 gitweb gitweb.conf pack format User Manual Patching git apply git cherry-pick git rebase git revert Plumbing Commands git cat-file git check-ignore git commit-tree git count-objects git diff-index git for-each-ref git hash-object git ls-files git merge-base git read-tree git rev-list git rev-parse git show-ref git symbolic-ref git update-index git update-ref git verify-pack git write-tree Server Admin git daemon git update-server-info Setup and Config git git config git help Sharing and Updating Projects git fetch git pull git push git remote git submodule
Figuren

命名

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。请注意该选项的逆转ourstheirs上面所述-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_pointgit 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 mergegit 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则使用版本;

  • 如果our版本引入空白变化,但their版本包含实质性更改,their则使用版本;

  • 否则,合并按照通常的方式进行。

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> 将成为当前分支。

交互模式

交互式重新定位意味着您有机会编辑重新提交的提交。您可以对提交进行重新排序,并且可以将其删除(清除不良或其他不需要的修补程序)。

交互模式适用于这种类型的工作流程:

  1. 有一个奇妙的想法

  2. 破解代码

  3. 准备一系列提交

  4. 提交

point 2. 由几个实例组成

a)常规使用

  1. 完成值得一提的东西

  2. 递交

b)独立修复

  1. 意识到某些事情不起作用

  2. 解决该问题

  3. 提交它

有时候在 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 reset HEAD^。其效果是 HEAD 被倒回1,并且索引也随之而来。但是,工作树保持不变。

  • 现在将更改添加到您想要在第一次提交中使用的索引中。您可以使用git add(可能交互式)或git gui(或两者)来做到这一点。

  • 使用现在适合的提交消息提交当前的索引。

  • 重复最后两个步骤,直到你的工作树干净。

  • 继续与git rebase --continue的 rebase 。

如果您不确定中间修订是否一致(它们会进行编译,通过测试套件等),您应该使用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

如果你现在继续开发像往常一样,并最终合并topicsubsystem,从提交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 --amendresetfilter-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]。)

  • 相对于提示topic:知道你topic有三次提交,旧的提示subsystem必须是topic~3

然后你可以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
Vorheriger Artikel: Nächster Artikel: