在 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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-19 10:35:26  来源:igfitidea点击:

automatically stash save/pop changes on git rebase?

gitgit-stash

提问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 rebasenow has --autostash. You can configure git rebaseto use --autostashby 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 stashis 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 -uor -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/stashreference is used to retain and find them).

stash 的特别之处在于它所做的提交——两次或,与-u-a,甚至三个提交——以一种不寻常的形式(作为一个不是真正合并的合并提交)而不是放在任何分支上(相反,特殊refs/stash引用用于保留和查找它们)。

Since they're not on a branch, rebasedoes not touch them, and in your workflow, it's the git stash popthat 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 Care your commits (I'll assume you've made 3), all on branch master. The i-whanging off commit Cis 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,BC是您的提交(我假设您已经提交了 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 rebasehas 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 stashlabel (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 - Cchain, and means we don't need the i-wbit 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 addand git commitwithout -a) to create an actual commit D. You start with:

现在,让我们得出,如果发生了什么,而不是git stash save,你只是做一个git commit -a(或git addgit commit没有-A)创建一个实际的承诺D。你开始:

... - o-*-A-B-C-D   <-- HEAD=master
         \
          @-@-@     <-- upstream/master

Now you git rebase upstream/master, which copies Athrough Dto 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 resetto step back one commit. We can use a --mixedreset—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 --softreset. (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 stashyou're actually making at least twocommits, which you then abandon later when you git stash popthem. 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 Care 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 a1is an initial stab at A, a2fixes a bug in a1, b1is an initial attempt to make bwork, a3is from realizing that b1requires Ato be different after all, b2fixes a bug in b1, a4fixes a bug in a3's change to a2, and b3is what b1should have done; then c1is an initial attempt at C, b4is another fix to b1, c2is a refinement, and so on.

哪里a1是最初的 stab at Aa2修复了一个 bug a1b1是一个开始b工作的尝试,a3是从意识到b1需要A毕竟不同,b2修复了一个 bug b1a4修复了一个 bug ina3的变化a2b3是什么b1应该有的完毕; 然后c1是对 的初步尝试Cb4是对 的另一个修复b1c2是改进,等等。

Let's say that after c3you think it's mostly ready. Now you run git rebase -i origin/masteror whatever, shuffle the picklines to get a1through a4into order, b1through b4into order, and c1through c3into order, and let the rebase run. Then you fix any conflicts and make sure stuff is still right, then you run another git rebase -ito collapse all four aversions 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 Athe first time (or maybe with a4or 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-ishcan 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。