我想使用以下工作流程:
- 暂存一些更改。
- 将未暂存的更改保存到堆栈中。
- 对阶段中的内容进行某些操作(构建、测试等)。
- 提交。
- 恢复未暂存的更改。
有没有办法执行第2步?
示例:
git init
echo one >file
git add file
git commit
echo two >>file
git add file
echo three >>file
git stash push
test
git commit
git stash pop
与问题相关的另一个提示:
当您使用
$ git stash save --keep-index
有效地隐藏您的未暂存更改时,您可能希望给stash一个消息,以便在执行 git stash list
时更容易看出您之前隐藏了什么内容,特别是如果您在随后进行更多保存的操作。例如:
$ git stash save --keep-index "changes not yet staged"
(实际上它包含了其他答案中提到的所有更改)。
例如,上述操作可能会立即跟随以下操作:
$ git stash save "staged changes for feature X"
但要注意,您则无法使用
$ git stash apply "stash@{1}" ### ✘ 并不能完全恢复您想要的未暂存更改
仅恢复未暂存的更改。
git stash push --staged
"(man):
此选项仅适用于 push 和 save 命令。
仅藏匿当前已暂存的更改。
这类似于基本的git commit
,但状态被提交到储藏而不是当前分支。
git stash push [--] [<pathspec>...]
,自Git 2.16+起(git stash save
已被弃用)。git stash push --all --keep-index ':(glob)**/*.testextension'
git stash
已经被重新实现为 C 语言代码(而不是一个 shell 脚本)。tgummerer
)。dscho
)。gitster
--于提交0ba1ba4中合并,日期为2019年4月22日)
built-in
stash
: handle:(glob)
pathspecs againWhen passing a list of pathspecs to, say,
git add
, we need to be careful to use the original form, not the parsed form of the pathspecs.This makes a difference e.g. when calling
git stash -- ':(glob)**/*.txt'
where the original form includes the
:(glob)
prefix while the parsed form does not.However, in the built-in
git stash
, we passed the parsed (i.e. incorrect) form, andgit add
would fail with the error message:fatal: pathspec '**/*.txt' did not match any files
at the stage where
git stash
drops the changes from the worktree, even ifrefs/stash
has been actually updated successfully.
--keep-index
存储所有更改的文件。git diff --name-only
),将换行符转换为空格(| tr '\n' ' '
),并使用 git stash push
存储所有这些文件。git stash push $(git diff --name-only | tr '\n' ' ')
git stash [-k|--keep-index]
在应用时间执行
git cherry-pick -m2 -n stash
-m2
选择阶段提交的第二个父级所做的更改,即存储的索引状态。 -n | --no-commit
阻止自动提交。 stash@{1}
将是堆栈上第二个贮藏的引用...
我对Python程序pre-commit
的实现方式很感兴趣。以下是代码。
https://github.com/pre-commit/pre-commit/blob/3fe38dff05957f609cf7b97f471b35a8d9e0659a/pre_commit/staged_files_only.py#L50
它在功能上等同于以下代码:
git diff-index --ignore-submodules --binary --exit-code --no-color --no-ext-diff $(git write-tree) -- >stash.patch
git checkout -- .
# Do stuff now
git apply stash.patch && rm stash.patch
git stash push
目前不可能仅保存工作树中未暂存的更改,即保存来自索引状态的更改。该命令会保存工作树中的所有更改(已暂存和未暂存的更改),即来自HEAD状态的更改,即使使用选项--keep-index
也会将工作树状态设置为索引状态而不是HEAD状态(因此在使用git stash pop
从HEAD状态恢复更改时会创建冲突)。如果git stash push
有一个选项-U|--unstaged
仅保存未暂存的更改(对我来说,选项--keep-index
存在缺陷)将非常方便,因为它已经有一个选项-S|--staged
仅保存已暂存的更改。
因此,目前您必须模拟实现。
git stash push --unstaged
git stash pop
使用临时文件:
git diff >unstaged
git restore .
git apply unstaged
rm unstaged
你的使用情况是在提交部分更改之前进行测试,这已经在参考文档中提到了,但是有一个有缺陷的选项--keep-index
会导致冲突。这里是使用模拟选项-U|--unstaged
的版本:
git init
echo one >file
git add file
git commit
echo two >>file
git add file
echo three >>file
git diff >unstaged
git restore .
test
git commit
git apply unstaged
rm unstaged
为了更好地理解存储,我认为每一步都查看工作树、索引和HEAD的状态是很重要的。让我们以您的用例为例。
git init
工作区 | 索引 | HEAD |
---|
echo one >file
工作区 | 索引 | HEAD |
---|---|---|
one |
git add file
工作区 | 索引 | HEAD |
---|---|---|
one | one |
git commit
工作区 | 暂存区 | 版本库 |
---|---|---|
one | one | one |
echo two >>file
工作区 | 暂存区 | 版本库 |
---|---|---|
one | one | one |
two |
git add file
工作区 | 暂存区 | 版本库 |
---|---|---|
one | one | one |
two | two |
echo three >>file
工作区 | 暂存区 | HEAD |
---|---|---|
one | one | one |
two | two | |
three |
git diff >unstaged
git restore .
工作区 | 暂存区 | HEAD |
---|---|---|
one | one | one |
two | two |
测试
git commit
工作区 | 索引区 | HEAD |
---|---|---|
one | one | one |
two | two | two |
git apply unstaged
rm unstaged
工作区 | 索引区 | HEAD |
---|---|---|
one | one | one |
two | two | two |
three |
git push [-k|--keep-index]
时,没有必要限制stash数据。相反,您可以在应用时决定并使用git cherry-pick -m2 -n stash
仅选择未暂存的更改。请参见详细答案。 - kxr