在 Git 中的根提交之前添加提交? [复制]

Posted

技术标签:

【中文标题】在 Git 中的根提交之前添加提交? [复制]【英文标题】:Add commits before root commit in Git? [duplicate] 【发布时间】:2013-05-21 15:50:24 【问题描述】:

新问题

两个问题:

    我正在尝试在 ALL 提交之前进行提交。 最底部的提交将660fb2a76211f36ec9a67d0454a90eab469e9fd0 作为 SHA。当我在每次提交时键入 git rebase -i 660fb2a76211f36ec9a67d0454a90eab469e9fd0 但最后一个会显示在列表中。 我真的需要这个提交出现,所以我可以把第一个提交放在最后!

    当我将第一个提交作为列表中的第一个 (意味着总共第二个提交,因为第一个不在上面提到的列表中) 我收到一个错误: error: could not apply b722c76... v1.4.3 BEAT release 我只是把它从列表的底部剪下来放到顶部! 我没有更改号码! 我也试过几次。同样的结果。

到此为止。如果您有任何问题,请继续提问!

原始问题

我刚刚发现了我项目的旧备份。这些备份是在我使用 git 之前创建的。 我现在想将它们作为旧提交添加到我的存储库中。这意味着我必须将这些提交放在 所有 其他提交之前。

现在有几个问题:

    我一般如何将提交放在其他提交之前? 我怎么能这么快呢? (我有很多备份!) 如何为这些 "old" 提交设置日期? (我知道备份的日期!)

如果有不清楚的地方,请提出来。那我就解决这个问题!

最后一件事:

我已经在 GitHub 上发布了这个。我主要使用他们的软件来提交提交。那么如何将其推送回 GitHub?

【问题讨论】:

这样做涉及重写您迄今为止所拥有的全部历史。如果其他人根据您当前的历史记录未合并的工作,您将与他们的代码产生大量冲突。如果是这种情况,重写你的整个历史以让你的旧工作在提交图中排在第一位是一个坏主意。 这不是问题。我一个人在做这个。我确实明白这意味着重写整个历史! 您的备份只是文件吗?还是来自另一个版本控制系统? 它们只是文件夹。 【参考方案1】:

更新

我找到了另一种稍微简单的方法,使用git rebase--root 标志。我用我的一个 repo 进行了尝试,所以我知道它是有效的(至少对于完全 linear 的历史,最后会详细介绍)。

第 1 步:创建存储库的备份克隆

让我们谨慎行事并进行备份,以防万一我们最终做出灾难性的事情并丢失您的数据:

git clone original-repo backup-repo

第 2 步:创建孤立分支(这将是您的新根)

检查孤立分支。指定起点提交/分支是可选的,如果您不考虑该参数,那么 Git 将默认使用您当前的提交作为起点(它遵循标准分支语法的模式):

git checkout --orphan new-master firstCommitOfOldMaster

我指定了旧 master 的根目录作为起点,因为它可能会使接下来的步骤更快(删除工作目录文件)。

第 3 步:删除工作目录文件

从您的旧 master 创建孤立分支 new-master 可能会在您的工作目录和索引中留下文件,具体取决于您分支的提交中文件的状态:

索引和工作树的调整就像您之前运行过git checkout <start_point> 一样。这允许您通过轻松运行git commit -a 来创建一个新的历史记录,该历史记录记录一组类似于<start_point> 的路径以进行根提交。 — git checkout docs

您现在要做的是彻底清除分支的状态,以便您真正从头开始(从***文件夹运行):

git rm -rf .

这是来自文档的解释:

如果你想开始一个断开的历史记录,记录一组与<start_point> 完全不同的路径,那么你应该在创建孤立分支后立即通过运行git rm -rf . 清除索引和工作树从工作树的顶层开始。之后,您将准备好准备新文件、重新填充工作树、从其他地方复制它们、提取 tarball 等等。

第 4 步:将旧工作重新提交到 new-master

现在您需要开始将您的旧工作提交到new-master。完成后,您将重新调整旧主人的基础。

我不确定你的备份文件夹是如何组织的,但我会假设每个都是你的项目文件夹的一个有日期的副本,以YYYY-MM-DD 格式命名,例如2013-01-30。您可以手动将每个文件夹的内容添加为新的提交,也可以编写脚本来自动执行该过程。

假设您将所有备份文件夹放在名为backup 的***文件夹中,该文件夹与您的主项目文件夹位于同一目录中。然后这里是一些用于自动化提交每个文件夹过程的脚本的伪代码:

# Get backups and sort them from oldest to newest
backups = open("backups").toList().sort("date ascending")
for each (backup in backups)
    copy files from backup to project folder
    execute `git add "*"`
    execute `git commit --date="#backup.date" --message="#backup.date"`

上面的脚本只会在提交之间添加和修改文件。如果您的任何备份被删除、移动或重命名,脚本将不会记录这些。您需要编写一个更聪明的脚本来执行此操作(可能使用 git diff 或其他一些差异工具),或者手动记录这些更改。

第 5 步:将旧的 master 重新定位到 new-master

看看 GIT 的强大力量! 现在我们要重写你的整个历史! 背诵这些神秘的力量话语,见证奇迹发生!:

git rebase --onto new-master --root master

TA-DA!您可能需要解决new-master 的最后一次提交和master 的首次提交之间的冲突,但除此之外,应该就是这样!

--root 标志 rebase 解决了我们之前遇到的问题,即rebase 通常不会在 Git 存储库的第一个(根)提交上运行。 --root 标志基本上告诉 Git 将其包含在 rebase 中:

对所有可从<branch> 访问的提交重新设置基准,而不是用<upstream> 限制它们。这允许您在分支上重新设置根提交。当与--onto 一起使用时,它将跳过已经包含在<newbase> 中的更改(而不是<upstream>),而没有--onto 它将对每个更改进行操作。 — rebase docs

第 6 步:验证最终提交是否仍然相同

为了确保我们没有弄乱任何代码,我们需要将master 的当前最终状态与执行rebase 之前的状态进行比较。以下任一命令都将起作用(它们是相同的)。在master 分支上:

git diff orig_head   # "Original" head
git diff master@1  # Master at its 1st prior position

如果您没有从 diff 得到任何输出,那么这意味着您重新定位的 master 的最终状态与 rebase 之前的状态相同。干得好!

注意:不确定合并提交会发生什么...

现在,如果您有完美的线性历史记录,即您没有任何合并提交,上述步骤将可以正常工作。我不确定如果你这样做它是否仍然有效。在我之前的回答中,我提到将--preserve-merges 标志与rebase 一起使用可能会帮助您保持这些合并,但文档中提到该标志还与--onto--root 标志交互:

当 [--root] 与 --onto--preserve-merges 一起使用时,所有根提交都将被重写为以 <newbase> 作为父提交。 — rebase docs

目前我不确定这意味着什么……我可能需要尝试一下,看看会发生什么。这是否意味着如果您有超过 1 个根提交(例如 10 个),那么这些根提交中的 9 个最终将重新定位到作为新根的最后一个?这似乎不是我们想要的行为。我们只想保留一个根被重新定位到另一个根的合并提交。


上一个答案

MGA 有the right idea with rebase。我不确定这是否能解决您的问题,但也许您需要在您的存储库中进行新的根提交,将您的备份文件作为新提交添加到它之上,然后在顶部重新设置您的旧提交图。

例如,git checkout 根据the documentation 有一个--orphan 标志:

--orphan <new_branch> 创建一个新的孤立分支,命名为<new_branch>,从<start_point> 开始并切换到它。在这个新分支上进行的第一次提交将没有父级,它将成为与所有其他分支和提交完全断开的新历史的根。

也许你可以试试

git checkout --orphan <temp_branch>

然后将您的备份文件提交给它。然后temp_branch 签出,做

git cherry-pick <first commit from master>
git rebase --onto temp_branch <first commit from master> master

我认为您可能需要cherry-pick 第一次提交,因为rebase --onto 的第二个参数是您要变基的分支的旧基础(在本例中为master),而旧基础不是' t 包括在内。所以这就是为什么我认为你需要 cherry-pick 才能得到它,因为它本身没有基本提交,所以你不能在 rebase 中为它指定一个。

还要注意,如果您的提交历史不是线性的(即它具有合并提交),那么rebase 通常不会保留这些。它有一个--preserve-merges 标志,但我以前从未使用过它,所以我不确定它是如何工作的。来自the docs:

-p--preserve-merges 不要忽略合并,而是尝试重新创建它们。

这在内部使用了 --interactive 机制,但除非您知道自己在做什么(请参阅下面的错误),否则将其与 --interactive 选项明确结合通常不是一个好主意。

那么,也许所有这些都行得通?

【讨论】:

这几乎是完美的!我去看看有没有什么不正常的。如果是这样,我稍后会回来! 好的。我让它工作了:首先我必须创建那个新分支并在日期更改时进行所有提交。完成后,我使我的工作目录看起来像我的主分支的第一次提交。然后我做出一个删除所有文件的提交。之后我做rebase。我再次进行变基并删除删除所有文件的提交和我主人的原始第一次提交并完成!非常感谢非常 @YannickSchinko 所以我的建议基本上奏效了?您刚刚修改了一些步骤? 基本上。这些其他步骤或多或少是必要的。 只是想在这里添加一个简短的说明,git v.1.7.12+ 有一个--root 交互式变基选项(如this answer 中所述)。它允许您重写根提交,因此作为解决方案的一部分可能会有所帮助。【参考方案2】:

1) 您可以使用git rebase。在这种情况下,您可以执行以下操作:

首先,提交您的旧更改。那么:

$ git rebase --interactive master

这将在文本编辑器中打开一个包含您提交的文件。只需更改提交的顺序(剪切/粘贴您的最后一次提交作为第一个提交,包括左侧的“pick”命令),保存并关闭编辑器。 此时变基将开始。要在系统要求您确认时继续,请输入:

$ git rebase --continue

直到全部完成。更多信息here.

2) 我不知道有什么方法可以让它更快,但它应该已经相当快了。

3) 要更改提交日期,您必须更改 GIT_AUTHOR_DATE 和 GIT_COMMITTER_DATE。 为此,有一个漂亮的例子in the accepted answer for this *** question。

希望对你有帮助!

【讨论】:

当我输入git rebase --interactive master 命令时,会打开有线屏幕,但不会打开文本编辑器! (第一行说“noop”) 嗯,这里(我在 Windows 7 上)打开了一个文本编辑器。 git rebase --interactive HEAD~5 怎么样(将数字替换为您想要显示的任意数量的提交)? 好的。我修好了。但一直说“noop”。它不会显示任何提交! 即使使用 git rebase --interactive HEAD~5 (见上面的评论)? 没有。但我想先提交最后一个。 (我有 53 次提交)我可以输入 git rebase --interactive HEAD~52 但不能输入 git rebase --interactive HEAD~53!那是我的问题! git rebase --interactive master 在文本文件中给了我noop

以上是关于在 Git 中的根提交之前添加提交? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

如何编辑以前的 git 提交? [复制]

Git中添加但未提交的已删除文件可以恢复吗? [复制]

如果我之前手动添加文件,我可以让 git 拒绝所有提交吗?

git常用命令总结

即使我在提交之前 git 添加了所有文件,我可以恢复到只有一个文件的先前版本吗?

我忘记在初始提交中添加文件。我如何返回并添加它? [复制]