Как отменить коммит git в удаленном репозитории
Перейти к содержимому

Как отменить коммит git в удаленном репозитории

  • автор:

 

Обзор команд Git для отмены изменений

Предположим, что мы случайно удалили файл, например myfile.txt :

Для его восстановления выполняем команду:

Отменяем индексирование файла

Допустим, мы по ошибке проиндексировали файл, выполнив команду git add myfilename . Для отмены этого действия воспользуемся командой:

Восстанавливаем предыдущие версии

Посмотрим, как восстановить более ранние версии в случае необходимости. С помощью команды git log переходим в историю коммитов, выбираем код SHA ранней версии (достаточно первых символов) и выполняем команду git checkout <SHA> :

Получаем следующее сообщение:

Вы увидите, что находитесь в более ранней версии. Вышеприведенная команда вывода checkout объясняет ситуацию. Эти изменения можно сохранить, если создать новую ветку.

Оказавшись в состоянии “detached HEAD” вполне можно запаниковать, не зная, как вернуться к последней версии в главной ветке master . Посмотрим, как это сделать:

Откатываем изменения на один коммит

Для отмены предыдущего коммита выполняем команду:

Удаляем неотслеживаемый файл

Допустим, вы добавили файлы, которые еще не подготовлены к коммиту. Чтобы от них избавиться, выполняем команду:

Это пробный запуск, который отображает файлы, подлежащие удалению. Подтверждаем выполнение этой операции командой:

Отменяем git init

Работая с Git, вы инициализируете проект с помощью git init . Для отмены данной операции просто удаляем файл .git из каталога.

Дополнительные команды: удаляем файл из удаленного репозитория

При работе с Git и GitHub/GitLab можно случайно отправить файл в удаленный репозиторий. В таком случае возникает необходимость его удалить. Рассмотрим ситуацию на примерах. Создаем удаленный репозиторий GitHub и локально его клонируем:

Меняем рабочий каталог на клонированный репозиторий. Как видно, здесь есть файлы README.md и .git .

Далее создаем файл wrong.txt , который мы отправим в удаленный репозиторий:

Файл wrong.txt добавлен в удаленный репозиторий.

Удаляем файл

При необходимости удалить файл как из удаленного каталога, так и локальной файловой системы выполняем команды:

Как видим, цель достигнута:

Удаляем файл из удаленного репозитория Git, но сохраняем его локально

Повторяем последовательность действий по созданию файла wrong.txt , который на этот раз мы удалим только из удаленного репозитория:

Отправляем файл wrong.txt :

Удаляем wrong.txt только из удаленного репозитория. Для этого используем тег cached .

Проверяем локальный репозиторий:

Проверяем удаленный каталог:

wrong.txt успешно удален из удаленного репозитория!

Со списком наиболее распространенных команд Git и GitHub вы можете ознакомиться по ссылке Git and GitHub Cheatsheet.

Отмена коммитов и изменений

В этом разделе мы обсудим доступные стратегии и команды Git для выполнения отмены изменений. Прежде всего необходимо отметить, что в Git не существует традиционной системы отмены, как в текстовых редакторах. Лучше воздержаться от сопоставления операций Git с какой бы то ни было традиционной концепцией отмены изменений. Кроме того, Git имеет собственную систему терминов для операций отмены, и в обсуждении лучше всего использовать их. В числе таких терминов — сброс (reset), возврат (revert), переключение (checkout), очистка (clean) и другие.

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

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

Поиск утерянного: просмотр старых коммитов

В основе любой системы управления версиями лежит идея хранения «безопасных» копий проекта, чтобы у разработчиков не возникало опасений безвозвратно испортить базу кода. Когда в проекте сохранена история коммитов, можно повторно оценивать и анализировать любые ранее выполненные коммиты. Один из лучших инструментов для просмотра истории репозитория Git — команда git log . В примере ниже мы используем команду git log для получения последних коммитов популярной графической библиотеки с открытым исходным кодом.

Каждый коммит имеет уникальный идентифицирующий хеш SHA-1. Эти идентификаторы используются для перемещения по временной шкале коммитов и возвращения к коммитам. По умолчанию git log показывает только коммиты текущей выбранной ветки. Но не исключено, что искомый коммит находится в другой ветке. Для просмотра всех коммитов во всех ветках используется команда git log —branches=* . Команда git branch используется для просмотра и посещения других веток. Так, команда git branch -a возвращает список имен всех известных веток. Просмотреть весь журнал коммитов одной из этих веток можно с помощью команды git log .

