如何从分支的顺序部分合并提交?

时间:2012-03-24 08:15:42

标签: git

好的,这是我的情况。我有以下分支

  • 开发
  • development-kirby(270个提交开发前)

我的老板希望我的开发变革kirby合并到开发中,但他不希望整个事情在一个巨大的合并中。勺子如何在几个较小的块中提交提交(一次50个,按顺序)?我只是在开发 - kirby分支中检查几个点并以这种方式合并它们还是有更好的东西?

我确信这个问题已经以更好的方式提出并得到了解答,但搜索结果并没有太多。

2 个答案:

答案 0 :(得分:2)

要直接回答,您可以分支选定的提交(语法:git branch< branchname>< start-point>),将主干合并到此新分支中,将合并结果拉(推)到主干中,删除临时分支并转到下一个提交部分。

OTOH我想你的老板是错的:除非将提交重新组织成逻辑组,可以单独应用而不破坏整个应用程序逻辑,否则没有任何好处。但在这种情况下,不应简单地计算承诺。您可以使用交互式rebase重新组织它们(重新排序,重新排序)。 [更新:情况并非如此,见下文]

答案 1 :(得分:1)

是的,你可以完全按照你的要求做(但是,不要;见下文)。例如(这是一个非常脆弱,因为我假设270号是正确的,并且第一次合并将起作用;它也完全未经测试......):

b=development-kirby
m=development
git checkout $m
# 270 = 50 + 50 + 50 + 50 + 50 + 20
# hence, to be done in groups of 50, the commit-points are:
# $b~219, $b~169, $b~119, $b~69, $b~19, and $b (aka $b~0)
for dist in 219 169 119 69 19 0; do
    # want to merge $b~$dist into $m, and we're (still) on $m
    git merge --no-ff $b~$dist -m "mechanical merge at $b~$dist"
done

这应该给你类似的东西:

   ---- K1 --...-- K50 ---- K51..K100 --- ... --- K201...K250 --- K251..K270     development-kirby
 /                        \            \              \              \      \
* --- D1 --- D2 --- D3 --- M1 --------- M2 --- ... --- M4 ----------- M5 --- M6  development

其中K1..K270是您的270次提交,D1到D3是自您开始工作以来放在development上的三个示例提交。

--no-ff确保所有六个实际的合并提交,M1到M6,在回购中结束,否则你会快速前进,而你在上面只有M1,K51通过顶部的K270,分支development现在指向与分支development-kirby相同的位置:

   ---- K1 --...-- K50 ---     --- K51..K270  development-kirby, development
 /                        \   /
* --- D1 --- D2 --- D3 --- M1

[可能有助于注意,在每次“中间快进”期间,development将指向K51..K269范围内的某些提交,而development-kirby将继续指向提交K270。只有在最后一次快进之后,两个分支名称才会指向相同的提交。])

问题是,这是一个奇怪且不聪明的事情。那些中间合并提交,如果他们纯粹机械地选择(“每50”或其他什么),是没用的。他们只通过一次270次提交合并就绝对没有给你带来任何好处。在你的私有开发分支中选择“有意义”的点(例如,“完全实现的特征X”)会更有意义。假设你已经离开并选择了几个这样的点,例如:

git checkout $b~215
... poke around to make sure it's a good merge point,
    and maybe move to $b~213 instead ...
git tag mp1 # merge point 1
git checkout $b~170
... poke around more, move to $b~172 because that's better ...
git tag mp2 # merge point 2
git checkout ...
# add more temporary tag labels as needed
# let's say you wind up with 4 total merge points
# which I've simply numbered mp1, mp2, mp3, mp4 
git checkout development # get on target merge branch
git merge --no-ff mp1
<put in a sensible commit message this time, and resolve any merge conflicts>
git merge --no-ff mp2
<put in a sensible commit message for merge point 2>
git merge --no-ff mp3
<etc>
git merge --no-ff mp4
<etc>
git tag -d mp1 mp2 mp3 mp4 # get rid of temporary tags

