修改后rebase分支?

kdb*_*kdb 1 git rebase gerrit

是否可以重新建立修改后的提交并让 git 自动跳过旧版本的提交?

动机:Gerrit 补丁集

对于 Gerrit,通常需要在未通过自动或人工代码审查后修改提交。但修改后,还需要对此后所做的更改进行rebase,这是一个容易出错的过程。

How to git commit --amend a commit that's the base of abranch 中,给出了手动省略修改的提交的选项,但由于问题不要求自动化,因此没有建议这样的解决方案。

例子

假设我需要推送提交D以供审核,但已经进行了进一步的更改,尚不适合推送。

A---B---C(origin/master)---D---E---F(devel)

>>> git push origin <hash-of-D>:refs/for/master
Run Code Online (Sandbox Code Playgroud)

现在假设远程构建失败,或者审阅者发现问题。Gerrit 要求推送更新的更改是单个提交,因此我需要更改提交。

对于简单的修改,我可以交互地重新设置开发分支

>>> git rebase origin/master devel
edit D
pick E
pick F

A---B---C(origin/master)---D'---E---F(devel)
Run Code Online (Sandbox Code Playgroud)

更一般地说,我可能需要将更改作为临时分支进行检查,或者我可能有多个开发分支。那时,此选项不再可用。相反,我可能会做类似的事情:

>>> git checkout -b amend <hash-of-D>
>>> ### Make some changes
>>> git commit --all --amend

A---B---C(origin/master)---D'(amend)
        |
        '---D---E---F(devel)

>>> git push origin HEAD:refs/for/master
Run Code Online (Sandbox Code Playgroud)

现在我需要变基,但由于 D 和 D' 重叠,自动合并可能会失败,或者撤消从 D 到 D' 所做的更改。此时看来解决方案似乎是可能的:

但无论如何,这取决于提交哈希值的无意混合,因此未推送的提交可能会被意外删除。devel此外,有时会在推送的更改之上构建多个分支,并且它们都需要重新建立基础。我发现由于必要的手动错误检查,这些事情可能会花费大量时间。

如果不止一项提交被推送进行审查,并且中间的一项需要修改,那么问题就会变得更加复杂,从而增加了对自动化的渴望。

tor*_*rek 5

使用git rebase --onto。这仍然有点棘手,但您不需要进行交互并专门告诉 Gitdrop特定提交:您可以通过命令行选择哪些提交被复制,哪些不被复制。

\n\n

事实上,您一直在这样做,因为git rebase从根本上来说,它是通过复制某些提交集来工作的。也就是说,git rebase有三个或四个阶段,具体取决于您如何计算:

\n\n
    \n
  1. 列出要复制的提交的提交哈希 ID。pick当您使用交互式样式 rebase 时,这些将成为命令,因此您实际上已经知道这部分是如何工作的。

  2. \n
  3. 使用(或等效项)在特定目标提交时git checkout --detach进入分离 HEAD模式。目标提交由您决定:您通过命令行告诉 Git,要在此处查看哪个提交。

  4. \n
  5. git cherry-pick在要选择的每个提交上重复运行。(此特定步骤的具体细节根据您使用的变基样式而有很大差异。)

  6. \n
  7. 现在已经复制了所需的提交,获取我们在步骤 2\xe2\x80\x94 中放弃的分支名称,该分支名称重新设置记录在文件\xe2\x80\x94 中,并强制该分支名称指向当前( )HEAD提交,并重新连接 HEAD,以便我们回到分支上。

  8. \n
\n\n

如果我可以稍微重画一下你的例子,你实际上是从这个开始的:

\n\n
A--B--C   <-- origin/master\n       \\\n        D    [refs/for/master in the Gerrit repo at `origin`]\n         \\\n          E--F   <-- devel\n
Run Code Online (Sandbox Code Playgroud)\n\n

当您使用--amend或其他一些操作时,这根本没有真正改变D,正如您所看到的:它只是进行一个新的提交,D\'其父级是C并且其快照考虑了您想要的任何更新。所以现在你有:

