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

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

问题描述

我有问题,请多多指教!我已经有一个git repo,并且由于各种原因(我不会在这里讨论),我正在尝试创建一个ROOT提交

I have a problem, please be kind enough to advise! I have an existing git repo, and for various reasons(that I wont go into here), I am trying to create a ROOT commit

说这是我的git提交历史记录:

Say this is my git commit history:

(ROOT) C1 <-C2 <-C3 <-C4 <-C5   <--branchname (HEAD)

我想在C1之前添加一个初始提交(CX,它不是空的).因此它应该像这样结束:

I want to add an initial commit (CX, which is not empty) BEFORE C1. So it should end up like this:

(NEW ROOT) CX-C1 <-C2 <-C3 <-C4 <-C5   <--branchname (HEAD)

我在这里发现了类似的问题:在之前插入提交Git中的根提交?但这是为了在现有根目录之前附加一个EMPTY git commit.我还在这里尝试了答案中的步骤: https://stackoverflow.com/a/9736098/11491070 进行了一次更改:我用 git add替换了 git commit --allow-empty -m'initial'.git commit -m初始laravel提交";git push; ,然后执行此重新设置步骤: git rebase --onto newroot --root master 失败,并带有大量合并冲突:

I found a similar question here: Insert a commit before the root commit in Git? But it's for appending an EMPTY git commit before the existing root. I also tried the steps in the answer here: https://stackoverflow.com/a/9736098/11491070 with one change: I replaced git commit --allow-empty -m 'initial' with git add .; git commit -m "initial laravel commit"; git push; and then this rebase step: git rebase --onto newroot --root master is failing with a TON of merge conflicts:

First, rewinding head to replay your work on top of it...
Applying: add initial quickadminpanel, with admin intrface and APIs for tags. Also added (but not yet enabled) ajax datatables module
Using index info to reconstruct a base tree...
.git/rebase-apply/patch:4537: trailing whitespace.
     * 
.git/rebase-apply/patch:4539: trailing whitespace.
     * 
.git/rebase-apply/patch:4547: trailing whitespace.
     * 
warning: 3 lines add whitespace errors.
Falling back to patching base and 3-way merge...
CONFLICT (add/add): Merge conflict in webpack.mix.js
Auto-merging webpack.mix.js
CONFLICT (add/add): Merge conflict in routes/web.php
Auto-merging routes/web.php
CONFLICT (add/add): Merge conflict in routes/api.php
Auto-merging routes/api.php
CONFLICT (add/add): Merge conflict in resources/views/welcome.blade.php
Auto-merging resources/views/welcome.blade.php
CONFLICT (add/add): Merge conflict in resources/sass/app.scss
Auto-merging resources/sass/app.scss
CONFLICT (add/add): Merge conflict in resources/sass/_variables.scss
Auto-merging resources/sass/_variables.scss
CONFLICT (add/add): Merge conflict in resources/lang/en/validation.php
Auto-merging resources/lang/en/validation.php
CONFLICT (add/add): Merge conflict in resources/lang/en/passwords.php
Auto-merging resources/lang/en/passwords.php
CONFLICT (add/add): Merge conflict in resources/lang/en/pagination.php
Auto-merging resources/lang/en/pagination.php
CONFLICT (add/add): Merge conflict in resources/lang/en/auth.php
Auto-merging resources/lang/en/auth.php
CONFLICT (add/add): Merge conflict in resources/js/bootstrap.js
Auto-merging resources/js/bootstrap.js
CONFLICT (add/add): Merge conflict in resources/js/app.js
Auto-merging resources/js/app.js
CONFLICT (add/add): Merge conflict in package.json
Auto-merging package.json
CONFLICT (add/add): Merge conflict in database/seeds/DatabaseSeeder.php
Auto-merging database/seeds/DatabaseSeeder.php
CONFLICT (add/add): Merge conflict in database/migrations/2014_10_12_100000_create_password_resets_table.php
Auto-merging database/migrations/2014_10_12_100000_create_password_resets_table.php
CONFLICT (add/add): Merge conflict in database/factories/UserFactory.php
Auto-merging database/factories/UserFactory.php
CONFLICT (add/add): Merge conflict in database/.gitignore
Auto-merging database/.gitignore
CONFLICT (add/add): Merge conflict in config/services.php
Auto-merging config/services.php
CONFLICT (add/add): Merge conflict in config/logging.php
Auto-merging config/logging.php
CONFLICT (add/add): Merge conflict in config/database.php
Auto-merging config/database.php
CONFLICT (add/add): Merge conflict in config/cache.php
Auto-merging config/cache.php
CONFLICT (add/add): Merge conflict in config/broadcasting.php
Auto-merging config/broadcasting.php

如何解决此问题?请帮忙!

How can I fix this problem? Please help!

推荐答案

首先,请注意:您已经向前绘制了存储库.Git会倒退它们.将根提交 last (即左侧),然后 latest 提交 first (即右侧):

First, a side note: you've drawn your repository forwards. Git does them backwards. Put the root commit last, i.e., on the left, and the latest commit first, i.e., on the right:

C1 <-C2 <-C3 <-C4 <-C5   <--branchname

分支名称始终包含 latest 提交的哈希ID.这样Git就知道哪个是最新的.Git使用 C5 查找 C4 ,因为提交 C5 本身具有唯一的哈希ID,包含提交 C4 的哈希ID.同时, C4 保留 C3 的哈希ID,依此类推; C2 保留 C1 的唯一哈希ID,并且 C1 是根提交,因为>其中没有父哈希ID.

