Почему `git stash -p` иногда дает сбой?

Я ♥ git stash -p. Но иногда, после удовлетворительного сеанса y, n и s, я получаю следующее:

Saved working directory and index state WIP on foo: 9794c1a lorum ipsum
error: patch failed: spec/models/thing_spec.rb:65
error: spec/models/thing_spec.rb: patch does not apply
Cannot remove worktree changes

Почему?


person John Bachir    schedule 18.02.2011    source источник
comment
Копаю глубже; В конце концов, я отправлю что-нибудь в список рассылки и опубликую здесь, когда сделаю это. Я хочу посмотреть, смогу ли я понять, как это исправить, вместо того, чтобы просто сказать им, что _1_ работает неправильно.   -  person Cascabel    schedule 19.02.2011
comment
Уф, слишком много возни с индексами для вечера пятницы. Моя электронная почта для списка находится здесь.   -  person Cascabel    schedule 19.02.2011
comment
А пока вы можете обойти это, просто используя _1_, чтобы поместить все, что вы хотите сохранить, в индекс, а затем _2_, чтобы спрятать другие части.   -  person Cascabel    schedule 19.02.2011
comment
Вот такая же проблема с add -p: gist.github.com / nh2 /   -  person Cascabel    schedule 19.02.2011
comment
@Jefromi ссылка на список рассылки теперь мертва, я не думаю, что вы знаете, как найти, куда она переместилась? и / или почему эта ошибка все еще существует 6 лет спустя?   -  person nh2    schedule 17.05.2014
comment
К сожалению, обратные ответы не всегда приводят к достаточно большим различиям.   -  person John Bachir    schedule 05.04.2017
comment
Объяснение разницы в поведении версий 2.17 и 2.19 очень полезно.   -  person John Bachir    schedule 05.04.2017


Ответы (4)


git stash -p должен меньше выходить из строя с Git 2.17 (2 квартал 2018 г.).
До этого git add -p (который разделяет логику с git stash) ленился объединять разделенные патчи перед передачей результата в базовый git apply, что приводило к ошибкам крайних случаев; логика для подготовки патча к применению после ужесточения выбора фрагментов.

См. фиксацию 3a8522f, совершить b3e0fcf, 2b8ea7f (5 марта 2018 г.), совершить fecc6f3, совершить 23fea4c, совершить 902f414 (1 марта 2018 г.) и nofollow.com совершить 11489a6, совершить e4d671c, nofollow.com (19 февраля 2018 г.) Филип Вуд (phillipwood).
(объединено Junio ​​C Hamano - gitster - в commit 436d18f, 14 марта 2018 г.)

add -p: исправлен подсчет пустых контекстных строк в отредактированных патчах

(добавить, но опять же, можно применить к тайнику)

Поскольку commit 8cbd431 (git-add--interactive: замените пересчет фрагментов на apply --recount, 2008-7 -2, Git v1.6.0-rc0), если фрагмент пропускается, мы полагаемся на контекстные строки, чтобы применить последующие фрагменты в нужном месте.

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

Чтобы исправить это, отрегулируйте смещение последующих блоков, чтобы исправить любое изменение количества вставок или удалений из-за пропущенного блока. Изменение смещения из-за отредактированных блоков, у которых изменено количество вставок или удалений, здесь игнорируется, оно будет исправлено в следующей фиксации.

Вы можете увидеть здесь.


Git 2.19 улучшает git add -p: когда пользователь редактирует патч в git add -p и пользовательский редактор настроен на удаление конечных пробелов без разбора, пустая строка, которая не изменилась в патче, станет полностью пустой (вместо строки с единственным SP на ней).
Код, представленный в Git 2.17 timeframe, не смог проанализировать такой патч, но теперь он научился замечать ситуацию и справляться с ней.

См. commit f4d35a6 (11 июня 2018 г.) от Филип Вуд (phillipwood).
(Объединено Junio ​​C Hamano - gitster - в совершить 5eb8da8, 28 июня 2018 г.)

add -p: исправить checkout -p с патологическим контекстом

recount_edited_hunk() введено в commit 2b8ea7f (add -p: вычислить дельту смещения, 2018 для отредактированных патчей) -03-05, Git v2.17.0) требовал, чтобы все контекстные строки начинались с пробела, пустые строки не учитывались.
Это было сделано, чтобы избежать каких-либо проблем с пересчетом, если пользователь ввел пустые строки в конце при редактировании патч.

Однако это привело к регрессии в 'git add -p', поскольку кажется, что редакторы обычно удаляют конечные пробелы из пустых контекстных строк при редактировании патчей, тем самым вводя пустые строки, которые следует учитывать.
'git apply' знает, как это сделать. имеют дело с такими пустыми строками, и POSIX утверждает, что наличие или отсутствие пробела в пустой строке контекста определяется реализацией (см. команда diff).

Исправьте регрессию, подсчитав строки, которые состоят исключительно из новой строки, а также строки, начинающиеся с пробела, в качестве контекстных строк и добавьте тест, чтобы предотвратить будущие регрессии.


