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

Jas*_*n C 1 git rebase git-rebase

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

我错误地犯了一个错字,虽然我可以提交撤消,但我想我会尝试完全删除提交。

提交哈希是dbcbf96b,并且ded82215是它之前的提交。因此,根据这些说明,我这样做了:

  1. git rebase -i ded82215
  2. 这在编辑器中显示了“待办事项”列表,如预期的那样,仅包含以下内容:

    pick dbcbf96 Writer update.
    
    Run Code Online (Sandbox Code Playgroud)
  3. 大概是按照该生成文件的注释中的说明,我将该行更改为:

    drop dbcbf96 Writer update.
    
    Run Code Online (Sandbox Code Playgroud)
  4. 我保存了文件并关闭了编辑器。然后它说:

    Successfully rebased and updated refs/heads/master.
    
    Run Code Online (Sandbox Code Playgroud)
  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
    
    Run Code Online (Sandbox Code Playgroud)
  7. 这有点道理,但至于变基,我不确定发生了什么。现在我确实git pull更新了本地存储库,但一切都刚刚回到我开始dbcbf96b的地方,回到本地文件仍然包含更改,本地存储库是最新的,并且 GitLab 仍然显示修订版。该pull命令产生:

    Updating ded8221..dbcbf96
    
    Run Code Online (Sandbox Code Playgroud)

所以我的 rebase 尝试什么也没做。我在这里错过了什么?


因此,我git push -f按照以下答案的建议在第 4 步和第 5 步之间进行了尝试,但失败了,结果是:

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/...'
Run Code Online (Sandbox Code Playgroud)

然后我尝试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.
Run Code Online (Sandbox Code Playgroud)

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

Jon*_*ely 5

所以我的 rebase 尝试什么也没做。我在这里错过了什么?

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

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

在 rebase 之后,您需要执行git push -f将更改后的本地存储库强制推送到 GitLab,以删除两个存储库中的错误提交。

  • git 不建议这样做,因为如果其他人已经删除了您要删除的较早提交,那么您会严重破坏他们对存储库的视图/历史记录。如果他们从要删除的提交中创建了一个分支,则更是如此。并且您收到错误消息是因为您的分支已配置为受保护的分支(大概是为了防止我提到的问题)。 (2认同)

tor*_*rek 3

Git 的构建是为了添加新的东西(其中“东西”意味着“提交”)而不是删除东西。这就是为什么你必须使用强制推送:你的推送会“带走东西”,即放弃你的提交。

\n\n

你给了你的 rebase 一个明确的指令,“放弃这个提交”,所以它做到了(用一堆安全气囊和安全带让你恢复它,通常至少 30 天)。常规者git push不会让你放弃提交;只有用力推才能做到。

\n\n

由于强制推送可能会丢失提交,因此某些服务器提供“受保护分支”模式,其中一些/许多/所有人根本不允许强制推送。这就是你现在遇到的情况。要成功强制推送,您必须将自己设置为允许执行此操作的\xe2\x80\x94,通常是暂时取消对分支的保护,然后在推送后重新对其进行保护。

\n\n

讨论

\n\n

确保您没有同时删除其他任何人的提交!实际上,分支名称只是提交上的标签。提交本身构成了存储库中的提交历史记录。每个提交在其内部都有其父提交的 ID 。我们说每个提交“指向”其父级:

\n\n
... <- commit 1234567   <-- branch\n
Run Code Online (Sandbox Code Playgroud)\n\n

要添加新的提交,Git 只需将分支“向前”推进(即使所有内部 Git 箭头都指向“向后”)到较新的提交。例如,如果我们从以下开始:

\n\n
... <-B <-C   <--branch\n
Run Code Online (Sandbox Code Playgroud)\n\n

我们D通过创建D指向 的a 来添加新的提交C,然后将名称branch指向D

\n\n
... <- B <- C <- D   <--branch\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
...--B--C--D--E   <-- branch\n
Run Code Online (Sandbox Code Playgroud)\n\n

当我们现在强制branch指向C时,和 都 D 丢失 E了:

\n\n
...--B--C        <-- branch\n         \\\n          D--E   [abandoned]\n
Run Code Online (Sandbox Code Playgroud)\n\n

有了git push,这(几乎)就是您能做的一切。(有一个可用的安全带,称为“强制租约”,您可以让 Git 告诉他们的 Git:“我相信您的名字branch指向 commit D。如果是这样,则强制它指向C。如果不是,请给我但没有什么更奇特的,因为它们在您自己的存储库中。在您自己的存储库中,提供将选定的提交复制新的但不同的提交的 git rebase -i能力,以便您可以删除中间体。只有强制选项。)git push

\n\n

正因为如此,Git 还提供了“反转”提交的功能:获取一些现有的提交,弄清楚它做了什么,然后执行完全相反的操作\xe2\x80\x94放回所有已删除的文件;删除所有添加的文件;删除添加的行并将删除\xe2\x80\x94 行添加为提交。因此,您可以在较新的提交中撤消任何较旧的提交,这只会添加到历史记录中。例如,如果在:

\n\n
A--B--C--D--E\n
Run Code Online (Sandbox Code Playgroud)\n\n

序列,我们发现这很糟糕,我们可以告诉 Git“进行与”相反的B新提交,给出:FB

\n\n
A--B--C--D--E--F\n
Run Code Online (Sandbox Code Playgroud)\n\n

它“做同样的事情”,就好像我们在序列中从未有过 或 一样B F但这样做不需要C打扰E. 通过添加反转来撤销整个提交的命令是git revert

\n\n

思考这一切的简单方法

\n\n

您自己的 Git 存储库就是:您的。您可以对其执行任何操作,包括删除(放弃)链末尾的提交,或者通过复制该点之后的git rebase所有提交来复制提交(通过 )以删除中间体。这可以让您“编辑您的历史记录”。

\n\n

但是,每个提交都有一个唯一的 ID。这就是 Git 时不时向您展示的又大又丑的哈希 ID。唯一 ID 是进入commit\xe2\x80\x94 的所有内容的加密校验和,因此“编辑历史记录”会产生全新的 ID。Git 通过ID交换(推送和获取)提交,因此一旦您将提交提供给另一个Git 实例\xe2\x80\x94(例如git push\xe2\x80\x94),您就已经发布了这些提交。您可以尝试“取消发布”它们,但谁知道成功与否。这有点像试图删除一条推文:可能已经有人捕获了它。

\n\n

这并不意味着“永远不要尝试更改已发布的历史记录”,只是“请注意,如果您确实尝试更改已发布的历史记录,它可能不起作用,并且任何开始使用该已发布历史记录的人都可能会感到恼火。 ”

\n