Git初学者:分离Git提交

Nin*_*nja 2 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的所有变化.

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

tor*_*rek 6

正如Andrew C在评论中指出的那样,您可以通过显示您使用的实际命令来帮助答案.但在这种情况下,我可以猜到你做了什么.

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

...- o   <-- HEAD=develop, origin/develop
Run Code Online (Sandbox Code Playgroud)

这个分支提示o是你标记的original.(我不得不在这里猜测分支是否已命名develop并跟踪,origin/develop因此这些名称可能与您的名称不同.)

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

         A1 - A2 - A3   <-- HEAD=develop
       /
...- o                  <-- origin/develop
Run Code Online (Sandbox Code Playgroud)

请注意,此处的"提交指向另一个提交"箭头应指向左侧区域,而不是向右:A3指向A2,A2指向A1A1指向o.原因很简单:"指向"是通过在新提交中存储先前提交的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
Run Code Online (Sandbox Code Playgroud)

并且该merge步骤添加了一个合并提交,将"他们的东西"和"你的东西"组合在你当前的分支(develop)上:

         A1 - A2 - A3 - M  <-- HEAD=develop
       /              /
...- o - B1 - B2 - B3      <-- origin/develop
Run Code Online (Sandbox Code Playgroud)

如果你在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
Run Code Online (Sandbox Code Playgroud)

这是你描述它们的方式(但箭头指向错误的方式).实际上,父/子关系是由提交图确定的.如果使用git log with --graph,则log命令首先根据图形进行排序,并以更合理(以图形方式)的顺序显示提交,而不是"提交的日期/时间"顺序.

然后你可能 - 再次,我必须猜测 - 使用git rebase -i并改变了很多pick命令squash.虽然rebase通常被描述为"改变"或"重写"提交,但它真正做的是复制提交.它必须,因为提交不能改变.因此,对于每次提交,rebase都是通过复制提交的内容(但没有完全提交)开始的,然后进行一些更改 - 通常是一个小的,但可选地是非常大的 - 然后才会从结果中进行新的提交.当你告诉rebase时squash,它会延迟3先前的提交,直到还包括要被压缩的提交的更改.通过挤压全部提交,新副本仅仅是一个单一的承诺,其中包含每一个承诺(所做的一切工作A1,通过A3B1通过B3加合并M).

结果就是C1你提到的一个新提交.但是,由于这是一个新的提交,并且默认情况下git保留一个月左右的提交,旧的提交仍在那里.git做的是让你的developbranch-name指向新的提交:

         A1 - A2 - A3 - M  <-- [invisible, but still there]
       /              /
...- o - B1 - B2 - B3      <-- origin/develop
       \
         C1                <-- HEAD=develop
Run Code Online (Sandbox Code Playgroud)

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

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

如果没有,你可以使用git的"reflogs".

你的每个分支都有一个reflog,并且还有一个额外的reflog HEAD.该命令git reflog将显示特定reflog的内容,或者HEAD默认情况下:

git reflog develop
Run Code Online (Sandbox Code Playgroud)

输出非常类似于git log(实际上git reflog只是调用git log了几个选项,包括-g,这是短版本--walk-reflogs).这将让你找到你想要的SHA-1.

一旦你确定你有正确的SHA-1,并且你的工作树是干净的(没有任何提交),检查你想要重新设置的分支 - 你可能已经在它上面,但是git checkout develop,假设分支是develop,不会伤害任何东西,只会说Already on branch 'develop'在这种情况下使用:git reset --hard sha1

git reset --hard 1234567
Run Code Online (Sandbox Code Playgroud)

(假设它1234567当然是).这将使您当前的分支(我们刚刚确定)develop指向提交1234567.也就是说,现在不是M无形之中-我认为1234567是的ID M-它是C1不可见:

         A1 - A2 - A3 - M  <-- HEAD=develop
       /              /
...- o - B1 - B2 - B3      <-- origin/develop
       \
         C1                <-- [invisible]
Run Code Online (Sandbox Code Playgroud)

现在C1只有在reflogs中,而不是M.

如果你reset --hard要的ID A3,develop将指向A3,使得无论M C1无形的:

                   A3      <-- HEAD=develop
                 /    \
         A1 - A2        M  <-- [invisible]
       /              /
...- o - B1 - B2 - B3      <-- origin/develop
       \
         C1                <-- [invisible]
Run Code Online (Sandbox Code Playgroud)

这是与以前相同的图表; 我们所做的就是改变develop点的地方.(HEAD只包含develop所有情况下的名称.使用每个命令,git打开文件HEAD,看到它显示"develop",并立即忽略HEADdevelop直接使用.)


一旦你恢复旧状态,由你决定如何从那里开始.对于代码审查,通常的建议是将git rebase您的工作放到新开发分支的顶端.和以前一样,rebase只需复制 ; 让我们复制A1A3(离开无形M完全看不见这个时间):

         A1 - A2 - A3
       /
...- o - B1 - B2 - B3                     <-- origin/develop
                      \
                        A1' - A2' - A3'   <-- HEAD=develop
Run Code Online (Sandbox Code Playgroud)

在这里,"prime"或'标记A1'等表示这些是副本(无论需要做什么改变,首先,使它们适用于B3而不是o,然后指向B3,然后指向A1',等等).

但是,当代码分支"过分分散"时,最好检查合并提议,即查看以合并结束的顺序M.这不是一个单一正确答案的问题:这取决于你和审查/接受代码的人,如何做到这一点.(这也取决于你,复数,同意什么"分歧太多"甚至意味着.)


1要真正完成,为什么箭头必须指向"倒退"的图片,注意,提交的SHA-1 ID是的一个crypographic校验内容的那次提交,包括时间戳的承诺.因此,只有在提交完成后才会知道ID:如果不创建它,则无法预测未来的commit-ID.

2此处的图表假设您有更新版本的git.在所有版本中,git pull确实使用git fetch,但在旧版本中,git pull调用方式git fetch告诉git fetch不要更新origin/develop远程跟踪分支.如果您git fetch手动运行,它会更新远程分支; 对于1.8.4或更高版本,它甚至在运行时也会更新远程分支git pull.

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