【问题标题】:Git - Remove in between commitGit - 在提交之间删除
【发布时间】:2014-01-02 06:29:30
【问题描述】:

我们的团队正在用 PHP 做几个项目。我们错误地将一个项目的文件夹提交给另一个项目。现在,我们要从项目中删除该特定提交。如果我们从项目中删除特定文件夹/提交,那么我们的项目就没有问题。

如果我们只是删除文件夹并在当前位置发出新的提交,那么该文件夹将被删除,但它将保留在 Git 的历史记录中。因此,我们希望将其从 Git 的 refs、history 和其他内容中完全删除。

我们也可以创建一个单独的分支,但是作者的提交引用会丢失。我们只想删除那个特定的提交。我们在重写历史或重新构建历史方面没有问题,但不知道该怎么做。

在项目中,我们已经完成了 136 次提交,并且想要删除第 76 号提交。 SHA所需信息如下

5d39775b          //136th commit
a4df5ee9          //135th commit
6971cf35          //134th commit
.....
....
162f833c          //76th commit
32603274          //75th commit
.....
....
9770059          //1st commit

【问题讨论】:

    标签: git github repository git-commit


    【解决方案1】:

    在你的主分支中,你可以交互地变基:

    git rebase -i 162f833c^
    

    这将在提交之前违规提交的顶部重新设置。 现在只需从列表中删除有问题的提交,保存并存在编辑器(*nix 平台中的默认值是 vi)。

    这将使您的分支在有问题的分支之前的提交之上重新定位,没有它,这似乎是您想要实现的目标。

    【讨论】:

    • "git push -f" 在 rebase 操作之后应该在回答中提到以反映上游的变化。
    【解决方案2】:

    我已经尝试了所有提供的方法,即 rebase 和cherry-pick。我在这里提供有关我尝试过的方法和所做的事情的完整信息,以帮助更好地理解事情

    准备工作

    为了检查哪个是最好的,我做了这些事情:

    1. fork 远程 repo 以便原件完好无损并且可以轻松理解。所以,在你确定所做的事情是否正确之前,不要做任何原始回购。

    2. 首先获取 Git Repo 的干净副本。通常,每当我们存储或做其他事情时,这些事情也作为本地 Git 数据库存储在 Git 中。所以,那个 Git 数据库没有任何本地或临时的东西。

    3. 我已经计算了文件夹 .git 占用的总字节数。由于 repo 是干净的,因此这里的最小字节数是正确的。为了更深入,我记录了磁盘上占用的字节数和字节数。因为两者是不同的东西,它们是:

    bytes - 7,963,769 and size on disk - 8,028,160
    
    1. 如果您使用的是 Windows 并安装了任何防病毒软件,那么您必须禁用活动/实时模式。作为服务,Git 需要快速访问 i/o 文件检查。当文件由 Git 创建时会发生什么,活动模式会锁定新的 Created 以检查病毒,并且当 Git 尝试重新访问它由于防病毒锁定而失败时会发生什么。如果失败,您必须从 1 重新开始您的流程。

    方法一 - 变基

    在 re-basing 方法中可以通过两种方式完成。一种是通过--onto,另一种是通过-i。

    -i 方法

    我使用了以下命令:

     git rebase -i 162f833c^
    

    它打开了 vim 编辑器,我看到了从 162f833c 开始的提交列表,而不是来自 master。如果结尾,则提供以下几行。

    # Rebase 3260327..5d39775 onto 3260327
    #
    # Commands:
    #  p, pick = use commit
    #  r, reword = use commit, but edit the commit message
    #  e, edit = use commit, but stop for amending
    #  s, squash = use commit, but meld into previous commit
    #  f, fixup = like "squash", but discard this commit's log message
    #  x, exec = run command (the rest of the line) using shell
    #
    # If you remove a line here THAT COMMIT WILL BE LOST.
    # However, if you remove everything, the rebase will be aborted.
    #
    

    我删除了一行,这样提交将丢失并保存文件并退出编辑器,当我退出时,它开始重新定位,如下所示:

    Rebasing ( 1/64) ..(2/64)
    
    Successfully rebased and updated refs/heads/master.
    

    然后尝试检查日志以查看提交是否丢失,并且在日志中我找不到要删除的提交。表示成功完成命令。我试图检查状态为:

    # On branch master
    # Your branch and 'origin/master' have diverged,
    # and have 64 and 65 different commit(s) each, respectively.
    

    由于提交被删除,但在派生它的远程需要被推送。因此,使用强制推送来覆盖之前的代码。

    git push -f
    

    之后我删除了本地 repo 并再次重新获取 repo 和它显示的大小:

    bytes 6,831,765 bytes
    size on disk 6,897,664 bytes
    

    --onto方法

    在浏览了许多博客和手册后,我找到了 1 个博客here,这真的让我知道如何使用它。所以我使用的命令是:

    git rebase --onto 3260327 79504a5~1
    

    输出是:

    First, rewinding head to replay your work on top of it...
    Applying: 77th Message
    Applying: 78th Message
    Applying: 79th Message
    ....
    Last commit
    

    然后尝试检查日志以查看提交是否丢失,并且在日志中我找不到要删除的提交。表示成功完成命令。我试图检查状态为:

    # On branch master
    # Your branch and 'origin/master' have diverged,
    # and have 64 and 65 different commit(s) each, respectively.
    

    所以,使用单个命令完成所有事情,然后我像之前一样强制推送检查字节等

    git push -f
    

    之后我删除了本地 repo 并再次重新获取 repo 和它显示的大小:

    bytes - 6,831,389
    size on disk - 6,893,568    
    

    变基 -i 与 --onto

    因此,在做 rebase 方法之后。我真的同意 --onto 方法来删​​除中间的提交,因为它是单个命令,并且字节也比 -i 方法保存得略高。真正的好处是我们不必在 --onto 方法中做额外的事情。

    方法二 - 樱桃采摘

    Cherry-pick 非常好的方法,但运行了许多命令,运行命令时您必须小心谨慎。首先,我必须创建一个单独的日志文件

    git log --all --decorate --oneline --graph > 1
    

    这向我展示了 repo 的日志,因为我们需要一次又一次地查看它。从这个 repo 中确定您要删除的提交并复制 SHA 密钥最后一个正确的提交含义,从而确定我们不想更改任何内容的提交。所以,下一个命令是

    git checkout 3260327 -b repair
    

    生成的输出:

    Switched to a new branch 'repair'
    

    所以,直到最后一个 Good 提交,我们已经创建了一个新分支。所以,最后的事情好提交不会改变。我再次运行以下命令来查看所有好的提交:

    git log --decorate --oneline --graph > 2
    

    我已经删除了世界 --all 因为我只想查看分支修复的提交。正如它向我展示的那样,Good Commits 标记正确。现在,下一个命令要谨慎使用,因为它包含错误提交作为起点和最后一次提交完成的终点,它将是:

    git cherry-pick 162f833..5d39775
    

    输出:

    [repair 9ed3f18] xxxxxx
    x files changed, xxx insertions(+), xx deletions(-)
    [repair 7f06d73] xxxxx
    xx files changed, xxx insertions(+), xx deletions(-)
    .....
    ...
    

    此命令的作用是重新提交所有提交,离开上面提供的第一个提交,即 (162f833) 到提供的最后一个提交 (5d39775)。因此,sha 提交值将在它一一重新提交时相应地更改。现在是时候查看日志了:

    git log --all --decorate --oneline --graph > 3
    

    输出为:

    * f61a9a5 (HEAD, repair) xxxxxx
    * 25be3b9 xxxxx
    * 49be029 xxxxx
     .......
     .......
    | * 5d39775 (origin/master, origin/HEAD, master)
    | * a4df5ee xxxxx
    | * 6971cf3 xxxxxx
    | .......
    | .......
    | * 162f833 xxxx
    |/
    * 3260327 xxxxx
    ......
    * 9770059 xxxxx
    

    因此,查看图表让我们知道它已经重新提交了除错误提交之外的所有提交。向您展示所有带有新键的旧 sha 键。如果一切正常,我们必须将修复分支设为 master 并将 master 分支删除为:

    git checkout master
    

    并输出为:

    Switched to branch 'master'
    

    首先结帐 master 分支,以便我们可以覆盖 master 分支上的更改。那么

    git reset --hard 3260327
    

    并输出为:

    HEAD is now at 3260327 xxxxx
    

    它会在良好提交之后丢弃提交,现在我们必须将修复分支与 master 合并:

    git merge repair
    

    并输出为:

    Updating 3260327..40d290d
    Fast-forward
    

    现在,如果您查看日志,它不会向您显示错误的提交,并且所有事情都已完成。我像之前一样强制推动检查字节等

    git push -f
    

    之后我删除了本地 repo 并再次重新获取 repo 和它显示的大小:

    bytes - 6,831,556
    size on disk - 6,897,664
    

    命令排名

    变基 --onto [第一]

    bytes - 6,831,389
    size on disk - 6,893,568    
    

    樱桃采摘[第二]

    bytes - 6,831,556
    size on disk - 6,897,664
    

    变基 -i [第三个]

    bytes 6,831,765 bytes
    size on disk 6,897,664 bytes
    

    我想优先使用 git rebase --onto 命令,因为使用单个命令可以轻松完成所有操作。

    【讨论】:

    【解决方案3】:

    您可以使用交互式变基。

    由于您要删除的提交具有 sha1 162f833c,因此只需执行 git rebase -i 162f833c^

    文本编辑器将打开一个提交列表。只需删除与您要删除、保存和关闭的提交对应的行。

    作为一个安全网,当我做这种事情时,我喜欢先在我的 HEAD 上放一个标签,这样如果出现问题,我可以检查这个标签并检索我的初始状态。

    【讨论】:

    • 在这样做之后表明你的分支和原点/主节点已经分歧并且有 64 和 65 个不同的提交
    • @Vineet1982:是的,这就是你要求的(你说“重写历史没有问题”)。要“删除”一个提交,rebase 必须进行 64 个 new 提交,省略最底部的第 65 个您想要删除的提交。您现在将这个新的 64 提交链的末端称为“分支的尖端 master”,您只需要让其他所有人都调用相同的提交 master 并放弃之前的 master。这就是“改写历史”的意思!
    • @torek 我想删除 76 次提交而不是第 64 次或第 65 次提交
    • @Vineet1982:啊,我明白了。你确定你在计算正确方向的提交吗? 136-76 = 60,如果从那以后有一些新的提交(使它更像 140 - 76),那将是正确的。 (另外,如果序列中有任何合并,请注意:变基通常会将它们压平。)
    【解决方案4】:

    您可以自动删除提交并重写历史记录(其中${ref_to_delete} 是您要删除的提交,并假设master 是它所在的分支):

    git rebase --onto ${ref_to_delete}~ ${ref_to_delete} master
    

    如果您使用远程存储库,则必须 push-f

    git push -f
    

    【讨论】:

      【解决方案5】:

      您也可以为此使用git cherry-pick

      $ g  # g is my alias for git log --all --decorate --oneline --graph --color
      * 618f8e5 [2013-12-14 15:13] (HEAD, master) me: good 6
      * e27d6d7 [2013-12-14 15:13] me: good 5
      * 533f6c3 [2013-12-14 15:13] me: good 4
      * 877585f [2013-12-14 15:13] me: bad
      * 00c06f3 [2013-12-14 15:12] me: good 3
      * e9f80a4 [2013-12-14 15:12] me: good 2
      * 3122ba7 [2013-12-14 15:12] me: good 1
      * 98da603 [2013-12-14 15:12] me: first
      $ git checkout 00c06f3 -b repair
      Switched to a new branch 'repair'
      $ git cherry-pick 877585f..618f8e5
      [repair b340e21] good 4
       1 file changed, 1 insertion(+)
      [repair 1d2e0d0] good 5
       1 file changed, 1 insertion(+)
      [repair 1ed1d19] good 6
       1 file changed, 1 insertion(+)
      $ g
      * 1ed1d19 [2013-12-14 15:13] (HEAD, repair) me: good 6
      * 1d2e0d0 [2013-12-14 15:13] me: good 5
      * b340e21 [2013-12-14 15:13] me: good 4
      | * 618f8e5 [2013-12-14 15:13] (master) me: good 6
      | * e27d6d7 [2013-12-14 15:13] me: good 5
      | * 533f6c3 [2013-12-14 15:13] me: good 4
      | * 877585f [2013-12-14 15:13] me: bad
      |/  
      * 00c06f3 [2013-12-14 15:12] me: good 3
      * e9f80a4 [2013-12-14 15:12] me: good 2
      * 3122ba7 [2013-12-14 15:12] me: good 1
      * 98da603 [2013-12-14 15:12] me: first
      $ git checkout master
      Switched to branch 'master'
      $ git reset --hard repair
      HEAD is now at 1ed1d19 good 6
      $ git branch -d repair
      Deleted branch repair (was 1ed1d19).
      $ g
      * 1ed1d19 [2013-12-14 15:13] (HEAD, master) me: good 6
      * 1d2e0d0 [2013-12-14 15:13] me: good 5
      * b340e21 [2013-12-14 15:13] me: good 4
      * 00c06f3 [2013-12-14 15:12] me: good 3
      * e9f80a4 [2013-12-14 15:12] me: good 2
      * 3122ba7 [2013-12-14 15:12] me: good 1
      * 98da603 [2013-12-14 15:12] me: first
      $ # Done :)
      

      git rebase 可能更好,所以至少我表明在 git 中通常有多种方法。毕竟,rebase 是如此强大,以至于“git 中所有有意义的操作都可以用 rebase 命令来表达”(Linus Torvalds)。

      【讨论】:

        【解决方案6】:

        git rebase -i 32603274.. 这将显示一个巨大的提交列表。

        选择 162f833c .. 选择...

        将第一个提交更改为 d 162f833c ..

        并保存。

        这将单独删除第 76 次提交

        【讨论】:

          猜你喜欢
          • 2015-12-01
          • 1970-01-01
          • 2013-09-10
          • 2017-07-09
          • 2011-09-03
          • 1970-01-01
          • 2016-11-22
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多