这是关于“git merge”的主要知识,在这种特殊情况下,总会有一个实际的合并提交(即,不是快进):当你运行git merge时你在做什么正在添加 - 您当前的分支 - 一个新的提交,其父母是:

  • 您所在分支的当前HEAD(在这种情况下最好是development),
  • 您在git merge命令中命名的提交ID(可以在任何地方任何提交ID)。

(新提交的内容当然是将“HEAD commit”的内容与“named commit”的内容合并的结果,这意味着找到这两个提交之间发生了什么变化等,有时获得帮助解决合并冲突。)在新的提交进入后,当前的分支提示被重新指向新的合并提交。在这种情况下,development会向前移动到新添加的合并。

因此,您需要做的就是重复运行git merge --no-ff(强制实际的合并提交,而不是快进),每次在development-kirby分支中指定一个适当的提交ID “拓扑顺序”,即mp1&lt; mp2&lt; mp3&lt; mp4(否则你最终会遇到合并图的灾难;并看到脚注)。有很多方法可以指定commit-ID,例如使用上面的~表示法,但“最佳”(对于某些“最佳”值)的方法是找到单独的“好的”({ {1}}),然后标记它们(git checkout),这样您就不必记住长数字字符串。

<小时/> 脚注:您可以使用git tag查看某些提交ID列表的顺序是否正确,但这有点痛苦。我不知道一个更好的方法,快速的谷歌搜索没有改变任何东西,所以我写了一个简短的脚本:

git rev-list --no-walk --topo-order

在制作一组合并点标签之后,你可以运行,例如:

#! /bin/sh
#
# check a list of IDs to see if they're in "topo order"
usage()
{
    echo "usage: $0 id [...]"
}

case $# in
0) usage 1>&2; exit 1;;
esac

TF1=$(mktemp)
TF2=$(mktemp)
trap "rm -f $TF1 $TF2; exit" 0 1 2 3 15

# parse the arguments into one file
git rev-parse $@ > $TF1 || exit 1
# and topo-sort the arguments into another
git rev-list --topo-order --no-walk --reverse $@ > $TF2 || exit 1
# If the list is in the correct order the files will be the same
cmp -s $TF1 $TF2 || {
    # If the files differ, it's possible that some argument(s) name
    # the same rev...
    [ $(wc -l < $TF1) -eq $(wc -l < $TF2) ] || {
        echo "ERROR: there are repeats in $@"
        # finding them is a pain, we don't bother trying
        exit 1
    }
    echo "ERROR: $@ NOT in topo order"
    echo "use instead:"
    # read the topo-ordered raw IDs
    while read sha1; do
        # and find the (single) arg in $@ that names this one
        for i; do
            if [ $(git rev-parse $i) = $sha1 ]; then
                printf ' %s' $i
                break
            fi
        done
    done < $TF2
    echo
    exit 1
}
echo "$@ in topo order"
exit 0

它会告诉你mp1&lt; mp2&lt; mp3&lt; MP4。

<小时/> 而且,虽然你没有问过这个问题,但在270次提交中崩溃(“压缩”)可能是合理的。很大程度上取决于您的公司喜欢做事的方式,但最常见的是,将您的270个提交链组合成一个新的“添加功能F,添加功能G,添加功能H”链总共可能需要5到10次提交。

例如,您可能会结束3次提交“添加功能F” - 可能“清理以准备F”,然后“为F添加基础设施”,然后是“启用功能F” - 然后只需1 ,以及2或3次提交以添加功能H.这将为您提供一个6-commit-long分支(例如,可能称之为check-topo-order mp1 mp2 mp3 mp4 )。此时,您有一个非常干净的开发链,可以直接拼接到名为devel-kirby-squashed的分支中或其上。

当然,有时保留所有中间工作的理由,在这种情况下,一系列development合并是合理的。