TL; DR
Вам необходимо git reset --hard HEAD
(или что-нибудь эквивалентное) перед применением с --index
, как это. Применяются все обычные предостережения в отношении жесткого сброса.
Long
Я связал в комментарии к Как правильно сделать git stash / pop в хуках перед фиксацией, чтобы получить чистое рабочее дерево для тестов? , которое показывает, как сделать финальное всплывающее окно (или эквивалентное), и некоторые предостережения вокруг этого. Тем не менее, ответ на заданный вопрос - в частности Как заставить git использовать ускоренную перемотку вперед при применении тайника - заключается в том, что вы не можете, и на самом деле вопрос даже не имеет смысла : быстрая перемотка вперед - это не то же самое, что спрятать и убрать. 1
Git stash - это просто набор коммитов (два, если вы не используете опцию --all
или --include-untracked
, тогда вы получите три) со специальным соглашением. Сохранение коммитов:
- указатель на момент
git stash
(используется git write-tree
);
- содержимое рабочего дерева во время
git stash
(с использованием довольно сложного кода);
- и последний в этом списке, но на самом деле сделано раньше, если вы использовали
--all
или --include-untracked
, файлы, которые не были отслежены, включая игнорируемые файлы (--all
), или файлы, которые не были отслежены, за исключением игнорируемых файлов (--include-untracked
).
Затем Git сбрасывает рабочее дерево, обычно в соответствии с коммитом HEAD
, и, если использовались --all
или --include-untracked
, удаляет также файлы, сохраненные в третьем коммите. Однако, когда вы используете --keep-index
, Git сбрасывает рабочее дерево, чтобы соответствовать содержимому индекса.
Ссылка с именем refs/stash
изменена, чтобы указывать на фиксацию рабочего дерева. Этот коммит имеет в качестве своих родителей коммит HEAD
(родительский номер 1), индексный коммит (родительский номер 2) и, если он присутствует, фиксацию неотслеживаемых файлов (родительский номер 3). Индекс имеет в качестве родителя фиксацию HEAD
. Фиксированный файл без отслеживания не имеет родителя (является корневым):
...--o--o--o <-- refs/heads/somebranch (HEAD)
|\
i-w <-- refs/stash
/
u
или более типично, то же самое без u
.
Когда git stash
сбрасывается до HEAD
(т.е. без --keep-index
), все, что вам нужно сделать, чтобы отменить то, что git stash
сделал, это запустить git stash pop --index
(примечание: не --keep-index
!). Он запускает git stash apply
с теми же параметрами и аргументами, 2 и, если это удается без конфликтов слияния, запускает git stash drop
в том же хранилище.
Приложение может использовать как фиксацию индекса, так и фиксацию рабочего дерева для восстановления того, над чем вы работали, но по умолчанию оно игнорирует фиксацию индекса. Добавление --index
указывает Git применить индексную фиксацию (превращенную в diff для текущего содержимого индекса) к текущему содержимому индекса, используя git apply --index
. Если это не удается, git stash
останавливается и ничего не делает. В этом я бы предложил превратить тайник в новую ветку, используя git stash branch
, хотя git stash
просто предлагает применить без --index
. 3
В любом случае Git затем пытается применить фиксацию рабочего дерева к текущему рабочему дереву. 4 Если вы спрятали без --keep-index
и не внесли изменений к текущему рабочему дереву это всегда будет успешным: текущий индекс и рабочее дерево будут соответствовать фиксации HEAD
, так что это оставит текущий индекс без изменений и применит все различия в фиксации рабочего дерева к рабочему дереву само по себе, что приводит к восстановлению скрытого рабочего дерева.
На данный момент проблема в том, что вы использовали --keep-index
, поэтому рабочее дерево current соответствует настроенному вами index , а не чем соответствие фиксации HEAD
. Следовательно, прежде чем применять тайник (с --index
или без него), вы должны сначала сбросить рабочее дерево, чтобы оно соответствовало фиксации HEAD
, то есть git reset --hard
. Состояния индекса и рабочего дерева, которые вы хотите, находятся в хранилище, которое вы собираетесь применить, так что это безопасно, если текущий индекс и рабочее дерево не были изменены каким-либо кодом предварительной фиксации / предварительной загрузки, который у вас есть. .
Как только вы это сделаете, git apply --index
коммитов stash восстановит и индекс, и рабочее дерево (по модулю этой ошибки в связанном вопросе!).
Сноска
Они специально вышли из строя, потому что сноска 1 слишком длинная.
2 Аргумент git stash apply
по умолчанию равен refs/stash
.Если вы дадите ему какой-либо аргумент, его поведение будет немного изящнее: в последних версиях Git, если вы дадите ему полностью числовой аргумент n , он проверяет stash@{<em>n</em>}
, в противном случае он использует все, что вы ему дали.Она передает эту строку в git rev-parse
, чтобы убедиться, что она преобразуется в действительный хэш-идентификатор, и что при суффиксе :
, ^1
, ^1:
, ^2
и ^2:
они также преобразуются вдействительные хэш-идентификаторы.Если строка создает действительный идентификатор хеша с ^3
и ^3:
, они также запоминаются.Все вместе они образуют w_commit
, w_tree
, b_commit
, b_tree
, i_commit
и i_tree
, а также u_commit
и u_tree
, если они существуют.Подробнее о том, как это работает, см. В документации gitrevisions .
Суть этого сводится к тому, что любой аргумент, передаваемый вами git stash apply
, должен иметь форму совершить слияние, по крайней мере, с двумя родителями.Git не проверяет, есть ли дополнительные родители помимо предполагаемых трех, и не является ли этот коммит слияния действительно тайником: он просто предполагает, что, если у него правильный набор родительского возраста, вы намереваетесь использовать его как один.
3 Это может быть достаточно разумно для неофитов Git, которые не пытаются хранить индекс отдельно и использовали --index
на git stash apply
или git stash pop
, не понимая этого.Как только вы действительно поймете индекс, это явно не так: вы хотели восстановить изменения сохраненного индекса относительно вашего текущего индекса в вашем текущем индексе, а не игнорировать их полностью!Фиксация текущего индекса, если это уместно, затем фиксация текущего рабочего дерева, если это уместно, а затем превращение тайника в ветку и фиксация его рабочего дерева дает вам все необходимое для построения правильных конечных результатов.
4 Технические подробности: приложение использует git merge-recursive
- это то, что реализует git merge -s recursive
- с некоторыми секретными переменными среды для установки имен на маркерах конфликта, если возникает конфликт.База слияния - это фиксация, которая была HEAD
, когда был сделан тайник, текущее дерево - результат записи текущего (в нерабочее время) индекса, а объединяемый элемент - фиксация рабочего дерева, илиточнее, его дерево.Это использует тот факт, что некоторые слияния могут выполняться с незафиксированными изменениями.Команда переднего плана git merge
запрещает попытки слияния с незафиксированными изменениями, так как результаты могут быть очень запутанными при возникновении проблемы.
1 Концепция fast-forward также немного сложнее, чем обычно кажется на первый взгляд.То есть мы видим это при слиянии - см. В чем разница между `git merge` и` git merge --no-ff`? - но на самом деле это относится к обновлению ссылки, такой как ветвьназвание.Обновление имени ветви - это ускоренная перемотка в том и только в том случае, если хеш нового коммита имеет хэш старого коммита в качестве предка, т. Е. Если git merge-base --is-ancester $old_hash $new_hash
возвращает нулевой статус выхода.
Когда git merge
выполняет один из нихиз этих операций ускоренной пересылки это означает, что Git изменил коммит HEAD
, чтобы он указывал на новый хеш, а также обновил индекс и рабочее дерево по мере необходимости.Если бы вам пришлось быстро перейти к коммиту с рабочим деревом в тайнике, это раскрыло бы странную техническую коммит с рабочим деревом для остальной части Git, где это было бы, по крайней мере, очень запутанным.
Нетт. е. что git fetch
и git push
также выполняют операции ускоренной перемотки или, с помощью --force
, разрешают не пересылку изменений в ветви и (для извлечения) имен удаленного отслеживания.Получатель push-сообщения обычно требует быстрой перемотки вперед, потому что это означает, что обновленное имя ветви содержит все коммиты, которые он использовал, плюс некоторые дополнительные коммиты.Принудительное обновление без ускоренной пересылки отменяет фиксацию из ветви (независимо от того, добавляет ли она новые).Несколько загадочный вывод git fetch
записывает, было ли имя для удаленного отслеживания переадресовано или форсировано тремя (!) Способами:
$ git fetch
remote: Counting objects: 1701, done.
remote: Compressing objects: 100% (711/711), done.
remote: Total 1701 (delta 1363), reused 1318 (delta 989)
Receiving objects: 100% (1701/1701), 975.29 KiB | 3.65 MiB/s, done.
Resolving deltas: 100% (1363/1363), completed with 284 local objects.
From [url]
3e5524907..53f9a3e15 master -> origin/master
61856ae69..ad0ab374a next -> origin/next
+ fc16284ea...4bc8c995a pu -> origin/pu (forced update)
9125ddae1..9db014fc5 todo -> origin/todo
* [new tag] v2.18.0 -> v2.18.0
* [new tag] v2.18.0-rc2 -> v2.18.0-rc2
Обратите внимание на +
перед строкой, записывающее обновление origin/pu
и добавлены слова (forced updated)
.Это два из трех способов.Обратите внимание на точки между двумя сокращенными хэшами фиксации: все другие строки, которые не являются принудительными обновлениями, показывают две точки, но это обновление показывает три точек.Это потому, что мы можем использовать git rev-list
или git log
с тем же синтаксисом из трех точек для просмотра добавленных и удаленных коммитов:
$ git log --oneline --decorate --graph --left-right fc16284ea...4bc8c995a
> 4bc8c995a (origin/pu) Merge branch 'sb/diff-color-move-more' into pu
|\
| > 76db2b132 SQUASH????? Documentation breakage emergency fix
| > f2d78d2c6 diff.c: add white space mode to move detection that allows indent changes
| > a58e68b88 diff.c: factor advance_or_nullify out of mark_color_as_move
[massive snippage]
< fc16284ea Merge branch 'mk/http-backend-content-length' into pu
|\
| < 202e4a2ff SQUASH???
| < cb6d3213e http-backend: respect CONTENT_LENGTH for receive-pack
< | 4486a82e5 Merge branch 'ag/rebase-p' into pu
< | a84cc85f3 Merge branch 'nd/completion-negation' into pu
[much more snippage]
Опция --left-right
вместе с синтаксисом из трех точек,говорит Git, чтобы отметить, с какой стороны поступили коммиты.В этом случае коммиты >
теперь находятся в ветке раскладки, а коммиты <
сняты с нее.Эти конкретные удаленные коммиты теперь полностью не ссылаются и скоро будут собирать мусор (ish).