\n\n
        D\'  [whatever name you like can go here]\n       /\nA--B--C   <-- origin/master\n       \\\n        D   [refs/for/master in the Gerrit repo at `origin`]\n         \\\n          E--F   <-- devel\n
Run Code Online (Sandbox Code Playgroud)\n\n

E-F要以自动方式复制,您需要一种命名 commit 的方法D。它的实际哈希 ID 始终有效,但哈希 ID 又大又丑又烦人。如果您在自己的存储库中插入一个名称\xe2\x80\x94,那么效果会更好,可以记住任何类型的名称\xe2\x80\x94。

\n\n

可用名称的“种类”有:

\n\n
    \n
  • 分支名称:git branch创建和删除它们;
  • \n
  • 标签名称:git tag创建和删除这些名称;和
  • \n
  • 您的发明的任何其他名称:这些是身体部位的(轻微)痛苦,因为您必须使用它们的全名,包括诸如refs/for/或 之类的前缀refs/xyzzy/。Gerritrefs/for/命名空间是这些发明之一:它不是您的,而是 Gerrit 的,但它只是一个完整的类别,任何人都可以在其中粘贴名称,并且如果每个人都将名称留给refs/for/Gerrit并发明了自己的个人事物, refs/for/它们不会发生冲突。
  • \n
\n\n

其中,分支名称可能是您最好的选择,但这取决于您。对于其余部分,我假设您使用分支名称。(标签名称也工作得很好,我已经尝试过将它们用于我自己的用途。请小心不要git push错误地使用它们,因为标签很快就会弄乱其他人的存储库!)

\n\n

所以,假设你有:

\n\n
        D\'  <-- in-review/master/2\n       /\nA--B--C   <-- origin/master\n       \\\n        D   <-- in-review/master/1\n         \\\n          E--F   <-- devel\n
Run Code Online (Sandbox Code Playgroud)\n\n

你自己的个人方式是在哪里记住我用 推动了这个提交。由于您已经完成了两次,因此我们有两个不同的数字。(我刚刚为了这个答案发明了这个命名系统,所以它可能很糟糕。选择一个适合的。)in-review/master/numbergit push origin refs/for/master

\n\n

当您使用以下命令运行交互式变基时:

\n\n
git checkout devel\ngit rebase -i origin/master\n
Run Code Online (Sandbox Code Playgroud)\n\n

git rebase列出要复制的提交是D-E-F.

\n\n

那是因为它实际上列出了F-E-D-C-B-A\xe2\x80\x94可以通过从 开始(名为 via 的提交)并向后工作找到的每个提交。然后,它单独列出:可以通过从 开始、由 命名的提交并向后查找来找到的每个提交。它将第二个列表中的任何提交从第一个列表中剔除,留下,然后将其反转为樱桃采摘所需的顺序。FdevelC-B-ACorigin/masterF-E-D

\n\n

提交列表是:

\n\n
    \n
  • 从当前分支 ( devel) 可到达的分支,减去
  • \n
  • 在这种情况下,可以从upstream您给予git rebase:的参数中获得这些值origin/master
  • \n
\n\n

这完成了步骤 1。(实际上,它更复杂:可以从列表中剔除更多提交。默认情况下,自动丢弃合并提交。可以通过 patch-ID 匹配和 fork-point 模式丢弃额外的提交rebase。但是我们在这里忽略所有这些。)

\n\n

upstream参数还提供了 Git 将在步骤 2 中使用的目标提交,即git checkout分离 HEAD 的目标提交。

\n\n

如果你可以告诉 Git:

\n\n
    \n
  • 使用提交C作为目标...
  • \n
  • 但使用 commitD作为提交列表的末尾来淘汰
  • \n
\n\n

这样就可以完成这项工作,而无需您使用git rebase -i和手动编辑。事实证明,这很容易做到:

\n\n
git rebase --onto in-review/master/2 in-review/master/1\n
Run Code Online (Sandbox Code Playgroud)\n\n

该参数将目标部分从 中--onto分离出来,释放参数意味着只承诺不复制upstreamupstream

\n\n

这就是为什么我们给有趣的提交指定了特定的名称。 在更复杂的场景中,您将从以下开始:

