我有一个同事声称 git pull
是有害的,并且每当有人使用它时都会感到不安。git pull
命令似乎是更新本地存储库的规范方式。是否使用 git pull
制造问题?它会产生什么问题?有没有更好的方法来更新 git 存储库?
最佳答案
概括
默认情况下,git pull
创建 merge 提交,这会增加代码历史的噪音和复杂性。另外,pull
让您很容易不去考虑您的更改可能会如何受到传入更改的影响。git pull
命令是安全的,只要它只执行快进 merge 。如 git pull
配置为仅进行快进 merge ,当无法进行快进 merge 时,Git 将退出并显示错误。这将使您有机会研究传入的提交,思考它们可能如何影响您的本地提交,并决定最佳的行动方案( merge 、 rebase 、重置等)。
使用 Git 2.0 及更新版本,您可以运行:
git config --global pull.ff only
将默认行为更改为仅快进。对于 1.6.6 和 1.9.x 之间的 Git 版本,您必须养成键入的习惯:
git pull --ff-only
但是,对于所有版本的 Git,我建议配置一个
git up
别名如下:git config --global alias.up '!git remote update -p; git merge --ff-only @{u}'
并使用
git up
而不是 git pull
.我更喜欢这个别名而不是 git pull --ff-only
因为:origin/*
上游不再存在的分支。 git pull
的问题git pull
如果使用得当,也不错。 Git 最近的几项更改使其更易于使用 git pull
正确,但不幸的是普通 git pull
的默认行为有几个问题:git pull
暂停您正在做的事情以查看其他人的工作很烦人下面更详细地描述这些问题。
非线性历史
默认情况下,
git pull
命令相当于运行 git fetch
其次是 git merge @{u}
.如果本地仓库有未推送的提交,git pull
的 merge 部分创建 merge 提交。merge 提交本身没有什么坏处,但它们可能是危险的,应该受到尊重:
当然, merge 是有时间和地点的,但是了解何时应该和不应该使用 merge 可以提高存储库的实用性。
请注意,Git 的目的是使共享和使用代码库的演变变得容易,而不是在历史展开时准确地记录历史。 (如果您不同意,请考虑
rebase
命令及其创建原因。) git pull
创建的 merge 提交不要向其他人传达有用的语义——他们只是说在你完成更改之前有人碰巧推送到存储库。如果这些 merge 提交对其他人没有意义并且可能很危险,为什么要进行这些 merge 提交?可以配置
git pull
rebase 而不是 merge ,但这也有问题(稍后讨论)。相反,git pull
应配置为仅进行快进 merge 。重新引入重新定位的提交
假设有人重新设置分支并强制推送它。这通常不应该发生,但有时是必要的(例如,删除意外提交和推送的 50GiB 日志文件)。 merge 由
git pull
完成将上游分支的新版本 merge 到本地存储库中仍然存在的旧版本中。如果你 push 结果, fork 和手电筒将开始向你走来。有些人可能会争辩说,真正的问题是强制更新。是的,通常建议尽可能避免强制 push ,但有时它们是不可避免的。开发人员必须准备好处理强制更新,因为它们有时会发生。这意味着不要通过普通的
git pull
盲目地 merge 旧的提交。 .惊喜工作目录修改
在
git pull
之前,无法预测工作目录或索引的样子。已经完成了。在执行其他任何操作之前,您可能必须解决 merge 冲突,它可能会在您的工作目录中引入一个 50GiB 的日志文件,因为有人不小心推送了它,它可能会重命名您正在工作的目录等。git remote update -p
(或 git fetch --all -p
)允许您在决定 merge 或 rebase 之前查看其他人的提交,从而允许您在采取行动之前制定计划。难以审查其他人的提交
假设您正在进行一些更改,而其他人希望您查看他们刚刚推送的一些提交。
git pull
的 merge (或 rebase )操作会修改工作目录和索引,这意味着您的工作目录和索引必须是干净的。您可以使用
git stash
然后 git pull
,但是当你完成审查后你会做什么?要回到原来的位置,您必须撤消 git pull
创建的 merge 。并应用 stash 。git remote update -p
(或 git fetch --all -p
)不会修改工作目录或索引,因此随时运行都是安全的——即使您已经暂存和/或未暂存的更改。您可以暂停正在执行的操作并查看其他人的提交,而无需担心存储或完成您正在处理的提交。 git pull
没有给你那种灵活性。重新定位到远程分支
一个常见的 Git 使用模式是做一个
git pull
引入最新的变化,然后是 git rebase @{u}
消除 git pull
的 merge 提交介绍。 Git 有一些配置选项可以通过告诉 git pull
将这两个步骤减少为一个步骤,这很常见。执行 rebase 而不是 merge (参见 branch.<branch>.rebase
、 branch.autosetuprebase
和 pull.rebase
选项)。不幸的是,如果您想要保留未推送的 merge 提交(例如,将推送的功能分支 merge 到
master
的提交),则既不是 rebase pull ( git pull
与 branch.<branch>.rebase
设置为 true
)也不是 merge pull (默认 git pull
行为)后跟 rebase 将起作用。这是因为 git rebase
在没有 --preserve-merges
的情况下消除 merge (它使 DAG 线性化)选项。不能将 rebase-pull 操作配置为保留 merge ,并且 merge pull 后跟 git rebase -p @{u}
不会消除由 merge pull 引起的 merge 。 更新:添加 Git v1.8.5 git pull --rebase=preserve
和 git config pull.rebase preserve
.这些原因git pull
要做git rebase --preserve-merges
获取上游提交后。 (感谢 funkaster 的提醒!)清理已删除的分支
git pull
不修剪与从远程存储库中删除的分支对应的远程跟踪分支。例如,如果有人删除分支 foo
从远程仓库中,您仍然会看到 origin/foo
.这会导致用户意外地复活被杀死的分支,因为他们认为它们仍然处于事件状态。
更好的选择:使用
git up
而不是 git pull
而不是
git pull
,我建议创建和使用以下 git up
别名:git config --global alias.up '!git remote update -p; git merge --ff-only @{u}'
此别名从所有上游分支下载所有最新提交(修剪死分支)并尝试将本地分支快进到上游分支上的最新提交。如果成功,则没有本地提交,因此不存在 merge 冲突的风险。如果有本地(未推送)提交,快进将失败,让您有机会在采取行动之前查看上游提交。
这仍然会以不可预测的方式修改您的工作目录,但前提是您没有任何本地更改。不像
git pull
, git up
永远不会让您看到提示,希望您修复 merge 冲突。另一种选择:
git pull --ff-only --all -p
以下是上述
git up
的替代方案别名:git config --global alias.up 'pull --ff-only --all -p'
此版本
git up
与之前的 git up
具有相同的行为别名,除了:-p
参数,它被传递给 fetch
)在 Git 如果您运行的是 Git 2.0 或更新版本
使用 Git 2.0 及更新版本,您可以配置
git pull
默认情况下只进行快进 merge :git config --global pull.ff only
这会导致
git pull
表现得像 git pull --ff-only
,但它仍然没有获取所有上游提交或清除旧的 origin/*
分支,所以我还是更喜欢 git up
.
关于git - 在什么情况下 `git pull` 可能有害?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15316601/