我曾经问过如何压缩git存储库中的前两次提交。

虽然这些解决方案相当有趣,而且不像git中的其他一些东西那样令人费解,但如果您需要在项目开发过程中多次重复这个过程,那么它们仍然是一个众所周知的伤害。

所以,我宁愿只经历一次痛苦,然后能够永远使用标准的交互式rebase。

那么,我想做的是有一个空的初始提交,它的存在仅仅是为了成为第一个。没有代码,什么都没有。只是占地方,这样就可以做地基了。

我的问题是,有了一个现有的存储库,我如何在第一个提交之前插入一个新的空提交,并将其他所有人向前移动?


当前回答

启动一个新的存储库。

将日期设置为您想要的开始日期。

按照你希望的方式做每件事,调整系统时间以反映你希望那样做的时间。根据需要从现有存储库中提取文件,以避免大量不必要的输入。

当您到达今天时,交换存储库,您就完成了。

如果你只是一个疯狂的(成熟的)但相当聪明的人(很可能,因为你必须有一定的智慧才能想出像这样疯狂的想法),你将编写这个过程。

当你决定让过去的事情从现在开始以另一种方式发生时,这也会让事情变得更好。

其他回答

我喜欢亚里士多德的答案。但我发现对于大型存储库(>5000次提交),基于以下几个原因,filter-branch比rebase更好 1)它更快 2)当存在合并冲突时,不需要人工干预。 3)它可以重写标签——保存它们。 注意,filter-branch可以工作,因为每次提交的内容都没有问题——它与“rebase”之前完全相同。

我的步骤是:

# first you need a new empty branch; let's call it `newroot`
git symbolic-ref HEAD refs/heads/newroot
git rm --cached -r .
git clean -f -d

# then you apply the same steps
git commit --allow-empty -m 'root commit'

# then use filter-branch to rebase everything on newroot
git filter-branch --parent-filter 'sed "s/^\$/-p <sha of newroot>/"' --tag-name-filter cat master

注意,'——tag-name-filter cat'选项意味着标签将被重写以指向新创建的提交。

遵循亚里士多德和其他人的回答,但使用更简单的命令

zsh% git checkout --orphan empty     
Switched to a new branch 'empty'
zsh% git rm --cached -r .
zsh% git clean -fdx
zsh% git commit --allow-empty -m 'initial empty commit'
[empty (root-commit) 64ea894] initial empty commit
zsh% git checkout master
Switched to branch 'master'
zsh% git rebase empty
First, rewinding head to replay your work on top of it...
zsh% git branch -d empty 
Deleted branch empty (was 64ea894).

请注意,您的回购不应该包含任何等待提交的本地修改。 注意git检出——孤儿将在新版本的git中工作,我猜。 注意,大多数时候git状态给出了有用的提示。

启动一个新的存储库。

将日期设置为您想要的开始日期。

按照你希望的方式做每件事,调整系统时间以反映你希望那样做的时间。根据需要从现有存储库中提取文件,以避免大量不必要的输入。

当您到达今天时,交换存储库,您就完成了。

如果你只是一个疯狂的(成熟的)但相当聪明的人(很可能,因为你必须有一定的智慧才能想出像这样疯狂的想法),你将编写这个过程。

当你决定让过去的事情从现在开始以另一种方式发生时,这也会让事情变得更好。

我认为使用git replace和git filter-branch是一个比使用git rebase更好的解决方案:

更好的性能 更简单,风险更低(你可以在每一步验证你的结果,并撤销你所做的…) 与多家分公司合作,工作成果有保证

它背后的理念是:

在过去创建一个新的空提交 用一个完全类似的提交替换旧的根提交,除了新的根提交被添加为父提交 验证一切都如预期的那样,并运行git filter-branch 再次验证一切正常,并清除不再需要的git文件

以下是前2步的脚本:

#!/bin/bash
root_commit_sha=$(git rev-list --max-parents=0 HEAD)
git checkout --force --orphan new-root
find . -path ./.git -prune -o -exec rm -rf {} \; 2> /dev/null
git add -A
GIT_COMMITTER_DATE="2000-01-01T12:00:00" git commit --date==2000-01-01T12:00:00 --allow-empty -m "empty root commit"
new_root_commit_sha=$(git rev-parse HEAD)

echo "The commit '$new_root_commit_sha' will be added before existing root commit '$root_commit_sha'..."

parent="parent $new_root_commit_sha"
replacement_commit=$(
 git cat-file commit $root_commit_sha | sed "s/author/$parent\nauthor/" |
 git hash-object -t commit -w --stdin
) || return 3
git replace "$root_commit_sha" "$replacement_commit"

您可以毫无风险地运行此脚本(即使在执行以前从未执行过的操作之前进行备份也是个好主意;)),如果结果不是预期的,只需删除在.git/refs/replace文件夹中创建的文件并重试;)

一旦您验证了存储库的状态是您所期望的,运行以下命令来更新所有分支的历史:

git filter-branch -- --all

现在,你必须看到2个历史记录,旧的和新的(更多信息请参阅filter-branch上的帮助)。您可以比较这两个并再次检查是否一切正常。如果您满意,删除不再需要的文件:

rm -rf ./.git/refs/original
rm -rf ./.git/refs/replace

你可以返回到你的主分支并删除临时分支:

git checkout master
git branch -D new-root

现在,一切都该完成了;)

我知道这篇文章很旧了,但当谷歌搜索“插入提交git”时,这个页面是第一个。

为什么要把简单的事情变得复杂?

你有A-B-C,你想要A-B-Z-C。

git rebase -i trunk(或B之前的任何东西) 更改pick在B行上编辑 进行更改:git add .. git commit (git commit - modify将编辑B而不创建Z)

[你可以在这里进行任意数量的git提交,以插入更多的提交。]当然,在第5步中您可能会遇到麻烦,但是使用git解决合并冲突是您应该具备的技能。如果没有,那就练习!]

Git rebase—继续

很简单,不是吗?

如果你理解git rebase,添加一个“根”提交应该不是问题。

玩得开心!