仅在git中存储分阶段的更改-可能吗?


364

有什么办法可以隐瞒我分阶段进行的更改?我遇到的情况是我在给定的时间处理了多个错误,并进行了一些未分级的更改。我希望能够分别暂存这些文件,创建我的.patch文件,并将其存放起来,直到代码获得批准为止。这样,当批准时,我可以隐藏整个(当前)会话,弹出该错误并推送代码。

我会以错误的方式处理吗?我是否误解了git如何以其他方式简化我的流程?


是的,您可能做错了这种情况。仍然是一个有用的问题。在开始下一个修复程序之前,您确实应该存储或分支。相切的答案stackoverflow.com/a/50692885可能是在git中处理此问题的更好方法。如果我从上游撤回提交,则经常在隐藏区中玩耍会给我的工作区域带来奇怪的东西。
塞缪尔·奥斯隆(SamuelÅslund),

Answers:


469

是的,DOUBLE STASH有可能

  1. 暂存您需要存放的所有文件。
  2. 运行git stash --keep-index。此命令将创建包含所有更改(暂存和未暂存)的存储,但是会将暂存的更改保留在工作目录中(仍处于暂存状态)。
  3. git stash push -m "good stash"
  4. 现在你"good stash"已经ONLY上演文件

现在,如果您需要在存储之前未暂存的文件,只需应用第一个存储(用创建的--keep-index存储),现在就可以删除存储到的文件"good stash"

请享用


即使未分阶段,它也会存储子模块中的更改。有没有解决的办法?
rluks

1
这样就把所有新文件(甚至上演了)都排除在外了。
Aurimas

8
@Aurimas,要存储新文件,您需要使用-u开关。
Gyromite

2
当您重新应用第一个存储并取回所有更改时,而您可能只对未使用的更改使用git stash apply --index选项感兴趣。这将尝试保持您的(暂存)状态。现在可以更轻松地从工作树中删除不需要的更改。
otomo

虽然我不需要完全按照答案回答,但了解--keep-index标志非常有帮助。
亚伦·克劳斯

128

在最新的git中,您可以使用--patchoption

git stash push --patch  

git stash save --patch   # for older git versions

git会要求您对文件中的每个更改进行添加或不添加到存储中。
您只要回答yn

UPD
别名的双重存储

git config --global alias.stash-staged '!bash -c "git stash --keep-index; git stash push -m "staged" --keep-index; git stash pop stash@{1}"'

现在您可以暂存文件,然后运行git stash-staged
结果,您暂存的文件将被保存到stash中

如果您不希望保留暂存的文件并将其移到存储中。然后,您可以添加另一个别名并运行git move-staged

git config --global alias.move-staged '!bash -c "git stash-staged;git commit -m "temp"; git stash; git reset --hard HEAD^; git stash pop"'

17
从技术上讲不能回答问题-而是一种非常好的技术,可以实现选择性隐藏。
alexreardon

6
同意,这没关系,但是这里的问题是我已经完成了所有我要进行的更改工作(表面上是最初要提交,但现在想隐藏),而不是仅仅考虑再做一遍。
史蒂文·卢

4
不适用于新创建的文件(仅适用于修改后的文件)
Derek Liang

@DerekLiang:根本不跟踪新创建的文件。您可能应该检查-u|--include-untrackedgit-stash
Eugen Konkov '18

2
文档开始:“ 保存:不建议使用此选项,而推荐使用git stash push。它与'stash push'不同,因为它不能采用pathspecs,并且任何非选项参数都构成消息。”
Borjovsky

53

我编写了一个脚本,该脚本仅存储当前正在上演的内容,并保留其他所有内容。当我开始进行太多无关的更改时,这真棒。只需暂存与所需提交无关的内容,然后隐藏即可。

(感谢Bartłomiej为起点)

#!/bin/bash

#Stash everything temporarily.  Keep staged files, discard everything else after stashing.
git stash --keep-index

#Stash everything that remains (only the staged files should remain)  This is the stash we want to keep, so give it a name.
git stash save "$1"

#Apply the original stash to get us back to where we started.
git stash apply stash@{1}

#Create a temporary patch to reverse the originally staged changes and apply it
git stash show -p | git apply -R

#Delete the temporary stash
git stash drop stash@{1}


3
这很棒!我已经对其进行了调整,以提示用户是否在命令行中未输入存储说明:gist.github.com/brookinc/e2589a8c5ca33f804e4868f6bfc18282
brookinc

1
这在git 2.23.0中根本不起作用。
thnee

谢谢,我在这里投票并将其变成别名:stackoverflow.com/a/60875067/430128
拉曼

33

TL; DR只需-- $(git diff --staged --name-only)为您的git <pathspec>参数添加

这是一个简单的单线:

git stash -- $(git diff --staged --name-only)

并简单地添加一条消息:

git stash push -m "My work in progress" -- $(git diff --staged --name-only)

v2.17.1v2.21.0.windows.1上测试

局限性:

  • 请注意,如果您没有暂存文件,则这将隐藏每件事。
  • 同样,如果您的文件只是部分暂存(即,仅某些更改的行已暂存,而其他某些更改的行未暂存),则整个文件将被保存(包括未暂存的行)。

