“git pull”命令是否可以自动暂存并恢复挂起的更改?

178

我知道如何解决这个问题:

user@host$ git pull
Updating 9386059..6e3ffde
error: Your local changes to the following files would be overwritten by merge:
    foo.bar
Please, commit your changes or stash them before you can merge.
Aborting

但是难道没有让git pull为我完成stashpop的方法吗?

如果这个命令有另一个名称,那也可以。

git stash; git pull; git stash pop创建一个shell别名是一种解决方法,但我正在寻找更好的方法。


1
那么关于 Git 别名呢? - Яois
35
以编程方式运行 git stash; git pull; git stash pop 是危险的,因为如果没有需要暂存的内容,git stash 将无法执行任何操作,但是 git stash pop 会弹出最后一个暂存(如果有的话),这几乎肯定 是您想要的。用户 torek 在 Stack Overflow 上发表了一篇很棒的帖子,但我找不到它... - jub0bs
2
@Jubobs 这个?https://dev59.com/RWIj5IYBdhLWcg3wTTcG#20412685 还是这个?https://dev59.com/yWIj5IYBdhLWcg3wPy0a#20480591 - VonC
2
@guettli 我并没有暗示你的问题是重复的,我只是在回答Jubobs的评论。 - VonC
2
作为更进一步的步骤,操作只有在拉取后可以干净地应用存储时才能成功。如果存在冲突,则整个操作原子性地失败,以便树不会改变。这是我想做的:要么将更改与我的本地更改合并,要么因错误而失败,并让我手动决定下一步该怎么做。这种git“事务”是否可能? - Ed Avis
显示剩余3条评论
5个回答

275

对于 Git 2.6+(发布于2015年9月28日)

唯一有用的git config设置是:

rebase.autostash

(with Git 2.27, Q2 2020,现在您还可以使用merge.autostash,请见下文)
当设置为true时,在操作开始前自动创建一个临时存储区,并在操作结束后应用它。这意味着您可以在脏工作树上运行rebase。
但是,请谨慎使用:成功完成rebase后最终的stash应用可能会导致非常复杂的冲突。默认值为false。
结合以下内容:

pull.rebase

当为真时,在运行“git pull”时,将分支变基到获取的分支之上,而不是合并默认远程存储库中的默认分支。
在给定的存储库中:
git config pull.rebase true
git config rebase.autoStash true

这对于在一个脏树中进行简单的 git pull 工作来说已经足够了。
在这种情况下不需要别名。


请查看提交53c76dc(2015年7月4日)由Kevin Daudt (Ikke)提交。
(合并于提交e69b408,由Junio C Hamano -- gitster --在2015年8月17日完成)

pull:启用rebase.autostash时允许脏树

当rebase遇到一个脏工作树时,它学会了储藏更改,但是git pull --rebase没有。只有在未启用rebase.autostash时才验证工作树是否脏。
注意:如果您想在不使用自动存储(即使设置了rebase.autoStash true)的情况下进行拉取操作,则自git 2.9(2016年6月)以来可以这样做:
 pull --rebase --no-autostash

请参见 提交 450dd1d, 提交 1662297, 提交 44a59ff, 提交 5c82bcd, 提交 6ddc97c, 提交 eff960b, 提交 efa195d (2016年4月2日),以及 提交 f66398e, 提交 c48d73b (2016年3月21日) ,作者为 Mehul Jain (mehul2029)
(由 Junio C Hamano -- gitster -- 合并于 提交 7c137bb,2016年4月13日)

提交 f66398e 特别包括:

pull --rebase: 添加 --[no-]autostash 标志

如果设置了 rebase.autoStash 配置变量,则无法通过命令行为 "git pull --rebase" 覆盖它。

教会 "git pull --rebase" 命令行标志 --[no-]autostash,如果设置,则覆盖当前值 rebase.autoStash。由于 "git rebase" 理解 --[no-]autostash 选项,所以只需要在调用 "git pull --rebase" 时将该选项传递给底层的 "git rebase"。


警告:在Git 2.14之前(2017年第三季度),"git pull --rebase --autostash"当本地历史记录快进到上游时不会自动存储。

