如何在没有合并提交的情况下自动从线性历史转换到具有合并提交的线性历史记录?

时间:2017-11-02 14:17:05

标签: git

我如何从看起来像这样的存储库自动转换(没有合并提交,单行 - screenshot here):

* 559b18e (HEAD -> feature-d) feature-d-commit-c
* 255defd feature-d-commit-b
* b0fab4c feature-d-commit-a
* bd13c11 (feature-c) feature-c-commit-c
* ad3c099 feature-c-commit-b
* 89396f4 feature-c-commit-a
* 4c6e6d9 (feature-b) feature-b-commit-c
* bfbef8f feature-b-commit-b
* 1bc98cc feature-b-commit-a
* 3711c5b (feature-a) feature-a-commit-c
* 36e0042 feature-a-commit-b
* 1e24bee feature-a-commit-a
* 6b02ead (master, dev) Initial Commit

进入一个看起来像这样的存储库(使用merge commit - screenshot here):

*   d755428 (HEAD -> master, dev) Merge branch 'feature-d' into dev
|\  
| * e293b42 (feature-d) feature-d-commit-c
| * 1d8fb58 feature-d-commit-b
| * d00bbd5 feature-d-commit-a
|/  
*   758b701 Merge branch 'feature-c' into dev
|\  
| * c6b1e3c (feature-c) feature-c-commit-c
| * 5ecba0f feature-c-commit-b
| * 754245d feature-c-commit-a
|/  
*   6aa292a Merge branch 'feature-b' into dev
|\  
| * 7877571 (feature-b) feature-b-commit-c
| * 76ed497 feature-b-commit-b
| * 52d060b feature-b-commit-a
|/  
*   6635bd0 Merge branch 'feature-a' into dev
|\  
| * 3711c5b (feature-a) feature-a-commit-c
| * 36e0042 feature-a-commit-b
| * 1e24bee feature-a-commit-a
|/  
* 6b02ead Initial Commit

在初始存储库中,我为每个功能创建了一个线性历史记录和一个分支。当我完成一个功能,而不是合并'开发'我喜欢开发更多的功能,所以我可以稍后查看历史记录并在必要时重新排序提交(这有助于我以后了解我开发的内容是如何完成的并使用存储库作为教程 - 我在我的开发拥有)。一旦我对历史感到满意,我就会进行rebase并合并,直到我获得最终的存储库(这有助于我更好地识别哪些提交属于哪个功能)。这个工作流程有意义吗?

在这种特殊情况下,我可以通过以下方式实现我的目标:

git checkout dev
git merge --no-ff feature-a --no-edit

git checkout feature-b
git rebase dev
git checkout dev
git merge --no-ff feature-b --no-edit

git checkout feature-c
git rebase dev
git checkout dev
git merge --no-ff feature-c --no-edit

git checkout feature-d
git rebase dev
git checkout dev
git merge --no-ff feature-d --no-edit

git checkout master
git merge dev --no-edit

我认为这可以很容易地自动化(除非已经有一个git命令)这样的脚本:

dev_branch="dev"
first_feature_branch="feature-a"

git checkout $dev_branch
git merge --no-ff $first_feature_branch --no-edit

for feature_branch in "feature-b" "feature-c" "feature-d"
do
    git checkout $feature_branch
    git rebase $dev_branch
    git checkout $dev_branch
    git merge --no-ff $feature_branch --no-edit
done

git checkout master
git merge $dev_branch --no-edit

我可以将dev_branchfirst_feature_branch作为参数传递给脚本,然后我想我需要在它们之间获取一个分支列表(feature-b feature-c feature-d),我不知道该怎么做或是否有可能。

我还没有找到解决问题的方法。在开始新功能之前,所有文章,答案和博客似乎都会进行合并提交。

1 个答案:

答案 0 :(得分:0)

你需要编写自己的脚本(当你正确理解它并构建了一些有效的东西时,你可能根本不需要这个脚本,但它仍然是一个很好的练习:-))

  

我可以将dev_branchfirst_feature_branch作为参数传递给脚本,然后我想我需要在它们之间获取一个分支列表(feature-b feature-c feature-d),我不知道不知道怎么做或是否有可能。

是可能的,模数是一个相当大的问题:""之间的定义很滑。此外,提供一个单一的起点可能更有意义。 commit / branch-name,然后使用HEAD作为结束符。