The branch name always holds the hash ID of the latest commit. That's how Git knows which one is the latest. Git uses C5 to find C4, because commit C5 itself, which has a unique hash ID, contains the unique hash ID of commit C4. Meanwhile C4 holds the hash ID of C3, and so on; C2 holds the unique hash ID of C1, and C1 is a root commit because it has no parent hash ID inside it.

第二,实际上不能更改任何提交.因此,您将在此处执行 not "change commit C1 "-您和Git都无法执行此操作-而是进行一个新的提交, C1' C21 或任何我们想调用的名称.在进行此新提交之前,我们需要另一个新提交 CX ,它是一个根提交并包含所需的内容.

Second, no commit can actually be changed. So what you will do here is not "change commit C1"—neither you nor Git can do this—but rather, make a new commit, C1' or C21 or whatever we want to call it. Before this new commit we want another new commit, CX, that is a root commit and contains the desired content.

可以使用 Antony Hatchkins的答案中的方法,但是如今,这种方式要做的就是从 git checkout --orphan (而不是棘手的符号ref命令)开始:

You can use the method in Antony Hatchkins's answer, but these days, the way to do that would be to start with git checkout --orphan (rather than tricky symbolic ref commands):

git checkout --orphan newroot
git rm -rf --cached .            # same as before
<arrange work tree as desired>   # same as before
git add .                        # or git add each file, same as before
git commit                       # same as before

这是创建提交 CX 的操作的顺序,因此我们具有:

That's the sequence of operations that creates commit CX, so that we have:

C1--C2--C3--C4--C5   <-- master

CX   <-- newroot

现在我们进入最有趣的部分.使用 git rebase 是错误的方法,因为 只能通过挑选樱桃来实现.这会将提交 C1 转换为对每个文件进行 add 的请求-提交 C1 作为根提交,与

Now we get to the most interesting part. Using git rebase is the wrong way to go because rebase works by cherry-picking. This turns commit C1 into a request to add every file—commit C1, being a root commit, gets compared to the empty tree to see what changed—and as you've seen, this results in an add/add conflict for every file you added in C1 that is already in CX.

您在这里必须回答的问题-我无法为您回答-是:您想要在新的 C1 副本中想要什么内容 C1 除了它以 CX 作为其父代之外?也就是说,最后,我们将拥有:

The question you must answer here—I cannot answer it for you—is: What content do you want in your new copy of C1 that is like C1 except that it has CX as its parent? That is, in the end, we'll have:

C1--C2--C3--C4--C5    [abandoned]

CX--C1'-C2'-C3'-C4-C5'   <-- master

在我们的存储库中.如果我对 C1'的哈希ID运行 git checkout ,我是否应该看到相同的内容,就像我运行 git checkout C1 的哈希ID?这两个提交将具有不同的哈希ID,但具有相同的作者和提交者,时间戳和日志消息,依此类推.如果它们也应该具有相同的 tree (快照),则使用 git replace git filter-branch 现在可以轻松完成这项工作.这是基本的食谱:

in our repository. If I run git checkout on the hash ID of C1', should I see the same content as if I run git checkout on the hash ID of C1? The two commits will have different hash IDs, but the same author and committer and timestamp and log messages and so on. If they should have the same tree (snapshot) too, the job is now pretty easy, using git replace and git filter-branch. Here is the basic recipe:

git replace --graft <hash-of-C1> newroot

(此时,您可以运行 git log 以确保它看起来正确).然后运行:

(and at this point you can run git log to make sure it looks right). Then run:

git filter-branch -- master

(假设您只希望复制来自 master 的提交,并更新ref master ).无操作筛选器分支(未指定筛选器)以正确的顺序(即,向前,即向Git的后退)复制每个可到达的提交,先复制 C1 ,然后再复制 C2 ,依此类推,但至关重要的是,服从替换指令.所以现在我们有了这个:

(assuming you want only the commits reachable from master copied, and ref master updated). The no-op filter-branch (no filters specified) copies each reachable commit, in the right (i.e., forwards, which is backwards to Git) order, copying C1 first, then C2, and so on, but—crucially—obeying the replacement directive. So now we have this:

C1--C2--C3--C4--C5    refs/original/refs/heads/master

CX   <-- refs/replace/<hash-of-C1>
  \
   C1'-C2'-C3'-C4-C5'   <-- master

如果这是理想的最终结果,我们现在只需要删除 refs/original/名称,以及不再有用的 refs/replace/名称:

If this is the desired final result, we now need only remove the refs/original/ name, and the no-longer-useful refs/replace/<hash-of-C1> name:

git update-ref -d refs/original/refs/heads/master
git update-ref -d refs/replace/<hash-of-C1>

您的历史记录现在已被重写.所有 old 克隆都必须销毁(或者至少应该停止使用它们),因为其中的哈希ID是您不想要的原始提交的哈希ID.回到你的生活,使生活变得悲惨.更新后的克隆中被废弃的C1至C5是无害的:它们可以以被废弃的状态留在那里,直到Git's Grim Reaper Collector( git gc )出现为止对其进行垃圾收集.

Your history is now rewritten. All old clones must be destroyed (or at least, you should stop using them) because the hash IDs in them are those of the original commits, that you don't want to come back into your life to make that life miserable. The abandoned C1-through-C5 in your updated clone are harmless: they can be left there, in their abandoned state, until Git's Grim Reaper Collector, git gc, gets around to garbage-collecting them.

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

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