我如何重命名一个git隐藏?

我有一堆名字不正确的东西。我想修改一下名字,让它更准确。

我如何重命名收藏?

49181 次浏览

我认为这是不可能的。已经有关于存储重命名的建议,但它还没有实现。

我的大意是:

  1. 实现一个新的git reflog update命令,更新与特定reflog条目相关的消息。为此,一个新的update_reflog_ent()函数(在reflog.c中)将更改与特定reflog条目相关的消息以更新。update_reflog()函数将使用for_each_reflog_ent()update_reflog_ent来实际进行更改。

  2. git stash rename命令只需要使用适当的ref和新消息调用git reflog update

当然,你也可以弹出隐藏并执行git stash save [message]

除非你手动操作或对Git做出改进,否则你可以使用别名:

git config --global alias.stash-rename '!_() { rev=$(git rev-parse $1) && git stash drop $1 || exit 1 ; git diff-index --quiet HEAD; s=$?; [ $s != 0 ] && git stash save "tmp stash from stash-rename"; git stash apply $rev && shift && git stash save "$@" && [ $s != 0 ] && git stash pop stash@{1}; }; _'

用法:“git stash-rename <stash> [save options] [<message>]"

[save options]中,git stash save: [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet] [-u|--include-untracked] [-a|--all]为任意选项

例子:

$ 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

这将工作,即使你有本地的非阶段性变化:)

编辑2016/02/22

简化脚本,演职员到qzbhttps://stackoverflow.com/a/35549615/515973

git config --global alias.stash-rename '!_() { rev=$(git rev-parse $1) && git stash drop $1 || exit 1 ; git stash store -m "$2" $rev; }; _'

用法:“git stash-rename <stash> [<message>]"

让我们假设你的收藏清单是这样的:

$ git stash list
stash@{0}: WIP on master: Add some very important feature
stash@{1}: WIP on master: Fix some silly bug

首先,你必须删除你想重命名的stash条目:

$ git stash drop stash@{1}
Dropped stash@{1} (af8fdeee49a03d1b4609f294635e7f0d622e03db)

现在只需在删除后使用sha的commit返回的新消息再次添加它:

$ git stash store -m "Very descriptive message" af8fdeee49a03d1b4609f294635e7f0d622e03db

就是这样:

$ git stash list
stash@{0}: Very descriptive message
stash@{1}: WIP on master: Add some very important feature

这个解决方案需要git 1.8.4或更高版本,是的,它也适用于脏工作目录。

下面是朱利安的别名的修改版本,它可以让你正确地处理通常用于存储名称的On <branch>前缀:

git config --global alias.stash-rename '!_() { newmsg="$1" && stash=${2:-"stash@{0}"} && newbranch="$3" && sha=$(git rev-parse "$stash") && olddesc="$(git stash list --format=%gs -1 "$stash")" && newdesc="$(if [[ "$newbranch" = "." ]]; then echo "$newmsg"; else if [[ -n "$newbranch" ]]; then echo "On $newbranch: $newmsg"; else if [[ "$olddesc" =~ ":" ]]; then echo "$(echo "$olddesc" | cut -f1 -d":"): $newmsg"; else echo "$newmsg"; fi; fi; fi)" && git stash drop "$stash" > /dev/null || exit 1; git stash store -m "$newdesc" "$sha" && git stash list; }; _'

语法:

git stash-rename <new-name> [<stash> [<new-branch-name> | .]]

使用示例:

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,所以它没有丢失)。
  • 将一个新的参考保存到具有相同提交信息但不同的reflog主题的存储中。
  • git stash list来列出操作结束后的存储。注意,新的存储总是被推到列表的开头。为了恢复它的原始位置,有必要在感兴趣的隐藏之前重新推所有的隐藏。

最简单的方法:弹出你的stash与git stash流行,然后保存它再次与git stash保存你的名字

为了方便读者,这里有一个扩展到目前接受的正确答案

如果您不仅想更正存储消息,还想更正存储的提交消息,例如

git stash list

而且

git log --oneline -1 stash

双方都同意所展示的内容,你需要更多一点。也许有更好的方法,但我希望这个食谱很容易理解。

为了能够做git commit --amend,你需要在一个分支的尖端。因此,解决方案是:

git checkout -b scratch stash@{1}
git stash drop stash@{1}
git commit --amend -m "$MESSAGE"
git stash store -m "$MESSAGE" HEAD
git checkout master
git branch -D scratch

解释道:

  • 创建一个新的(还不存在)“刮”分支从“藏匿在问题”,并切换到它
  • 把旧的藏起来。这是安全的,因为树枝上还有这个。
  • 使用git commit --amend替换提交消息,这将改变“问题中的stash”的SHA
  • 基于qzb的回答存储存储
  • 切换回去(假设你来自“master”)和清理

缺点:

  • 这会临时切换分支。因此,此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

正如你还可以看到的,refs/stash也改变了SHA。

这很简单。首先,撤销最后的藏匿与:

git stash pop

在这之后,你可以保存一个自定义的名称,以这种方式:

git stash save "your explanatory name"

希望对大家有用。:)

