更改 git 分支的根,将旧提交保留为分支 HEAD?

Change git branch's root, retainig older commit as branch HEAD?

请原谅我措辞不当,但我有 mastermaster-variant,后者是我正在开发的通用产品的变体。它包含其他文件和一些更改的文件。

问题是我正在创建这个 "in reverse",因为我从变体开始,现在需要这个新的通用 "root",因为我将添加其他分支的新变体它。

所以我从 master 中的原始变体开始,并为当前状态创建了一个分支 master-variant,没有任何更改(即它指向与 master 相同的提交).

然后我修改了 master 的内容使其成为 "generic",即删除所有特定于变体的内容并将其提交到那里。在这种状态下,mastermaster-variant 指向的提交相比有一个新的提交。

但我希望它看起来好像我从通用 master(那里的最后一次提交)开始,然后分支 master-variant,并添加了特定于变体的内容,即master.

中上一次提交的状态

我会尝试说明。我有的是这个 (m = master, v = master-variant):

m0---m1---m2---m3 (changes to create generic variant)
           \
            v1 (just a new ref to m2)

注意 m0..m2master 分支中,但是它们的 contents 是我现在想要的原始变体而是在一个分支中。

我想更改它,使其看起来像这样:

m0---m1---m2---m3 (same as above, the generic variant)
                \
                 v1 (same content as "v1" above, which is also the same content as "m2" in both illustrations)

我怎样才能做到这一点?

我的第一个想法是使用 git rebase,但我看不出这怎么可能,因为它所做的是重播提交,而我需要重播 reverse 提交,即 m3 提交的反向。

我想我可以隐藏 m2/v1 状态,删除 master-variant 分支,切换到 master(即提交 m3),弹出隐藏状态并将其提交到新的 master-variant 分支。

但是有更直接的方法吗?

简而言之,我想我真正想要的是创建一个包含历史提交状态的分支,但该分支以 HEAD 为根(而不是历史提交)。

提醒:提交 是文件状态的 快照。提交本身形成提交图,而 分支名称 只是标识(指向)特定提交的标签。

您说在您的图表中,v1 实际上是 提交 m2 的引用。也就是说,它还不是自己的单独提交。所以你真正拥有的是:

C0--C1--C2   <-- v1
          \
           C3   <-- master

其中 v1master 是指向由提交本身形成的图形的标签(名称),它们是 C0C3。您将自己描述为想要的图表添加了一个新提交 C4 ,其内容与 C2 的内容匹配,但其父项(历史提交就在其后面)是 C3:

C0--C1--C2    C4   <-- v1
          \  /
           C3   <-- master

鉴于这种情况,您有两个相对容易的选择。一种是进入 Git 所谓的 plumbing 命令 并使用一个命令进行新的提交,给它一个特定的保存快照和父集。新提交是您的 C4。然后,将 C2 复制到 C4,您可以将名称 v1 移动到指向 C4。两个特定的 Git 命令是:

hash=$(git commit-tree -p master v1^{tree} -F /tmp/message)

其中 /tmp/message 包含您要使用的提交日志消息。另请注意,某些 shell 可能会解释 ^{ 字符;如果是这样,请引用它们。然后您可以 运行 git log $hash 来验证提交看起来是否正确。然后,如果您当前 "on" 分支 v1:

git reset $hash

将调整 v1 标签。 (确保您的索引和工作树是干净的,即没有未提交的东西要保存。)如果不是:

git branch -v v1 $hash

将调整 v1 标签。

或者,考虑还原 提交 C3 会取消其更改并为您留下匹配 C2 的新提交。所以你可以:

git checkout v1
git merge --ff-only master

这将产生:

C0--C1--C2
          \
           C3   <-- v1 (HEAD), master

那么你只需 运行:

git revert HEAD

做一个新的提交来回避 C2C3 之间的区别,给你一个新的提交 C4:

C0--C1--C2    C4   <-- v1 (HEAD)
          \  /
           C3   <-- master

再次完成。

如果您有许多要还原的提交——即,如果您想要:

C0--C1--C2   <- v1
          \
           C3--C4--C5   <-- master

成为:

C0--C1--C2            C6   <- v1
          \          /
           C3--C4--C5   <-- master

那么 git commit-tree 方法就没那么有用了(尽管你仍然可以使用 git revert,在这种情况下你只需要 -n,同时还原 C5C4,和 C3,或随后的 git rebase -i,您将所有新提交压缩在一起)。换句话说,git commit-tree 正是实现以下目标的工具:

... what I really want is to create a branch that contains the state of a historic commit, but have that branch rooted at HEAD (not at the historic commit).