После того как вы нашли ссылку на нужный коммит в истории, для перехода к нему можно использовать команду git checkout . Команда git checkout — это простой способ «загрузить» любой из этих сохраненных снимков на компьютер разработчика. При стандартном процессе разработки указатель HEAD обычно указывает на главную ветку main или другую локальную ветку. Но при переключении на предыдущий коммит HEAD указывает уже не на ветку, а непосредственно на сам коммит. Такая ситуация называется состоянием открепленного указателя HEAD , и ее можно представить так:

Переход к старой версии файла не перемещает указатель HEAD . Он остается в той же ветке и в том же коммите, что позволяет избежать открепления указателя HEAD. После этого можно выполнить коммит старой версии файла в новый снимок состояния, как и в случае других изменений. Соответственно, такое использование команды git checkout применительно к файлу позволяет откатиться к прежней версии отдельного файла. Для получения дополнительной информации об этих двух режимах посетите страницу команды git checkout .

Просмотр старых версий

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

Допустим, история вашего проекта выглядит примерно так:

Для просмотра коммита «Make some important changes to hello.txt» можно использовать команду git checkout в следующем виде:

Это приведет к тому, что ваш рабочий каталог будет в точности соответствовать состоянию коммита a1e8fb5 . Вы можете просматривать файлы, компилировать проект, запускать тесты и даже редактировать файлы, не боясь потерять текущее состояние проекта. Никакие внесенные здесь изменения не будут сохранены в репозитории. Чтобы продолжить разработку, необходимо вернуться к текущему состоянию проекта:

Предположим, вы ведете разработку в главной ветке main по умолчанию. При каждом возвращении в ветку main можно использовать команду git revert или git reset для отмены нежелательных изменений.

Отмена коммита снимка

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

Займемся отменой коммита 872fa7e Try something crazy . Возможно, безумный эксперимент зашел слишком далеко.

Отмена коммита с помощью git checkout

С помощью команды git checkout мы можем перейти к предыдущему коммиту , a1e8fb5, и вернуть репозиторий в состояние, предшествовавшее этому безумному коммиту. Переход к отдельному коммиту переведет репозиторий в состояние открепленного указателя HEAD . Работа при этом перестает принадлежать какой-либо из веток. При открепленном указателе HEAD все новые коммиты будут оставаться без родителя, пока вы не вернете ветки в положенное состояние. «Сборщик мусора» в Git удаляет коммиты без родителя. Этот сервис работает с определенными интервалами и удаляет такие коммиты без возможности восстановления. Чтобы такие коммиты не были удалены «сборщиком мусора», перед их выполнением нужно убедиться, что мы работаем в ветке.

При наличии открепленного указателя HEAD можно выполнить команду git checkout -b new_branch_without_crazy_commit . Она создаст новую ветку с именем new_branch_without_crazy_commit и совершит переход в это состояние. Теперь репозиторий находится на новой временной шкале, где коммита 872fa7e не существует. На этом этапе мы можем продолжить работу в новой ветке, где коммита 872fa7e не существует и его можно считать «отмененным». К сожалению, если вам нужна предыдущая ветка (возможно, это главная ветка main ), такая стратегия не подходит. Поэтому рассмотрим другие стратегии отмены. Более детальную информацию и примеры см. в нашей подробной статье о git checkout .

Отмена публичного коммита с помощью git revert

Предположим, мы вернулись к исходному примеру истории коммитов. Истории, в которую входит коммит 872fa7e . В этот раз попробуем отмену путем обратной операции. При исполнении команды git revert HEAD Git создаст новый коммит с операцией, обратной последнему коммиту. В текущую историю ветки будет добавлен новый коммит, и она будет выглядеть следующим образом:

На этом этапе мы снова технически «отменили» коммит 872fa7e . Хотя коммит 872fa7e по-прежнему существует в истории, новый коммит e2f9a78 отменил изменения 872fa7e . В отличие от нашей предыдущей стратегии переключения с помощью команды checkout, мы можем продолжить работать с этой же веткой, поэтому данная стратегия является удовлетворительной. Это идеальный способ отмены при работе в открытых общих репозиториях, однако если у вас есть требование вести минимальную «очищенную» историю Git, эта стратегия может не подойти.

