跳至主要内容

Git Pull 强制:如何用远程版本覆盖本地分支

了解为什么 git pull --force 并不是用远程版本覆盖本地分支的最佳方式,并学习使用 git fetch 和 git reset 的正确方法。

使用 AI 探索

在某些情况下,我们可能希望重置本地Git分支的状态,使其与远程仓库保持一致,从而丢弃所有本地更改。这通常被称为强制拉取。

这会产生一个误解:实现这一目标的方法是对 git pull 命令使用 --force 选项。本文将说明它们并不是一回事。

如果您是 Git 新手,建议先阅读我们的Git push 与 pull 教程。

我们可以使用下面的命令将本地状态覆盖为远程状态,将 <branch_name> 替换为您的分支名。此操作将永久删除该分支中的所有本地更改。

git fetch
git reset --hard origin/<branch_name>

本文将深入介绍这些命令的工作原理。我们不仅给出速成方案,还会理解其底层机制,从而学会在不冒丢失重要更改风险的前提下,更有效地达成目标。我们也会澄清误解,并了解 git pull 的 --force 选项实际在做什么。

何时考虑覆盖本地更改

通常,用这种方式覆盖本地更改违背了 Git 的设计初衷。几乎在任何情况下,都有合适的 Git 工作流来解决问题。然而,其中一些方法往往需要更深入的 Git 理解,并花费更多时间。即便这不是最佳实践,覆盖本地更改有时却是最快完成工作的方式。

对我而言,这通常发生在我处理自己不太熟悉的功能时。我先创建一个分支并提交了几次。当我卡住时,我请同事帮忙。他们从我犯错之前的较早提交开始,解决了问题并推送了更改。此时,我需要用远程的更新版本替换我的本地版本,同时丢弃本地更改,以便继续开发该功能。

另一个例子是出现冲突时,我觉得修复冲突比在最新代码上重新实现我们的更改更难或更耗时。

无论原因如何,请务必确认这些更改确实不再需要且可以安全删除,或先备份您的分支,以避免丢失重要工作。

如何正确覆盖本地更改

第一步是在本地机器上获取(下载)远程仓库的最新内容。使用 Git 的fetch 命令完成:

git fetch

默认情况下,这会获取当前分支的内容,通常这正是我们所需。我们也可以选择性地指定要下载的分支(将下方的 <branch_name> 替换为目标分支名):

git fetch origin/<branch_name>

或者,我们可以使用 --all 选项下载全部内容:

git fetch –all

从远程仓库获取更改不会影响您当前工作分支目录中的文件。fetch 命令会下载远程仓库的更改,但不会进行合并。本地仓库会同时保留本地文件与远程文件的视图,而获取操作会更新远程文件的视图。

从远程仓库下载更改后,我们可以使用Git 的 reset 命令将当前工作分支重置到指定状态:

git reset --hard origin/<branch_name>

必须使用 --hard 选项,才能确保本地更改被远程更改覆盖。在上述命令中,我们选择用 origin/<banch_name>(对应刚刚获取到的该分支的远程版本)替换当前工作分支。

git reset 命令会删除本地更改,即使这些更改已经提交。因此,建议在执行该命令前先为当前分支创建本地备份。可以这样做:

git branch <name_of_the_backup_branck>

将 <name_of_the_backup_branch> 替换为您想要的备份分支名称。

综合起来,我们可以通过以下步骤,用远程的最新版本覆盖指定分支的所有本地更改:

git fetch
git branch <name_of_the_backup_branch>
git reset --hard origin/<branch_name>

如果您想进一步了解在 Git 中使用分支,欢迎查看克隆指定 Git 分支的教程。

通过删除未跟踪文件实现全面清理

Git 不会处理未跟踪文件(从未通过 git add 添加的文件)。如果我们还想删除这些文件,可以使用:

git clean -fdx

这是一个危险命令,用于从工作目录中移除未跟踪文件。务必谨慎使用,因为它会永久删除这些文件,无法通过 Git 恢复。以下是选项说明:

  • -f:强制执行 clean 操作。出于防止意外数据丢失的安全考虑,没有该标志时 Git 会拒绝删除工作目录中的未跟踪文件。
  • -d:指示 git clean 不仅删除未跟踪的文件,还删除未跟踪的目录。默认情况下,git clean 只针对未跟踪的文件并忽略目录。
  • -x:默认情况下,git clean 会遵循 .gitignore 规则,不会删除匹配这些模式的文件或目录。加入 -x 会覆盖这一行为,指示 Git 同时删除被 .gitignore 或其他忽略规则忽略的文件/目录。这会进行非常彻底的清理,移除仓库中所有未跟踪文件,包括通常被忽略的构建产物、日志文件和其他生成物。

理解 Git Pull

我们通常使用 git pull 命令来用远程更改更新本地仓库。然而,该命令不能用来覆盖本地更改,因为 git pull 会尝试将本地版本与远程版本合并。

