在Git中提交根提交之前插入一个提交?

kch*_*kch 210 git version-control rebase git-rebase

我之前已经问过如何在git存储库中压缩前两个提交.

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

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

那么,我想做的是拥有一个空的初始提交,仅仅是为了成为第一个提交.没有代码,没有任何东西.只是占用空间,因此它可以作为rebase的基础.

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

Ari*_*zis 287

2017年中期答案

创建一个没有副作用的新的完全空提交可能最好直接使用Git的管道.这样做可以避免任何副作用:不接触工作副本或索引,没有临时分支来清理等等.所以:

  1. 要创建一个提交,我们需要一个目录树,所以我们先创建一个空的:

    tree=`git hash-object -wt tree --stdin < /dev/null`
    
    Run Code Online (Sandbox Code Playgroud)
  2. 现在我们可以围绕它进行包装:

    commit=`git commit-tree -m 'root commit' $tree`
    
    Run Code Online (Sandbox Code Playgroud)
  3. 现在我们可以重申:

    git rebase --onto $commit --root master
    
    Run Code Online (Sandbox Code Playgroud)

就是这样.如果你足够了解你的外壳,你可以将整个东西重新排列成一个单行.

(注意:在实践中我现在使用filter-branch.稍后会编辑它.)


历史答案(由其他答案引用)

这是同一解决方案的一个更清晰的实现,因为它无需创建额外的存储库,使用遥控器,并纠正一个独立的头部:

# first you need a new empty branch; let's call it `newroot`
git checkout --orphan newroot
git rm -rf .

# then you apply the same steps
git commit --allow-empty -m 'root commit'
git rebase --onto newroot --root master
git branch -d newroot
Run Code Online (Sandbox Code Playgroud)

瞧,你最终master重写了它的历史,包括一个空根提交.


注意:在没有--orphan切换到的旧版本的Git 上checkout,你需要管道创建一个空分支:

git symbolic-ref HEAD refs/heads/newroot
git rm --cached -r .
git clean -f -d
Run Code Online (Sandbox Code Playgroud)

  • 为什么不使用瓷器代替管道命令?我用_git checkout替换_git symbolic-ref HEAD refs/heads/newroot_ - new newroot_ (7认同)
  • @nenopera:因为这个答案是在`git-checkout`之前编写的.我已经更新了它首先提到的方法,感谢指针. (4认同)
  • 如果您的 newroot 不为空,请使用 `git rebase --merge -s recursive -X theirs --onto newroot --root master` 自动解决所有冲突(请参阅 [this](https://stackoverflow.com/a/ 38092651/3075942)回答)。@亚历山大库津 (2认同)

Ant*_*ins 29

合并亚里士多德Pagaltzis和Uwe Kleine-König的答案和Richard Bronosky的评论.

git symbolic-ref HEAD refs/heads/newroot
git rm --cached -r .
git clean -f -d
# touch .gitignore && git add .gitignore # if necessary
git commit --allow-empty -m 'initial'
git rebase --onto newroot --root master
git branch -d newroot
Run Code Online (Sandbox Code Playgroud)

(只是把所有东西放在一个地方)


小智 11

我喜欢亚里士多德的回答.但是发现对于一个大型存储库(> 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
Run Code Online (Sandbox Code Playgroud)

请注意,'--tag-name-filter cat'选项意味着将重写标记以指向新创建的提交.


Phi*_*ppe 7

我认为使用git replaceandgit filter-branch是比使用 a 更好的解决方案git rebase

  • 更好的性能
  • 更容易且风险更小(你可以在每一步验证你的结果并撤消你所做的......)
  • 与保证结果的多个分支配合良好

其背后的想法是:

  1. 在很久以前创建一个新的空提交
  2. 用完全相似的提交替换旧的根提交,除了新的根提交被添加为父提交
  3. 验证一切是否符合预期并运行 git filter-branch
  4. 再次验证一切正常并清理不再需要的 git 文件

这是前两个步骤的脚本:

#!/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"
Run Code Online (Sandbox Code Playgroud)

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

确认存储库的状态符合您的预期后,运行以下命令以更新所有分支的历史记录:

git filter-branch -- --all
Run Code Online (Sandbox Code Playgroud)

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

rm -rf ./.git/refs/original
rm -rf ./.git/refs/replace
Run Code Online (Sandbox Code Playgroud)

您可以返回您的master分支并删除临时分支:

git checkout master
git branch -D new-root
Run Code Online (Sandbox Code Playgroud)

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


tig*_*200 7

切换根提交:

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

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

git rebase -i --root
Run Code Online (Sandbox Code Playgroud)

编辑器将出现在根提交之前的提交中,例如:

pick 1234 old root message
pick 0294 A commit in the middle
pick 5678 commit you want to put at the root
Run Code Online (Sandbox Code Playgroud)

然后,您可以将所需的提交放在第一行中。在示例中:

pick 5678 commit you want to put at the root
pick 1234 old root message
pick 0294 A commit in the middle
Run Code Online (Sandbox Code Playgroud)

退出编辑器,提交顺序将发生变化。

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

git config --global core.editor name_of_the_editor_program_you_want_to_use
Run Code Online (Sandbox Code Playgroud)

  • 现在 rebase 有了 --root,这是迄今为止最巧妙的解决方案。 (2认同)

lda*_*v1s 5

我成功地使用了亚里士多德和肯特的答案:

# first you need a new empty branch; let's call it `newroot`
git checkout --orphan newroot
git rm -rf .
git commit --allow-empty -m 'root commit'
git filter-branch --parent-filter \
'sed "s/^\$/-p <sha of newroot>/"' --tag-name-filter cat -- --all
# clean up
git checkout master
git branch -D newroot
# make sure your branches are OK first before this...
git for-each-ref --format="%(refname)" refs/original/ | \
xargs -n 1 git update-ref -d
Run Code Online (Sandbox Code Playgroud)

master除了标签之外,这还将重写所有分支(不仅仅是).


Ror*_*ory 5

如果您忘记在“git init”之后立即创建空提交,则在存储库开头添加空提交:

git rebase --root --onto $(git commit-tree -m 'Initial commit (empty)' 4b825dc642cb6eb9a060e54bf8d69288fbee4904)
Run Code Online (Sandbox Code Playgroud)

  • 4b825dc...是空树的哈希:/sf/ask/683581741/ a-符号 (6认同)