这些是关键项目:

  • 在Git中,分支名称只是指针,指向(存储哈希ID)单个提交。我们称之为提交分支的提示,但正如您在自己的存储库中看到的那样,作为分支的"提示"并不意味着没有进一步的提交!

  • 因此,虽然我们经常将提交视为" on"某个分支,任何给定的提交可以同时在许多分支上。例如,您的提交1e24bee feature-a-commit-a位于所有四个功能分支上。同时,您的提交559b18e (HEAD -> feature-d) feature-d-commit-c仅在一个功能分支feature-d上。由于......

  • 这也是当前的提交(HEAD
  • 名称HEAD包含当前分支的名称(通常,无论如何)。这是这种情况:HEAD包含名称feature-d。名称feature-d指向(包含哈希ID)commit 559b18e。实际上,HEAD指向feature-dfeature-d指向559b18e,这是559b18e当前提交的原因。

(可能有一个"分离的HEAD",在这种情况下HEAD包含提交的原始哈希ID。该提交可能有也可能没有一些分支名称直接指向它,可以通过从一些分支名称开始并向后工作来找到或者找不到.Git通常做的是从一个或多个分支名称和/或HEAD开始,并且工作向后查找更多提交。每个提交存储其父提交的哈希ID,允许这种向后查找所有提交的方法,从分支提示提交开始。)

现在,有很多有用的Git工具可以解决这个问题。两个关键的是:

  • git for-each-ref:这是git tag版本的git branchgit for-each-ref refs/heads 的分支和标记列表形式。您可以使用以下命令获取所有分支的列表:

    refs/heads

    (此处refs/heads/附加一个斜杠,然后引用git rev-list名称空间中的每个引用,这是Git存储其所有分支的地方)。

  • git log:这是git rev-list的瑞士军用电锯版本,用于编写脚本。 (事实上​​,在内部,这两个命令都是从相同的源文件构建的。它们只是设置了一些不同的选项,以便一个对人类表现得更好,另一个对脚本表现得更好。)

要找到指向提交的所有分支名称"介于" -again之间,这是图感中的滑动概念 - 一些起点和一些结束点,您可以使用git rev-list --topo-order --reverse --ancestry-path ^start^@ end 枚举该范围内的每一次提交:

--ancestry-path
例如

。我将逻辑放在^start^@git rev-list --topo-order --reverse ^start^ end 之后,因为如果你能保证一个线性的提交链,你可以将它简化为:

start^

the Swiss Army Chainsaw中描述了start符号:它表示&#34; ...--0--S--1--2--3--4--5--E <-- end (HEAD) &#34;的父级。假设一个漂亮,简单,线性的提交链:

S

如果我们找到git rev-list的哈希ID,并向^S end询问1 2 3 4 5 E,我们会获得提交列表S(每个哈希ID一个)线)。但我们也想看看提交git rev-list!因此,我们需要告诉S要排除,而不是S^,而是0,即提交--topo-order

git rev-list标记告诉--reverse对这些内容进行排序&#34;拓扑&#34;顺序:基本上,孩子先,然后父。 (事实上​​,在这个简单的情况下,它实际上并不重要,因为默认顺序是&#34;优先级队列&#34;顺序用日期戳打破关系以产生时间顺序,但是没有联系在我们简单的线性链中。为了将来更复杂的用途,保持旗帜仍然是一个好主意。)

git rev-list标志告诉git log按我们想要的顺序列出提交:最祖先(&#34;最老&#34;)到最少祖先(&#34;最新&# 34)。正如我在上面的括号段中所指出的那样,Git非常喜欢 - 实际上,已经向后工作,从分支提示提交到其父提交然后再转移到父母的提交, 等等。 git rev-list--reverse都以这种方式工作,但是只要他们能够同时查看两个提交,同时&#34;,他们需要做更多的工作来选择下一个提交的工作。在任何情况下,git for-each-ref refs/heads都会使它们以相反的顺序发出提交,在我们的例子中,这将是&#34;转发顺序&#34; (您希望执行各种Git操作的顺序)。

所以,现在我们已经有了所有候选提交的列表,剩下的问题是看看哪个分支很有意思。在这里,您可以运行git rev-list列出所有分支及其提交ID(有关修改默认输出的方法,请参阅the gitrevisions documentation以使其对您自己的案例最有用) 。然后,您可以确定哪些提交ID与{{1}}输出中的提交ID匹配,以及它们的位置。这将为您提供一个可以操作的分支名称列表。