仅存储git中的阶段性变化 - 是否可能?

时间:2013-02-07 19:50:04

标签: git git-stash

有没有办法可以隐藏我的分阶段变化?我遇到问题的情况是,我在给定时间处理了几个错误,并且有几个未分阶段的更改。我希望能够单独存档这些文件,创建我的.patch文件,并将它们存放起来,直到代码被批准为止。这样,当它被批准时,我可以存储我的整个(当前)会话,弹出该错误并推送代码。

我是以错误的方式来做这件事的吗?我是否误解了git如何以其他方式工作以简化我的流程?

14 个答案:

答案 0 :(得分:348)

是的,无论是否相信,它都是可能的, DOUBLE STASH

  1. 暂存您藏匿所需的所有文件。
  2. 运行git stash --keep-index。此命令将使用 ALL 更改(暂存和未暂存)创建存储,但会在工作目录中保留暂存的更改(仍处于暂存状态)。
  3. 运行git stash save "good stash"
  4. 现在,您的"good stash"仅限暂存文件
  5. 现在,如果您在存储之前需要非暂存文件,只需应用第一个存储(使用--keep-index 创建的存储文件),现在您可以删除存储到"good stash"的文件。< / p>

    享受

答案 1 :(得分:71)

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

这是一个简单的单线:

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

要简单添加一条消息:

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

v2.17.1 v2.21.0.windows.1

上进行了测试

限制:

  • 请注意,如果没有暂存的文件,这将隐藏每件事。
  • 此外,如果您的文件仅被部分暂存(即,仅更改了某些行,则暂存了文件,而其他更改的行未暂存),那么 整个文件将被藏起来(包括未暂存的行)。

答案 2 :(得分:65)

使用最新的git,您可以使用--patch选项

git stash push --patch

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

答案 3 :(得分:41)

我创建了一个脚本,只保存当前暂存的内容,并保留其他所有内容。当我开始做太多无关的更改时,这很棒。只需暂存与所需提交无关的内容,并将其存储起来。

(感谢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}

答案 4 :(得分:7)

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

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)的相反问题,请参见this answer

答案 5 :(得分:6)

为什么不为某个bug提交更改并从该提交及其前身创建补丁?

# 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

或者你可以为每个bug创建单独的分支,这样你就可以单独合并或修改bug修复,甚至删除它们,如果它们不能解决的话。

答案 6 :(得分:4)

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

git checkout -b temp/bug1

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

git checkout -b temp/bug2

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

答案 7 :(得分:1)

是否绝对有必要同时处理几个错误?并立即通过&#34;&#34;我的意思是&#34;同时为多个错误编辑文件。&#34;因为除非你绝对需要,否则我只会在您的环境中一次处理一个错误。这样你就可以使用当地的分支机构和rebase,我觉得比管理一个复杂的藏匿/阶段容易得多。

让我们说主人在提交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

让我们假装其他人犯E&amp;当你正在等待审查时,你就掌握了。

    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的问题:

好吧,你可以回到每个bug的存储,并且一次只能处理一个bug。 Atleast可以为您节省分期问题。但经过尝试,我个人觉得很麻烦。在git日志图中,stashes有点乱。更重要的是,如果你搞砸了什么,你就无法恢复。如果你有一个脏的工作目录并且你弹出一个藏匿处,你就不能撤消&#34;那个流行音乐搞砸现有的提交要困难得多。

所以git rebase -i

将一个分支重新绑定到另一个分支时,可以交互方式(-i标志)。执行此操作时,您可以选择要对每个提交执行的操作。 Pro Git是一本很棒的书,也是HTML格式的在线书,有一个很好的部分关于变基和&amp;挤压:

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

为方便起见,我会逐字窃取他们的例子。假装您有以下提交历史记录,并且您想要改变&amp;将bug1压制到主人身上:

    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.
#

要将分支的所有提交压缩为单个提交,请将第一个提交保留为&#34;选择&#34;并替换所有后续&#34;选择&#34;用&#34;壁球&#34;或者只是&#34; s&#34;。您也将有机会更改提交消息。

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

所以是的,挤压是一种痛苦,但我仍然会建议它大量使用藏匿处。

答案 8 :(得分:1)

要完成同一件事...

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

景气。不需要的文件被保存起来。您想要的文件已准备就绪。

答案 9 :(得分:0)

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

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

  

stash:修复了使用--keep-index

处理已删除文件的问题      

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

     

当前,当从索引中删除文件时,此操作无法正确执行。
  代替保留在磁盘上,**-keep-index 当前可恢复文件。**

     

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

     

