Как мне извлечь один файл (или изменения в файл) из git stash?

777

Я хотел бы знать, возможно ли извлечь отдельный файл или diff из файла git stash, не отключая набор изменений stash.

Может ли кто-нибудь предоставить некоторые предложения / идеи по этому поводу?

Дэнни
источник

Ответы:

1133

На странице руководства git stash вы можете прочитать (в разделе «Обсуждение» сразу после описания «Опции»), что:

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

Таким образом, вы можете рассматривать stash (например, stash stash@{0}first / topmost) как коммит слияния и использовать:

$ git diff stash@{0}^1 stash@{0} -- <filename>

Объяснение: stash@{0}^1означает первого родителя данного тайника, который, как указано в приведенном выше объяснении, является коммитом, при котором изменения были спрятаны. Мы используем эту форму «git diff» (с двумя коммитами), потому что stash@{0}/ refs/stashявляется коммитом слияния, и мы должны указать git, против какого родителя мы хотим использовать diff. Более загадочный:

$ git diff stash@{0}^! -- <filename>

также должен работать (см. страницу руководства git rev-parse для объяснения rev^!синтаксиса в разделе «Задание диапазонов»).

Точно так же вы можете использовать git checkout для проверки одного файла из тайника:

$ git checkout stash@{0} -- <filename>

или сохранить его под другим именем файла:

$ git show stash@{0}:<full filename>  >  <newfile>

или

$ git show stash@{0}:./<relative filename> > <newfile>

( обратите внимание, что здесь <полное имя файла> - это полный путь к файлу относительно верхнего каталога проекта (думаю: относительно stash@{0})).


Вам может потребоваться защитить stash@{0}от расширения оболочки, то есть использовать "stash@{0}"или 'stash@{0}'.

Якуб Наребски
источник
8
Это довольно круто ... Я не совсем понял, как работает тайник, пока не прочитал твой ответ (что привело меня к добавлению git-checkout). Я действительно не понял, что когда вы выполняете копирование, git сохраняет ДВУХ коммитов - один для состояния индекса и один для состояния рабочей копии, которая является слиянием между индексом и исходным HEAD. Это объясняет странные деревья, которые я видел, когда я визуализировал хранилище с помощью «gitk --all», когда присутствуют тайники.
Пэт Нотц
4
В основном я считаю, что приложение git checkout - лучший способ выполнить то, что я хотел. Однако я был любопытен и дважды проверил git checkoutсправочную страницу. Он не может перенести файл в другое место. Ссылка на это есть в: stackoverflow.com/questions/888414/…
Дэнни
84
$ git checkout stash @ {0} - <имя_файла> очень полезно, спасибо
гравитация
43
Обратите внимание, что git checkoutподход копирует точный файл из тайника - он не сливается с тем, что находится в вашем рабочем каталоге, как git stash applyбыло бы. (Так что если у вас есть какие-либо изменения в базе, на которой был создан тайник, они будут потеряны).
peterflynn
4
Обратите внимание, что для git stash applyобъединения изменений в файле, который был изменен в рабочем дереве с момента его сохранения, этот файл в рабочем дереве должен быть подготовлен. Для автоматического слияния одинаковые файлы не могут быть изменены как в рабочей копии, так и в сохраненной копии для объединения. Наконец, применение stash не удаляет элемент из stash, как git stash popбыло бы.
Вилле
46

Если вы используете git stash applyвместо git stash pop, он будет применять тайник к вашему рабочему дереву, но все равно сохраните тайник.

После этого вы можете add/ commitфайл, который вы хотите, а затем сбросить оставшиеся изменения.

Тим Хениган
источник
2
Чтобы выскочить конкретный тайник: git stash pop stash@{0}(Перечислите спрятанных изменения: git stash list)
MrYoshiji
Если другие файлы в тайнике вызывают конфликт слияния, git не позволяет мне зафиксировать файл, который я хочу :(
cambuncsive
33

Существует простой способ получить изменения из любой ветки, включая тайники:

$ git checkout --patch stash@{0} path/to/file

Вы можете опустить спецификацию файла, если вы хотите патчить во многих частях. Или пропустите патч (но не путь), чтобы получить все изменения в одном файле. Замените 0на номер тайника из git stash list, если у вас их больше одного. Обратите внимание, что это как diff, и предлагает применить все различия между ветвями. Чтобы получить изменения только от одного коммита / тайника, взгляните на git cherry-pick --no-commit.

WALF
источник
Копирует ли это точный файл из тайника, или он сливается? В случае копирования, если у вас есть какие-либо изменения с момента создания тайника, они будут потеряны.
Даниэль
2
@Danijel Читать git help checkout. --patchвыполняет интерактивное слияние, применяет любой блок (и), который вы одобряете в оболочке (или все, что вы сохраняете, если решите eудалить патч). Один только путь перезапишет файл, как я написал, «все изменения».
Уолф
1
небольшое улучшение: git config --global alias.applydiffat '!git checkout --patch "$1" -- $(git diff --name-only "$1"^ "$1")' - тогда в процессе git applydiffat stash@{4}используются только файлы, которые изменились между тайником и его родителем.
Марк
25

Короткий ответ

Чтобы увидеть весь файл: git show stash@{0}:<filename>

Чтобы увидеть разницу: git diff stash@{0}^1 stash@{0} -- <filename>

Любош Турек
источник
1
Я не думаю, что он / она был заинтересован в просмотре тайника для конкретного файла - только выскочил этот файл.
Амальговинус
1
Это то, что я искал. Затем вы можете заменить diffс difftoolиспользовать ваш любимый внешний диф.
Пит Бритс
19
$ git checkout stash@{0} -- <filename>

Ноты:

  1. Убедитесь, что вы поставили пробел после "-" и параметра имени файла

  2. Замените ноль (0) вашим конкретным номером тайника. Чтобы получить список тайников, используйте:

    git stash list
    

На основании ответа Якуба Наренбского - более короткая версия

Баран
источник
11

Вы можете получить diff для тайника с помощью « git show stash@{0}» (или любого другого номера тайника; см. «Git stash list»). Легко извлечь раздел diff для одного файла.

Натан Кухня
источник
2
Все еще проще для умов, подобных мне: используйте, git show stashчтобы показать самый верхний тайник (обычно единственный, который у вас есть). Точно так же вы можете показать разницу между вашей текущей веткой и тайником с git diff head stash.
Dizzley
5

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

Если вы сделаете, git stashчтобы спрятать их все, git stash applyвернуть их снова и затем git checkout f.cв файл, чтобы эффективно сбросить его.

Если вы хотите распаковать этот файл, запустите a, git reset --hardа затем git stash applyснова запустите , воспользовавшись тем, что git stash applyне очищает diff из стека stash.

Philluminati
источник
1

Если спрятанные файлы необходимо объединить с текущей версией, используйте предыдущие способы, используя diff. В противном случае вы можете использовать их git popдля распаковки, git add fileWantToKeepдля размещения вашего файла, и сделать git stash save --keep-index, чтобы спрятать все, кроме того, что находится на сцене. Помните, что отличие этого способа от предыдущих заключается в том, что он «выталкивает» файл из stash. Предыдущие ответы сохраняют git checkout stash@{0} -- <filename>его в соответствии с вашими потребностями.

Привет Сой Эду Фелиз Навидад
источник
0

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

git diff stash^! -- <filename> | git apply

Как правило, это лучше, чем использовать, git checkoutпотому что вы не потеряете все изменения, внесенные в файл с момента создания тайника.

cambunctious
источник