在 Git 中,是否有可能做这样的事情?
master B - C - D
到这个?
master A - B - C - D
我有一个使用 Git 的现有项目,一切都很顺利。但是我找到了该项目的旧版本,我希望将其提交到同一个 repo,但将其作为 A 并使其看起来像是该项目的最早版本。可以在 Git 中做到这一点吗?还是有其他解决方法?
在 Git 中,是否有可能做这样的事情?
master B - C - D
到这个?
master A - B - C - D
我有一个使用 Git 的现有项目,一切都很顺利。但是我找到了该项目的旧版本,我希望将其提交到同一个 repo,但将其作为 A 并使其看起来像是该项目的最早版本。可以在 Git 中做到这一点吗?还是有其他解决方法?
是的,这很容易做到,但我将从标准警告开始,以了解它将B-C-D
变为B'-C'-D'
,也就是说,它们将具有新的提交哈希,并且将与存储库的任何现有克隆发生冲突。假设你没问题...
我们首先了解 git 是由一系列提交组织的,每个提交都有一个指向其父级的指针。以这种方式创建了提交树。
tree 1: B - C - D
由于您想要做的是在第一次提交之前添加一些东西——并且在第一次提交之前没有什么要解决的——我们必须创建一个新树来执行此操作。
要创建一个新树,我们将从一个空提交开始。将空提交作为存储库的第一次提交使这样的事情变得更容易。这是通过以下命令完成的(添加数字以供以后参考)
git checkout --orphan new-branch (1)
git rm -r --force --cached . (2)
git clean -fd (3)
git clean -fd (3a)
git commit --allow-empty -m 'Initialize' (4)
这将创建一个未连接到任何其他分支 (1) 的新分支。这将缓存工作目录中的所有文件并准备好提交。我们不想提交这些文件,因此 (2) 从缓存中删除它们;while (3) 将它们从目录中删除。有时需要清理两次,因为忽略的文件不会被清理,但 .gitignore 文件会——留下需要再次清理的文件。
最后,(4) 创建一个空提交作为该分支中的第一个提交。这个--allow-empty
论点使这成为可能——任何消息都可以。
现在,添加项目早期版本中的所有文件A
,然后提交。
git add .
git commit -m 'A'
------------------
tree 1: B - C - D
tree 2: A
我们准备好继续前进B-C-D
了A
。仍然打开时new-branch
,此命令将移动它们。
git rebase -i --onto new-branch --root D
这将结帐D
,将树(倒带)返回到根B
并按顺序将每个提交移动到A
- 每一步都进行新的提交,直到它回到D
. 注意:这里很有可能会出现合并冲突,请准备好处理它们。
完成后,树看起来像这样
tree 1: <empty> <-- this will be garbage collected in time
tree 2: A - B' - C' - D'
此时推送回您可能拥有的任何远程存储库 - 您需要强制推送
git push --force <remote> <local-branch>:<remote-branch>
这将覆盖远程分支——确保这是您想要的。
这里的所有都是它的。请记住不要在您唯一的存储库副本上工作,以便您可以重新克隆并在它被打结时重试。除此之外,我建议您继续阅读,git rebase ...
以便您熟悉正在发生的事情 - 以及您可能遇到的怪癖,具体取决于B-C-D
您实际存储库中的复杂性。
您是否可以将 A 添加为新提交,然后将交互式重新设置为 B ( rebase -i B~1
) 之前的提交,以按照您想要的方式重新排序提交?
是的,有一个警告。假设您从以下内容开始:
B - C - D
结果将是
A B C D'
请注意“主要”标记——B' 将具有与 B 相同的树(内容),但由于其父级不同,它将具有不同的 SHA-1 哈希。
最简单的方法是使用移植物。创建一个同时包含 A 和 B 的存储库:
一种 B - C - D
请注意它们如何不相互连接。您通过“嫁接”( Wiki 页面)将它们彼此连接起来,这使 A 成为 B 的父...
一种 \ ... \ B - C - D
我用了一条虚线,因为移植是暂时的。嫁接是通过编辑.git/info/grafts
和写作创建的:
<B 的哈希值> <A 的哈希值>
使用以下命令使移植物永久化:
git filter-branch --tag-name-filter cat -- --all
这会将图表更改为:
A B C D'
使其永久化后,删除移植文件。
通常的警告适用——您正在重写历史记录,因此任何拉取您的存储库的人都必须使用git reset
,只要他们没有本地更改。
简而言之,没有。
Git 中的每个提交都由一个哈希表示,“父”提交是哈希生成的元素之一。
因此,如果更改 B 的父级,则 B 将不再是 B(即哈希将不再相同)
您仍然可以通过使用 rebase 来做到这一点,即首先创建 A,然后在 A 上 rebase B、C、D...。但是,正如我之前所说,哈希将不再相同。如果这对你来说很好,那就继续吧:)