Git rebase和git push:非快进,为什么使用?

70

我有一个分支应该对其他贡献者可用,并且应该始终保持与主分支最新。

不幸的是,每次我使用 'git rebase' 然后尝试推送时,都会出现“非快速向前”(non-fast forward)消息并中止推送。唯一的方法是使用 --force 强制推送。这是否意味着如果我的分支已经发布并且其他人正在上面工作,我应该使用 'git merge' 而不是 rebase?

2个回答

102

关于 git 工作原理的一些说明(非技术性):

当你进行变基操作时,git 会将涉及到的提交重新提交在一个干净的历史记录顶部。这样做是为了防止历史记录出现:

Description: tree -> mywork -> merge -> mywork -> merge -> mywork -> merge
Commit SHA1: aaaa -> bbbb   -> cccc  -> dddd   -> eeee  -> ffff   -> gggg

在 rebase 后,可能看起来像这样(或类似):

Description: tree -> rebase
Commit SHA1: aaaa -> hhhh

问题在于你正在尝试推送的新提交不是你要推送到的分支顶部提交的子代

现在,你知道提交中包含相同的信息,但git负责不仅覆盖那些已存在的提交(如上面的bbbb-gggg)。


共享仓库模型

如果你正在使用一个共享的仓库,那么像这样的事情可能会变得非常混乱。让我解释一下为什么。假设另一个开发者拉取了该分支,并在他们的分支中拥有aaaa -> gggg提交。然后他们创建了一个提交iiii

同时,您进行了变基并强制推送,导致树看起来像这样:

Description: tree -> rebase
Commit SHA1: aaaa -> hhhh

当另一个开发者尝试推送时,会收到一个“非快进”的消息。当他进行合并时,两个历史记录将被重新链接在一起,并且你最终会得到一个混乱的结果。

类似这样(混乱):

Description: tree -> rebase -> mywork -> merge -> mywork -> merge -> mywork -> merge -> devwork -> merge 
Commit SHA1: aaaa -> hhhh   -> bbbb   -> cccc  -> dddd   -> eeee  -> ffff   -> gggg -> iiii    -> jjjj

换句话说,如果其他人正在推送和拉取,最好使用git merge,或者在重置之后避免push(并且仅重置您的工作)。


公开可见的仓库模型

也许你正在使用一个不同的(更符合git的)模型,你只想让人们从你的仓库中拉取。在这种情况下,git push --force 不太糟糕,因为然后他们可以处理跟进。他们可以将他们的更改重新基于您的更改,然后将他们的补丁提交给您。它可以防止您的仓库出现混乱。

但是,也许还有更好的方法。git push --mirror

来自 http://www.kernel.org/pub/software/scm/git/docs/git-push.html

不需要命名每个要推送的引用,只需指定$GIT_DIR/refs/(其中包括但不限于refs/heads/、refs/remotes/和refs/tags/)下的所有引用都被镜像到远程仓库。本地新创建的引用将被推送到远程端,本地更新的引用将在远程端强制更新,并且已删除的引用将从远程端删除。如果设置了配置选项remote..mirror,则这是默认设置。


Git的一个伟大之处在于它非常灵活,可以支持许多不同的工作流程。但是,它真正的优势在于它是分布式的模型,因此我认为最有收益的方法是使用它。


7
合并操作会创建合并提交,可能会存在不必要的混淆。相反,建议使用git fetchgit rebase origin/master命令,将本地分支和中央(master)分支上的冲突解决后,只推送向前移动的纯净提交。使用git pull --rebase也有类似的工作流程。这是一种强大的选项,但需要小心使用,因为对错误的内容进行rebase可能会重写已经在origin/master中的提交。例如,除非其他分支已经在当前的origin/master上进行了rebase操作,否则不要对其进行rebase。 - Kzqai
同意,在进行git pull时应避免合并提交。然而,我认为当有意将特性分支合并到主分支时,保留它们是有好处的。 - ndbroadbent
3
我喜欢有关推送变基分支的问题解释,但我不确定 --mirror 能够实现什么。阅读描述时,“指定*所有引用(refs)将被镜像”让我感到困扰。我不想镜像所有引用(refs)*,只想要一个分支。:) 只命名一个分支是否能够仅镜像该分支?从 Git 文档中并不清楚。 - John C

2
不,使用rebase与公共存储库完全合法,甚至可能有助于使历史记录流畅。只需记住,您不得使用rebase重写已发布的提交的历史记录。也就是说,rebase 只能应用于您自己的本地提交,而这些提交您从未发布过。当您fetch并且需要在那里进行调整时,您可以使用rebase 将您的提交放在它们的顶部。您收到此类消息的另一个原因可能是您正在推送的分支已更新,您需要同步 -- 获取并将您的提交rebase到您已获取的内容的顶部。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接