作業ツリーを変更せずに隠し場所を保存するgitコマンドを、gitのリセットやインデックスを台無しにするために行う可能性のあることから安全な軽量バックアップとして使用したいと思っていました。基本的に「gitstashsave && git stash apply」と機能的に同等ですが、作業コピーに触れない点が異なります。これにより、特定のテキストエディタ/ IDEが不機嫌になる可能性があるためです。
このようなものが私が望むものに近づいていますが、完全ではありません:
git update-ref refs/stash `git stash create "Stash message"`
これは機能的には機能しますが、実際のstashコミットにメッセージが含まれていても、「gitstashlist」にstashメッセージが表示されないという問題があります。スタッシュがどれだけ大きくなるかを考えると、スタッシュメッセージは非常に重要です。
Charlesのヒントのおかげで、bashスクリプトを作成して、自分がやりたいことを正確に実行しました(これをエイリアスとして実装する際に問題が発生していました)。 git stash saveと同じように、オプションのstashメッセージを受け取ります。何も指定されていない場合は、gitstashによって生成されたデフォルトのメッセージが使用されます。
#!/bin/sh
#
# git-stash-snap
# Save snapshot of working tree into the stash without modifying working tree.
# First argument (optional) is the stash message.
if [ -n "$1" ]; then
git update-ref -m "$1" refs/stash "$(git stash create \"$1\")"
else
HASH=`git stash create`
MESSAGE=`git log --no-walk --pretty="tformat:%-s" "$HASH"`
git update-ref -m "$MESSAGE" refs/stash "$HASH"
fi
編集:以下のコメントで指摘されているように、このスクリプトをパスのどこかにgit-stash-snap
として保存すると、git stash-snap
と入力してスクリプトを呼び出すことができます。
ここでの良い点は、このメソッドで作成されたスタッシュをドロップしても、ダングリングコミットのgit log [commit-hash]を使用してスタッシュメッセージを表示できることです。
編集:git 2.6.0以降、--create-reflog
をupdate-ref
に追加すると、git stash list
が以前に使用されていなくても、git stash
にこれが表示されます。
編集:Gitはstash Push
と呼ばれる新しいstashサブコマンドを導入したので、このスクリプトにgit-stash-Push
からgit-stash-snap
という名前を付けるための推奨事項を更新しました。
update-ref
はメッセージを受け取らないため、stash create
ではなくstash create
にメッセージを渡す必要があります(refを更新しないため、入力するreflogエントリがありません)。
git update-ref -m "Stash message" refs/stash "$(git stash create)"
git stash store "$(git stash create)"
作業ディレクトリとインデックスに実際に触れてクリアすることなく、git stash
で取得するものと同様のstashエントリを作成します。
Stashリストを確認するか、すべてのコミットグラフ(stashを含む)を見ると、通常のgit stash
の呼び出しで得られる結果と同様の結果であることがわかります。 stashリストのメッセージだけが異なります(通常は "stash @ {0}:WIP on master:14e009e init commit"、ここではll get "stash @ {0}:" git stash store ""を介して作成)
$ git status --short
M file.txt
A file2.txt
$ git stash list
$ git stash store "$(git stash create)"
$ git stash list
stash@{0}: Created via "git stash store".
$ git stash show 'stash@{0}'
file.txt | 2 +-
file2.txt | 2 ++
2 files changed, 3 insertions(+), 1 deletion(-)
$ git log --oneline --graph --all
* 85f937b (refs/stash) WIP on master: 14e009e init commit
|\
| * 26295a3 index on master: 14e009e init commit
|/
* 14e009e (HEAD -> master) init commit
$ git status
M file.txt
A file2.txt
もう少し説明:
Git stashエントリは、いくつかの定義された構造を持つ通常のコミットを使用して表されます。基本的には、2つの親(または--include-untracked
オプションを使用する場合は3つ)を持つ通常のコミットオブジェクトです(詳細 1 、 2 )。
git stash create
は、stashエントリを表すこのコミットを作成し、コミットオブジェクト(2つまたは3つの親を持つオブジェクト)の オブジェクト名 (SHA-1)を返します。 ダングリングコミット です(git fsck
の後にgit stash create
を呼び出すことで確認できます)。 refs/stash
がこのぶら下がっているコミットを指すようにする必要があり、git stash store
によって(または他の回答のようにgit update-ref
によって、git stash store
git update-ref
その作業を行うため)。
git stash Push
の実際のソースコードを見て、基本的に git stash create
およびgit stash store
を呼び出してから、 一部のロジック ファイルをクリーンアップします(どちらはgit stash Push
で使用したオプションによって異なります)。
エリオットのソリューションに触発されて、私は彼のスクリプトを少し拡張しました。
#!/bin/sh
#
# git-stash-Push
# Push working tree onto the stash without modifying working tree.
# First argument (optional) is the stash message.
#
# If the working dir is clean, no stash will be generated/saved.
#
# Options:
# -c "changes" mode, do not stash if there are no changes since the
# last stash.
if [ "$1" == "-c" ]; then
CHECK_CHANGES=1
shift
fi
if [ -n "$1" ]; then
MESSAGE=$1
HASH=$( git stash create "$MESSAGE" )
else
MESSAGE=`git log --no-walk --pretty="tformat:%-s" "HEAD"`
MESSAGE="Based on: $MESSAGE"
HASH=$( git stash create )
fi
if [ "$CHECK_CHANGES" ]; then
# "check for changes" mode: only stash if there are changes
# since the last stash
# check if nothing has changed since last stash
CHANGES=$( git diff stash@{0} )
if [ -z "$CHANGES" ] ; then
echo "Nothing changed since last stash."
exit 0
fi
fi
if [ -n "$HASH" ]; then
git update-ref -m "$MESSAGE" refs/stash "$HASH"
echo "Working directory stashed."
else
echo "Working tree clean, nothing to do."
fi
Eliotのスクリプトに次の変更を実装しました。
-c
が使用されている場合、最後のスタッシュと比較して変更がなければ、スクリプトは終了します。これは、このスクリプトを「タイムマシン」として使用し、10分ごとに自動スタッシュを作成する場合に役立ちます。何も変更されていない場合、新しいスタッシュは作成されません。このスイッチがないと、同じn個の連続したスタッシュが発生する可能性があります。スイッチ-c
が正しく機能するためには、少なくとも1つのスタッシュが存在する必要があります。存在しない場合、スクリプトはgit diff stash@{0}
でエラーをスローし、何もしません。
このスクリプトを「タイムマシン」として使用し、次のbashループを使用して10分ごとにスナップショットを作成します。
while true ; do date ; git stash-Push ; sleep 600 ; done