Git 2.23 (3 квартал 2019 г.) улучшает git add -p, используемый git checkout -p, которому необходимо выборочно применять исправление в обратном порядке: раньше он плохо работал.

См. commit 2bd69b9 (12 июня 2019 г.) на Филипп Вуд (phillipwood).
(Объединено Junio ​​C Hamano - gitster - в , 09 июл 2019)

built-in add -p: при необходимости измените заголовки фрагментов

Фиксация fecc6f3 (add -p: корректировка смещения последующих блоков при пропуске одного из них, 2018-03 -01, Git v2.17.0-rc0) исправлено добавление ханков в правильное место, когда предыдущий ханк был пропущен.

Однако он не касался исправлений, которые применяются в обратном порядке.

В этом случае нам нужно отрегулировать смещение предварительного изображения так, чтобы при применении реверсирования патча смещение пост-изображения корректировалось правильно.
Мы вычитаем, а не добавляем дельту, когда патч перевернут (самый простой способ думать речь идет о пропущенном куске удалений - в этом случае мы хотим уменьшить смещение, поэтому нам нужно вычесть).


В Git 2.25 (первый квартал 2020 г.) попытки переместить git-add--interactive Perl-скрипт на C продолжаются.

В результате упомянутые выше исправления реализуются повторно.

Подписано: Йоханнес Шинделин

stash -p: (частично) исправить ошибку, касающуюся разделенных фрагментов

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

Эта проблема была обнаружена в 23fea4c240 (t3701: add неудачный тест для строк патологического контекста, 2018 г. -03-01, Git v2.17.0-rc0 - слияние) и исправлено в версия Perl в fecc6f3a68 (add -p: корректировка смещения последующих фрагментов при пропуске одного из них, 01.03.2018, Git v2.17.0-rc0 - слияние).

И этот патч исправляет это в версии C git add -p.

В отличие от Perl-версии, мы стараемся сохранить без изменений лишний текст в заголовке блока (который обычно содержит сигнатуру функции, код которой изменен в блоке).

Примечание: хотя версия C не поддерживает изменения промежуточного режима на данном этапе, мы уже готовимся к этому, просто пропуская заголовок блока, если и старое, и новое смещение равно 0 (этого не может произойти для обычных блоков, и мы будем использовать это как индикатор того, что мы смотрим на особый ломоть).

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

До Git 2.27 (второй квартал 2020 г.), позволяющая пользователю разбивать фрагмент патча, пока git stash -p не работает должным образом; был добавлен лейкопластырь, чтобы это (частично) работало лучше.


См. совершить 7723436, commit 121c0d4 (8 апреля 2020 г.), Йоханнес (dscho).
(Объединено Junio ​​C Hamano - gitster - в commit e81ecff, 28 апреля 2020 г.)

Подписано: Йоханнес Шинделин

add -p: исправить утечку памяти

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

и команда не сможет спрятать нужные части изменений рабочего дерева (даже если stash ref действительно был обновлен правильно).

error: patch failed: <file>:<line>
error: test: patch does not apply
Cannot remove worktree changes

У нас даже есть тестовый пример, демонстрирующий этот сбой, который продолжается уже четыре года.

Объяснение: при разделении фрагмента измененные строки больше не разделяются более чем 3 строками (это количество контекстных строк, используемых Git diff по умолчанию), но меньше.

Таким образом, при постановке только части блока различий для хранения, результирующее различие, которое мы хотим применить к рабочему дереву в обратном порядке, будет содержать те изменения, которые должны быть отброшены, в окружении трех контекстных строк, но поскольку различие относится к HEAD, а не к worktree, эти строки контекста не будут совпадать.

Пример время. Предположим, что файл README содержит следующие строки:

и рабочее дерево добавило несколько строк, так что вместо этого оно содержит эти строки:

We
the
people

и пользователь пытается спрятать строку, содержащую are, тогда команда внутренне перенесет эту строку во временный индексный файл и попытается восстановить разницу между HEAD и этим индексным файлом.
Различия между HEAD и этим индексным файлом.
выглядят примерно так:

We
are
the
kind
people

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

@@ -1776,3 +1776,4
 We
+are
 the
 people

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

Примечание: это не полное исправление проблемы.
Как показано в тестовом примере t3701 «add -p работает с патологическими строками контекста», в формате diff есть неоднозначности. На практике, конечно, очень редко можно встретить такие повторяющиеся строки.

Полным решением для таких случаев было бы заменить подход генерации различий из тайника с последующим применением его в обратном порядке путем имитации git revert (т.е. выполнения трехстороннего слияния). Однако в git stash -p это будет применяться не к HEAD, а к рабочему дереву, что делает это нетривиальным для реализации, если мы также поддерживаем версию add -i со сценарием.

Git 2.29 (4 квартал 2020 г.) вносит исправление в git add -p (используется stash -p)


