Как игнорировать добавленные hunks в git stash -p '

Представьте себе этот сценарий:

# edit two files
git add -p // add hunks from one file

Теперь, когда вы запускаете git stash -p, он снова спросит вас, хотите ли вы спрятать куски, которые вы только что выбрали, через git add -p. Есть ли способ настроить git игнорировать эти уже добавленные куски по умолчанию? Большую часть времени я не хочу записывать вещи, которые я добавил уже.

Ответ 1

Хорошо, из комментариев, то, что нужно, - это прикрытие всех изменений, которые еще не были добавлены (исключено во время git add -p или просто еще не добавлено).

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

Это прямая git stash -k, все как обычно, задержите индексированное состояние и соответствующую рабочую строку, т.е. Очистите от worktree все, что я не собираюсь совершать.

Так:

git stash -k
git clang-format
git commit

и в хранилище теперь есть четыре интересных моментальных снимка: исходное содержимое aka stash base, индекс моментальных снимков, снимок рабочей строки и текущий индекс (, commit и worktree), который является снимком индекса при stash^2 с применяемыми очистками. Обратите внимание, что все три новых моментальных снимка, которые совершаются здесь, содержат базу данных в качестве родительской.

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

Если Git предложит то, что вы хотите напрямую, "запишите все изменения рабочей группы, кроме тех, которые указаны в индексе", вы могли бы использовать это, а затем всплывающее окно не было бы проблемным, это сделало бы прямую git stash pop. К счастью, если Git хорош в чем-либо, это слияние, объединение, расщепление и общее многогранное различие.

git cherry-pick -nm2 stash
# cherry-pick updated the index, too. maybe git reset here to unstage that stuff.
git stash drop

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

Таким образом, вишня -p ick - -n, no commit, -m2, mainline для изменений - ее второй родитель, то есть все различия, которые вы сделали, но не добавленные при сшивании.

Пример может помочь,

 cd 'mktemp -d'
 git init
 printf >file %s\\n 1 2 3 4 5
 git add .;git commit -m1
 printf >file %s\\n 1 2a 3 4 5
 git add .
 printf >file %s\\n 1 2a 3 4a 5

теперь вы эффективно git add -p 'd изменение 2a, а изменение 4a - только в вашей рабочей группе.

 $ git stash -k
 $ cat file
 1
 2a
 3
 4
 5
 $ sed -i '2s,^,_,' file   # indent the 2a line
 $ git commit -am2

Теперь начальная фиксация : /1 равна 1 2 3 4 5, ваш текущий фиксатор, индекс и рабочая строка - все 1 _2a 3 4 5, ваш спрятанный индекс равен 1 2a 3 4 5 а ваша спрятанная рабочая строка - 1 2a 3 4a 5.

Изменения, которые вы хотите вернуть, - это разница между вашим спрятанным индексом и вашей спрятанной рабочей линией, что кошелек фиксирует различия со вторым родителем. Следовательно, это вишня -p ick.


Альтернативные способы написания вишни -p включают

git cherry-pick -nm1 -Xours stash

который применяет все измененные изменения рабочей строки, но принимает локальную версию в случае конфликта (в основном это поиск и устранение противоречивых различий вместо того, чтобы просто избегать их, как это -m2), и

git diff stash^2..stash|git apply -3

Сделать все это проще на себе - это скриптовая территория, самый простой способ рассказать о ее настройке - это как псевдоним git,

git config --global alias.poptree '!git cherry-pick -nm2 stash; git reset; git stash pop'

и теперь у вас есть команда git poptree чтобы делать то, что вы хотите.


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

( index='git write-tree' &&
  git cherry-pick -nm2 stash &&
  git read-tree $index &&
  git stash drop
)

как я реализую это по-настоящему.

Ответ 2

В man-странице есть аналогичный пример:

man git stash:

"Testing partial commits
You can use git stash save --keep-index when you want 
to make two or more commits out of the changes in the 
work tree, and you want to test each change before 
committing:

# ... hack hack hack ...
$ git add --patch foo            # add just first part to the index
$ git stash save --keep-index    # save all other changes to the stash"*

Я могу подтвердить:

Если вы используете git stash -p (что подразумевает --keep-index), вас все равно спросят, должны ли изменения, которые уже есть в индексе, спрятать (как вы описали).

Итак, кажется, что manpage запутывает, что также упоминается в другом месте: https://github.com/progit/progit2/issues/822

Подвести итог:

--keep-index (или -p который подразумевает --keep-index) просто оставляет индекс неизменным. Изменения, которые уже были поставлены, по-прежнему вставляются в тайник. И AFAIK, нет способа сделать то, что вы описали.

Или, более точно (опять же из man-страницы):

With --patch, you can interactively select hunks from 
the diff between HEAD and the working tree to be stashed. 

The stash entry is constructed such that its index state 
is the same as the index state of your repository, and its 
worktree contains only the changes you selected interactively.

Альтернативы:

Есть как минимум 3 способа достижения желаемого (более или менее):

  • Не используйте -p с git stash. Спрячьте все (с помощью --keep-index и, возможно, --all, чтобы убедиться, что вы все спокойно убрали).
  • Завершите свои устные изменения перед стиркой. Таким образом, у вас не будет разницы между HEAD и рабочим деревом для этих изменений, которые вы хотите опустить из stash. Но что, если вы не уверены, что хотите это сделать? Вы можете всегда вносить изменения позже и использовать --amend для изменения существующего фиксации.
  • Внесите ваши изменения (удалите из индекса), а затем нажмите.

Ответ 3

Когда дело доходит до git stash push (поскольку вызов git stash без каких-либо аргументов эквивалентен git stash push), подумайте над добавлением опции --keep-index.

Это означает, что все изменения, уже добавленные к индексу, остаются нетронутыми.

Поэтому -p (patch) не должна запрашивать для этих (уже кэшированных) кусков.

Примечание. Параметр --patch подразумевает --keep-index, поэтому (для тестирования) убедитесь, что вы используете последнюю версию Git (2.17) и попробуйте git stash push -p.

Если проблема не устранена, то, как прокомментировано, выполнение фиксации сначала позволит stash -p работать с чистым индексом.
git reset --soft @~ восстановит зафиксированный индекс.