请参见提交f15e7cf(由Tyler Brazier (tylerbrazier)于2017年6月1日提交)。
(由Junio C Hamano -- gitster --合并于提交35898ea,2017年6月5日)

pull:ff --rebase --autostash 在脏仓库中工作

当在一个脏的代码库中使用git pull --rebase --autostash命令时,如果结果是快进(fast-forward),则自动存储(autostash)不会被执行,导致拉取失败。
这是因为我们避免运行变基(rebase)的快捷方式,但是在该代码路径上忽略了自动存储(autostash)。

更新:Mariusz Pawelski评论中提出了一个有趣的问题:

所以当你使用rebase(或pull --rebase)时,每个人都在写有关autostash的文章。

但是当您进行带有合并的普通拉取时,没有人谈论自动存储。
那么这方面没有自动切换吗?还是我漏掉了什么?我更喜欢执行git pull --rebase,但OP问的是“标准”git pull

回答:

讨论此autostash功能的原始线程最初实现了git pull(合并)和git pull --rebase

但是... Git维护者Junio C Hamano指出:

如果“pull-merge”是会引发触发此主题的“烦恼”的东西,根据定义,本地更改与合并重叠,并且此内部“stash pop”将触及合并触及的路径,并且可能不会导致“已删除”,而是留下进一步需要解决的冲突。

我怀疑“pull.autostash”配置不是一个好的添加,因为它鼓励了一种糟糕的、令人痛苦的工作流程。
在简单情况下,它可能不会有影响,但当本地更改很复杂时,它会积极伤害而不是没有它,并且该配置剥夺了选择的动力。

对于“pull-rebase”,方程式有所不同,因为“rebase”坚持从干净的工作树开始,因此“下载然后停止”的烦恼感更大。我怀疑放松可能是解决实际问题的更有效的方法。

因此,关于经典的pull-merge,最好是:

在运行“git pull”之前,鼓励用户考虑工作树中WIP的性质。它是一个过于复杂的怪物,可能会干扰其他人正在做的事情,还是一个微不足道的更改,可以将其藏起来并弹出来?
如果是前者,他最好使用“checkout -b”,继续工作,直到本地更改变得更好,然后进行“提交”,再合并到原始分支。
如果是后者,他最好执行以下操作:
  • git pull
  • 找到冲突后,运行
    • git stash
    • git merge FETCH_HEAD
    • git stash pop

