为什么我的 rebase 没有做任何事情?

Why did my rebase not do anything?

这个项目只有一个 master 分支,所以所有的工作都在那里完成。

我打错了字,虽然我可以撤消,但我想我会尝试完全删除提交。

提交哈希是 dbcbf96bded82215 是它之前的提交。所以,从 these instructions 开始,我这样做了:

  1. git rebase -i ded82215
  2. 这会在编辑器中调出 "todo" 列表,如预期的那样仅包含以下内容:

    pick dbcbf96 Writer update.
    
  3. 大概按照生成文件的注释中的说明,我将该行更改为:

    drop dbcbf96 Writer update.
    
  4. 我保存了文件并关闭了编辑器。然后它说:

    Successfully rebased and updated refs/heads/master.
    
  5. 然后我检查了 GitLab(我用来托管它的东西)希望看到提交消失了,但它没有,它仍然存在。

  6. 我做了 git status 以查看它推荐的操作并且它产生了:

    On branch master
    Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
      (use "git pull" to update your local branch)
    nothing to commit, working tree clean
    
  7. 这有点道理,但至于变基,我不确定发生了什么。现在我做了 git pull 来更新本地仓库,但一切都回到了我开始的地方,回到了 dbcbf96b,本地文件仍然包含更改,本地仓库是最新的,GitLab仍然显示修订。 pull 命令产生:

    Updating ded8221..dbcbf96
    

所以我的变基尝试没有任何作用。我在这里错过了什么?


所以我按照下面的答案在第 4 步和第 5 步之间尝试 git push -f,但失败了,结果是:

Total 0 (delta 0), reused 0 (delta 0)
remote: GitLab: You are not allowed to force push code to a protected branch on this project.
To https://gitlab.com/...
 ! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'https://gitlab.com/...'

然后我尝试 git push 只是为了咧嘴一笑,但它也失败了,屈服:

To https://gitlab.com/...
 ! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to 'https://gitlab.com/...'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

这也是有道理的,我想这就是 -f 的目的。第二位的提示再次推荐 git pull,我已经知道这不是正确的操作。

So my rebase attempt did nothing at all. What did I miss here?

这不是真的,它确实做了一些事情。

您重新定位了您的 local 存储库,它对 GitLab 上的远程存储库没有任何作用。所以远程的仍然有不需要的提交,当你 git pull 你获取它并将它合并回你的本地仓库。

在 rebase 之后,你需要做一个 git push -f 来强制将改变的本地 repo 推送到 GitLab,以删除两个 repos 中的错误提交。

Git 用于 添加新内容 (其中 "stuff" 表示 "commits")而不是 带走东西。这就是你必须使用强制推送的原因:你的推送会 "take away stuff",即放弃你的提交。

你给了你的 rebase 一个明确的指令,"drop this commit",它确实做到了(用一堆安全气囊和安全带让你恢复它,通常至少 30 天)。常规 git push 不会让您放弃提交;只能用力推。

因为 强制推送可能会丢失提交,一些服务器提供 "protected branch" 模式,其中 some/many/all 人们根本不允许强制推送。这就是你现在遇到的。要成功强制推送,您必须将自己设置为允许执行此操作——通常是暂时取消对分支的保护,然后在推送后重新保护它。

讨论

确保您不会同时删除任何其他人的 提交!实际上,分支名称只是提交上的标签。提交本身构成了存储库中的提交历史记录。每个提交本身都有其 parent 提交的 ID。我们说每个提交 "points back to" 它的父级:

... <- commit 1234567   <-- branch

要添加新提交,Git 只需将分支 "forward" 推进(即使所有内部 Git 箭头指向 "backwards")到较新的提交。例如,如果我们开始于:

... <-B <-C   <--branch

我们通过创建指向 CD 添加新提交 D,然后使名称 branch 指向 D:

... <- B <- C <- D   <--branch

为了删除 D,我们让 branch 再次指向 C,但是如果其他人出现并进行了新的提交 E,现在的图片看起来像这样:

...--B--C--D--E   <-- branch

当我们现在强制 branch 指向 C 时,both D and E 丢失:

...--B--C        <-- branch
         \
          D--E   [abandoned]

对于 git push,这(几乎)是您所能做的。 (有一个可用的安全带,称为 "force with lease",你可以让你的 Git 告诉他们的 Git: "I believe your name branch points to commit D. If so, force it to point to C instead. If not, give me an error." 但没有什么比这更漂亮的了,因为他们在你自己的存储库。在你自己的存储库中,git rebase -i提供了复制选定提交到新[=101=的能力],但不同的是,commits,这样你就可以删除中间体。git push 只有 force 选项。)

正因为如此,Git 还提供了 "reverse" 提交的能力:获取一些现有的提交,弄清楚它做了什么,然后做恰恰相反的事情——放回任何文件已删除;删除添加的任何文件;删除添加的行和添加删除的行——作为 new 提交。因此,您可以在较新的提交中取消任何较旧的提交,而这只是 添加 到历史记录。例如,如果在:

A--B--C--D--E

序列,我们发现 B 不好,我们可以告诉 Git "make a new commit F that is the reverse of B",给出:

A--B--C--D--E--F

"does the same thing" 就好像我们在序列中从来没有 B F 一样,但是这样做而不必打扰 CE。通过添加其反转来取消整个提交的命令是 git revert.

思考这一切的简单方法

您自己的 Git 存储库就是:您的。你可以做任何你想做的事,包括在链的末尾删除(放弃)提交,或复制提交(通过 git rebase)通过复制所有提交 past[=101] 来删除中间体=] 那个点。这让你 "edit your history".

但是,这些提交中的每一个都有一个唯一的 ID。那就是不时向您展示的丑陋的大哈希 ID Git。唯一 ID 是 进入 提交的所有内容的加密校验和——因此 "editing history" 会产生全新的 ID。 Gits 交换(推送和获取)通过他们的 ID 提交,所以一旦你让你的提交对 另一个 Git 实例——例如 git push——你已经 发布了 那些提交。您可以尝试 "un-publish" 它们,但谁知道会成功。这有点像试图删除推文:可能有人已经捕获了它。

这并不意味着 "never try to change published history",只是 "be aware that if you do try to change published history, it might not work, and anyone who's started to use that published history may be annoyed."