Как в обратном порядке применить заначку?

233

У меня есть небольшой патч, спрятанный в моем git-тайнике. Я применил это к моей рабочей копии, используя git stash apply. Теперь я хотел бы отменить эти изменения, применив патч в обратном порядке (вроде как то, что git revertможно сделать, но против тайника).

Кто-нибудь знает как это сделать?

Пояснение: в моей рабочей копии есть другие изменения. Мой конкретный случай трудно описать, но вы можете представить некоторый отладочный или экспериментальный код, который находится в тайнике. Теперь он смешан в моей рабочей копии с некоторыми другими изменениями, и я хотел бы увидеть эффект с изменениями из хранилища и без них.

Не похоже, что stash поддерживает это в настоящее время, но git stash apply --reverseбыло бы неплохо.

Пэт Нотц
источник
1
Разве нельзя просто создать обратный патч, различая текущую и предыдущую ревизию? И затем применить это?
ralphtheninja
Есть ли изменения в рабочем дереве, кроме примененного тайника?
Грег Бэкон
Добавление этого здесь, ... должно было быть FAQ, а не вопросом ... stackoverflow.com/questions/59973103/…
Дон Томас Бойл

Ответы:

188

Согласно man-странице git-stash : «Шкатулка представляется в виде коммита, дерево которого записывает состояние рабочего каталога, а его первый родительский элемент - коммит, в котором HEADбыл создан тайник», и « git stash show -pдает нам» изменения, записанные в stash как разница между stashed состоянием и его исходным родителем.

Чтобы сохранить другие изменения без изменений, используйте, git stash show -p | patch --reverseкак показано ниже:

$ git init
Initialized empty Git repository in /tmp/repo/.git/

$ echo Hello, world >messages

$ git add messages

$ git commit -am 'Initial commit'
[master (root-commit)]: created 1ff2478: "Initial commit"
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 messages

$ echo Hello again >>messages

$ git stash

$ git status
# On branch master
nothing to commit (working directory clean)

$ git stash apply
# On branch master
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   messages
#
no changes added to commit (use "git add" and/or "git commit -a")

$ echo Howdy all >>messages

$ git diff
diff --git a/messages b/messages
index a5c1966..eade523 100644
--- a/messages
+++ b/messages
@@ -1 +1,3 @@
 Hello, world
+Hello again
+Howdy all

$ git stash show -p | patch --reverse
patching file messages
Hunk #1 succeeded at 1 with fuzz 1.

$ git diff
diff --git a/messages b/messages
index a5c1966..364fc91 100644
--- a/messages
+++ b/messages
@@ -1 +1,2 @@
 Hello, world
+Howdy all

Редактировать:

Небольшое улучшение по сравнению с git applyпатчем:

git stash show -p | git apply --reverse

В качестве альтернативы, вы также можете использовать git apply -Rв качестве краткого обозначения git apply --reverse.

Я нашел это очень удобным в последнее время ...

Грег Бэкон
источник
2
Здорово, спасибо. Похоже, это может быть хорошей функцией для тайника.
Пэт Нотц
5
Да, git apply -Rэто улучшение, по крайней мере, для меня на моем windows-окне с git bash, так как patch --reverseбыли проблемы с поиском файла для исправления (не знаю, почему альтернатива работала). +1 и хорошее объяснение
хакре
не лучше ли добавить --indexпросто так git stash show -p | git apply --reverse --index. Потому что вам больше не нужно добавлять в индекс изменения, которые возвращаются обратно.
theUnknown777
3
@Greg Bacon, эй, я пытался пройти через скрипт , который вы очерченные, но патч не удалось , когда я столкнулся git stash show -p | git apply -R -vс сообщением: Checking patch messages... error: while searching for: Hello, world Hello again error: patch failed: messages:1. Вы знаете, что может быть не так?
Макс Корецкий
5
Я получаю сообщение об ошибке: сбой патча: /src/filename.java:46 ошибка: патч src / filename.java не применяется
Тим Боланд,
83

git stash[save]берет ваше рабочее состояние каталога и ваше состояние индекса, и сохраняет их, устанавливая индекс и рабочую область в HEADверсию.

git stash applyвозвращает эти изменения, поэтому git reset --hardудалил бы их снова.

git stash popвозвращает эти изменения и удаляет изменения, сохраненные сверху, поэтому git stash [save]в этом случае вернется в предыдущее (предварительно всплывающее) состояние.

Якуб Наребски
источник
83
git checkout -f

удалит все не зафиксированные изменения.

