Возврат нескольких коммитов и слияние обратно в конкретный коммит без принудительного нажатия - PullRequest
0 голосов
/ 14 февраля 2019

Проблема:

  • Удаленный репозиторий с ~ 50 коммитами, которые должны быть возвращены к 1 конкретному коммиту (например, git reset --hard "my конкретный коммит")
  • фиксирует и объединяет в удаленном
  • вообще без принудительного нажатия

Я знаю, что могу сделать git revert -n "мой конкретный коммит" .. ГОЛОВИТЬ проблемус тем, что он останавливается при первом слиянии.передача -m 1 исправит это, но затем остановится без слияний.

git reset --hard и принудительное нажатие - НЕ вариант.Я не могу принудительно нажать на этот хост git.

Ситуация: (зачем мне это нужно)

  • Плохие изменения в мастере, поэтому мы проверяемновую ветвь и хотите построить новую сборку из старого состояния (из «конкретного коммита»), чтобы мы могли затем развернуть эту сборку в prod.
  • Отдельные коммиты легко вернуть, но массовый возврат занимает много времени,в лучшем случае я бы хотел отменить множество коммитов одновременно

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

Ответы [ 3 ]

0 голосов
/ 15 февраля 2019

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.

0 голосов
/ 15 февраля 2019
git reset --hard good_commit_hash
git reset --soft current_commit_hash
git commit

будет делать то, что вы хотите.

(Вы можете просто использовать "@ {u}" как "current_commit_hash", если ветвь upstream (удаленная) - это место, где вы хотите зафиксировать коммит.)

Short: если вы хотите выполнить какую-либо очистку в своем хранилище, используйте git reset.Это своего рода швейцарский армейский нож.

0 голосов
/ 15 февраля 2019

Вы можете использовать git checkout -p <sha1>, чтобы вернуть рабочее дерево к заданному sha1, не меняя, где находится ваша текущая ветвь.

git checkout -p "my specific commit"

Ответьте «a» на каждый вопрос - я не нашел, как заставитьэто неинтерактивный вариант, хотя вы можете использовать команду Linux yes - она ​​просто наберет «у» навсегда:

yes | git checkout -p "my specific commit"

Затем подтвердите и нажмите

git commit -m'Reverting to my specific commit'
git push

Результатэто один коммит, который отменяет плохую историю одним махом.

РЕДАКТИРОВАТЬ: Предупреждение : Если файлы были удалены из my specific commit в текущий HEAD, эта команда не восстановит их.Поэтому решения, предложенные @torek и @Marcus, предпочтительнее этого.

...