Как мне извлечь один файл (или изменения в файл) из 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.


Копирует ли это точный файл из тайника, или он сливается? В случае копирования, если у вас есть какие-либо изменения с момента создания тайника, они будут потеряны.
— Даниэль

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.


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потому что вы не потеряете все изменения, внесенные в файл с момента создания тайника.


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.