删除Git Commits - 最近的一个和更旧的一个

时间:2018-03-12 17:15:35

标签: git commit

我已经看到删除最近一次提交的多个答案,但找不到删除旧提交的任何地方。

我最后四次提交为:

bc8354f Revert Merge branch 'repo/develop' into M372046"
edce31a Good commit2 M372046
926b7de Good commit1 M372046
422f6cb Merge branch 'repo/develop' into M372046

我想删除bc8354f和422f6cb而不影响其他人。

我试过了: git rebase -i HEAD~4 但它只显示bc8354f而不是另一个。 如果我使用d删除该提交,drop = remove commit,它会将我转到该原始分支的HEAD引用,并且不知道如何使其生效,我也不知道如何删除422f6cb提交。

如果我尝试将此HEAD参考推到原版(M372046),我会得到: 此存储库是为Git LFS配置的,但在您的路径中找不到“git-lfs”。如果您不再希望使用Git LFS,请删除.git / hooks / pre-push来删除此挂钩。

错误:无法将某些引用推送到......

我还尝试删除在执行rebase时以“pick”开头的整行,但是当我保存它时,它说“无事可做”并且没有进行任何更改。

2 个答案:

答案 0 :(得分:1)

git rebase -i HEAD~4应该向您显示所有4次提交。但要注意,rebase通常不会显示合并提交。如果您删除包含要删除的提交的行(或将其更改为drop),则应获得预期结果。

另一种方法是将分支重置为422f6cb^(最后一次提交),然后选择edce31a926b7de

答案 1 :(得分:1)

TL; DR

git branch tmp
git reset --hard HEAD~4
git cherry-pick tmp~2
git cherry-pick tmp~1

之后你应该仔细检查结果,如果一切都好看,删除分支tmp。然后,您必须强制推送(git push -f或等效)以放弃自git reset提交以来所有人(包括您)所做的所有工作,以支持您刚刚制作的副本。 确保没有其他人完成工作并推送必须保留的提交。

(检查下面的长答案,并确保图表确实是我绘制它的方式,然后盲目地这样做。)

