我有一位同事声称这git pull
是有害的,并且每当有人使用它时都会感到不安.
该git pull
命令似乎是更新本地存储库的规范方法.使用git pull
创建问题?它创造了什么问题?有更好的方法来更新git存储库吗?
Ric*_*sen 543
默认情况下,git pull
创建合并提交,这会增加代码历史记录的噪音和复杂性.此外,还pull
可以轻松地不考虑您的更改如何受到传入更改的影响.
该git pull
命令是安全的,只要它只执行快进合并.如果git pull
配置为仅执行快进合并,并且当无法进行快进合并时,则Git将退出并显示错误.这将使您有机会研究传入的提交,考虑它们可能如何影响您的本地提交,并确定最佳的操作过程(合并,变基,重置等).
使用Git 2.0和更新版本,您可以运行:
git config --global pull.ff only
Run Code Online (Sandbox Code Playgroud)
将默认行为更改为仅快进.使用1.6.6和1.9.x之间的Git版本,你将不得不养成输入的习惯:
git pull --ff-only
Run Code Online (Sandbox Code Playgroud)
但是,对于所有版本的Git,我建议配置如下git up
别名:
git config --global alias.up '!git remote update -p; git merge --ff-only @{u}'
Run Code Online (Sandbox Code Playgroud)
并使用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
创建合并提交.
合并提交没有任何本质上的错误,但它们可能是危险的,应该受到尊重:
当然有合并的时间和地点,但是理解何时应该和不应该使用合并可以提高存储库的实用性.
请注意,Git的目的是使共享和使用代码库的演变变得容易,而不是精确地记录历史记录.(如果您不同意,请考虑该rebase
命令及其创建原因.)创建的合并提交git pull
不会将有用的语义传达给其他人 - 他们只是说在您完成更改之前,其他人碰巧会推送到存储库.为什么这些合并提交如果对他人没有意义并且可能是危险的?
可以配置git pull
为rebase而不是merge,但这也存在问题(稍后讨论).相反,git pull
应配置为仅执行快进合并.
假设有人重新分支一个分支并强制推动它.这通常不应该发生,但有时是必要的(例如,删除意外调试和推送的50GiB日志文件).合并完成git pull
将合并新版本的上游分支到仍存在于本地存储库中的旧版本.如果你推动结果,桨叉和火炬将开始按你的方式.
有些人可能认为真正的问题是强制更新.是的,通常建议尽可能避免使用力推力,但它们有时是不可避免的.开发人员必须准备好处理强制更新,因为它们有时会发生.这意味着不要盲目地通过普通的方式合并旧的提交git pull
.
在完成之前,无法预测工作目录或索引的外观git pull
.在您可以执行任何其他操作之前,您可能需要解决合并冲突,它可能会在您的工作目录中引入50GiB日志文件,因为有人意外推送它,它可能会重命名您正在使用的目录,等等.
git remote update -p
(或git fetch --all -p
)允许您在决定合并或变更之前查看其他人的提交,允许您在采取行动之前制定计划.
假设您正在进行一些更改,而其他人希望您查看他们刚刚推送的一些提交. git pull
的合并(或rebase)操作会修改工作目录和索引,这意味着您的工作目录和索引必须是干净的.
你可以使用git stash
然后git pull
,但是当你完成审查后你会怎么做?要回到原来的位置,您必须撤消创建的合并git pull
并应用存储.
git remote update -p
(或git fetch --all -p
)不修改工作目录或索引,因此即使您有分阶段和/或未分阶段的更改,也可以随时运行.您可以暂停正在执行的操作并查看其他人的提交,而无需担心存储或完成您正在处理的提交.git pull
不会给你这种灵活性.
常见的Git使用模式是执行a git pull
以引入最新更改,然后执行a git rebase @{u}
以消除git pull
引入的合并提交.这是常见的,以至于Git有一些配置信息,告诉降低这两个步骤,一个步骤git pull
来执行,而不是合并衍合(见的branch.<branch>.rebase
,branch.autosetuprebase
和pull.rebase
选项).
不幸的是,如果你有一个unpushed合并提交要保存(如提交合并推送的特性分支成master
),既不是底垫式(git pull
带branch.<branch>.rebase
设定为true
),也不是合并式(默认git pull
行为),随后是rebase将起作用.这是因为git rebase
在没有--preserve-merges
选项的情况下消除了合并(它使DAG线性化).不能将rebase-pull操作配置为保留合并,并且合并拉动后跟a git rebase -p @{u}
将不会消除merge-pull引起的合并. 更新: 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}'
Run Code Online (Sandbox Code Playgroud)
此别名从所有上游分支下载所有最新提交(修剪死分支)并尝试将本地分支快进到上游分支上的最新提交.如果成功,则没有本地提交,因此没有合并冲突的风险.如果存在本地(未刷新)提交,则快进将失败,使您有机会在采取操作之前查看上游提交.
这仍然以不可预测的方式修改您的工作目录,但前提是您没有任何本地更改.不同的是git pull
,git up
永远不会让你想到你修复合并冲突的提示.
git pull --ff-only --all -p
以下是上述git up
别名的替代方法:
git config --global alias.up 'pull --ff-only --all -p'
Run Code Online (Sandbox Code Playgroud)
此版本git up
与前一个git up
别名具有相同的行为,但以下情况除外:
-p
参数,传递给它fetch
),可能会在未来的Git版本中发生变化使用Git 2.0和更新版本,您可以配置git pull
为默认只执行快进合并:
git config --global pull.ff only
Run Code Online (Sandbox Code Playgroud)
这会导致git pull
行为git pull --ff-only
,但它仍然无法获取所有上游提交或清除旧origin/*
分支,所以我仍然喜欢git up
.
Sér*_*lho 194
我的答案,从讨论中拉起来的HackerNews:
我觉得我很想用热线新闻头条法回答这个问题:为什么被git pull
认为有害?事实并非如此.
Hun*_*ick 26
如果你正确使用Git,这被认为是有害的.在给定用例的情况下,我看到它会对您产生负面影响,但您可以通过不修改共享历史记录来避免问题.
Mar*_*age 18
接受的答案声称
无法将rebase-pull操作配置为保留合并
但是从Git 1.8.5开始,这个帖子就是这个答案,你可以做到
git pull --rebase=preserve
Run Code Online (Sandbox Code Playgroud)
要么
git config --global pull.rebase preserve
Run Code Online (Sandbox Code Playgroud)
要么
git config branch.<name>.rebase preserve
Run Code Online (Sandbox Code Playgroud)
该文件说,
当
preserve,
也传递--preserve-merges
给'git rebase'时,运行'git pull'不会使本地提交的合并提交变平.
前面的讨论有更详细的信息和图表:git pull --rebase --preserve-merges.它也解释了为什么git pull --rebase=preserve
不相同git pull --rebase --preserve-merges
,哪些不正确.
之前的其他讨论解释了rebase的保留合并变体实际上做了什么,以及它如何比常规rebase复杂得多:git的"rebase --preserve-merges"究竟做了什么(以及为什么?)
归档时间: |
|
查看次数: |
87844 次 |
最近记录: |