自动存储git rebase上的保存/弹出更改?

gcb*_*gcb 35 git git-stash

我的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
Run Code Online (Sandbox Code Playgroud)

所以这里我们有4个命令,1 =失败的rebase,2 = stash,3 = rebase,4 = stash pop.除了3之外的任何东西都是无意识的工作

所以,问题是:最推荐的自动化方法是什么?每次都运行git stash/rebase/pop的别名?一些git配置强制rebase隐藏或将其视为另一个提交后再重新应用?别的什么?

tor*_*rek 45

编辑:从Git版本1.8.4开始,但是在Git版本2.0.1中修复了一个重要的漏洞,git rebase现在已经有了--autostash.您也可以配置git rebase--autostash默认使用git config --global rebase.autoStash true.请注意文档中的以下句子:

但是,谨慎使用:成功重组后的最终存储应用程序可能会导致非平凡的冲突.

(我仍然喜欢提交.)

TL; DR回答:只做一个提交(然后取消它)

它可能会帮助你意识到这git stash是真的只是git commit(以更复杂的形式,首先提交索引,然后是工作树 - 当你应用存储时,你可以保持索引和工作树的分离,或者将它们组合起来只是一个工作树的变化).

是什么让一个藏匿特别的是,提交它使-两个或,与-u-a,甚至三个提交,在一个不寻常的形式作出(如合并提交,这不是一个真正的合并),而不是放在任何分支(相反,特殊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
Run Code Online (Sandbox Code Playgroud)

在这一点上,这是你拥有的:

... - o - * - A - B - C     <-- HEAD=master
           \          |\
            \         i-w   <-- stash
             \
              @-@-@         <-- upstream/master
Run Code Online (Sandbox Code Playgroud)

这里A,BC是你的提交(我假设你已经取得了3),所有分支master.该i-w挂承诺C是你的藏匿处,这是不是在树枝上,但仍是一个两犯"混帐藏匿袋",并实际连接到您的最新提交(C).在@提交(有可能是只有一个)是新的上游提交.

(如果你没有提交,你的存储包会挂起提交*,你当前的分支指向提交*,所以git rebase除了向前移动你当前的分支指针之外别无其他工作.在这种情况下,一切都一样,但我会假设有一些提交.)

现在你跑git rebase upstream/master.这会将您的提交复制到新的提交,使用新的ID和新的父ID,以便它们位于最后@.存储袋不会移动,因此结果如下所示:

... - o - * - A - B - C     [abandoned, except for the stash]
           \          |\
            \         i-w   <-- stash
             \
              @-@-@         <-- upstream/master
                   \
                    A'-B'-C'   <-- HEAD=master
Run Code Online (Sandbox Code Playgroud)

您现在使用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
Run Code Online (Sandbox Code Playgroud)

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

... - o-*-A-B-C-D   <-- HEAD=master
         \
          @-@-@     <-- upstream/master
Run Code Online (Sandbox Code Playgroud)

现在你git rebase upstream/master复制A通过D将它们放在最后一个结尾@,你有这个:

... - o-*-@-@-@     <-- upstream/master
               \
                A'-B'-C'-D'   <-- HEAD=master
Run Code Online (Sandbox Code Playgroud)

唯一的问题是你有这个不需要的额外提交D(好吧,D'现在),而不是未提交的工作树更改.但是,git reset如果退一步提交,这很简单.我们可以使用--mixed重置 - 默认 - 来重新设置索引(暂存区域),以便"取消添加"所有文件,或者如果您希望它们保持git add不变,则--soft重置.(不会影响生成的提交图,只有索引状态不同.)

git reset --mixed HEAD^   # or leave out `--mixed` since it's the default
Run Code Online (Sandbox Code Playgroud)

这是看起来像:

... - o-*-@-@-@     <-- upstream/master
               \
                A'-B'-C'      <-- HEAD=master
                        \
                         D'   [abandoned]
Run Code Online (Sandbox Code Playgroud)

您可能认为这是低效的,但是当您使用时,git stash您实际上至少进行了两次提交,然后您将在以后放弃这些提交git stash pop.真正的区别在于,通过进行临时的,非出版的提交,您可以自动重新生成.

不要害怕临时提交

git有一个通用的规则:做大量的临时提交,随时保存你的工作.你可以随时将它们改掉.也就是说,而不是这个:

... - * - A - B - C   <-- mybranch
Run Code Online (Sandbox Code Playgroud)

在那里A,B(并且C是完美的)最终提交*(从其他人或早期发布的东西),做到这一点:

... - * - a1 - a2 - b1 - a3 - b2 - a4 - b3 - c1 - b4 - c2 - c3
Run Code Online (Sandbox Code Playgroud)

a1最初的攻击在哪里A,a2修复了一个bug a1,b1是最初尝试做的b工作,a3是从认识到b1需要A完全不同,b2修复了一个bug b1,a4修复了一个bug a3改变了a2,b3b1应该有什么完成; 然后c1是最初的尝试C,b4是另一个修复b1,c2是一个改进,等等.

让我们说c3你认为它已经准备就绪了.现在你运行git rebase -i origin/master或什么的,洗牌pick线获得a1通过a4进入订单,b1通过b4为订单,c1通过c3成秩序,让底垫运行.然后你修复任何冲突,确保东西仍然是正确的,然后你运行另一个冲突,将git rebase -i所有四个a版本折叠成A,依此类推.

当你完成所有工作后,看起来A第一次创造了一个完美的东西(或者可能a4还有其他一个,这取决于你保留哪些提交以及丢弃哪些提交以及你是否重新设置任何时间戳).其他人可能不希望或不需要看到您的中间工作 - 尽管您可以保留它,而不是组合提交,如果这有用的话.与此同时,您永远不需要拥有必须重新定义的未提交的东西,因为您只是提交了部分内容.

它有助于在单行提交文本中提供这些提交名称,以指导您以后的rebase工作:

git commit -m 'temp commit: work to enable frabulator, incomplete'
Run Code Online (Sandbox Code Playgroud)

等等.

  • 推送一个提交的方法是将它放在自己的分支上(使用`git cherry-pick`复制它).假设你有临时提交的所有东西,`git checkout -b pushme upstream/master`来创建一个命名分支`pushme`,然后`git cherry-pick <commit-identifier>`来复制它,然后`git push upstream pushme:例如,master`将一个提交从本地分支`pushme`推送到上游`master`.一旦推送完成,您可以将临时链重新绑定到新更新的`upstream/master`(并省略您复制的那个,尽管如果可以的话,rebase会自动执行此操作). (2认同)

abh*_*ekp 16

一个简单的答案: git rebase -i --autosquash --autostash <tree-ish>

-i = interactively rebase

https://devdocs.io/git/git-rebase


这将...

  • 自动存储您的更改
  • 互动地改变 <tree-ish>
    • 自动定位你的南瓜和固定装置
  • 在rebase之后自动弹出存储在工作目录中

tree-ish可以是提交哈希,分支名称,标签任何标识符.


Mar*_*arc 5

整个工作流程只需一个命令,包括获取:

git pull --rebase --autostash [...]
Run Code Online (Sandbox Code Playgroud)