创建包含特定 pre-existing 提交的分支

Create branch containing specific, pre-existing commits

一个项目具有以下结构:一个 master 分支,其唯一提交是 M,一个 develop 分支(垂直分支)和许多 release-X.Y 分支(水平分支机构)。 o 是常规提交,*vX.Y.Z 标记的提交。 release-X.Y 分支中的一些有用改进已合并回 develop(图中未显示)。

o -- * -- o -- o -- *
|
o
|
o -- o -- *
|
o
|
M

如何使 master 分支在正确的[即字典顺序]不改变现有结构的顺序?

也就是说,如果水平分支是release-0.1release-0.2,标记的提交是v0.1.0v0.2.0v0.2.1,如何使master 这样 git log master 会产生(模额外信息)

v0.2.1
v0.2.0
v0.1.0
M

在图表中它会像

       .__________.
      /            \
o -- * -- o -- o -- *
|      \
o       \
|        \
o -- o -- *
|        /
o   .___/
|  /
M./

(当然,特定的树以及是否标记了所需的提交对于问题来说最终是无关紧要的,但希望它有助于构建激发问题的上下文:让 master 对所有内容进行分类“官方”提交软件。)

我已经使用 mergecommitrebasecherry-pick 命令查找了可能性,但是 none 似乎有效(有些可以创建新的content-equivalent 次提交,但目标是保持完全相同的标记提交)。

有三个标题相似但意图却大不相同的问题:one, two and

我认为可以使用 low-level 手动设置引用的指令和 objects 以正确的方式将标记的提交堆叠在 master 上,但我更希望有一个更系统,higher-level解决方案(如果有的话)。

some can create new content-equivalent commits, but the goal is to keep the same exact tagged commits

你不能。 Git 提交,包括它的出身,是不可变的。还有一件好事!否则Git就没用了

因此,创建“新 content-equivalent 提交”正是您需要做的。

(先看

在 Git 中绘制分支有点棘手。

在某些版本控制系统中,分支是真实存在的。也就是说,您创建一个分支并用提交填充它,这些提交在 that 分支上,并且 only 该分支,现在和永远。所以在这样的仓库中绘制commit时,我们可以把分支名放在左边,把commit列在右边,像这样:

br1:   A--B--C

br2:   D--E--F

br3:   G--H

提交 Cbr1 上(仅),并且将永远永远在该分支上,只要提交继续存在。提交 Hbr3 上(仅),并且同样会永远在该分支上。我们不能在没有分支的情况下进行提交(尽管我们至少在理论上可以在没有提交的情况下进行分支)。

但这在Git中并非如此。在Git中,分支名称是一个可移动标签。它没有单独的存在:它是短暂的、不真实的,只是一个经过的影子。它的目的是帮助我们定位一个特定的commit,我们称之为分支的tip commit .因此,如果我们像上面那样绘制提交 left-to-right,我们应该将 分支名称 放在 右边

在 Git 中 提交之间的连接不存在,因为它们在哪个分支上。相反,它们本身就是提交的永久部分。这些连接存在 独立于任何分支 。我们甚至可以在 根本没有分支 上进行提交,因为提交本身独立于分支。

我们不能在 Git 中拥有一个没有提交的分支。分支名称 的存在需要 对它的特定提交 point-to,并且 多个名称 可以指向任何特定提交.所以我们必须在 右边 上画出名字,用箭头从他们身上伸出来表明他们是哪个提交 point-to:

A--B--C   <-- br1
       \
        D--E--F   <-- br2
               \
                G--H   <-- br3

提交 A-B-C 所有三个 分支上;通过 F 的提交在 两个 分支上(它们不是“在”br1 上,而是在 br2br3 上);并且提交 G-H 仅在一个分支 br3 上。至少,现在是这样。如果我们选择这样做,我们可以 完全删除 名称 br1

A--B--C
       \
        D--E--F   <-- br2
               \
                G--H   <-- br3

我们不再需要图表中的第一个扭结,在 CD 之间,因为通过 F 的提交都在剩下的两个分支上。 G-H 仅保留 br3。我们是否也应该删除 br2,所有提交现在都在 one 分支上,br3。我们甚至可以创建一个新名称 br4,或 re-create 旧名称 br1,或移动它:

A--B--C--D--E--F--G--H   <-- br1, br3

现在所有提交都在两个分支上。

这意味着在一个非常重要的意义上,Git 的分支 不是真实的 。它们甚至不存在!如果某物今天是 X,但明天是 Y,那么称它为 X 有什么意义?1 但是当然,我们这些短暂的人类会喜欢其他短暂的事物,所以我们继续只要它们有助于 and/or 娱乐我们,就使用这些幻影。

这里的真正教训是你不能也不应该过分依赖 Git 中的 分支 。它们只是临时服务实体。如果使用得当,标签名称2 会更可靠:使用标签来标记具有某种历史意义的提交。


1Exigency,大概吧。

2Git 不会让你移动标签,所以除非你使用 git tag -f 强制 re-create 现有标签,或删除然后 re-create 标签, 选择的提交哈希 ID 任何特定标签保持不变。我们可以只使用原始哈希 ID,但人类厌恶这些。

(请注意,1.8.2 之前的 Git 在 git fetch 期间意外地将 branch-name 规则应用于 tag-name 更新。也就是说,名称移动被允许,如果并且仅当它是 fast-forward。这只是一个错误。)

您不能在提交时仅更改分支上的某些提交。但是,您可以,所以在您的分支 git log 时间类似的东西主要由合并组成。将每个release一个接一个合并到master分支:

git checkout master
git merge vX.Y.Z
git merge vX2.Y2.Z2
...

您可能希望使用 theirs 合并策略来确保在发生合并冲突时在 master 中获得最新版本,而不必手动解决冲突。

您可以仅显示合并提交,即每个版本一个,通过使用 git log

的“--first-parent”选项
git log --first-parent master

将仅向您显示所有合并提交,这将与发布 1:1 对应。