"git stash apply" с интерактивным режимом
У меня есть серия файлов в тайник (stash{0}
) и я бы хотел git apply
только некоторые части / фрагменты этих файлов (обычно называемые интерактивным режимом).
Является ли это возможным?
Я видел, что можно выполнить
git stash save -p 'Stash name'
но, кажется, сделать это невозможно
git stash apply -p 'Stash name'
Вы знаете способ достичь этого?
3 ответа
Является ли это возможным?
Да, это!
git checkout -p stash@{0}
Где вы можете заменить 0
в stash@{0}
с индексом тайника, который вы хотите применить.
использование git stash list
а также git show -p stash@{n}
если не уверены, какой n
это тайник, который вы хотите применить.
Не забудь git stash drop stash@{n}
когда вы знаете, этот тайник больше не нужен, так как git checkout
очевидно, не бросит тайник для вас.
Почему это работает?
Ключевым моментом является осознание того, что тайники - это, по сути, ссылки на коммиты, как теги и ветви.
Действительно, они хранятся в .git/refs/stash
по одной строке на хэш.
Предостережения
Как mgadda упоминается в комментариях ниже, git checkout -p
пытается применить всю разницу между коммитом и текущим рабочим пространством.
В случае git stash, если stash, который вы пытаетесь применить, был сделан против другого коммита, то git checkout -p stash@{n}
постараюсь применить интерактивно все различия между коммитом stash@{n}
и фиксация текущей рабочей области, включая все их родительские коммиты, которые отличаются.
Например, если вы пытаетесь применить тайник, который был сохранен "много коммитов назад" в текущем рабочем пространстве, git checkout -p stash@{n}
будет пытаться применить не только изменения в собственном тайнике, но также попытаться отменить все изменения, произошедшие между фиксацией, на которой основан тайник, и текущей фиксацией.
И наоборот, если вы пытаетесь применить тайник "из будущего", то есть в ветвь, которая представляет собой количество коммитов, предшествовавших коммиту, на котором основан тайник, то git checkout -p stash@{n}
постарается также применить все другие изменения, которые произошли между текущим коммитом и коммитом из будущего, кроме изменений из самого тайника.
(Если вам интересно, git checkout -p stash@{n}
тайник из параллельной ветви попытается отменить все изменения между текущим коммитом и исходной точкой ветвления, а также применить все изменения между точкой ветвления и другой ветвью, кроме изменения в тайнике).
обходные
Есть несколько обходных путей, но ни один из них не подходит для любой ситуации:
- Будьте очень осторожны с патчами, которые вы принимаете, когда делаете
git checkout -p stash@{n}
- Будьте очень осторожны с патчами, которые вы принимаете, когда делаете
- Сделать
git stash pop
, затемgit stash
еще раз, прежде чем делатьgit checkout -p ...
, Но если вы хотите частично применить свой тайник, чтобы избежать конфликтов, это действительно не поможет. В этом случае см. Решение 4 ниже.
- Сделать
Если у вас есть графический инструмент diff, поддерживаемый git (например, meld), вы можете использовать
git difftool
и "применить налево" только те изменения, которые вас интересуют:git difftool -d stash@{n}
сравнить весь тайник и все его файлыgit difftool stash@{n} -- path/to/file
сравнить один файл
- (Основано на ответе @andrew). На отдельной голове вернитесь к "родительскому" коммиту тайника, который вас интересует, примените тайник, заново поместите в интерактивном режиме только те части, которые вам интересны, вернитесь назад и повторно примените тайник поменьше.
Шаг за шагом:
git checkout stash@{n}^ # notice the "^".
# Now you're in a detached head in the parent commit of the stash.
# It can be applied cleanly:
git stash apply stash@{n}
# Now save only the diffs you're interested in:
git stash -p
# remove the rest of the old stash
git checkout -- . # be careful or you could remove unrelated changes
# go back to the branch where you want to apply the smaller stash
git checkout <my previous branch>
# apply the smaller stash
git stash pop
Что я часто делаю (в git bash), так это
git stash show -p 'stash@{0}' >tmp.patch
Затем я редактирую файл и удаляю те части, которые мне не нужны. Наконец я говорю
<tmp.patch git apply
или
<tmp.patch patch -p1
Однако это не работает для двоичных файлов, но и для них не работает принятый ответ (с использованием checkout -p).
Один из возможных способов - сбросить индекс, а затем использовать интерактивное добавление.
# 0. ensure there are no uncommitted changes
git status
# 1. apply a changeset as is
git stash apply stash@{n}
# ... fix or discard conflicts if any
# 2. reset the index
git reset
# 3. interactively add the required chunks (except new files)
git add -p
# 4. stash all other changes
git stash save --keep-index "comment"
# 4. or just discards all other changes in the working tree
git checkout-index -f -a
# 5. commit
git commit -m "comment"
Другой способ - использовать интерактивный сброс вместо интерактивного добавления.
# 0. ensure the working tree does not have unstaged changes
git status
# 1. apply a changeset as is
git stash apply stash@{n}
# ... fix or discard conflicts if any
# 2. interactively exclude the unneeded chunks from the index
git reset -p
# 3. stash all other changes
git stash save --keep-index "comment"
# 3. or just discards all other changes in the working tree
git checkout-index -f -a
# 4. commit
git commit -m "comment"
Я не думаю, что есть способ применить изменения с помощью фрагментов (или даже файлов). Вам придется применить тайник, а затем скрыть изменения, которые вы не хотите, в интерактивном режиме (с git stash save -p
). Если вас беспокоят конфликты, вы можете сначала спрятать любые незафиксированные изменения, применить тайник, спрятать любые конфликтующие фрагменты, а затем применить другой тайник.