Возврат одного файла к предыдущей версии в git

Есть ли способ пропустить различные коммиты в файле. Скажем, я изменил файл 5 раз, и я хочу вернуться к изменению 2 после того, как я уже совершил и нажал на репозиторий.

В моем понимании единственный способ - сохранить много ветвей, не так ли? Если я прав, у меня будет несколько веток через несколько дней, поэтому я, вероятно, не понимаю этого.

Может ли кто-нибудь прояснить это, пожалуйста?

Ответ 1

Начнем с качественного описания того, что мы хотим сделать (многое из этого сказано в ответе Бен Штрауба). Мы совершили несколько коммитов, пять из которых изменили данный файл, и мы хотим вернуть файл в одну из предыдущих версий. Прежде всего, git не поддерживает номера версий для отдельных файлов. Он просто отслеживает контент - коммит - это, по сути, снимок рабочего дерева, а также некоторые метаданные (например, сообщение фиксации). Итак, мы должны знать, какая фиксация имеет версию файла, который мы хотим. Как только мы это узнаем, нам нужно будет сделать новый коммит, возвращая файл в это состояние. (Мы не можем просто обманывать историю, потому что мы уже подтолкнули этот контент и редактировали историю со всеми остальными.)

Итак, начнем с поиска правильной фиксации. Вы можете увидеть коммиты, которые внесли изменения в данный файл очень легко:

git log path/to/file

Если ваши сообщения о фиксации недостаточно хороши, и вам нужно посмотреть, что было сделано с файлом в каждой фиксации, используйте параметр -p/--patch:

git log -p path/to/file

Или, если вы предпочитаете графический просмотр gitk

gitk path/to/file

Вы также можете сделать это, как только вы запустили gitk через меню просмотра; одним из вариантов представления является список путей для включения.

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

# get the version of the file from the given commit
git checkout <commit> path/to/file
# and commit this modification
git commit

(Команда checkout сначала считывает файл в индекс, а затем копирует его в дерево работы, поэтому нет необходимости использовать git add, чтобы добавить его в индекс при подготовке к фиксации.)

Если ваш файл может не иметь простой истории (например, переименования и копии), см. замечание VonC. git может быть направлена ​​на поиск более тщательно для таких вещей, за счет скорости. Если вы уверены, что история простая, вам не нужно беспокоиться.

Ответ 2

Git очень гибкий. Вам не нужны сотни веток, чтобы делать то, что вы просите. Если вы хотите вернуть состояние полностью назад к 2-му изменению (и это действительно изменение, которое уже было выполнено и нажато), используйте git revert. Что-то вроде:

git revert a4r9593432 

где a4r9593432 - это начальные символы хэша коммита, который вы хотите отменить.

Если коммит содержит изменения во многих файлах, но вы просто хотите вернуть только один из файлов, вы можете использовать git reset ( 2-я или 3-я форма):

git reset a4r9593432 -- path/to/file.txt
# the reverted state is added to the staging area, ready for commit
git diff --cached path/to/file.txt        # view the changes
git commit
git checkout HEAD path/to/file.txt        # make the working tree match HEAD           

Но это довольно сложно, и git reset является опасным. Используйте git checkout <hash> <file path> вместо этого, как предполагает Джероми.

Если вы просто хотите посмотреть, как выглядит файл в commit x, вы можете использовать git show:

git show a4r9593432:path/to/file.txt

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

Ответ 3

Скажите, что ваши 5 коммитов: A, B, C, D, E - A являются первыми, а E - последними. Вы хотите вернуть файл file.txt так, как это было после фиксации A. Вам не нужно изучать или запоминать разные версии или параметры команд git - reset и git -checkout. Run:

git show A:file.txt >file.txt

# If you want to commit the older version:
git add file.txt
git commit

Команда git -show показывает содержимое или информацию о любом объекте в репозитории Git. Когда заданы ссылки фиксации, такие как A (или master ^ или HEAD ~ 5 или...), за которыми следует двоеточие, за которым следует имя файла, он покажет содержимое этого файла, как это было после этого фиксации. Тогда это просто вопрос перенаправления вывода в файл.

Ответ 4

Git не думает о версиях файлов. Версия в git - это снимок всего дерева.

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

Я не знаю, есть ли один-liner, который вернет один файл в содержимое из 5 коммитов назад, но решение lo-fi должно работать: checkout master~5, скопировать файл в другое место, checkout master, скопируйте файл обратно, затем выполните.

Ответ 5

Вы можете использовать diff, который отменяет сделанные вами изменения и фиксирует это.

например. Если вы хотите отменить изменения в диапазоне from..to, выполните следующие

git diff to..from > foo.diff  # get a reverse diff
patch < foo.diff
git commit -a -m "Undid changes from..to".