Git команда, чтобы сохранить тайник без изменения рабочего дерева? - PullRequest
40 голосов
/ 11 июня 2011

Я давно хотел использовать команду git, которая сохраняет тайник без изменения моего рабочего дерева, в качестве облегченной резервной копии, которая защищена от любого сброса git или чего бы я ни делал, чтобы испортить мой индекс.По сути, это функциональный эквивалент «git stash save && git stash apply», за исключением того, что к рабочей копии никогда не прикасаются, поскольку это может сделать некоторые текстовые редакторы / IDE капризными.не совсем:

git update-ref refs/stash `git stash create "Stash message"`

Это работает функционально, но проблема, с которой я сталкиваюсь, заключается в том, что в «git stash list» не появляется ни одного сообщения stash, даже если в самом коммите stash есть мое сообщение.Учитывая, насколько большим может быть тайник, тайник-сообщения очень важны.

Ответы [ 4 ]

30 голосов
/ 12 июня 2011

Благодаря совету Чарльза я создал сценарий bash, чтобы сделать именно то, что хотел (я столкнулся с проблемами, реализующими это только как псевдоним).Требуется необязательное сообщение на stash, как в git stash save.Если ничего не указано, он будет использовать сообщение по умолчанию, сгенерированное git stash.

#!/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 под названием stash push, поэтому я обновил свою рекомендацию по именованию этого скрипта с git-stash-push до git-stash-snap.

13 голосов
/ 11 июня 2011

Вам нужно передать сообщение update-ref, а не stash create, поскольку stash create не принимает сообщение (оно не обновляет ни один ref, поэтому у него нет записи reflog для заполнения).

git update-ref -m "Stash message" refs/stash "$(git stash create)"
3 голосов
/ 16 ноября 2018

git stash store "$(git stash create)"

Создает запись в stash, аналогичную той, которую вы получили бы с git stash, фактически не затрагивая и не очищая ваш рабочий каталог и индекс.

Если вы проверите список тайников или посмотрите на весь график коммитов (включая тайник), то увидите, что это результат, аналогичный тому, который вы получили бы при обычном вызове git stash. Просто сообщение в списке хранилищ отличается (обычно это что-то вроде "stash @ {0}: WIP на master: 14e009e init commit" , здесь мы получим "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 родителя (или 3, если вы используете опцию --include-untracked) (дополнительная информация 1 , 2 ).

git stash create создает этот коммит, представляющий запись в тайник, и возвращает вам имя объекта (SHA-1) объекта коммита (тот, у которого 2 или 3 родителя). Это висячий коммит (вы можете проверить его, позвонив 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).

3 голосов
/ 09 марта 2016

Вдохновленный решением Элиота, я немного расширил его сценарий:

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

Я внес следующие изменения в сценарий Элиота:

  1. Когда рабочий каталог чист,скрипт будет корректно завершать работу
  2. Когда используется переключатель -c, если нет изменений по сравнению с последним тайником, скрипт завершится.Это полезно, если вы используете этот сценарий как «машину времени», делая автоматическое копирование каждые 10 минут.Если ничего не изменилось, новый тайник не создается.Без этого переключателя вы можете получить n одинаковых последовательных тайников.

Не для того, чтобы коммутатор -c работал правильно, должен существовать хотя бы один тайник, иначе скриптвыдает ошибку на git diff stash@{0} и ничего не делает.

Я использую этот скрипт как «машину времени», снимая каждые 10 минут, используя следующий цикл bash:

 while true ; do date ; git stash-push ; sleep 600 ; done
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...