$ git stash list
stash@{0}: On master: Pep8 format
stash@{1}: On master: co other than master with local changes
stash@{2}: On master: tests with deployAtEnd
# Let's say I want to rename the stash@{2} adding an issue reference:
$ git stash-rename stash@{2} NXP-13971-deployAtEnd
$ git stash list
stash@{0}: On master: NXP-13971-deployAtEnd
stash@{1}: On master: Pep8 format
stash@{2}: On master: co other than master with local changes
repo[master] % touch tmp && git add tmp && git stash save first
Saved working directory and index state On master: first
HEAD is now at bd62064 Initial commit
repo[master] % touch tmp && git add tmp && git stash save second
Saved working directory and index state On master: second
HEAD is now at bd62064 Initial commit
repo[master] % git stash list
stash@{0}: On master: second
stash@{1}: On master: first
repo[master] % git stash-rename renamed
stash@{0}: On master: renamed
stash@{1}: On master: first
repo[master] % git stash-rename also-renamed stash@{1}
stash@{0}: On master: also-renamed
stash@{1}: On master: renamed
repo[master] % git stash-rename branch-changed stash@{0} new-branch
stash@{0}: On new-branch: branch-changed
stash@{1}: On master: renamed
repo[master] % git stash-rename branch-name-persists
stash@{0}: On new-branch: branch-name-persists
stash@{1}: On master: renamed
repo[master] % git stash-rename no-branch stash@{0} .
stash@{0}: no-branch
stash@{1}: On master: renamed
repo[master] % git stash-rename renamed
stash@{0}: renamed
stash@{1}: On master: renamed
repo[master] % git stash-rename readd-branch stash@{0} develop
stash@{0}: On develop: readd-branch
stash@{1}: On master: renamed
该命令的大部分用于解析参数并确定应该对分支名称做什么。使用的git工具如下:
git rev-parse <stash>来找到隐藏的SHA。
git stash list --format=%gs -1 <stash>来找到藏匿物的reflog主题。注意,这与存储的提交消息不同,这个命令不会改变提交消息。reflog主题是在git stash list中出现的,你可以在不改变与存储相关的提交的哈希值的情况下更改reflog主题。然而,你总能找到原始的提交消息,所以不要使用git stash-rename删除敏感信息!
git stash drop <stash>来删除旧的参考到stash(但我们仍然有SHA,所以它没有丢失)。
这会临时切换分支。因此,此recipe只能在git status --porcelain为clean时应用(读取:不输出任何内容)
它重新编号存储,因此更改后的存储变成stash@{0}
你需要输入$MESSAGE两次或使用一些环境变量(在本例中:MESSAGE)
您需要找到一个未使用的分支名称
有一些方法可以在不切换分支的情况下做到这一点,但这超出了本文的范围。
例子
git init scratch
cd scratch
for a in A B C D; do date >$a; git add $a; git commit -m $a; done
for a in X Y; do echo $a > Z; git stash save --all; done
git log --oneline --graph --decorate --all; git stash list
输出
*-. e0e281b (refs/stash) WIP on master: 8bdcc32 D
|\ \
| | * 4d62f52 untracked files on master: 8bdcc32 D
| * 096f158 index on master: 8bdcc32 D
|/
* 8bdcc32 (HEAD, master) D
* c84c659 C
* 49bb2da B
* b1852c6 A
stash@{0}: WIP on master: 8bdcc32 D
stash@{1}: WIP on master: 8bdcc32 D
现在不改变commit(注意:下面的SHA在你这边会不一样):
git stash drop stash@{1}
git stash store -m ...changed... 2fbf9007dfdfb95ae269a19e13b8b9ca3e24181c
git log --oneline --graph --decorate --all; git stash list
输出
*-. 2fbf900 (refs/stash) WIP on master: 8bdcc32 D
|\ \
| | * 246dc5c untracked files on master: 8bdcc32 D
| * 80c5ea0 index on master: 8bdcc32 D
|/
* 8bdcc32 (HEAD, master) D
* c84c659 C
* 49bb2da B
* b1852c6 A
stash@{0}: ...changed...
stash@{1}: WIP on master: 8bdcc32 D
如你所见,stash@{0}仍然显示为git log中的2fbf900 (refs/stash) WIP on master: 8bdcc32 D。如果仔细观察,就会发现有几个提交更改了SHA。这与处理存储的方式有关(SHA包括父存储,并且存储将它们的存储作为父存储)。
修复:
git checkout -b scratch stash
git stash drop
git commit --amend -m ...changed...
git stash store -m ...changed... HEAD
git checkout master
git branch -D scratch
git log --oneline --graph --decorate --all; git stash list
输出
*-. 4d55186 (refs/stash) ...changed...
|\ \
| | * 246dc5c untracked files on master: 8bdcc32 D
| * 80c5ea0 index on master: 8bdcc32 D
|/
* 8bdcc32 (HEAD, master) D
* c84c659 C
* 49bb2da B
* b1852c6 A
stash@{0}: ...changed...
stash@{1}: WIP on master: 8bdcc32 D
$ # Stash any pending changes you have, if applicable.
$ git stash -um "temporary stash"
$ # Re-apply the stashed changes whose message you want to change.
$ git stash apply stash@{1}
$ # Now stash again with the message you want.
$ git stash push -um "good message"
$ # Now you can pop your temporary stash and drop your poorly named one.
$ git stash pop
$ git stash drop stash@{1}
$ # If we have any pending changes, stash them first.
$ git stash -um "temporary stash before rewording another stash commit"
Saved working directory and index state On master: temporary stash...
$ git checkout 24e1f0a
Note: switching to '24e1f0a'.
You are in 'detached HEAD' state.
...snip: protracted warning message
HEAD is now at 24e1f0a On master: bad message
$ git commit --amend -m 'On master: good message!'
[detached HEAD 714d9be] On master: good message!
Date: Wed Jun 22 09:40:27 2022 -0400
$ git switch -
Warning: you are leaving 2 commits behind, not connected to
any of your branches:
714d9be On master: good message!
7b91532 index on master: a839f24 foo
If you want to keep them by creating a new branch, this may be a good time
to do so with:
git branch <new-branch-name> 714d9be
Switched to branch 'master'
$ # If we made a temporary stash earlier, pop it now.
$ git stash pop
blah blah blah
我们可以使用git store将重新编写的提交放入存储列表中。
$ git stash store -m 'On master: good message!' 714d9be
$ git stash list
714d9be - stash@{0} (2 minutes ago) On master: good message!
24e1f0a - stash@{1} (10 minutes ago) On master: bad message
让我们放心,我们不会失去任何东西:
$ git merge-tree @ stash@{0} stash@{1}
$ # No output is good. But if we're still not certain...
$ diff <(git show stash@{0}) <(git show stash@{1})
1c1
< commit 714d9be27dee44e96ddd3c5a4a43b35cff34a4cc
---
> commit 24e1f0aa39ede6e3065dd5c7bbb337af5b2a5d91
6c6
< On master: good message!
---
> On master: bad message
看起来唯一的区别是提交哈希(预期)和提交消息(这是我们想要的)。
所以现在,我们可以放心地放下旧的东西了。
$ git stash drop stash@{1}
Dropped stash@{1} (24e1f0aa39ede6e3065dd5c7bbb337af5b2a5d91)
$ git stash list
714d9be - stash@{0} (5 minutes ago) On master: good message!
_commit=$(git stash drop ${stash:-0} | sed -Ee 's/Dropped.+\((.+)\)$/\1/') &&
git stash store -m "$msg" $(
git commit-tree $(
git show -s --pretty=%T\ %P $_commit |
sed -Ee 's/ / -p /g'
) -m "$msg"
)
注意,这取决于shell字拆分内部命令替换的输出。
下面是一个可读性更好的脚本。
#!/bin/bash
message=$1
stash=${2:-0}
commit=$(
git stash drop $stash 2>/dev/null |
sed -nEe 's/Dropped.+\((.+)\)$/\1/p'
)
if [[ -z $message || -z $commit ]]; then
echo >&2 "usage: $(basename "$0") MESSAGE [STASH]"
exit 1
fi
args=(
# Suppress diff output.
--no-patch
# Get the tree hash and the hashes of all the parents.
--pretty=%T\ %P
)
refs=$(git show "${args[@]}" $commit)
# Format the refs as arguments to `git commit-tree` by inserting "-p" in front of
# each of the parent hashes and then splitting on spaces to separate arguments.
args=($(sed -Ee 's/ / -p /g' <<< "$refs"))
# Create a new commit with the same tree and parents as the old stash, but with
# the new message.
new_commit=$(git commit-tree "${args[@]}" -m "$message")
# Store the new commit in the stash.
git stash store -m "$message" $new_commit