在Git 2.27(2020年第二季度)中,"git pull"学会了在不存在pull.rebase配置且没有给出--[no-]rebase--ff-only(这将导致合并)时发出警告。请参见提交d18c950(由Alex Henrie (alexhenrie)于2020年3月10日提交)。(由Junio C Hamano -- gitster --于2020年3月27日合并到提交1c56d6f

pull: 如果用户没有说明是rebase还是merge,则发出警告

签名:Alex Henrie

通常,新手Git用户会忘记使用“pull --rebase”,最终会从上游合并不必要的内容。他们通常想要的是在简单情况下使用“pull --rebase”,或者使用“pull --ff-only”更新主集成分支的副本,并单独对其工作进行变基。虽然pull.rebase配置变量可以帮助他们解决简单的情况,但没有机制让这些用户意识到它的存在。当命令行中没有--[no-]rebase选项和pull.rebase配置变量时,发出警告消息。这将给那些从不想“pull --rebase”的人带来不便,但这种不便只需每个用户支付一次,这应该是帮助许多新用户的合理代价。
使用 Git 2.27 (2020 年第二季度),“git merge” 学会了 “--autostash” 选项,并且新增了 merge.autostash 设置。

请参见 提交 d9f15d3, 提交 f8a1785, 提交 a03b555, 提交 804fe31, 提交 12b6e13, 提交 0dd562e, 提交 0816f1d, 提交 9bb3dea, 提交 4d4bc15, 提交 b309a97, 提交 f213f06, 提交 86ed00a, 提交 facca7f, 提交 be1bb60, 提交 efcf6cf, 提交 c20de8b, 提交 bfa50c2, 提交 3442c3d, 提交 5b2f6d9 (于2020年4月7日), 提交 65c425a (于2020年4月4日), 以及 提交 fd6852c, 提交 805d9ea (于2020年3月21日),这些提交是由Denton Liu (Denton-L)执行的。
(由Junio C Hamano -- gitster 合并于 提交 bf10200, 2020年4月29日)

pull: 传递 --autostash 给合并操作

签署者: Denton Liu

之前,--autostash 只能与 git pull --rebase 操作一起使用。

然而,在最新的补丁中,合并操作也学会了使用 --autostash,因此我们没有理由再限制其使用了。
让 pull 操作像 rebase 操作一样将 --autostash 参数传递给合并操作。

rebase: 使用 sequencer.c 中的 apply_autostash() 函数

签署者: Denton Liu

apply_autostash()函数在builtin/rebase.c中,与sequencer.c中的apply_autostash()函数非常相似,除了它们接受的arg类型不同。将sequencer.c版本设置为extern,并在rebase中使用它。

rebase版本是在6defce2b02("内置rebase:支持--autostash选项",2018-09-04,Git v2.20.0-rc0 - 合并列在batch #8中)中作为shell转换为C的一部分引入的。
它选择复制该函数,因为当时还有另一个正在进行的项目将交互式rebase从shell转换为C,他们不想通过重构apply_autostash()sequencer.c版本来与该项目发生冲突。
由于两个项目都已完成,现在我们可以自由地将它们结合在一起。


随着Git 2.30(2021年第一季度)的发布,用户界面得到了改进:

请参见提交 e01ae2a(由Johannes Schindelin (dscho)于2020年11月19日提交)。
(由Junio C Hamano -- gitster --合并于提交 290c940,2020年11月30日)

pull: 为设置 pull.rebase 的提示添加着色

指出者:Ævar Arnfjörð Bjarmason
签署者:Johannes Schindelin

d18c950a69f ("pull: warn if the user didn't say whether to rebase or to merge", 2020-03-09, Git v2.27.0-rc0 -- merge listed in batch #2) 中,引入了一个新的提示来鼓励用户通过配置pull.rebase设置,有意识地决定他们想要将其拉取合并还是变基。
这个警告显然旨在为用户提供建议,但正如this thread中指出的那样,它使用了warning()而不是advise()
其中一个后果是建议没有像其他类似消息一样着色。
因此,让我们改用advise()

随着 Git 2.33 (Q3 2021) 的到来,git pull --rebase 得到了简化:

请看 提交记录 a7d18a1, 提交记录 a751e02, 提交记录 3400622 (2021年6月17日) 由 Felipe Contreras (felipec) 提交。
(由 Junio C Hamano -- gitster --提交记录 221ec24 中合并, 2021年7月8日)

拉取:清理自动存储检查

Signed-off-by: Felipe Contreras

目前 "git pull --rebase"(man) 在可以进行快进合并的情况下采取了捷径;run_merge() 被调用时使用了 --ff-only。
然而,"git merge"(man) 没有 --autostash 选项,所以当 "git pull --rebase --autostash``"(man) 被调用时,并且采取了快进合并捷径,则拉取失败。
这在 commit f15e7cf ("pull: ff --rebase --autostash works in dirty repo", 2017-06-01, Git v2.14.0-rc0 -- merge listed in batch #7) 中得到修复,方法是简单地跳过快进合并捷径。
后来 "git merge" 学会了 --autostash 选项 [a03b555 ("merge: teach --autostash option", 2020-04-07, Git v2.27.0-rc0 -- merge listed in batch #5)],因此 "git pull"(man) 也学会了 [d9f15d3 ("pull: pass --autostash to merge", 2020-04-07, Git v2.27.0-rc0 -- merge listed in batch #5)]。
因此,在使用 --rebase --autostash 调用时,不再需要跳过快进合并捷径。
让我们始终采取快进合并捷径,实质上是撤销 f15e7cf

"git merge --autostash"(man)创建的自动暂存的本地更改被混合到了工作树中留下的冲突状态中,这个问题已经在Git 2.38(2022年第三季度)中得到了解决。

请查看提交d3a9295(2022年8月23日)由Elijah Newren (newren)提交。
(由Junio C Hamano -- gitster --提交3a47790中合并,2022年9月1日)

merge:仅在适当时应用自动存储

签署者:Elijah Newren

如果合并失败,我们会将冲突留在工作目录中供用户解决,此时不应尝试应用任何自动存储。另外,如果我们未能应用自动存储(因为合并失败或用户请求了--no-commit),那么我们应该告诉用户如何稍后应用它。添加一个测试用例以验证我们已经纠正了这个行为。
新指令: 完成后,请使用git stash pop应用存储的更改。

截至2.4.2版本,这尚未实现。也许有朝一日会实现。rebase.autoStash仅适用于使用rebase的情况。pull.rebase只适用于使用pull的情况。 - Randal Schwartz
1
@Pradhan 我同意。这个实现今天早上刚刚进入主分支,应该会在 git 2.6 中发布。我已经编辑了答案以明确这一点。 - VonC
SVN会自动完成所有这些操作。 - Cosmin
1
所以每个人都在谈论当您执行rebase(或 pull --rebase)时的 autostash。但是没有人谈论当您使用合并的普通pull时的 autostash。 那么这方面没有自动切换吗?还是我错过了什么?我更喜欢使用git pull --rebase,但 OP 问及“标准”的`git pull”。 - Mariusz Pawelski
1
@MariuszPawelski 非常有趣的问题。我已经编辑了我的答案,解释了为什么在普通的git pull(合并)中没有自动存储功能。 - VonC
显示剩余4条评论

71

为了节省后来者的时间,这里是一个总结(感谢@VonC):

git pull --rebase --autostash

或者,如果你想将它设置为默认值(如在已接受的答案中所述),

git config pull.rebase true
git config rebase.autoStash true

然后只需要

git pull

11
重点是:在运行了git config pull.rebase truegit config rebase.autoStash true之后,你只需要运行git pull命令,就可以完成所有操作。只需使用git pull命令,无需其他选项。 - VonC
4
似乎您需要至少 Git 2.9 版本才能使用 --autostash 选项。而 -c rebase.autoStash=true 则适用于 Git 2.6 及以上版本。 - ntc2

16

如上面的评论所述,在git pull中设置这两个配置值目前无效,因为自动存储(autostash)仅适用于实际的变基操作。这些git命令可以满足您的需求:

git fetch
git rebase --autostash FETCH_HEAD

或者将其设置为别名:

git config alias.pullr '!git fetch; git rebase --autostash FETCH_HEAD'

然后执行:

git pullr

当然,这个别名可以根据需要进行重命名。


7

使用Git 2.6+,你可以使用以下内容:

alias gup='git -c rebase.autoStash=true pull --rebase'

这个--rebase选项会使得git-pull使用rebase而不是merge,因此像--ff-only这样的设置/选项将不会生效。

我使用一个别名来默认使用--ff-only拉取(git pull --ff-only),如果无法进行快进合并或存在存储的更改,则可以使用上面提到的gup


git pull --ff-onlygit pull --rebase --autostash 之间的主要区别是什么? - alper

-2

正如您所提到的,这是处理它的方法。您可以将其用作别名以节省输入并使用快捷方式,也可以将其用于单行(也可以作为别名)

git stash && git pull --rebase && git stash pop

它将执行与您完成的相同操作,但在单个行中(&&),如果将其设置为别名,则甚至会更短。

以下几行将显示拉取/推送之前的传入/传出更改

git log ^master origin/master
git log master ^origin/master

11
这种方法是不安全的:如果没有需要隐藏的内容,第一条命令将不会执行任何操作,那么stash pop将从以前的一些随机内容中取消隐藏。 - John Zwinck
只为更加清晰:即使 'git stash' 没有存储任何内容,它仍然不会"返回"错误代码,因此 && 仍将继续使用git pullgit stash pop并弹出以前的存储。因此,最好不要使用它,除非您非常确定它会存储一些东西! - MoonLite

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