6
我认为这是所描述情况的最佳选择:易于理解,不涉及任何黑魔法!
路易斯

1
这很整齐。我最终用它创建了一个别名!
Kalpesh Panchal

陪他们投票科曼😉
索莫S.

您可以分享@KalpeshPanchal吗?我不确定如何转义它,所以它不能正确解释它。
Igor Nadj


15

为了完成同样的事情...

  1. 仅暂存您要处理的文件。
  2. git commit -m 'temp'
  3. git add .
  4. git stash
  5. git reset HEAD~1

繁荣。不需要的文件被保存起来。所需的文件已准备就绪。


3
这很容易是最好的答案,也是最容易记住的
Kevin

9

在这种情况下,我更愿意为每个问题创建新的分支。我使用前缀temp /,所以我知道以后可以删除这些分支。

git checkout -b temp/bug1

暂存修复bug1的文件并提交。

git checkout -b temp/bug2

然后,您可以根据需要从各个分支中挑选提交并提交拉取请求。


2
虽然花哨的隐藏声音很容易了解,但实际上,这似乎是我不太可能采用的方法。
ryanjdillon '18 -10-19

1
使用“ git cherry-pick tmpCommit”通过合并提交返回临时提交,或者使用“ git merge tmpCommit” +“ git reset HEAD ^”来获得没有提交的更改。
塞缪尔·奥斯隆(SamuelÅslund),

1
正如该答案所示,有时最好直接问您想要实现什么,而不是用给定的技术如何实现。在复杂的情况下,临时分支和摘樱桃非常方便。
Guney Ozsan '19

如果你上演了文件部分,则需要重新搬回原来的分支和弹出之前藏匿更改
扬GLX

6

您为什么不提交针对某个错误的更改,并根据该提交及其前身创建补丁?

# hackhackhack, fix two unrelated bugs
git add -p                   # add hunks of first bug
git commit -m 'fix bug #123' # create commit #1
git add -p                   # add hunks of second bug
git commit -m 'fix bug #321' # create commit #2

然后,要创建适当的补丁,请使用git format-patch

git format-patch HEAD^^

这将创建两个文件:0001-fix-bug-123.patch0002-fix-bug-321.patch

或者,您可以为每个错误创建单独的分支,以便可以单独合并或调整错误修复的基础,如果无法解决,则可以删除它们。


2

git stash --keep-index 是一个很好的解决方案...除了在已删除的路径上无法正常工作外,该路径已在Git 2.23(2019年第三季度)中修复

参见Thomas Gummerer()的commit b932f6a(2019年7月16日(由Junio C Hamano合并--commit f8aee85中,2019年7月25日)tgummerer
gitster

stash:修复处理带有 --keep-index

git stash push --keep-index 应该保留所有已添加到索引的更改,无论是在索引中还是在磁盘上。

当前,当从索引中删除文件时,这不能正常工作。
**-keep-index当前会还原文件,而不是将其保留在磁盘上。**

通过git checkout在无覆盖模式下使用' ' 来修复该问题,该模式可以忠实地还原索引和工作树。
这也简化了代码。

请注意,如果未跟踪的文件与在索引中已删除的文件具有相同的名称,则这将覆盖未跟踪的文件。


2

仅在Git中存储索引(分阶段的更改)比应有的困难。我发现@Joe的答案可以正常工作,并将它的一个很小的变化变成了这个别名:

stash-index = "!f() { \
  git stash push --quiet --keep-index -m \"temp for stash-index\" && \
  git stash push \"$@\" && \
  git stash pop --quiet stash@{1} && \
  git stash show -p | git apply -R; }; f"

它会将已分阶段和未分阶段的更改推入临时存储区,仅保留已分阶段的更改。然后,它将分段的更改推入存储区,这是我们要保留的存储区。传递给别名的参数,例如--message "whatever"将添加到此存储命令。最后,它弹出临时存储库以恢复原始状态并删除临时存储库,然后最后通过反向修补程序从工作目录中“删除”隐藏的更改。

对于仅存储未分段的更改(别名stash-working)的相反问题,请参见此答案


1

绝对需要一次处理多个错误吗?“一次”是指“同时编辑多个错误的文件”。因为除非您绝对需要,否则我一次只能在您的环境中处理一个错误。这样,您可以使用本地分支和重新设置基准,这比管理复杂的存储/阶段要容易得多。

假设master在提交B处。现在处理错误#1。

git checkout -b bug1

现在,您正在使用分支bug1。进行一些更改,提交,等待代码审查。这是本地的,因此您不会影响其他任何人,并且应该很容易从git diffs制作补丁。

A-B < master
   \
    C < bug1

现在,您正在研究bug2。去到主用git checkout master。新建一个分支git checkout -b bug2。进行更改,提交,等待代码审查。

    D < bug2
   /
A-B < master
   \
    C < bug1

假设您在等待审查时,其他人在master上提交了E&F。

    D < bug2
   /
A-B-E-F < master
   \
    C < bug1

批准代码后,可以通过以下步骤将其重新建立为掌握代码的基础:

git checkout bug1
git rebase master
git checkout master
git merge bug1

这将导致以下结果:

    D < bug2
   /
A-B-E-F-C' < master, bug1

然后,您可以推送,删除本地bug1分支,然后退出。您的工作空间中一次只能出现一个错误,但是通过使用本地分支,您的存储库可以处理多个错误。并且这避免了复杂的舞台/隐藏舞蹈。

在评论中回答ctote的问题:

好了,您可以针对每个错误返回存储,并且一次只能处理一个错误。Atleast为您节省了分期问题。但是尝试过此之后,我个人觉得很麻烦。git日志图中的散乱有点混乱。更重要的是,如果您搞砸了,您将无法还原。如果您的工作目录很脏并且弹出存储,则无法“撤消”该弹出窗口。搞砸已经存在的提交要困难得多。

这样git rebase -i

将一个分支重新建立到另一个分支时,可以进行交互操作(-i标志)。执行此操作时,您可以选择选择每次提交要执行的操作。Pro Git是一本很棒的书,它也可以HTML格式在线发布,并且有一个很好的章节介绍了重新夯实和压榨:

http://git-scm.com/book/ch6-4.html

为了方便起见,我将逐字窃听他们的示例。假设您具有以下提交历史记录,并且想要将bug1重新定位并压入master:

    F < bug2
   /
A-B-G-H < master
   \
    C-D-E < bug1

这是您输入时会看到的内容 git rebase -i master bug1

pick f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

要将分支的所有提交压缩为单个提交,请将第一个提交保留为“ pick”,然后将所有后续“ pick”条目替换为“ squash”或简单的“ s”。您还将有机会更改提交消息。

pick f7f3f6d changed my name a bit
s 310154e updated README formatting and added blame
s a5f4a0d added cat-file
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit

是的,挤压有点痛苦,但是我还是建议您不要大量使用藏匿物。


1
感谢您的详细帖子!这可以肯定地解决了我的很多问题-我看到的唯一问题是我们当前的团队已要求我们将所有交付保持在一次提交中。:(
MrDuk

1
如果他们不需要或想要生产回购中的工作历史记录,那就很好:通过应用差异而不是合并分支,使跟踪主记录的历史记录减少。您可以找出如何保留具有实际合并历史记录的装饰母版分支,并从中进行实际工作,这样可以轻松地自动生成正确的差异。
jthill

2
请注意,git checkout master; git checkout -b bug2可以缩短为git checkout -b bug2 master。同样适用于git checkout bug1; git rebase master; git checkout master; git merge bug1,它等同于git rebase master bug1; git push . bug1:master(授予,push技巧并不明显)
knittl

1
我给了一个演练在主要答复上面积攒,所以我可以用别致的格式
麦克Monkiewicz

6
我投票不足,因为这不能回答原始问题。我在某个分支上从事某项工作,我刚刚进行了一项更改,我认为应该单独将其提交给集成分支。我要做的就是阶段更改并存储它,以便我可以切换到另一个分支并单独提交,而不是我当前的“进行中的”分支。(警告,git混为一谈。)如此困难,这是荒谬的。我必须想象这是普遍现象。(在一个分支中工作并发现需要进行的快速更改,而忘记先进行切换。)
jpmc26 2015年

0

在对Mike Monkiewicz回答的评论中,我建议使用一个更简单的模型:使用常规开发分支,但使用merge的squash选项在您的master分支中获得单个提交:

git checkout -b bug1    # create the development branch
* hack hack hack *      # do some work
git commit
* hack hack hack *
git commit
* hack hack hack *
git commit
* hack hack hack *
git commit
git checkout master     # go back to the master branch
git merge --squash bug1 # merge the work back
git commit              # commit the merge (don't forget
                        #    to change the default commit message)
git branch -D bug1      # remove the development branch

此过程的优点是您可以使用正常的git工作流程。


我看不出这个答案有什么帮助。它与原始问题无关。
frapen

0

TL; DR ;git stash-staged

创建别名后:

git config --global alias.stash-staged '!bash -c "git stash -- \$(git diff --staged --name-only)"'

这里git diff返回--staged文件列表,--name-only
然后我们将此列表传递pathspecgit stash逗号。

来自man git stash

git stash [--] [<pathspec>...]

<pathspec>...
   The new stash entry records the modified states only for the files
   that match the pathspec. The index entries and working tree
   files are then rolled back to the state in HEAD only for these
   files, too, leaving files that do not match the pathspec intact.


-1

要修剪意外更改,尤其是删除多个文件,请执行以下操作:

git add <stuff to keep> && git stash --keep-index && git stash drop

换句话说,将废料藏起来并与废料一起扔掉。

在git版本2.17.1中测试


不加评论的投反对票对我或下一个读者都无济于事... zaenks脾气暴躁的匿名。尽管我可以想象这种单行代码存在一个问题:应该非常小心,不要忘记将所有需要的更改添加到索引中,否则那些重要的更改也将被删除。但是话又说回来,在最坏的情况下,不小心使用任何cli工具可能对他的宝贵时间和工作非常危险。
wmax
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.