Git rebase递归分支

时间:2017-03-24 10:54:32

标签: git rebase git-rebase

我正在编写一门编程课程,其中我想展示如何逐步编写程序。我以为我可能会为此目的使用git。我们的想法是将每节课作为一个单独的分支,并随着课程的进行创建新的分支。 normal state

一切都很好,直到我发现我在lesson1犯了一个错误。所以我去那里修理它。 fix lesson 1

现在问题出现了:我必须重新定义每个分支。所以:

git checkout lesson2
git rebase lesson1

enter image description here

之后lesson3lesson4的内容相同。 enter image description here

我每节课大约有20节课,所以每个错误都非常痛苦。有没有办法自动化它或者至少让我更容易?

顺便说一句。我用来创建图像的工具here可用。

5 个答案:

答案 0 :(得分:4)

所以不得不回到绘图板......

我之前建议使用简单的filter-branch命令,但这有一个重大缺陷。 ( tl; dr - 我不再将此作为filter-branch --parent-filter的用例;除非您关心原因,否则可以跳转到下一段。)当您重新使用{时{1}}它不会为有效的合并重新应用更改,而是将树保留在重新提交的提交中(基本上创建新的差异)。 git filter-branch仍有可能,但它需要filter-branchtree-filter,这将开始变得相当复杂。 (如果您可以在脚本中自动执行修复,那么将该脚本用作index-filter应该可以工作 - 可能在rev-list参数中有一点点技巧 - 但是我们假设一般情况下这不是我觉得脚本编写方法是将“修复”提交中的更改合并到移植中的每个提交中,但这可能会导致每次转弯都发生冲突,而且也不是那么容易......)

那又该做什么呢?好吧,像Libin Varghese这样的脚本化方法表明如果没有冲突就可以了,并且假设您可以以合理的方式遍历ref名称。但假设可能存在冲突,还有另一种方式......

所以,如果你有

tree-filter

你要做的基本上是

1)将 Bfix <--(lesson1) / A --- B --- C --- D --- E <--(lesson3)(HEAD) | (lesson2) CD重新应用于E BfixC'D' (单个rebase操作)

2)将所有引用从被替换的提交(E')移动到其替换(X

使用单个rebase可最大限度地减少冲突解决的数量。如果您只是重新定义X',那么您将拥有

lesson3

然后你只需要为第一节和最后一节课以外的分支重写refs。这意味着您需要从“旧提交 (lesson1) | Bfix --- C' --- D' --- E' <--(lesson3)(HEAD) / A --- B --- C <--(lesson2) ”到“替换提交X”的映射。

在rebase结束时,这样的映射会在stdin上传递给.git / hooks / post-rewrite(如果存在)。因此,您可以编写一个脚本,使用X'将ref(分支)名称映射到“旧”SHA1值,然后使用stdin上的映射查找相应的“新”SHA1值,并调用git show-ref

(我打算提供一个示例脚本,但我在测试回购中遇到一些问题;所以如果我稍后有一段时间,我会回到这个。但如果你感觉舒服使用脚本和钩子,上面概述了需要做的事情。)

答案 1 :(得分:1)

start=2
end=10
for i in {$start..$end}
do
        git checkout lesson$i
        git rebase lesson$(($i-1)) || break
done
start=$i

假设你没有冲突,这个循环通过第2课到第10课,执行变基。

如果rebase失败,则将开始设置为失败的位置。但请确保在继续

之前解决冲突并执行rebase --continue

答案 2 :(得分:0)

这是我尝试解决问题的方法。您将不得不修复我的语法错误,并完成自动化问题,但这可能是一个开始。

单行

git rebase lesson1 lesson2

具有相同的效果
git checkout lesson2
git rebase lesson1

您应该重新上一课,以便所有中间提交同时转移到新分支。您将不得不修复发生的任何冲突。

git rebase lesson1 lesson4

然后使用看起来类似的命令将分支转移到新的提交(如果课程是连续的)。

git branch lesson2a lesson4^2
git branch lesson3a lesson4^1

如果分支是连续的。 'git help revisions'显示了如何使用来自给定分支的提交消息来查找提交。

git branch lesson2a  lesson4^"{/Partial lesson2 commit message}"
git branch lesson3a  lesson4^"{/Partial Lesson3 commit message}"

一旦看起来正确删除旧提交

git branch -f lesson2 lesson2a
git branch -D lesson2a

请参阅'git help rebase'了解rebase语法

和'git help revisions'用于指定提交的不同方法。

答案 3 :(得分:0)

这是Mark答案中所述的重写后挂钩。

(我对shell脚本还没有经验,所以欢迎发表评论。)

#!/bin/bash

if [ "$1" != "rebase" ]; then
    exit 0
fi


orig=`git rev-parse ORIG_HEAD`

while read line
do
    IFS=' '
    read -ra map <<< "$line"
    old="${map[0]}"
    new="${map[1]}"

    heads=`git show-ref | grep -e " refs/heads" | grep "$old"`

    IFS=$'\n'
    for h in $heads; do

        IFS=' '
        read -ra ref_info <<< "$h"
        ref="${ref_info[1]}"

        # Don't update original branch as this causes rebase to fail
        if [ "$old" != "$orig" ]; then
            echo "Updating '$ref' to $new"
            `git-update-ref $ref $new $old`
        fi
    done
done

答案 4 :(得分:0)

TL;DR 使用/复制Graphite CLI的实现

这个开源 CLI 将执行递归分支变基(披露,我是贡献者): https://github.com/screenplaydev/graphite-cli

可以在这里看到主要的变基递归:https://github.com/screenplaydev/graphite-cli/blob/dfe4390baf9ce6aeedad0631e41c9f1bdc57ef9a/src/actions/fix.ts#L60

git rebase --onto ${parentBranch.name} ${mergeBase} ${currentBranch.name}

关键点是将分支父级存储在 git refs 中,以便在操作期间递归 DAG。如果没有父元数据,就不可能总是确定连续子分支的合并基。

const metaSha = execSync(`git hash-object -w --stdin`, {input: JSON.stringify(desc)}).toString();

execSync(`git update-ref refs/branch-metadata/${this.name} ${metaSha}`);

https://github.com/screenplaydev/graphite-cli/blob/dfe4390baf9ce6aeedad0631e41c9f1bdc57ef9a/src/wrapper-classes/branch.ts#L102-L109