在 git rebase 上自动隐藏保存/弹出更改?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27116671/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
automatically stash save/pop changes on git rebase?
提问by gcb
my git workflow uses rebase a lot. I always fetch upstream changes (the main repo i forked from) and then merge to my branches, and then rebase to remove useless (to me :D) merge commits and tree splits.
我的 git 工作流程经常使用 rebase。我总是获取上游更改(我从中分叉的主存储库)然后合并到我的分支,然后变基以删除无用的(对我来说:D)合并提交和树拆分。
one thing on this workflow that annoys me is:
在这个工作流程中让我烦恼的一件事是:
$ 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
so here we have 4 commands, 1=failed rebase, 2=stash, 3=rebase, 4=stash pop. anything but 3 is just mindless work.
所以这里我们有 4 个命令,1=failed rebase,2=stash,3=rebase,4=stash pop。除了 3 之外的任何东西都只是无意识的工作。
So, the question is: What is the most recommended way of automating it? an alias to run git stash/rebase/pop everytime? some git config that forces rebase to stash or treat it as another commit to reapply afterwards? something else?
所以,问题是:最推荐的自动化方法是什么?每次运行 git stash/rebase/pop 的别名?某些 git 配置强制 rebase 隐藏或将其视为另一个提交以在之后重新应用?还有什么?
回答by torek
Edit: As of Git version 1.8.4, but with an important side bug fixed in Git version 2.0.1, git rebase
now has --autostash
. You can configure git rebase
to use --autostash
by default as well, with git config --global rebase.autoStash true
. Please note the following sentence from the documentation:
编辑:从 Git 版本 1.8.4 开始,但在 Git 版本 2.0.1 中修复了一个重要的副作用,git rebase
现在有了--autostash
. 您也可以配置git rebase
为--autostash
默认使用,使用git config --global rebase.autoStash true
. 请注意文档中的以下句子:
However, use with care: the final stash application after a successful rebase might result in non-trivial conflicts.
但是,请谨慎使用:成功 rebase 后的最终存储应用程序可能会导致重大冲突。
(I still prefer to just make commits.)
(我仍然更喜欢只提交。)
TL;DR answer: just make a commit (then unmake it later)
TL; DR 答案:只需提交(然后稍后取消)
It may help you to realize that git stash
is really just git commit
(in a more complicated form, which commits the index first, then the work-tree—when you apply a stash, you can maintain the separation of index and work-tree, or combine them into just a work-tree change).
它可能会帮助你意识到这git stash
真的只是git commit
(以更复杂的形式,先提交索引,然后提交工作树——当你应用 stash 时,你可以保持索引和工作树的分离,或者将它们组合起来)只是一个工作树更改)。
What makes a stash special is that the commits it makes—the two or, with -u
or -a
, even three commits—are made in an unusual form (as a merge commit that's not really a merge) and not placed on any branch (instead, the special refs/stash
reference is used to retain and find them).
stash 的特别之处在于它所做的提交——两次或,与-u
或-a
,甚至三个提交——以一种不寻常的形式(作为一个不是真正合并的合并提交)而不是放在任何分支上(相反,特殊refs/stash
引用用于保留和查找它们)。
Since they're not on a branch, rebase
does not touch them, and in your workflow, it's the git stash pop
that brings the work-tree changes into your new work-tree. However, if you make your own (normal) commit, on a branch, and rebase and include that commit, this normal commit will be rebased along with any others. We'll get to one last problem in a moment; for now, let's draw this up, as a series of commits that do (or don't) get rebased:
由于它们不在分支上,rebase
不会触及它们,并且在您的工作流程中,它git stash pop
会将工作树更改带入您的新工作树。但是,如果您在分支上进行自己的(正常)提交,并重新设置并包含该提交,则此正常提交将与任何其他提交一起重新设置。稍后我们将解决最后一个问题;现在,让我们把它画出来,作为一系列做(或不)重新定位的提交:
... do some work ...
... make some commits ...
... more work ...
... do something that causes upstream/master to update, such as git fetch upstream
$ git stash
At this point, here's what you have:
在这一点上,这是你所拥有的:
... - o - * - A - B - C <-- HEAD=master
\ |\
\ i-w <-- stash
\
@-@-@ <-- upstream/master
Here, A
, B
, and C
are your commits (I'll assume you've made 3), all on branch master
. The i-w
hanging off commit C
is your stash, which is not on the branch, but is still a two-commit "git stash bag"and is actually attached to your latest commit (C
). The @
commits (there might be just one) are the new upstream commits.
在这里,A
,B
和C
是您的提交(我假设您已经提交了 3 个),全部在 branch 上master
。在i-w
关闭提交挂C
是你的藏匿处,这是不是在树枝上,但仍是一个两犯“混帐藏匿袋”,并实际连接到您的最新提交(C
)。在@
提交(有可能是只有一个)是新的上游提交。
(If you have made nocommits, your stash-bag hangs off commit *
, and your current branch points to commit *
, so that git rebase
has no work to do other than move your current branch pointer forward. Everything works out the same, in this case, but I'll assume there are some commits.)
(如果你没有提交,你的 stash-bag 挂起 commit *
,你当前的分支指向 commit *
,所以git rebase
除了向前移动你当前的分支指针之外没有其他工作要做。一切都是一样的,在这种情况下,但我会假设有一些提交。)
Now you run git rebase upstream/master
. This copies your commits to new commits, with new IDs and new parent IDs, so that they sit atop the last @
. The stash-bag does not move, so the result looks like this:
现在你跑git rebase upstream/master
。这会将您的提交复制到新提交,并带有新 ID 和新父 ID,以便它们位于最后一个@
. stash-bag 不动,所以结果是这样的:
... - o - * - A - B - C [abandoned, except for the stash]
\ |\
\ i-w <-- stash
\
@-@-@ <-- upstream/master
\
A'-B'-C' <-- HEAD=master
You now use git stash pop
, which restores the i/w stuff as work-tree changes, erasing the stash
label (more precisely, popping it so that stash@{1}
, if it exists, is now stash
, and so on). That releases the last references to the original A - B - C
chain, and means we don't need the i-w
bit either, which lets us redraw this as the much simpler:
您现在使用git stash pop
,它在工作树更改时恢复 i/w 内容,擦除stash
标签(更准确地说,弹出它以便stash@{1}
,如果存在,现在是stash
,等等)。这释放了对原始A - B - C
链的最后一个引用,意味着我们也不需要这个i-w
位,这让我们重绘它更简单:
... - @ <-- upstream/master
\
A'-B'-C' <-- HEAD=master plus work tree changes
Now let's draw what happens if, instead of git stash save
, you just do a git commit -a
(or git add
and git commit
without -a) to create an actual commit D
. You start with:
现在,让我们得出,如果发生了什么,而不是git stash save
,你只是做一个git commit -a
(或git add
并git commit
没有-A)创建一个实际的承诺D
。你开始:
... - o-*-A-B-C-D <-- HEAD=master
\
@-@-@ <-- upstream/master
Now you git rebase upstream/master
, which copies A
through D
to place them at the end of the last @
, and you have this:
现在你git rebase upstream/master
,它A
通过复制D
将它们放在最后一个 的末尾@
,你有这个:
... - o-*-@-@-@ <-- upstream/master
\
A'-B'-C'-D' <-- HEAD=master
The only problem is that you have this one unwanted extra commit D
(well, D'
now), instead of uncommitted work-tree changes. But this is trivially undone with git reset
to step back one commit. We can use a --mixed
reset—the default—to get the index (staging area) re-set too, so as to "un-add" all the files, or if you want them to stay git add
-ed, a --soft
reset. (Neither affects the resulting commit graph, only the index state is different.)
唯一的问题是你有这个不需要的额外提交D
(好吧,D'
现在),而不是未提交的工作树更改。但这只是简单地撤消了git reset
一次提交。我们可以使用--mixed
重置(默认设置)来重置索引(暂存区),以便“取消添加”所有文件,或者如果您希望它们保持git add
不变,则--soft
重置。(都不会影响生成的提交图,只有索引状态不同。)
git reset --mixed HEAD^ # or leave out `--mixed` since it's the default
Here's what that looks like:
这是它的样子:
... - o-*-@-@-@ <-- upstream/master
\
A'-B'-C' <-- HEAD=master
\
D' [abandoned]
You might think this is inefficient, but when you use git stash
you're actually making at least twocommits, which you then abandon later when you git stash pop
them. The real difference is that by making temporary, not-for-publication commits, you get those automatically rebased.
您可能认为这是低效的,但是当您使用时,git stash
您实际上至少进行了两次提交,然后在您提交时放弃git stash pop
。真正的区别在于,通过进行临时的、非公开的提交,您可以自动重新设置这些提交。
Don't be afraid of temporary commits
不要害怕临时提交
There's a general rule with git: make lotsof temporary commits, to save your work as you go. You can always rebase them away later. That is, instead of this:
git 有一个通用规则:进行大量的临时提交,以便在进行时保存您的工作。您以后可以随时将它们重新设置为基准。也就是说,而不是这样:
... - * - A - B - C <-- mybranch
where A
, B
, and C
are perfect and final commits atop commit *
(from someone else or earlier published stuff), make this:
其中A
, B
, 和C
是完美的最终提交*
(来自其他人或较早发布的内容),请执行以下操作:
... - * - a1 - a2 - b1 - a3 - b2 - a4 - b3 - c1 - b4 - c2 - c3
where a1
is an initial stab at A
, a2
fixes a bug in a1
, b1
is an initial attempt to make b
work, a3
is from realizing that b1
requires A
to be different after all, b2
fixes a bug in b1
, a4
fixes a bug in a3
's change to a2
, and b3
is what b1
should have done; then c1
is an initial attempt at C
, b4
is another fix to b1
, c2
is a refinement, and so on.
哪里a1
是最初的 stab at A
,a2
修复了一个 bug a1
,b1
是一个开始b
工作的尝试,a3
是从意识到b1
需要A
毕竟不同,b2
修复了一个 bug b1
,a4
修复了一个 bug ina3
的变化a2
,b3
是什么b1
应该有的完毕; 然后c1
是对 的初步尝试C
,b4
是对 的另一个修复b1
,c2
是改进,等等。
Let's say that after c3
you think it's mostly ready. Now you run git rebase -i origin/master
or whatever, shuffle the pick
lines to get a1
through a4
into order, b1
through b4
into order, and c1
through c3
into order, and let the rebase run. Then you fix any conflicts and make sure stuff is still right, then you run another git rebase -i
to collapse all four a
versions into A
, and so on.
假设c3
您认为它已基本准备就绪。现在你运行git rebase -i origin/master
或什么的,洗牌pick
线获得a1
通过a4
进入订单,b1
通过b4
为订单,c1
通过c3
成秩序,让底垫运行。然后您修复所有冲突并确保内容仍然正确,然后您运行另一个git rebase -i
将所有四个a
版本折叠为A
,依此类推。
When you're all done, it lookslike you created a perfect A
the first time (or maybe with a4
or some other one depending on which commits you keep and which ones you drop and whether you re-set any time stamps in things). Other people may not want or need to see your intermediate work—though you can retain it, notcombining commits, if that's useful. Meanwhile you never need to have uncommitted stuff that you have to rebase, because you just have commits of partial stuff.
当你全部完成后,看起来你A
第一次创建了一个完美的(或者可能和a4
其他一些提交,取决于你保留哪些提交,你放弃哪些提交,以及你是否在事物中重新设置任何时间戳)。其他人可能不想或不需要看到您的中间工作——尽管您可以保留它,而不是组合提交,如果这有用的话。同时,您永远不需要必须重新设置未提交的内容,因为您只提交了部分内容。
It does help to give these commits names, in the one-line commit text, that will guide your later rebase work:
在一行提交文本中给出这些提交名称确实有帮助,这将指导您以后的 rebase 工作:
git commit -m 'temp commit: work to enable frabulator, incomplete'
and so on.
等等。
回答by abhisekp
One Simple Answer:git rebase -i --autosquash --autostash <tree-ish>
一个简单的答案:git rebase -i --autosquash --autostash <tree-ish>
-i = interactively rebase
-i = interactively rebase
https://devdocs.io/git/git-rebase
https://devdocs.io/git/git-rebase
This will...
这会...
- Auto stash your changes
- Interactively rebase from
<tree-ish>
- Auto position your squashes and fixups
- Auto pop stash in working directory after rebase
- 自动隐藏您的更改
- 交互式地从
<tree-ish>
- 自动定位你的壁球和修复
- 变基后自动弹出工作目录中的存储
tree-ish
can be a commit hashor a branch nameor a tagor any identifier.
tree-ish
可以是提交哈希或分支名称或标签或任何标识符。
回答by Marc
The entire workflow in one command, including the fetching:
一个命令中的整个工作流程,包括获取:
git pull --rebase --autostash [...]
回答by giosh94mhz
You may use an external tools called git-up, which does exactly what you say for all branches. This will also help you keeping a clean history graph.
您可以使用名为git-up的外部工具,它完全符合您对所有分支所说的要求。这也将帮助您保持干净的历史图表。
I've used for some years and it works pretty well, especially if you are not a git expert. If you add auto-rebasing feature you should know how to properly recover from failing rebase (continue, abort, ...)
我已经使用了几年,而且效果很好,特别是如果您不是 git 专家。如果您添加自动变基功能,您应该知道如何从变基失败中正确恢复(继续,中止,...)
Install
安装
Open a shell and run:
打开一个shell并运行:
sudo gem install git-up
Configuration
配置
Open your global config file (~/.gitconfig
), and add the following:
打开您的全局配置文件 ( ~/.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 .."
See the official documentationfor more options.
有关更多选项,请参阅官方文档。
Invocation
调用
If everything is configured well, just run:
如果一切配置良好,只需运行:
git up
This is (roughly) equivalent of executing the following:
这(大致)相当于执行以下操作:
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
回答by Sisyphus
Answer from tjsingleton blogg
来自tjsingleton 博客的回答
make a command alias of :
创建一个命令别名:
git stash && git pull --rebase && git stash pop
git stash && git pull --rebase && git stash pop
update
更新
If you are using idea, pushing with a dirty working dir, it will prompt a dialog, choose rebase / merge, it will do stash, rebase / merge and pop automatically.
如果你使用的是idea,用一个脏的工作目录推送,它会提示一个对话框,选择rebase/merge,它会自动执行stash、rebase/merge和pop。