Отмена коммита с помощью git reset

Рассмотрение этой стратегии отмены мы продолжим на нашем рабочем примере. Команда git reset — это расширяемая команда с разнообразными функциями и вариантами использования. Если мы выполним команду git reset —hard a1e8fb5 , история коммитов будет сброшена до указанного коммита. Просмотр истории коммитов с помощью команды git log теперь будет выглядеть так:

Вывод команды log показывает, что коммиты e2f9a78 и 872fa7e больше не существуют в истории. На этом этапе мы можем продолжить работу и создавать новые коммиты так, словно «безумных» коммитов никогда не было. Этот метод отмены изменений оставляет историю максимально чистой. Отмена с помощью команды reset отлично подходит для локальных изменений, но при работе в общем удаленном репозитории создает сложности. Если у нас есть общий удаленный репозиторий, в котором с помощью команды push опубликован коммит 872fa7e , и мы попытаемся выполнить команду git push для ветки, в которой с помощью команды reset была сброшена история, система Git обнаружит это и выдаст ошибку. Git будет считать, что публикуемая ветка не была обновлена, поскольку в ней отсутствуют коммиты. В таких случаях лучше использовать отмену с помощью команды git revert .

Отмена последнего коммита

В предыдущем разделе мы рассмотрели различные стратегии отмены коммитов. Эти стратегии также можно применять и к последнему коммиту. Однако иногда последний коммит можно не удалять и не сбрасывать. Например, если вы просто сделали коммит преждевременно. В этом случае его можно исправить. После того как вы внесете дополнительные изменения в рабочий каталог и добавите их в раздел проиндексированных файлов с помощью команды git add , выполните команду git commit —amend . При этом Git откроет настроенный системный редактор, где вы сможете изменить комментарий к последнему коммиту. Новые изменения будут добавлены в исправленный коммит.

Отмена неотправленных изменений

Пока не выполнен коммит изменений в историю репозитория, они находятся в разделе проиндексированных файлов и в рабочем каталоге. Вам может потребоваться отменить изменения в этих двух областях. Раздел проиндексированных файлов и рабочий каталог являются внутренними механизмами управления состоянием Git. Подробную информацию о том, как работают эти два механизма, см. на странице git reset , где приводится их подробное описание.

Рабочий каталог

Рабочий каталог обычно синхронизируется с локальной файловой системой. Чтобы отменить изменения в рабочем каталоге, можно просто отредактировать файлы с помощью привычного редактора. Git имеет два инструмента для управления рабочим каталогом. Это команда git clean — удобная утилита для отмены изменений в рабочем каталоге, и команда git reset , которую можно вызвать с параметрами —mixed или —hard , чтобы сбросить изменения в рабочем каталоге.

Раздел проиндексированных файлов

Команда git add используется для добавления изменений в раздел проиндексированных файлов. Команда git reset предназначена главным образом для отмены изменений в разделе проиндексированных файлов. Команда reset с параметром —mixed перемещает все ожидающие изменения из раздела проиндексированных файлов обратно в рабочий каталог.

Отмена публичных изменений

При командной работе в удаленных репозиториях необходимо подходить к отмене изменений с особой осторожностью. Команда git reset , как правило, считается методом локальной отмены. Ее следует использовать для отмены изменений в частной ветке. Она безопасно изолирует удаление коммитов от других веток, которые могут использоваться другими разработчиками. Проблемы возникают, когда команда reset выполняется в общей ветке и затем эта ветка удаленно публикуется с помощью команды git push . В этом случае Git блокирует выполнение команды push и сообщает, что публикуемая ветка устарела, поскольку в ней отсутствуют коммиты, которые есть в удаленной ветке.

Предпочтительная команда для отмены общей истории коммитов — git revert . Команда revert безопаснее, чем reset, так как она не удаляет коммиты из общей истории. Команда revert сохраняет отменяемые вами коммиты и создает новый коммит с операцией, обратной последнему коммиту. Этот метод можно безопасно применять в общих распределенных рабочих средах, так как удаленный разработчик может выполнить пул ветки и получить новый коммит, который отменяет его нежелательный коммит.

Резюме

