"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} тайник из параллельной ветви попытается отменить все изменения между текущим коммитом и исходной точкой ветвления, а также применить все изменения между точкой ветвления и другой ветвью, кроме изменения в тайнике).

обходные

Есть несколько обходных путей, но ни один из них не подходит для любой ситуации:

    1. Будьте очень осторожны с патчами, которые вы принимаете, когда делаете git checkout -p stash@{n}
    1. Сделать git stash pop, затем git stash еще раз, прежде чем делать git checkout -p ..., Но если вы хотите частично применить свой тайник, чтобы избежать конфликтов, это действительно не поможет. В этом случае см. Решение 4 ниже.
    1. Если у вас есть графический инструмент diff, поддерживаемый git (например, meld), вы можете использовать git difftool и "применить налево" только те изменения, которые вас интересуют:

      • git difftool -d stash@{n} сравнить весь тайник и все его файлы

      • git difftool stash@{n} -- path/to/file сравнить один файл

    1. (Основано на ответе @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). Если вас беспокоят конфликты, вы можете сначала спрятать любые незафиксированные изменения, применить тайник, спрятать любые конфликтующие фрагменты, а затем применить другой тайник.

Другие вопросы по тегам