在底层,git pull 会执行以下步骤:

  1. 使用 git fetch 下载远程仓库的最新版本。
  2. 使用 git merge 将本地与远程分支进行合并。

由于存在合并步骤,在我们希望忽略并丢弃本地更改的场景中,git pull 并不合适。

<code data-mce-selected=

git pull --force 又如何?

查看git pull 命令的文档可以发现它提供了一个 --force 选项。一个常见的误解是,该选项会强制 git pull 通过用远程更改覆盖本地更改来执行。

当我们为 git pull 提供 --force 选项时,该选项会传递给 git fetch。因此,git pull --force 会执行如下步骤:

  1. git fetch --force
  2. git merge

 <code data-mce-selected=

我们了解到,fetch 用于更新本地对远程仓库的视图。如果您当前本地仓库与远程仓库之间的唯一区别是缺少一些提交(本地分支落后于远程版本),那么 git fetch 只会通过将这些新提交附加到历史中来更新本地视图。

当分支历史一致时,Git fetch 会将本地仓库与远程仓库同步

然而,也可能有人重写了远程分支的提交历史,导致与本地视图不兼容。在这种情况下,fetch 将无法正常工作。

如果分支历史不匹配,Git fetch 将失败,除非我们使用 <code data-mce-selected=

我们可以通过使用 --force 选项来绕过这一点,它会强制 Git 忽略本地历史并用远程历史覆盖。

可见,--force 选项与强制覆盖提交历史有关,与忽略工作分支目录中的本地更改无关。

整合本地更改

用远程更改覆盖本地更改并不是常见的 Git 做法,但在我们不想处理冲突或不在乎本地更改的某些场景下会很有用。

合并

通常,我们会先拉取更改并处理冲突。人们往往害怕处理冲突。有些冲突确实会让人应接不暇,但这并不常见。冲突发生于:

  1. 本地分支和远程分支在同一文件的同一位置都有更改。
  2. 一方删除了某个文件,而另一方修改了该文件。

在这些情况下,Git 无法判断我们想保留哪一方的更改,需要我们手动指定。Git 会像下面这样在文件中标示出冲突:

Git 冲突示例

要解决冲突,我们需要编辑文件,删除这些标记行,只保留想要的内容。处理完所有冲突后即可提交更改。详细指南请查看这篇Git 合并冲突解决教程。

拣选提交(Cherry-pick)

如果我们确实想先让本地版本与远程版本一致,再逐步整合我们的更改,可以将备份分支中的提交带回当前分支,方法如下:

git cherry-pick <commit_hash>

其中,<commit_hash> 是我们想整合的提交的哈希值。这假设在执行 git reset 之前,我们使用 git branch <name_of_the_backup_branck> 创建了一个备份分支,并且本地更改已经提交。

要找到 <commit_hash>,我们可以使用以下命令列出备份分支中的提交哈希:

git log <name_of_the_backup_branck>

暂存(Stash)

或者,如果我们不想提交更改或创建备份分支,可以在合并前暂存更改:

git stash

该命令可用于临时保存当前工作,而不将其提交到分支历史。暂存会把您的修改先放到一旁,使工作目录保持干净。

重置后,我们可以使用以下命令取回更改:

git stash pop

使用暂存后,完整工作流大致如下:

git fetch
git stash
git reset --hard origin/<branch_name>
git stash pop

想了解更多,请查看这份包含 stash 及其他 git 命令的速查表。

结论

尽管这不是最佳实践,但有时我们确实只想让本地分支的内容与远程分支一致,同时丢弃本地更改。

存在一种误解,认为可以通过 git pull --force 实现。这是错误的,因为 git pull --force 结合的是 git fetch --force 与 git merge。因此,它会尝试将本地更改与远程更改合并,而不是覆盖它们。

用远程仓库内容覆盖本地更改的正确方法是:

git fetch
git reset --hard origin/<branch_name>

必须谨慎使用 git reset 命令,因为它会删除本地更改,即使这些更改已被提交。如果我们有本地提交并创建了备份分支,可以使用 git cherry-pick 将它们带回。如果没有提交且想临时保存本地工作,可以在重置前使用 git stash,之后用 git stash pop 取回更改。

François Aubry

作者

François Aubry

全栈工程师,CheapGPT 创始人。我一直热爱教学。从学生时代起,我就积极寻找机会为同学提供辅导与帮助。这份热情促使我攻读博士学位,并在此期间担任助教,以支持我的学术追求。那些年里,我在传统课堂环境中收获了极大的成就感,既建立联系也促进学习。随着在线学习平台的兴起,我也看到了数字教育的变革潜力。事实上,我曾参与我们大学一款此类平台的建设。我致力于将传统教学理念与创新的数字方法相融合。我的热情在于打造既有吸引力、信息量充足,又能让当代学习者轻松获取的课程。

主题