初学者问题:
我最近在本地进行了大量的开发,然后今天做了一个git pull(我知道我应该更频繁地做拉动)并且引入了很多变化和不同的冲突。我修复了冲突并将我的所有提交压缩为1(拉出的提交穿插)。现在,当我发送审查时,我还有一个巨大的提交,也就是提交的提交的更改。我试图找到一种方法来解决这个问题,并将我的提交与这些提交分开。
示例:
我的提交:
原创 - > A1 - > A2 - > A3
远程提交:
原创 - > B1 - > B2 - > B3
合并:
原创 - > A1 - > B1 - > A2 - > B2 - > A3 - > B3
使用Git Rebase,我将提交压缩为1次提交,C1:
原创 - > C1
现在我的提交涵盖了从A1到A3和B1到B3的所有变化。
任何想法如何分离更改或修复此问题?
答案 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
指向A2
,A2
指向A1
,A1
指向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 之前的提交,直到还包括要进行压缩的提交更改为止。通过压缩所有提交,新副本只是一次提交,其中包含每次提交所完成的所有工作(A1
到A3
加B1
到B3
加上合并M
)。
如您所述,结果是一个新的提交C1
。但是,由于这是 new 提交,并且git默认保留一个月左右的旧提交,旧提交仍在那里。 git做的是让你的develop
分支名称指向新的提交:
A1 - A2 - A3 - M <-- [invisible, but still there]
/ /
...- o - B1 - B2 - B3 <-- origin/develop
\
C1 <-- HEAD=develop
根据您的问题,您希望将develop
恢复为指向M
或A3
(它不是很清楚哪个)。只要它们仍在您的存储库中,这很容易做到:唯一的技巧是找到M
或A3
的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
不可见 - 我假设M
是1234567
的ID - M
是不可见的:
C1
现在 A1 - A2 - A3 - M <-- HEAD=develop
/ /
...- o - B1 - B2 - B3 <-- origin/develop
\
C1 <-- [invisible]
仅在reflog中,而不是C1
。
如果M
标识为reset --hard
,A3
将指向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
只需复制;让我们复制develop
到git 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内部的尽可能多的承诺。 : - )