是否可以在保留历史记录的同时在远程上进行新的提交,以匹配本地存储的内容

时间:2017-11-09 21:41:59

标签: git

我的用例有点奇怪。我是以编程方式执行此操作,无法访问重置软/混合,并合并。

我的本​​地机器上有一个回购。我在本地进行了一些文件更改,但没有提交。有权访问同一个仓库的其他人也进行了更改并将其推送到远程。我希望我的本地目录的文件内容成为远程的头部。我基本上想要强制推送,但保持遥控器上的现有历史记录。一个例子应该有希望澄清:

Repo A:
    file1.txt
    file2.txt
Commits:
    "Init repo"

我和人2克隆回购A.人2进行一些更改并推动它们。远程仓库现在看起来像这样:

Repo A:
    file1.txt
    file3.txt
Commits:
    "add file3"
    "rm file2"
    "Init repo"

在我当地的回购中,我做了一些工作。现在我的本地仓库看起来像这样:

Repo A:
    file1.txt
    file2.txt
    fileA.txt
Commits:
    "Init repo"

我想做一个提交并推送,以便遥控器看起来像这样:

Repo A:
    file1.txt
    file2.txt
    fileA.txt
Commits:
    "did my own thing"
    "add file3"
    "rm file2"
    "Init repo"

我刚刚进行了一次新的提交,其中文件内容与我的本地目录完全匹配。我不想合并或类似的东西。但重要的是保持了历史。

这可能吗?我相信它可以通过软复位来完成,但我无法访问它。另一种选择是将repo A克隆到另一个文件夹,删除内容并复制我的本地文件。但这会太慢了。还有其他办法吗?

2 个答案:

答案 0 :(得分:2)

请做。它应该工作。我正常使用它并且它会变色。

  

git pull --rebase

答案 1 :(得分:1)

你要求做一件奇怪的事情,而且可能应该使用rebase(我看你已经接受了这个答案)。但是使用rebase不会做你要求的奇怪的事情。

如上所述,无法执行此操作。此外,您提供的描述具有误导性,因为存储库不包含文件;存储库包含提交。然后提交代表完整文件的树,但在你告诉Git提取一些特定的提交之前,你只需要进行一系列的提交。

如果您可以放松自己的限制,可以做到。我将在下面描述如何以及为什么。

Git做什么

当你有两个或更多的存储库时,就像这里的情况一样,每个存储库都有自己的提交集合,它自己的分支名称如master,它自己的概念是"提示最末端" (最近,如果你愿意)提交每个分支名称。在这种特殊情况下,涉及三个独立的存储库:

  • origin的那个,你打电话给#34; repo A";
  • 你的;
  • 人2' s。

通过使用git clone让您的计算机在origin上调用另一个Git并将整个存储库(所有提交)复制到您自己的位置,您完成了最后一项操作。您的Git使用了他们的分支名称,例如master,并将其重命名为远程跟踪名称,如origin/master。最后,您的git clone运行git checkout master,让您的Git创建您的拥有主服务器并将其设置为指向通过哈希ID提交:他们列出的相同哈希ID他们的master,您现在正在呼叫origin/master

git checkout也填写了索引工作树。索引是你让Git构建下一个提交的地方 - 这就是为什么在Git中你总是需要运行git add。您的工作树是您实际看到文件的地方。它们与您自己的Git存储库相关联,但实际上并不是存储库 - 与索引不同,它占据了一种阴影的位置,在提交的中间(永久性和不可更改)和工作树(这是你工作的地方,因此实际上改变了事情)。

当然,第2个人执行相同的git clone,它会创建存储库的副本,然后他们的git checkout创建他们的master并填写他们的 index和work-tree。

现在,任何存储库中的提交都有很多有趣的东西。一个是这个"(大多数)永久性的和(完全)不可改变的#34;属性。另一个是每个提交存储与该提交一起使用的所有文件的快照 - 当您git checkout特定提交时,这就是获取这些文件的原因。

一个关键的,相当神奇的属性是每个提交都有一个保证唯一的哈希ID ,如7668cbc60578f99a4c048f8f8f38787930b8147b。当我说 unique 时,我的意思是 unique:无论存储库有多少个副本,7668cbc60578f99a4c048f8f8f38787930b8147b都是相同的提交每个副本。它是存在还是不存在,取决于您是否已经拾取并保存它(如果其他人制作它),或者自己制作(如果您打算制作它)。

最后一个关键项是每个提交都可以存储先前或提交的哈希ID。它是这些哈希ID,加上像master这样的分支名称存储了最新的,它产生了历史记录。 Git总是从最近的提交开始,通过在master之类的分支名称下找到的哈希ID,然后使用它。然后,如果你想回顾历史,Git会找到提交的父ID,并查看父提交中的文件。父母有另一个父母 - 当前提交的祖父母 - 还有另一组文件;等等。

每当有人在任何Git中进行 new 提交时,Git所做的就是将当前提交保存为父ID,并构造一个新的,唯一的(全部)世界各地)新提交的哈希ID:

... <-hash  <-hash  <-tip   <-- master

变为:

... <-hash  <-hash  <-old-tip  <-new-tip   <-- master

由于散列ID在任何地方都是唯一的,任何Git都可以判断它是否已经有其他Git提交的提交:如果它是相同的提交,则ID匹配

当你将你的Git连接到另一个Git时,你让Git向他们询问他们最新的分支机构等等。他们告诉你他们的master现在是一些新的哈希。然后你的Git可以让他们的Git发送该提交,如果有必要,可以发送它的父级,以及祖父母等等,直到你有相同的提交。现在,您拥有 plus 所有历史记录的所有历史记录,而无需获取所有旧历史记录;现在你Git更新你的origin/master以记住他们的新分支提示。

或者,您可以将Git 发送新提交到他们的Git。但是当你这样做时,你的Git会要求他们的Git将他们的 master设置为相同的哈希作为你自己的master。因此,要实现这一点,您必须先获取所有提交,然后添加您自己的提交,以便他们能够完成提交。

如何使用

以上是您通常会运行git fetch origin && git merge origin/mastergit fetch origin && git rebase origin/master的原因。但这不是您想要做的,因为它不会占用您当前的工作树并使您的新提交的整个内容

你想完全忽略Person 2在工作方面所做的事情,而保留 Person 2在提交历史方面做了什么。

这是一件非常奇怪的事情,但这就是你想说的。

要获取该提交历史记录,您必须 git fetch上游提交。如果我们将提交哈希标记为单个字母(而不是实际的哈希ID),那么这就是你得到的:

...--E--F--G   <-- master (HEAD)
            \
             H   <-- origin/master

现在使用您的工作树构建一个新提交,但是在第2个人的最新提交之上构建它,您需要git reset --soft。这使您可以移动当前分支名称master,使其指向与他们origin/master相同的提交。在git reset --soft origin/master之后,您的存储库内容 - 这不会考虑您的索引和工作树 - 如下所示:

...--E--F--G
            \
             H   <-- master (HEAD), origin/master

由于--soft,您的索引和工作树未更改。您现在可以git add -A或类似地从工作树和git commit更新索引。这将生成一个新的提交I,其父级为H

...--E--F--G   I   <-- master (HEAD)
            \ /
             H   <-- origin/master

并且其内容完全忽略了 Person 2做了什么来提交H,这是你要求的奇怪的事情。