TL; DR
Используйте git read-tree -u <hash>
с последующим созданием нового коммита.
Long
Помните, что Git-репозиторий по сути представляет собой набор коммитов.Каждый коммит содержит полный и полный снимок всех файлов - ну, все файлы, которые находятся в этом коммите, но выраженные таким образом, это звучит тавтологически - плюс некоторые метаданные: имя и адрес электронной почты человека, которыйсделал фиксацию, отметку времени, когда они сделали это, сообщение в журнале, где они объясняют почему они сделали это, и так далее.(Одна из важных частей метаданных - это родительский идентификатор хита коммита, но на этот раз нам вообще не нужно заниматься этой частью!)
Истинное имялюбой данный коммит является его хеш-идентификатором.Вы можете временно вставить этот коммит в свое рабочее дерево, выполнив:
git checkout <hash-id>
, но, конечно, это просто даст вам «отсоединенную ГОЛОВУ», и когда вы снова присоедините свою ГОЛОВУ, выполнив git checkout master
,вы вернулись к сломанному снимку исходного кода.
Итак, каждый коммит представляет собой снимок всех файлов.В какой-то момент у вас был моментальный снимок, который вам нравится, и вы хотите вернуться к нему, но на самом деле без прямого перехода к с помощью git checkout <hash>
.Это означает, что вам нужно сделать новый коммит, чей снимок совпадает с существующим коммитом.Есть некоторый хороший коммит с некоторым существующим хеш-идентификатором, и git checkout <hash>
это ... ОК, но вы хотите новый коммит с новым хеш-идентификатором, который в противном случае совпадает с хорошимна (новом) конце текущей ветви.Этот новый коммит должен иметь в качестве своего родителя текущий коммит, точно так же, как любой новый коммит имеет текущий коммит в качестве своего родителя.
Метод, описанный в joanis 'answer будет работать: он сравнивает то, что находится в выбранном коммите, с тем, что находится в текущем рабочем дереве, и спрашивает вас, хотите ли вы настроить рабочее дерево, чтобы оно соответствовало выбранному коммиту, для каждого diff-hunk в каждом файле.Ответ «а» говорит: «взять все для этого файла», но это оставляет вам задачу вставить много ответов, по одному для каждого отдельного файла.
Проще - но некорректно - использовать другойрежим git checkout
:
git checkout <good-commit-hash> -- .
Этот режим сообщает git checkout
: Не переключать на другой коммит.Ни в коем случае не изменяйте HEAD
!Но do пройдите через базу данных Git, чтобы найти файлы, которые соответствуют спецификатору пути .
, которые находятся в хорошем коммите.Для каждого соответствующего файла возьмите его из этого коммита, скопируйте его в мой текущий индекс и скопируйте в мое текущее рабочее дерево.
Если вы делаете это на верхнем уровне вашей работы,tree, все файлы в хорошем коммите будут соответствовать пути-спецификатору .
.Так что если у вас есть файлы README
и main.py
в коммите <hash>
, они заменят README
и main.py
в вашем индексе и README
и main.py
в вашем рабочем дереве.Вы будете готовы к фиксации.
Недостаток в следующем: Что если у вас сейчас есть третий файл, скажем, bug.txt
, который не существует?t в хорошем коммите, на который вы пытаетесь переключиться обратно? Чтобы это исправить, вы должны явно удалить любой файл в вашем текущем индексе и рабочем дереве, которое не в хорошем коммите.
Вы можете вручную удалить любые такие файлы.Либо таких файлов может не быть, и в этом случае проблема носит чисто теоретический характер.Но есть гарантированное лекарство, которое безвредно, если нет проблем.Вы можете просто начать с:
git rm -r .
, чтобы удалить все .Это удаляет bug.txt
и main.py
и README
.Последующее git checkout <good-commit-hash> -- .
возвращает хорошие main.py
и README
, а bug.txt
остается удаленным, так что вы готовы к git commit
результату.
Естькоманда Git более низкого уровня, которая делает это за вас, и это git read-tree
.Эта команда не для повседневного использования, 1 , но это не повседневная проблема.В данном конкретном случае с помощью:
git read-tree -u <good-commit-hash>
расскажитеs команда слесарного дела: Сотри мой текущий индекс.Получите файлы с заданным идентификатором хеша и поместите их в мой индекс.Везде, где вы полностью удалили файл, удалите if из моего рабочего дерева.Везде, где вы заменили файл, замените его и в моем рабочем дереве. Это точно такой же результат с точки зрения файлов и их содержимого, что вы получаете с git rm -r .; git checkout <good-commit-hash> -- .
, за исключением того, что он более эффективен.
В любом случае вы теперь готовы сделать новый коммит, используя текущее содержимое индекса, которое теперь совпадает с текущим содержимым рабочего дерева (за исключением неотслеживаемых файлов, которые обычно не отслеживаются).
1 Команда git read-tree
на самом деле является сантехнической командой , предназначенной для создания новых причудливых интерфейсных команд, которые делают что-то, ну, конечно, причудливое.Например, кто-то может когда-нибудь написать команду git revert-to
, которая состоит из нескольких проверок состояния, за которыми следуют git read-tree -u <hash>
и git commit
.