qzb的回答中描述的__abc0 -方法只在两个地方之一更新了存储消息,导致许多git前端仍然显示旧消息。但是,可以创建一个提交,复制原始存储提交中的所有内容,但更改其消息。

  1. 找到存储提交树和父节点的哈希值:

    $ git show -s --pretty=raw stash@{0}
    commit f2adfc7bbebe852693ad8f6ac889e4923230c872
    tree 8160d88c6e00e90fcfa183e09d2563f3cdfb304b
    parent a013bd8052d3260fbc95608ed69d0b9cfa0c77cb
    parent 5d5eb80dc03bea8ff2bdd38962a1259b7725d169
    author ...
    committer ...
    
    
    Test stash
    
  2. 创建一个新的提交,使用相同的树和父树,但是不同的消息:

    $ git commit-tree 8160d88c6e00e90fcfa183e09d2563f3cdfb304b \
    -p a013bd8052d3260fbc95608ed69d0b9cfa0c77cb \
    -p 5d5eb80dc03bea8ff2bdd38962a1259b7725d169 \
    -m "Renamed stash"
    f2adfc7bbebe852693ad8f6ac889e4923230c872
    
  3. 存储这个提交作为一个新的stash

    $ git stash store \
    -m "$(git show -s --format=%B f2adfc7bbebe852693ad8f6ac889e4923230c872)" \
    f2adfc7bbebe852693ad8f6ac889e4923230c872
    

新的stash消息需要同时提供给git-commit-treegit-stash-store,因为git将其存储在两个地方(commit和logs/refs/stash)。

注意,用--keep-index创建的存储将有3个父对象,所以在这种情况下,你需要为git-commit-tree提供第三个父对象!

将此过程转换为一行程序以在git别名中使用将留给读者作为练习;-)。确保只使用管道命令(因此避免例如git-showgit-log)。

多年后回答我自己的问题:这是刚刚宣布的,所以我想我应该在这里加上它。

许多GUI git客户端(例如,叉1.58和更新的)现在支持直接重命名存储。

enter image description here

这里有很多复杂的答案。我是这么说的:

首先让我们找到你的收藏索引:

git stash list

现在将其应用于git stash apply {N},例如

git stash apply 2

现在可以用一条新消息保存更改

git stash push -m 'My descriptive stash message'

如果你想清理原来的存储,请记住将索引增加1,因为新的存储会增加所有现有的索引(因此我们在这里使用N + 1)

git stash drop 3

这里有一些简单的方法,适用于简单的情况。例如git stash apply:

$ # 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}
在简单的情况下,这是一个很好的,简单的技巧。但如果你想要改写的东西是很久以前做的,或者在 不同的分支,它可能不干净地应用,这可能会使这项技术成为一个主要的痛苦

使用git stash store似乎也很简单。只要git stash store -m "$msg" $ref,你就可以走了。但事实似乎并非如此 使用现代版本的git。可能是因为这在.git/logs/refs/stash中创建了一个包含你的 新消息,但不会修改实际提交的$refgit stash list中存储的消息 显示存储在stash提交中的消息,而不是日志中的消息

举个例子。我用符号包装长行。

$ git init
$ touch foo
$ git add foo
$ git commit -m foo foo
[master (root-commit) a839f24] foo
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 foo


$ git stash push -m "bad message"
Saved working directory and index state On master: bad message


$ git stash list
24e1f0a - stash@{0} (4 seconds ago) On master: bad message


$ cat .git/logs/refs/stash
0000000000000000000000000000000000000000 24e1f0aa39ede6e3065dd5c7bbb337af5b2a5d91 ⏎
PDaddy <pdaddy@not-my-real-email-address.com> 1655905352 -0400 bad message


$ git stash drop
Dropped refs/stash@{0} (24e1f0aa39ede6e3065dd5c7bbb337af5b2a5d91)


$ git stash store -m "good message?" 24e1f0a
$ git stash list
24e1f0a - stash@{0} (16 seconds ago) On master: bad message

什么?它仍然传达着同样的坏信息。

$ cat .git/logs/refs/stash
0000000000000000000000000000000000000000 24e1f0aa39ede6e3065dd5c7bbb337af5b2a5d91 ⏎
PDaddy <pdaddy@not-my-real-email-address.com> 1655905366 -0400 good message?

不过,它有新的信息。那么为什么stash list仍然显示坏消息呢?

$ git show stash@{0}
Merge: a839f24 7b91532
Author: PDaddy <pdaddy@not-my-real-email-address.com>
Date:   Wed Jun 22 09:40:27 2022 -0400


On master: bad message


diff --cc foo
index e69de29,e69de29..d1d375f
--- a/foo
+++ b/foo
@@@ -1,0 -1,0 +1,1 @@@
++Wed Jun 22 09:40:21 AM EDT 2022
您可以看到坏消息仍然是实际提交的元数据的一部分,我们没有修改。这样做 需要用一个新的commit替换commit 24e1f0a。对于可以从当前分支到达的提交,可以这样做 如果它是最近的提交,则使用git commit --amend,否则使用交互式rebase。但 24e1f0a不仅不是我们所使用的分支中最新的提交,而且它根本不能从我们的 分支。它形成了一棵独立的树枝状树的头部。所以我们不想在它上面重基

但是我们可以检查出来,然后修改它。这很容易做到。

$ # 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!

任务完成!🚀


参见:

  • 天奴的答案,它使用类似的技巧,但创建了一个临时分支,而不是使用分离的头

  • 布莱希特Machiels的答案,其中使用管道命令使更改更直接。很难做到 请理解,但这是一种更快、更直接的解决方案,因为它不需要存储任何暂挂

    这个方法的一个缺点是,因为它创建了提交,它改变了日期,从而改变了stash的日期 在列表中的位置和它的reflog选择器(重命名的stash变成stash@{0})

下面是实现Brecht Machiels方法的神奇一行代码(但为了可读性,它被分解成多行代码):

_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