请注意,如果未跟踪的文件与已在索引中删除的文件同名,则会覆盖未跟踪的文件。

答案 10 :(得分:0)

我还没有看到不需要使用git stash的解决方案:

您甚至根本不需要使用git stash。您可以使用专用的分支机构as covered here(分支机构很便宜)来解决这个问题。

实际上,您可以使用一些连续的命令分别隔离未更改的和分段的更改,这些命令可以捆绑在一起成为git别名:

创建并切换到新分支,在该分支中,您将分别提交暂存和未暂存的更改:see here

您随时可以git cherry-pick -e从创建的分支中提交一个提交,以将其应用于所需的位置(-e更改其提交消息)。

当您不再需要它时,可以删除此“存储分支”。您可能必须使用-D选项来强制删除(而不是-d常规选项),因为所述分支未合并,并且git可能会认为如果删除它,可能会丢失数据。如果您在删除之前还没有精心挑选的提交,那就是正确的:

git branch -D separated-stashes

您也可以将别名添加到~/.gitconfig中,以自动执行此操作:

git config --global alias.bratisla '!git switch -c separated-stashes; git commit -m "staged changes"; git add -u; git commit -m "unstaged changes"; git switch -' # why this name ? : youtu.be/LpE1bJp8-4w

before "stashing"
after "stashing"



当然,您也可以使用two consecutive stashes

获得相同的结果

如其他答案所述,您可以使用某些方法来使用git stash (-k|--keep-index)结合其他命令来仅存储未暂存的更改或仅暂存的更改。

我个人认为-k选项非常令人困惑,因为它隐藏了所有内容,但是却使分阶段的更改保持处于分阶段状态(这说明了为什么“ --keep-index”)。存放东西通常会将其移动到存放条目。使用-k,未暂存的更改通常会被隐藏,但是已暂存的更改只会被复制到相同的存储条目中。


第0步:您的git状态中有两件事:一个文件包含分段更改,另一个文件包含未分段更改。

第1步:存储未暂存的+已暂存的更改,但将已暂存的更改保留在索引中:

git stash -k -m "all changes"

-m "..."部分是可选的,git stash -k实际上是git stash push -k的别名(不用担心,它不会远程推送任何内容),它接受-m选项为您的隐藏条目添加标签以使其更加清晰(例如提交消息或标签,但包含隐藏条目)。它是已弃用的git stash save的较新版本。


步骤1之二(可选):

git stash

暂存暂存的更改(仍在索引中)。 以下步骤对于以下步骤不是必需的,但表明如果需要,您只能将分段的更改放入存储条目中。 如果使用此行,则必须git stash (pop|apply) && git add -u才能继续执行步骤2。


第2步:

git commit -m "staged changes"

进行一个仅包含步骤0中的阶段性更改的提交,它包含与步骤1之二中的存储项相同的内容。


第3步:

git stash (pop|apply)

还原步骤1中的存储。 请注意,此存储条目包含所有内容,但是由于您已经提交了暂存的更改,因此此暂存将仅添加从步骤0开始的未暂存的更改。

nb:“恢复”在这里并不表示“ git restore”,这是一个不同的命令。


第4步:

git add -u

将弹出的隐藏内容添加到索引中


第5步:

git commit -m "unstaged changes"

此处的“未暂存”,如步骤2和3的注释中的“暂存”,是指步骤0。实际上,您正在暂存并提交从步骤0开始的“暂存更改”。


完成! 现在,您有两个单独的提交,包含从步骤0开始的(未)暂存的更改。 您可能需要对它们进行修改/变基以进行其他更改,或者对它们进行重命名/删除/压缩。 根据您对隐藏堆栈(popapply)所做的操作,您可能还想git stash (drop|clear)。您可以看到您用git stash (list|show)

隐藏条目

答案 11 :(得分:0)

另一种方法是使用不想隐藏的文件创建一个临时提交,然后隐藏剩余文件并轻轻删除最后一个提交,保持文件完整:

git add *files that you don't want to be stashed*
git commit -m "temp"
git stash --include-untracked
git reset --soft HEAD~1

这样,您只需触摸要触摸的文件即可。

请注意,此处使用“ --include-untracked”还可以存储新文件(这可能是您真正想要的)。

答案 12 :(得分:-1)

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

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

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

在git版本2.17.1中进行了测试

答案 13 :(得分:-1)

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的列表
然后我们将此列表作为pathspec传递给git 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.