\n\n
\n

...如果不止一项提交已被推送进行审查...

\n
\n\n

在这种情况下,我们将有:

\n\n
...--G--H   <-- origin/master\n         \\\n          I--J--K   <-- in-review/master/1\n                 \\\n                  L   <-- feature/xyz\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果提交J需要修改,您可以签出提交K并给它一个新的分支名称in-review/master/2

\n\n
git checkout -b in-review/master/2 in-review/master/1\n
Run Code Online (Sandbox Code Playgroud)\n\n

这给了你这个:

\n\n
...--G--H   <-- origin/master\n         \\\n          I--J--K   <-- in-review/master/1, in-review/master/2 (HEAD)\n                 \\\n                  L   <-- feature/xyz\n
Run Code Online (Sandbox Code Playgroud)\n\n

您现在可以运行git rebase -i origin/master第二次提交并将其更改为edit. 当变基全部完成后,您可能会\xe2\x80\x94,具体取决于您是否也决定编辑I,和/或使用--force\xe2\x80\x94有:

\n\n
          I\'-J\'-K\'  <-- in-review/master/2 (HEAD)\n         /\n...--G--H   <-- origin/master\n         \\\n          I--J--K   <-- in-review/master/1\n                 \\\n                  L   <-- feature/xyz\n
Run Code Online (Sandbox Code Playgroud)\n\n

或者:

\n\n
...--G--H   <-- origin/master\n         \\\n          I--J\'--K\'   <-- in-review/master/2\n           \\\n            J--K   <-- in-review/master/1\n                \\\n                 L   <-- feature/xyz\n
Run Code Online (Sandbox Code Playgroud)\n\n

您现在可以git checkout feature/xyz; git rebase --onto in-review/master/2 in-review/master/1,就像以前一样。

\n\n

在某些情况下,这种技术会失败。Git 更需要一种多分支名称变基工具,但它没有这样的工具(构建一个服务良好且使用起来并不难的工具非常困难,这就是为什么没有人这样做的原因)。考虑:

\n\n
...--G--H   <-- origin/master\n         \\\n          I   <-- in-review/master/1\n           \\\n            J    <-- in-review/feature/tall/1\n             \\\n              K   <-- feature/short\n               \\\n                L   <-- feature/long\n
Run Code Online (Sandbox Code Playgroud)\n\n

您可能被迫对各种中间提交中的任何一个进行一些操作。由于对任何提交的出身和快照的任何更改都会导致复制它,因此如果您被迫将提交更改I为新的I\',您必须提出新的J\' and K\' (并可能L\'为 提交新的评论)。J\'

\n\n

请注意,复制到 后II\'单个git checkout feature/long; git rebase --onto in-review/master/2 in-review/master/1复制J-K-LJ\'-K\'-L\',但现在有三个标签需要移动。这是缺失的工具:一种可以移动多个标签的工具。但这张图太简单了,就像你可能看到的那样:

\n\n
...--G--H   <-- origin/master\n         \\\n          I   <-- in-review/master/1\n           \\\n            J    <-- in-review/feature/tall/1\n             \\\n              K--L   <-- feature/short\n               \\\n                M   <-- feature/long\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在feature/long单独变基是行不通的,因为它不会复制L;单独变基也不会feature/short,因为它会复制L但不会复制M。因此,多重变基工具需要知道:

\n\n
    \n
  • 哪些分支有趣,以及
  • \n
  • 在哪里将它们重新定位为一个组
  • \n
\n\n

然后它必须找出要复制的提交,构建从旧提交哈希到新提交哈希的映射,直到整个组被完全复制,然后才将所有分支名称移动到新的提交哈希 ID。合并保存模式(类似于 Git\'s git rebase --rebase-merges)也将是该工具的正确默认模式,因为这里的多分支可以在其子图中具有分支和合并模式(彼此分支和合并,或彼此独立,或两者)。

\n\n

新的 rebase-merges 代码是实现这一所需工具的大部分方法,但它仍然缺乏一种指定多个分支名称(因此,至少可能是多个提示提交)的方法以及需要调整的代码整个过程结束时的多个分支名称。

\n