Мы рассмотрели множество общих стратегий отмены изменений в Git. Важно помнить, что отменить изменения в проекте Git можно несколькими способами. Кроме того, здесь были затронуты и более сложные темы, которые подробно рассматриваются на страницах, посвященных соответствующим командам Git. Наиболее часто используемые инструменты для отмены — это команды git checkout, git revert и git reset . Вот несколько ключевых моментов, о которых следует помнить.

 

  • Обычно после коммита внесенные изменения отменить невозможно
  • Используйте git checkout для переходов и просмотра истории коммитов
  • Команда git revert — лучший инструмент для отмены общих публичных изменений.
  • Команду git reset лучше всего использовать для отмены локальных частных изменений.

Помимо основных команд отмены, мы рассмотрели другие команды Git: git log для поиска потерянных коммитов, git clean для отмены изменений, не подтвержденных коммитами, и git add для изменения индексирования.

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

Remove last commit from remote Git repository

How can I remove the last commit from a remote GIT repository such as I don’t see it any more in the log?

If for example git log gives me the following commit history

how can I go to

knittl's user avatar

2 Answers 2

Be careful that this will create an "alternate reality" for people who have already fetch/pulled/cloned from the remote repository. But in fact, it’s quite simple:

If you want to still have it in your local repository and only remove it from the remote, then you can use:

Как удалить коммит в удаленном репозитории?

Сделал коммит. Не заметил как в него попала пара сотен МБт дерьмища. Потом запушил и тут я обнаружил свой косяк. Подскажите, как в удаленном и локальном репозитории удалить последний коммит с сохранением изменений в рабочей дирректории?

git reset —hard HEAD

1 неподходит, т.к. возвращает меня к комиту 3х месячной давности! Нужно удалить коммит, но чтобы все изменения в рабочей директории сохранились! Спасибо!

Чем не устраивает —soft?

1. удаляешь коммит локально (например, через git rebase -i)

2. git push -f origin branchname

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

reset без —hard. —soft/—mixed на выбор. Далее вычищаешь всё ненужно, commit, push -f

. и нет проблем синхронизации.

Смотри-ка, сколько в трэде любителей push —force!

. и есть огромные проблемы с размером репозитория!

в него попала пара сотен МБт дерьмища

или git commit —amend

Через git rm удалить всё ненужное.
Потом git commit —amend, чтобы ранее добавленное и свежеудалённое аннигилировались.
Потом push —force, чтобы перезаписать последний коммит.
Profit!

А как ещё ты предлагаешь удалять часть истории?

В сабжевом случае хорошо подходит это. В том и цимес же, что историю на remote удолять не надо )

+1, сам так делаю, когда накосячу 🙂

Не заметил как в него попала пара сотен МБт дерьмища

Кстати, ТС, а как это случилось? Убогий gitignore?

Каким образом это подходит, если при этом подходе до скончания времен при любом git clone будет тянутся

Во-первых, вопрос именно о том, как её удалить.

Во-вторых, почему не надо-то? Мастер не стоит, но пока это моя бранча — что хочу, то и ворочу.

пока это моя бранча — что хочу, то и ворочу.

Даже если твой бранч уже смержили в мастер?

Не так уж и много + clone не каждый день/неделя/месяц.

А так да, на это внимания не обратил

Вот здесь не понял. С чего ты взял, что это не общая ветка?

Лучше перезапись последнего коммита с push —force в remote, чем мусор на несколько сотен мегабайт в репозитории, который поди <10МБ всего весит.

И за убожество вида revet ‘my shit commit’ в истории тоже надо линчевать.

И за убожество вида revet ‘my shit commit’ в истории тоже надо линчевать

Линчевать много за что можно, но иногда выбора нет. К тому же, смотря на сколько

В случае тонких ошибок полезно это в репе оставить, в назидание, так сказать. К сабжу это не относится, но там по-другому можно повернуть: какого хера gitignore не заюзали? А скорее всего — заюзали, но там шаблонов подходящих не было(на все случаи не напасёшься). Было бы неплохо иметь, скажем gitaccept, указывая какие файлы _отслеживать_, а не какие игнорировать. Через gitignore тоже можно, но там костыльно — сомневаюсь, что пригодно для продакшена. Сколько здесь кандидатур на линчевание?)

Из зол вида «сотни МБ хлама в БД до скончания веков» и «перезапись последнего запушенного коммита» — первая куда как больше.

Я очень сильно сомневаюсь, что у ТС огромный и оверпопулярный репозиторий, который форкают каждый день и каждый час отправляют в него PR’ы. А потому этот —force скорее всего никто кроме TC и не заметит.

 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *