Git初学者:分离Git提交

时间:2014-10-03 03:13:58

标签: git github

初学者问题:

我最近在本地进行了大量的开发,然后今天做了一个git pull(我知道我应该更频繁地做拉动)并且引入了很多变化和不同的冲突。我修复了冲突并将我的所有提交压缩为1(拉出的提交穿插)。现在,当我发送审查时,我还有一个巨大的提交,也就是提交的提交的更改。我试图找到一种方法来解决这个问题,并将我的提交与这些提交分开。

示例:

我的提交:

原创 - > A1 - > A2 - > A3

远程提交:

原创 - > B1 - > B2 - > B3

合并:

原创 - > A1 - > B1 - > A2 - > B2 - > A3 - > B3

使用Git Rebase,我将提交压缩为1次提交,C1:

原创 - > C1

现在我的提交涵盖了从A1到A3和B1到B3的所有变化。

任何想法如何分离更改或修复此问题?

1 个答案:

答案 0 :(得分:6)

作为Andrew C noted in a comment,您可以通过显示您使用的实际命令来帮助答案人员。但在这种情况下,我可以猜到你做了什么。

您开始使用本地仓库与origin同步,如下所示:

...- o   <-- HEAD=develop, origin/develop

此分支提示提交o是您标记为original的内容。 (我不得不在这里猜测分支名为develop并跟踪origin/develop,因此这些名称可能与您的名称不同。)

随着时间的推移,您进行了更改git add生成的工作文件以及git commit ed,以生成以下内容:

         A1 - A2 - A3   <-- HEAD=develop
       /
...- o                  <-- origin/develop

请注意,&#34;提交指向另一个提交&#34;此处的箭头应指向 left 区域,而不是向右:A3指向A2A2指向A1A1指向o。原因很简单:&#34;指向&#34;通过在新提交中存储先前提交的SHA-1 ID来完成。一旦提交实际存储在存储库中,就可以永远不会进行更改。这意味着A1在创建A2时无法指向尚不存在的提交(例如A1);它只能指向 存在的提交(例如上面的o)。稍后,当A2创建时,它可以指向A1,因为现在已存在,但A1无法更改为指向A2 1

提交永远无法改变的事实对您的康复至关重要。我们会稍微回过头来。

接下来,您运行了git pull,但未以任何方式重新配置,因此git pull运行git fetch(一如既往),然后git merge(如果你没有告诉它改变,那就像它一样)。 fetch步骤从origin获得了一些新提交: 2

         A1 - A2 - A3   <-- HEAD=develop
       /
...- o - B1 - B2 - B3   <-- origin/develop

并且merge步骤添加了一个合并提交,结合了#34;他们的东西&#34;使用&#34;您的东西&#34;,在您当前的分支(develop)上:

         A1 - A2 - A3 - M  <-- HEAD=develop
       /              /
...- o - B1 - B2 - B3      <-- origin/develop

如果您在没有git log的情况下运行--graph,它会按日期顺序对提交进行排序,因此根据时间安排,您可以将其视为:

M    merged origin/develop into develop
B3   their message for B3
A3   your message for A3
B2   [etc]
A2
B1
A1
o

这就是你描述它们的方式(但箭头指向错误的方式)。实际上,父/子关系是由提交图确定的。如果您将git log --graph一起使用,则log命令会先根据图表进行排序,并以更合理的方式显示提交(无论如何都是图形化的) )订单,而不是&#34;提交的日期/时间&#34;顺序。

然后你可能 - 再次,我必须猜测 - 使用git rebase -i并将大量pick命令更改为squash。虽然rebase通常被描述为&#34;改变&#34;或者&#34;重写&#34;提交,它真正做的是复制提交。它必须,因为提交不能改变。因此,对于每次提交,rebase都是通过复制提交的内容(但没有完全提交)开始,然后进行一些更改 - 通常是一个小的,但可选地是非常大的 - 然后才会从结果中进行新的提交。当您告知rebase为squash时,它会延迟 3 之前的提交,直到还包括要进行压缩的提交更改为止。通过压缩所有提交,新副本只是一次提交,其中包含每次提交所完成的所有工作(A1A3B1B3加上合并M)。

如您所述,结果是一个新的提交C1。但是,由于这是 new 提交,并且git默认保留一个月左右的旧提交,提交仍在那里。 git做的是让你的develop分支名称指向新的提交:

         A1 - A2 - A3 - M  <-- [invisible, but still there]
       /              /
...- o - B1 - B2 - B3      <-- origin/develop
       \
         C1                <-- HEAD=develop

根据您的问题,您希望将develop恢复为指向MA3(它不是很清楚哪个)。只要它们仍在您的存储库中,这很容易做到:唯一的技巧是找到MA3的SHA-1(无论您想要还原哪个)。

有时,SHA-1仍处于回滚窗口中,因此您只需向后滚动并复制它即可。

如果没有,你可以使用git&#34;&#34; reflogs&#34;。

您的每个分支都有一个reflog,并且HEAD本身还有一个额外的reflog。命令git reflog将显示特定reflog的内容,或默认为HEAD

git reflog develop

输出与git log的输出非常相似(实际上git reflog仅使用多个选项调用git log,其中包括-g--walk-reflogs的简短版本}})。这将让你找到你想要的SHA-1。

一旦你确定你就拥有了正确的SHA-1,并且你的工作树是干净的(无需提交),请查看你想要重新设置的分支 - 你可能已经在它上面,但git checkout develop,假设分支是develop,不会对任何事情造成伤害,并且在这种情况下只会说Already on branch 'develop' - 使用{ {1}}:

git reset --hard sha1

(假设它当然是git reset --hard 1234567 )。这将使您当前的分支(我们确定为1234567)指向提交develop。也就是说,现在而不是1234567不可见 - 我假设M1234567的ID - M是不可见的:

C1

现在 A1 - A2 - A3 - M <-- HEAD=develop / / ...- o - B1 - B2 - B3 <-- origin/develop \ C1 <-- [invisible] 仅在reflog中,而不是C1

如果M标识为reset --hardA3将指向develop,同时生成A3 { {1}}不可见:

M

这是与以前相同的图表;我们所做的就是改变C1指向的地方。 ( A3 <-- HEAD=develop / \ A1 - A2 M <-- [invisible] / / ...- o - B1 - B2 - B3 <-- origin/develop \ C1 <-- [invisible] 在所有情况下都只包含名称develop。使用每个命令,git打开文件HEAD,看到它显示&#34; develop&#34;,并立即忽略{ {1}}并直接与develop合作。)


一旦你恢复了原来的状态,它就取决于你如何从那里开始。对于代码审查,通常的建议是HEAD将您的工作放到新开发分支的顶端。和以前一样,HEAD只需复制;让我们复制developgit rebase(这次看不见的rebase完全不可见):

A1

这里,&#34; prime&#34;或A3中的M标记等表示这些是副本(无论需要进行哪些更改,首先要将其应用于 A1 - A2 - A3 / ...- o - B1 - B2 - B3 <-- origin/develop \ A1' - A2' - A3' <-- HEAD=develop 而不是',以及第二,回到A1',然后回到B3,依此类推。)

但是,当代码分支过多地分散时,最好是审核合并提案,即审核以合并o结尾的序列。这不是一个单一正确答案的问题:这取决于你和审查/接受代码的人,如何做到这一点。 (这也取决于你,复数,同意什么&#34;分歧太多&#34;甚至意味着。)


1 要真正完成箭头必须指向&#34;向后&#34;的图片,请注意提交的SHA-1 ID是的加密校验和该提交的内容,包括提交的时间戳。因此,只有在提交完成后才会知道ID:您无法在不创建提交ID的情况下预测未来的提交ID。

2 此图假设您有更新版本的git。在所有版本中,B3确实使用A1',但在旧版本中,M调用git pull的方式告诉git fetch不要更新git pull远程跟踪分支。如果您手动运行git fetch,它会更新远程分支;对于1.8.4或更高版本,即使从git fetch运行,它也会更新远程分支。

3 从技术上讲,origin/develop实际上进行了中间提交,然后重置了一个,所以你得到了一大堆中间提交。只是看起来像它会延迟提交。这符合git内部的尽可能多的承诺。 : - )