是否可以重新建立修改后的提交并让 git 自动跳过旧版本的提交?
对于 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' 所做的更改。此时看来解决方案似乎是可能的:
指定变基范围(如何 git commit --amend 是分支基础的提交)
>>> git rebase --onto amend <hash-of-E> devel
Run Code Online (Sandbox Code Playgroud)互动git rebase --onto amend devel
与
drop <hash-of-D>
pick <hash-of-E>
pick <hash-of-F>
Run Code Online (Sandbox Code Playgroud)但无论如何,这取决于提交哈希值的无意混合,因此未推送的提交可能会被意外删除。devel
此外,有时会在推送的更改之上构建多个分支,并且它们都需要重新建立基础。我发现由于必要的手动错误检查,这些事情可能会花费大量时间。
如果不止一项提交被推送进行审查,并且中间的一项需要修改,那么问题就会变得更加复杂,从而增加了对自动化的渴望。
使用git rebase --onto
。这仍然有点棘手,但您不需要进行交互并专门告诉 Gitdrop
特定提交:您可以通过命令行选择哪些提交被复制,哪些不被复制。
事实上,您一直在这样做,因为git rebase
从根本上来说,它是通过复制某些提交集来工作的。也就是说,git rebase
有三个或四个阶段,具体取决于您如何计算:
列出要复制的提交的提交哈希 ID。pick
当您使用交互式样式 rebase 时,这些将成为命令,因此您实际上已经知道这部分是如何工作的。
使用(或等效项)在特定目标提交时git checkout --detach
进入分离 HEAD模式。目标提交由您决定:您通过命令行告诉 Git,要在此处查看哪个提交。
git cherry-pick
在要选择的每个提交上重复运行。(此特定步骤的具体细节根据您使用的变基样式而有很大差异。)
现在已经复制了所需的提交,获取我们在步骤 2\xe2\x80\x94 中放弃的分支名称,该分支名称重新设置记录在文件\xe2\x80\x94 中,并强制该分支名称指向当前( )HEAD
提交,并重新连接 HEAD,以便我们回到分支上。
如果我可以稍微重画一下你的例子,你实际上是从这个开始的:
\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当您使用--amend
或其他一些操作时,这根本没有真正改变D
,正如您所看到的:它只是进行一个新的提交,D\'
其父级是C
并且其快照考虑了您想要的任何更新。所以现在你有:
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\nE-F
要以自动方式复制,您需要一种命名 commit 的方法D
。它的实际哈希 ID 始终有效,但哈希 ID 又大又丑又烦人。如果您在自己的存储库中插入一个名称\xe2\x80\x94,那么效果会更好,您可以记住任何类型的名称\xe2\x80\x94。
可用名称的“种类”有:
\n\ngit branch
创建和删除它们;git tag
创建和删除这些名称;和refs/for/
或 之类的前缀refs/xyzzy/
。Gerritrefs/for/
命名空间是这些发明之一:它不是您的,而是 Gerrit 的,但它只是一个完整的类别,任何人都可以在其中粘贴名称,并且如果每个人都将名称留给refs/for/
Gerrit并发明了自己的个人事物,但 refs/for/
它们不会发生冲突。其中,分支名称可能是您最好的选择,但这取决于您。对于其余部分,我假设您使用分支名称。(标签名称也工作得很好,我已经尝试过将它们用于我自己的用途。请小心不要git push
错误地使用它们,因为标签很快就会弄乱其他人的存储库!)
所以,假设你有:
\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/number
git push origin refs/for/master
当您使用以下命令运行交互式变基时:
\n\ngit checkout devel\ngit rebase -i origin/master\n
Run Code Online (Sandbox Code Playgroud)\n\ngit rebase
列出要复制的提交是D-E-F
.
那是因为它实际上列出了F-E-D-C-B-A
\xe2\x80\x94可以通过从 开始(名为 via 的提交)并向后工作找到的每个提交。然后,它单独列出:可以通过从 开始、由 命名的提交并向后查找来找到的每个提交。它将第二个列表中的任何提交从第一个列表中剔除,留下,然后将其反转为樱桃采摘所需的顺序。F
devel
C-B-A
C
origin/master
F-E-D
提交列表是:
\n\ndevel
) 可到达的分支,减去upstream
您给予git rebase
:的参数中获得这些值origin/master
。这完成了步骤 1。(实际上,它更复杂:可以从列表中剔除更多提交。默认情况下,自动丢弃合并提交。可以通过 patch-ID 匹配和 fork-point 模式丢弃额外的提交rebase。但是我们在这里忽略所有这些。)
\n\n该upstream
参数还提供了 Git 将在步骤 2 中使用的目标提交,即git checkout
分离 HEAD 的目标提交。
如果你可以告诉 Git:
\n\nC
作为目标...D
作为提交列表的末尾来淘汰这样就可以完成这项工作,而无需您使用git rebase -i
和手动编辑。事实证明,这很容易做到:
git rebase --onto in-review/master/2 in-review/master/1\n
Run Code Online (Sandbox Code Playgroud)\n\n该参数将目标部分从 中--onto
分离出来,释放参数意味着只承诺不复制。upstream
upstream
这就是为什么我们给有趣的提交指定了特定的名称。 在更复杂的场景中,您将从以下开始:
\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
:
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有:
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
,就像以前一样。
在某些情况下,这种技术会失败。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\'
请注意,复制到 后I
,I\'
单个git checkout feature/long; git rebase --onto in-review/master/2 in-review/master/1
复制J-K-L
到J\'-K\'-L\'
,但现在有三个标签需要移动。这是缺失的工具:一种可以移动多个标签的工具。但这张图太简单了,就像你可能看到的那样:
...--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
。因此,多重变基工具需要知道:
然后它必须找出要复制的提交,构建从旧提交哈希到新提交哈希的映射,直到整个组被完全复制,然后才将所有分支名称移动到新的提交哈希 ID。合并保存模式(类似于 Git\'s git rebase --rebase-merges
)也将是该工具的正确默认模式,因为这里的多分支可以在其子图中具有分支和合并模式(彼此分支和合并,或彼此独立,或两者)。
新的 rebase-merges 代码是实现这一所需工具的大部分方法,但它仍然缺乏一种指定多个分支名称(因此,至少可能是多个提示提交)的方法以及需要调整的代码整个过程结束时的多个分支名称。
\n 归档时间: |
|
查看次数: |
2076 次 |
最近记录: |