См. фиксацию 324efcf (7 сентября 2020 г.) на Филип Вуд (phillipwood).
(Объединено Junio ​​C Hamano - gitster - в фиксации 3ad8d3e a>, 18 сен 2020)

Подписано: Филлип Вуд
Подписано: Йоханнес Шинделин

Это происходит со мной каждый раз, когда я пытаюсь разбить кусок на более мелкие, которые расположены слишком близко друг к другу (менее 3 строк между изменениями). Краткое объяснение заключается в том, что в патче есть контекстные строки, которые конфликтуют с вашими локальными изменениями. Более полное объяснение ниже.

asan сообщает, что версия C add -p не освобождает всю память, которую она выделяет.

Исправьте это, введя функцию очистки struct add_p_state` и используйте ее вместо освобождения отдельных членов.

add -p: регулировать смещение последующих блоков, если один пропущен

person VonC    schedule 16.03.2018
comment
Я не думаю, что это ошибка в разделении ханка: я только что столкнулся с этой проблемой на _1_, где я никогда не выбирал разделение ханка, а только говорил _2 _ / _ 3_. - person Axel; 08.01.2019

Предположим, у меня есть репозиторий git с этими незафиксированными изменениями:


Если я спрячу первую сдачу, я получу:

--- a/pangram
+++ b/pangram
@@ -1,8 +1,8 @@
 The
-quick
+relatively quick
 brown
 fox
-jumps
+walks
 over
 the
 lazy

Команда git stash действительно успешно сохраняет патч (отметьте git stash list), но затем git использует этот патч в обратном порядке, чтобы удалить сохраненные изменения из моего рабочего каталога. Контекст после фрагмента имеет «прыжки», что не соответствует «прогулкам» в моем рабочем каталоге. Итак, git выручает

--- a/pangram
+++ b/pangram
@@ -1,5 +1,5 @@
 The
-quick
+relatively quick
 brown
 fox
 jumps

и оставляет все изменения в моем рабочем каталоге, и тайник становится практически бесполезным.

error: patch failed: pangram:1
error: pangram: patch does not apply
Cannot remove worktree changes

Я бы назвал это ошибкой в ​​поддержке разделения фрагментов в git. Если он знает, что разделяет изменения слишком близко, он может вырезать несколько строк контекста из патча или подправить патч, чтобы иметь измененные строки контекста вместо первоначальных. В качестве альтернативы, если разделение блоков на таком близком расстоянии официально не поддерживается, оно должно фактически отказаться от такого закрытия блоков.


После того, как

--- a/pangram
+++ b/pangram
@@ -1,8 +1,8 @@
 The
-quick
+relatively quick
 brown
 fox
-jumps
+walks
 over
 the
 lazy
потерпел неудачу таким же образом, мне повезло с этим обходным путем (git 2.0.2):

person Mu Mind    schedule 06.10.2012
comment
Конечно, это не ограничивается явным разделением фрагментов. Ошибка заключается в том, что git выбирает создание патча, основанного произвольно на изначальном состоянии, а не на вашем текущем рабочем состоянии, которое не работает, когда фрагменты слишком близки к другим изменениям. Наиболее распространенный сценарий - явное разделение блоков, но, по-видимому, это не единственный сценарий. - person nh2; 17.05.2014
comment
К сожалению, обратные ответы не всегда приводят к достаточно большим различиям. - person Mu Mind; 17.05.2014

Я не уверен, почему git add -p не потерпел неудачу так же, как git stash -p. Я предполагаю, потому что добавление работает с индексом, а не с созданием файла патча?

  • git stash -k, чтобы сохранить индекс и спрятать все остальное
  • git reset, чтобы продолжить работу с моими файлами
  • Принятый на данный момент ответ, к сожалению, может не работать, даже в Git 2.17.

git add -p, разделяя те же самые блоки, но с обратными ответами («y» - add «сохраняет» изменения, «n» - stash сохраняет изменения.)

person Johann    schedule 17.10.2014
comment
Интересный подход. +1. Я разочарован тем, что версия 2.17 все еще не работает в вашем случае. - person John Bachir; 05.04.2017

Если, как и я, вы потратили много усилий на создание идеального тайника и не хотите терять эти усилия, вы все равно можете получить то, что хотите, с помощью:

Это не удастся с отклонениями, но велика вероятность, что большинство фрагментов будут применяться правильно и, по крайней мере, сэкономят вам время на повторный просмотр всех файлов.

git stash show -p | patch -p1 -R

Похоже на довольно явную ошибку ... она не удалась, когда я в первый раз попробовал разделить (и оставил только один из двух полученных фрагментов). Что касается того, почему ... очевидно, что он не генерирует правильный патч в какой-то момент своей работы (возможно, при откате выбранных изменений), но для подробностей я мог бы перейти к списку рассылки, если у вас нет времени погрузиться в источник.

person Elliott Slaughter    schedule 07.04.2018
comment
Спасибо, я не осознавал, что тайник был фактически отправлен, что было почти всем, на что я надеялся (чтобы иметь запись изменений при их откате) - person VonC; 07.04.2018