在 Git 中的根提交之前插入提交? [英] Insert a commit before the root commit in Git?

查看:43
本文介绍了在 Git 中的根提交之前插入提交?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I've asked before about how to squash the first two commits in a git repository.

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

While the solutions are rather interesting and not really as mind-warping as some other things in git, they're still a bit of the proverbial bag of hurt if you need to repeat the procedure many times along the development of your project.

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

So, I'd rather go through pain only once, and then be able to forever use the standard interactive rebase.

然后,我想要做的是有一个空的初始提交,该提交仅存在于成为第一个的目的.没有代码,什么都没有.只是占用空间,所以它可以作为 rebase 的基础.

What I want to do, then, is to have an empty initial commit that exists solely for the purpose of being the first. No code, no nothing. Just taking up space so it can be the base for rebase.

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

My question then is, having an existing repository, how do I go about inserting a new, empty commit before the first one, and shifting everyone else forward?

推荐答案

有两个步骤可以实现:

  1. 创建一个新的空提交
  2. 重写历史以从这个空提交开始

为了方便起见,我们会将新的空提交放在临时分支 newroot 上.

We’ll put the new empty commit on a temporary branch newroot for convenience.

有多种方法可以做到这一点.

There is a number of ways you can do this.

最干净的方法是使用 Git 的管道直接创建一个提交,这样可以避免接触工作副本或索引或检出哪个分支等.

The cleanest approach is to use Git’s plumbing to just create a commit directly, which avoids touching the working copy or the index or which branch is checked out, etc.

  1. 为空目录创建一个树对象:

  1. Create a tree object for an empty directory:

tree=`git hash-object -wt tree --stdin < /dev/null`

  • 将提交包裹起来:

  • Wrap a commit around it:

    commit=`git commit-tree -m 'root commit' $tree`
    

  • 创建对它的引用:

  • Create a reference to it:

    git branch newroot $commit
    

  • 如果您足够了解自己的外壳,当然可以将整个过程重新排列成一行.

    You can of course rearrange the whole procedure into a one-liner if you know your shell well enough.

    使用普通的瓷器命令,你不能在没有检查 newroot 分支和重复更新索引和工作副本的情况下创建空提交,没有充分的理由.但有些人可能会觉得这更容易理解:

    With regular porcelain commands, you cannot create an empty commit without checking out the newroot branch and updating the index and working copy repeatedly, for no good reason. But some may find this easier to understand:

    git checkout --orphan newroot
    git rm -rf .
    git clean -fd
    git commit --allow-empty -m 'root commit'
    

    请注意,在缺少 --orphan 切换到 checkout 的非常旧版本的 Git 上,您必须将第一行替换为:

    Note that on very old versions of Git that lack the --orphan switch to checkout, you have to replace the first line with this:

    git symbolic-ref HEAD refs/heads/newroot
    

    2.重写历史以从这个空提交开始

    您在这里有两个选择:重新定位,或完全重写历史记录.

    2. Rewrite history to start from this empty commit

    You have two options here: rebasing, or a clean history rewrite.

    git rebase --onto newroot --root master
    

    这具有简单的优点.但是,它还会在分支上的每次最后一次提交时更新提交者姓名和日期.

    This has the virtue of simplicity. However, it will also update the committer name and date on every last commit on the branch.

    此外,对于一些边缘案例历史,它甚至可能由于合并冲突而失败——尽管您正在重新建立一个不包含任何内容的提交.

    Also, with some edge case histories, it may even fail due to merge conflicts – despite the fact that you are rebasing onto a commit that contains nothing.

    更简洁的方法是重写分支.与 git rebase 不同,您需要查找分支从哪个提交开始:

    The cleaner approach is to rewrite the branch. Unlike with git rebase, you will need to look up which commit your branch starts from:

    git replace <currentroot> --graft newroot
    git filter-branch master
    

    很明显,重写发生在第二步;这是需要解释的第一步.git replace 的作用是告诉 Git,每当它看到对您要替换的对象的引用时,Git 应该改为查看该对象的替换.

    The rewriting happens in the second step, obviously; it’s the first step that needs explanation. What git replace does is it tells Git that whenever it sees a reference to an object you want replaced, Git should instead look at the replacement of that object.

    使用 --graft 开关,你告诉它一些与平常略有不同的东西.您是说还没有替换对象,但是您想用自身的精确副本替换 提交对象除了父提交) 应该是您列出的那个(即 newroot 提交).然后 git replace 继续为您创建此提交,然后将该提交声明为原始提交的替换.

    With the --graft switch, you are telling it something slightly different than normally. You are saying don’t have a replacement object yet, but you want to replace the <currentroot> commit object with an exact copy of itself except the parent commit(s) of the replacement should be the one(s) that you listed (i.e. the newroot commit). Then git replace goes ahead and creates this commit for you, and then declares that commit as the replacement for your original commit.

    现在,如果你执行 git log,你会看到事情已经如你所愿:分支从 newroot 开始.

    Now if you do a git log, you will see that things already look as you want them to: the branch starts from newroot.

    但是,请注意 git replace 实际上并没有修改历史——它也不会传播到您的存储库之外.它只是向您的存储库添加从一个对象到另一个对象的本地重定向.这意味着没有其他人看到这种替换的效果——只有你自己.

    However, note that git replace does not actually modify history – nor does it propagate out of your repository. It merely adds a local redirect to your repository from one object to another. What this means is that nobody else sees the effect of this replacement – only you.

    这就是为什么 filter-branch 步骤是必要的.使用 git replace 您可以为根提交创建一个带有调整后的父提交的精确副本;git filter-branch 然后对所有以下提交重复此过程.这就是历史真正被改写的地方,以便您可以分享它.

    That’s why the filter-branch step is necessary. With git replace you create an exact copy with adjusted parent commits for the root commit; git filter-branch then repeats this process for all the following commits as well. That is where history actually gets rewritten so that you can share it.

    这篇关于在 Git 中的根提交之前插入提交?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