从技术上讲,您根本无法删除提交。您只能停止使用(如果您已删除Git可以找到的所有名称,它最终将通过git gc回收, Grim Reaper 收藏家。

  

我想删除bc8354f422f6cb而不影响其他人。

由于上述技术原因,您无法完全 。每个现有提交都是完全不可更改的。同时,提交926b7de,即您不想影响的提交,将提交422f6cb列为其父级。

如果422f6cb不存在 - 如果您以某种方式删除它 - 提交926b7de通常会变得无法使用,因为它会列出不存在的父级。 (Git可以通过这样的破解提交做一些事情,但是存储库会有缺陷。)

这意味着为了删除合并及其还原,您必须愿意复制您想要保留的两个提交。副本至少在一个方面是不同的:926b7de(具有一些不同的哈希ID)的副本将列出一些其他父项而不是422f6cb

这里缺少一些零碎的东西,我们(或你)需要找到/填写,以便做你想做的事。但是,几乎所有类似的东西的第一步是在纸上或白板上或其他类似的地方绘制提交图,或者足够重要。将每个提交作为节点绘制,并带有箭头。箭头应指向 previous 节点:该提交的父节点。

我喜欢将提交权限(最新)提交到左(最旧),箭头指向左侧。您可以选择其他方向:例如,git log --graph从顶部的最新提交开始,箭头指向下方。

普通提交只有一个父级。你提交的最后三个提交是普通的:

             <-926b7de  <-edce31a  <-bc8354f

由于哈希ID很笨重,因此请用单个大写字母替换它们。我们可以使用EB作为最后两个,因为它是实际哈希ID的第一个字符,而B可以代表Bad。我们将D用于这三个中的第一个,因为它以DE结尾:

             <-D        <-E        <-B

现在我们有了422f6cb Merge branch 'repo/develop' into M372046。这是一个合并提交。合并提交的特殊之处在于它有两个(或更多,但普通合并只有两个)父项。

我不知道他们的哈希ID,所以我只是以这种方式绘制合并提交M

...
   \
    M        <-D        <-E        <-B
   /
...

然后我会稍微调整一下图,原因我们稍后会看到,并添加一些分支名称:

...--o
      \
       M--D--E--B   <-- yourbranch (HEAD)
      /
...--o   <-- repo/develop

请注意分支名称yourbranch - 这是什么分支;它是HEAD附加到的{和repo/develop,指向一个特定提交。这是相应分支的 tip 提交。这就是Git找到提交的方式:它从提示开始,如分支 name 所示,然后使用每个提交中嵌入的提交哈希来向后工作。

现在,您想要完全摆脱提交M(合并)和提交B(撤消合并的错误提交)。 可以 通过简单地忘记它们来实现此目的,方法是将名称yourbranch直接指向M之前的任何提交。第一行:

...--o   <-- yourbranch (HEAD)
      \
       M--D--E--B   [abandoned]
      /
...--o   <-- repo/develop

现在提交B没有名称,因此您无法找到它。但由于您未能找到B,因此您也找不到ED。当然这也意味着你找不到M,但那没关系:你想要的!但是丢弃DE是实际问题。所以我们需要解决这个问题。

首先,让我们学习如何移动分支名称yourbranch(HEAD附加到其上),以便它指向之前的提交顶行M。执行此操作的命令是git reset。在我看来,git reset命令做了很多事情 - 太多事情 - 但这是它将要做的事情之一。我们确保没有未保存的工作并运行:

git reset --hard <hash>

更改我们HEAD所附加的分支名称,以便它指向给定的<hash>提交。因此,我们必须找到合适的父级,并执行git reset,然后我们将我们想要的图片作为我们的中间步骤。

现在 - 从技术上讲,之前我们这样做 - 我们必须保存我们想要复制的提交的哈希ID,或者将名称附加到最后一个或者在最后一个之后发生的任何事情。通常,更简单的方法是附加名称,因为我们可以使用git rebase -i --onto来集中复制提交。不过,让我们更难做到这一点,因为这样做更有启发性。

复制提交的命令是git cherry-pick。它通过将提交与其父级进行比较,将提交 - 快照转换为一组更改。也就是说,在提交E的情况下,它会将DE进行比较,以查看您更改的内容。它将保存这些更改,然后查看您当前已签出的当前提交 - 并应用相同的更改,然后进行新的提交

现在,在我们运行git reset之前,请在此处使用tmp附加第二个分支名称git branch

git branch tmp

...--o
      \
       M--D--E--B   <-- yourbranch (HEAD), tmp
      /
...--o   <-- repo/develop

然后,当我们执行git reset --hard时,tmp将保留:

...--o   <-- yourbranch (HEAD)
      \
       M--D--E--B   <-- tmp
      /
...--o   <-- repo/develop

名称tmp将使提交保持活跃状态​​。

这是一种在顶行命名提交的奇特方式。我们知道tmp,并且在git reset命令之前,yourbranch又名HEAD,都标识了提交B。如果我们从B退回一位家长,我们会转到E。如果我们退回2位父母,我们会前往D。如果我们退后三步,我们会到M。如果我们退后四步......好吧,现在我们遇到了一个问题:我们退后一步的方向:左转还是左转?

Git给出的答案是我们可以在合并时选择要走的路。 默认答案是移至&#34;第一个父母&#34;。任何合并的第一个父级是在进行合并之前位于分支顶端的提交。在我们的例子中,那个也是向上的,这就是我们想要的。

特殊语法~number表示计算许多第一父链接。因此,HEAD~4表示从HEAD开始并返回四个第一父链接,它们到达我们想要的提交。

现在我们可以使用git cherry-pick来制作我们希望保留的好提交的副本。请注意,我们应该在Git认为的反向顺序中执行它们,即首先执行较旧的,而不是首先执行较新的 ,如果提交E取决于提交D

要复制提交E,只需在git cherry-pick命令中命名即可。请注意,tmp名称提交Btmp~1名称Etmp~2名称D,因此:

git cherry-pick tmp~2
git cherry-pick tmp~1

将复制D,然后复制E,并提供:

...--o--D'-E'  <-- yourbranch (HEAD)
      \
       M--D--E--B   <-- tmp
      /
...--o   <-- repo/develop

其中D'E'是您刚刚制作的副本。

现在可以安全地删除tmp

git branch -D tmp

(好吧,只要一切看起来都不错 - 你当然可以稍后删除tmp