我正在编写一门编程课程,其中我想展示如何逐步编写程序。我以为我可能会为此目的使用git。我们的想法是将每节课作为一个单独的分支,并随着课程的进行创建新的分支。
一切都很好,直到我发现我在lesson1
犯了一个错误。所以我去那里修理它。
现在问题出现了:我必须重新定义每个分支。所以:
git checkout lesson2
git rebase lesson1
我每节课大约有20节课,所以每个错误都非常痛苦。有没有办法自动化它或至少让我更容易?
顺便说一句。我用来创建图像的工具是here。
所以不得不回到绘图板......
我之前建议过一个简单的filter-branch
命令,但这有一个重大缺陷。 (tl; dr - 我不再建议将此作为filter-branch --parent-filter
的用例;除非你关心为什么,否则你可以跳转到下一段。)当你重新使用git filter-branch
时,它不会重新应用更改为有效合并,而是将树保持在重新提交的提交中(基本上创建新的差异)。一个filter-branch
仍然是可能的,但它需要一个tree-filter
或index-filter
,这将开始变得相当复杂。 (如果您可以在脚本中自动执行修复,那么使用该脚本作为tree-filter
应该可以工作 - 可能在rev-list参数中有一点点技巧 - 但是我们假设一般情况下这不会那么容易。我考虑编写一种方法,将“修复”提交中的更改合并到移植中的每个提交中,但这可能会导致每次转弯都发生冲突,而且也不是那么容易...)
那又怎么办呢?好吧,像Libin Varghese这样的脚本化方法表明如果没有冲突就可以了,并且假设您可以以合理的方式遍历ref名称。但假设可能存在冲突,还有另一种方式......
所以,如果你有
Bfix <--(lesson1)
/
A --- B --- C --- D --- E <--(lesson3)(HEAD)
|
(lesson2)
你本来想做的就是
1)在C
上重新应用D
,E
和Bfix
作为C'
,D'
和E'
(单个rebase操作)
2)将所有引用从被替换的提交(X
)移动到其替换(X'
)
使用单个rebase可以最大限度地减少冲突解决的数量。如果你只是改变lesson3
然后你会有
(lesson1)
|
Bfix --- C' --- D' --- E' <--(lesson3)(HEAD)
/
A --- B --- C <--(lesson2)
然后你只需要为第一节和最后一节课以外的分支重写refs。这意味着您需要从“旧提交X
”到“替换提交X'
”的映射。
只是这样的映射在一个rebase结束时传递给stdin到.git / hooks / post-rewrite(如果存在)。因此,您可以编写一个脚本,使用git show-ref
将ref(分支)名称映射到“旧”SHA1值,然后使用stdin上的映射来查找相应的“新”SHA1值,并调用git update-ref
。
(我打算提供一个示例脚本,但是我在测试回购中遇到了一些问题;所以如果我稍后有一段时间,我会回到这个。但是如果你对脚本和钩子,上面概述了需要做什么。)
start=2
end=10
for i in {$start..$end}
do
git checkout lesson$i
git rebase lesson$(($i-1)) || break
done
start=$i
假设你没有冲突,这个循环通过lesson2到第10课,执行rebase。
如果rebase失败,则将start设置为失败的点。但请确保在继续之前解决冲突并执行rebase --continue
这是我尝试解决问题的方法。您将不得不修复我的语法错误,并完成自动化问题,但这可能是一个开始。
单行
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'用于指定提交的不同方法。
这是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