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

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

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

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

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


当前回答

切换根提交。

首先,创建您想要作为第一个提交的提交。

其次,使用以下命令切换提交顺序:

Git rebase -i—root

在根节点提交之前,会出现一个编辑器,比如:

选择1234旧根消息

在中间提交

选择你想放在根目录下的5678提交

然后,您可以将您想要的提交放在第一行中。在这个例子中:

选择你想放在根目录下的5678提交

选择1234旧根消息

在中间提交

退出编辑器,提交顺序将会改变。

PS:要更改git使用的编辑器,运行:

Git配置——全局核心。编辑name_of_the_editor_program_you_want_to_use

其他回答

如果你忘记在"git init"之后立即创建一个空提交,那么在存储库的开始添加一个空提交:

git rebase --root --onto $(git commit-tree -m 'Initial commit (empty)' 4b825dc642cb6eb9a060e54bf8d69288fbee4904)

我很兴奋,写了一个“幂等”版本的这个不错的脚本…它总是插入相同的空提交,如果你运行它两次,它不会每次都改变你的提交哈希值。所以,这是我对git-insert-empty-root的看法:

#!/bin/sh -ev
# idempotence achieved!
tmp_branch=__tmp_empty_root
git symbolic-ref HEAD refs/heads/$tmp_branch
git rm --cached -r . || true
git clean -f -d
touch -d '1970-01-01 UTC' .
GIT_COMMITTER_DATE='1970-01-01T00:00:00 +0000' git commit \
  --date='1970-01-01T00:00:00 +0000' --allow-empty -m 'initial'
git rebase --committer-date-is-author-date --onto $tmp_branch --root master
git branch -d $tmp_branch

它值得额外的复杂性吗?也许不是,但我会用这个。

这也应该允许在几个克隆的repo副本上执行这个操作,并最终得到相同的结果,因此它们仍然是兼容的…测试……是的,它可以工作,但也需要删除和添加你的遥控器,例如:

git remote rm origin
git remote add --track master user@host:path/to/repo

我认为使用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 log --reverse

tree=`git hash-object -wt tree --stdin < /dev/null`
commit=`git commit-tree -m 'Initialize empty repository' $tree`
echo $commit # copy below, interpolation didn't work for me

git filter-branch --parent-filter 'sed "s/^\$/-p <commit>/"' --tag-name-filter cat master

git log --reverse

请注意,在GitHub上,您将丢失CI运行数据和PR可能会搞砸,除非其他分支也得到修复。

Git将——root——重基到$emptyrootcommit

应该很容易做到吗