我的git工作流程经常使用rebase。我总是获取上游更改(我分叉的主要仓库)然后合并到我的分支,然后重新绑定以删除无用(对我来说:D)合并提交和树分割。
这个工作流程中有一件让我烦恼的事情是:
$ git rebase upstream/master
Cannot rebase: You have unstaged changes.
Please commit or stash them.
$ git stash
Saved working directory and index state WIP on cc: abc1234 Merge remote-tracking branch 'upstream/master' into local_branch
HEAD is now at abc1234 Merge remote-tracking branch 'upstream/master' into local_branch
$ git rebase upstream/master
First, rewinding head to replay your work on top of it...
Applying: awesome code change
$ git stash pop
所以这里我们有4个命令,1 =失败的rebase,2 = stash,3 = rebase,4 = stash pop。除了3之外的任何东西都是无意识的工作。
所以,问题是:最推荐的自动化方法是什么?每次都运行git stash / rebase / pop的别名?一些git配置强制rebase隐藏或将其视为另一个提交后再重新应用?别的什么?
答案 0 :(得分:36)
编辑:从Git版本1.8.4开始,但在Git版本2.0.1中修复了一个重要的漏洞,git rebase
现在有--autostash
。您也可以将git rebase
配置为默认使用--autostash
,git config --global rebase.autoStash true
。请注意the documentation中的以下句子:
然而,谨慎使用:最后藏匿 成功改组后的申请可能会导致非平凡 冲突。
(我仍然喜欢提交。)
它可能会帮助您意识到git stash
实际上只是git commit
(以更复杂的形式,首先提交索引,然后是工作树 - 当您应用存储时,您可以维护索引和工作树的分离,或者将它们组合成工作树的变化。)
什么使存储特别之处在于它所做的提交 - 两个或者-u
或-a
,甚至三个提交 - 以不寻常的形式制作(作为合并提交,#39) ; s不是真正的合并)并且没有放在任何分支上(相反,特殊的refs/stash
引用用于保留和查找它们。)
由于他们不在分支机构,rebase
不会触及它们,而在您的工作流程中,git stash pop
将工作树更改带入您的新工作区工作树。但是,如果您在分支上进行自己的(正常)提交,并且使用rebase并包含该提交,则此正常提交将与其他提交一起进行重新设置。我们马上就会遇到最后一个问题;现在,让我们来做这件事,因为一系列承诺(或者不会)得到重新定位:
... do some work ...
... make some commits ...
... more work ...
... do something that causes upstream/master to update, such as git fetch upstream
$ git stash
此时,您拥有的是:
... - o - * - A - B - C <-- HEAD=master
\ |\
\ i-w <-- stash
\
@-@-@ <-- upstream/master
此处,A
,B
和C
是您的提交(我假设您已成为3),所有这些都在分支master
上。 i-w
挂起提交C
是您的存储,它不在分支上,但仍然是双提交"git stash bag"并且实际上附加到您的最新提交(C
})。 @
提交(可能只有一个)是新的上游提交。
(如果您已提交 no 提交,则您的存储包会挂起提交*
,并且您当前的分支指向提交*
,以便{{1}除了向前移动你当前的分支指针之外没有任何工作要做。在这种情况下,一切都是一样的,但我会假设有一些提交。)
现在你运行git rebase
。这会将您的提交复制到新的提交,使用新的ID和新的父ID,以便它们位于最后git rebase upstream/master
的顶部。存储袋不会移动,因此结果如下:
@
现在使用... - o - * - A - B - C [abandoned, except for the stash]
\ |\
\ i-w <-- stash
\
@-@-@ <-- upstream/master
\
A'-B'-C' <-- HEAD=master
,它将i / w内容恢复为工作树更改,删除git stash pop
标签(更准确地说,弹出它以便stash
,如果它存在,现在是stash@{1}
,依此类推)。这会释放对原始stash
链的最后引用,这意味着我们也不需要A - B - C
位,这样我们就可以将其重绘为更简单的方法:
i-w
现在让我们了解一下,如果您... - @ <-- upstream/master
\
A'-B'-C' <-- HEAD=master plus work tree changes
(或git stash save
和git commit -a
没有-a)创建git add
,而不是git commit
,那么会发生什么?实际提交D
。你从:
... - o-*-A-B-C-D <-- HEAD=master
\
@-@-@ <-- upstream/master
现在你git rebase upstream/master
复制了A
到D
,将它们放在最后@
的末尾,你有了这个:
... - o-*-@-@-@ <-- upstream/master
\
A'-B'-C'-D' <-- HEAD=master
唯一的问题是你有一个不需要的额外提交D
(现在,D'
),而不是未提交的工作树更改。但是这很简单,git reset
撤回一个提交。我们可以使用--mixed
重置 - 默认 - 来重新设置索引(暂存区域),以便&#34;取消添加&#34;所有文件,或者如果您希望它们保持git add
- ed,--soft
重置。 (两者都不影响生成的提交图,只有索引状态不同。)
git reset --mixed HEAD^ # or leave out `--mixed` since it's the default
以下是这样的:
... - o-*-@-@-@ <-- upstream/master
\
A'-B'-C' <-- HEAD=master
\
D' [abandoned]
您可能认为这样做效率很低,但是当您使用git stash
时,您实际上至少会进行两次提交,稍后当您git stash pop
时放弃这些提交他们。真正的区别在于,通过临时,非发布提交,您可以自动重新生成。
git的一般规则是:make lot 临时提交,以便随时保存您的工作。你可以随时将它们改掉。也就是说,而不是:
... - * - A - B - C <-- mybranch
其中A
,B
和C
是提交*
(来自其他人或之前发布的内容)的最佳提交,请执行以下操作:
... - * - a1 - a2 - b1 - a3 - b2 - a4 - b3 - c1 - b4 - c2 - c3
其中a1
是A
的初始攻击,a2
修复a1
中的错误,b1
是初次尝试b
} {},a3
意识到b1
要求A
完全不同,b2
修复了b1
中的错误,a4
修正了a3
中的错误更改为a2
,而b3
是b1
应该做的错误;然后c1
是C
的初始尝试,b4
是b1
的另一个修正,c2
是一个改进,依此类推。
让我们说c3
之后你认为它已经准备好了。现在您运行git rebase -i origin/master
或其他任何内容,随机播放pick
行,以便a1
通过a4
订购,b1
到b4
按顺序排列, c1
到c3
按顺序排列,让rebase运行。然后,您修复任何冲突并确保内容仍然正确,然后再运行另一个git rebase -i
将所有四个a
版本折叠为A
,依此类推。
当你完成所有操作后,看起来就像你第一次创建一个完美的A
一样(或者可能是a4
或其他一个,具体取决于哪个承诺你保留,你丢下哪些,以及你是否重新设置任何时间戳记。其他人可能不希望或不需要看到您的中间工作 - 尽管您可以保留它,不组合提交,如果它有用。与此同时,你永远不需要有必须重新定义的未提交的东西,因为你只是提交部分内容。
在单行提交文本中提供这些提交名称有助于指导您以后的rebase工作:
git commit -m 'temp commit: work to enable frabulator, incomplete'
等等。
答案 1 :(得分:16)
一个简单的答案: git rebase -i --autosquash --autostash <tree-ish>
-i = interactively rebase
https://devdocs.io/git/git-rebase
这将...
<tree-ish>
进行互动式转换
tree-ish
可以是提交哈希或分支名称或标记或{ {3}}
答案 2 :(得分:2)
您可以使用名为git-up的外部工具,它完全按照您对所有分支的说法执行操作。这也有助于您保持干净的历史记录图表。
我已经使用了几年而且效果很好,特别是如果你不是git专家的话。如果您添加自动重新定位功能,您应该知道如何从失败的rebase中正确恢复(继续,中止,...)
打开一个shell并运行:
sudo gem install git-up
打开您的全局配置文件(~/.gitconfig
),然后添加以下内容:
[git-up "fetch"]
all = true # up all branches at once, default false
prune = true # prune deleted remote branches, default false
[git-up "rebase"]
# recreate merges while rebasing, default: unset
arguments = --preserve-merges
# uncomment the following to show changed commit on rebase
# log-hook = "git --no-pager log --oneline --color --decorate $1..$2"
有关更多选项,请参阅official documentation。
如果配置完好,只需运行:
git up
这(大致)等同于执行以下内容:
git stash
git fetch --all
[foreach branch]
git rebase --preserve-merges <branch> <remote>/<branch>
git merge --ff-only <branch>
[end foreach]
git checkout <prev_branch>
git stash pop
答案 3 :(得分:2)
一个命令中的整个工作流程,包括提取:
git pull --rebase --autostash [...]
答案 4 :(得分:1)
命名别名:
git stash&amp;&amp; git pull --rebase&amp;&amp; git stash pop
<强>更新强>
如果您正在使用想法,使用脏工作目录推送,它将提示一个对话框,选择rebase / merge,它将进行存储,rebase / merge并自动弹出。