САЛМАН
источник
4
спасибо, вы помогаете мне от поэтапного изменения, которое не было неприменимо.
Fa.Shapouri
1
Это было намного проще
Марк
Это было потрясающе!
Киран Ск
22

На man-странице V1 git была ссылка о том, как не применять тайник. Выдержка ниже.

Более новая справочная страница V2 git не содержит ссылки на отмену применения тайника, но ниже все еще работает хорошо

Отмена применения тайника В некоторых сценариях использования вы можете применить скрытые изменения, выполнить некоторую работу, но затем отменить те изменения, которые изначально были получены из тайника. Git не предоставляет такой команды stash un-apply, но добиться эффекта можно, просто извлекая патч, связанный с stash, и применяя его в обратном порядке:

$ git stash show -p stash@{0} | git apply -R

Опять же, если вы не указали тайник, Git предполагает самый последний тайник:

$ git stash show -p | git apply -R

Возможно, вы захотите создать псевдоним и эффективно добавить команду stash-unapply в ваш Git. Например:

$ git config --global alias.stash-unapply '!git stash show -p | git apply -R'
$ git stash apply
$ #... work work work
$ git stash-unapply
Чоко Смит
источник
1
По какой-то причине этот полезный раздел, на который вы ссылались «Отмена применения тайника », был удален из 2-й версии. Последняя версия - 2.1.146, 2019-04-15 - этой книги. V2 - Инструменты Git - копирование и очистка . Может быть потому, что авторы думают, что есть лучший способ сделать это, которого я не могу найти.
Над Алаба,
@NadAlaba спасибо за новость, обновил ответ, чтобы отметить разницу между v1 и v2 ... странно, что git авторы удалили раздел о
Choco Smith
13

Это давно закончилось, но если я правильно истолковал проблему, я нашел простое решение, обратите внимание, это объяснение в моей собственной терминологии:

git stash [save] сохранит текущие изменения и установит текущую ветку в «чистое состояние»

git stash list дает что-то вроде: stash@{0}: On develop: saved testing-stuff

git apply stash@{0}установит текущую ветку как раньше stash [save]

git checkout .Установит текущую ветку как после stash [save]

Код, который сохраняется в тайнике, не теряется, его можно найти git apply stash@{0}снова.

В любом случае, это сработало для меня!

Слим Сим
источник
Просто чтобы быть уверенным, я git stash apply --reverseсначала подал заявку, а потом просто вернулся к тому, git stash apply stash@{x}что вы упомянули. Работал без проблем.
Карлос Гарсия
3

Как в обратном порядке применить заначку?

Помимо того, что упоминали другие, проще всего сначала сделать

git reset HEAD

а затем оформить все локальные изменения

git checkout . 
Achal
источник
Это, безусловно, самый простой способ, если у вас нет абсолютно никакой местной работы, которую вы хотите сохранить. Если вы применили неправильный тайник к ветке или столкнулись с конфликтами слияния, которые вы не хотите разрешать, это быстрый и простой способ отменить его, полностью вернув рабочий набор к последнему коммиту вашего филиала.
Шадониня
2

В дополнение к ответу @Greg Bacon, в случае, если двоичные файлы были добавлены в индекс и были частью тайника с использованием

git stash show -p | git apply --reverse

может привести к

error: cannot apply binary patch to '<YOUR_NEW_FILE>' without full index line
error: <YOUR_NEW_FILE>: patch does not apply

Добавление --binaryрешает проблему, но, к сожалению, еще не выяснили, почему.

 git stash show -p --binary | git apply --reverse
MHosafy
источник
1

Вы можете следить за изображением, которым я поделился, чтобы распаковать, если вы случайно нажали на тайник.

Amanpreet Singh
источник
0

Это в дополнение к вышеупомянутым ответам, но добавляет поиск git stash на основе сообщения, так как номер stash может измениться при сохранении новых stash. Я написал пару функций bash:

apply(){
  if [ "$1" ]; then
    git stash apply `git stash list | grep -oPm1 "(.*)(?=:.*:.*$1.*)"`
  fi
}
remove(){
  if [ "$1" ]; then
    git stash show -p `git stash list | grep -oPm1 "(.*)(?=:.*:.*$1.*)"` | git apply -R
    git status
  fi
}
  1. Создать тайник с именем (сообщением) $ git stash save "my stash"
  2. Чтобы применить имя $ apply "my stash"
  3. Удалить именованный тайник $ remove "my stash"
lifesoordinary
источник