Как мне вернуть репозиторий Git к предыдущей фиксации?

Как мне вернуться из моего текущего состояния к моментальному снимку, сделанному при определенной фиксации?

Если да git log, то получаю следующий результат:

$ git log
commit a867b4af366350be2e7c21b8de9cc6504678a61b`
Author: Me <[email protected]>
Date:   Thu Nov 4 18:59:41 2010 -0400

blah blah blah...

commit 25eee4caef46ae64aa08e8ab3f988bc917ee1ce4
Author: Me <[email protected]>
Date:   Thu Nov 4 05:13:39 2010 -0400

more blah blah blah...

commit 0766c053c0ea2035e90f504928f8df3c9363b8bd
Author: Me <[email protected]>
Date:   Thu Nov 4 00:55:06 2010 -0400

And yet more blah blah...

commit 0d1d7fc32e5a947fbd92ee598033d85bfc445a50
Author: Me <[email protected]>
Date:   Wed Nov 3 23:56:08 2010 -0400

Yep, more blah blah.

Как мне вернуться к фиксации с 3 ноября, то есть к фиксации 0d1d7fc?

Ответов (25)

Решение

Это во многом зависит от того, что вы подразумеваете под «откатом».

Временно переключитесь на другую фиксацию

Если вы хотите временно вернуться к нему, подурачиться, а затем вернуться туда, где вы находитесь, все, что вам нужно сделать, это проверить желаемый коммит:

# This will detach your HEAD, that is, leave you with no branch checked out:
git checkout 0d1d7fc32

Или, если вы хотите совершать коммиты, пока вы там, продолжайте и создайте новую ветку, пока вы в ней:

git checkout -b old-state 0d1d7fc32

Чтобы вернуться туда, где вы были, просто проверьте ветку, в которой вы были снова. (Если вы внесли изменения, как всегда при переключении ветвей, вам придется иметь с ними дело соответствующим образом. Вы можете выполнить сброс, чтобы выбросить их; вы можете спрятать, оформить заказ, припрятать поп, чтобы взять их с собой; вы можете зафиксировать их в ветку там, если вы хотите ветку там.)

Жесткое удаление неопубликованных коммитов

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

# This will destroy any local modifications.
# Don't do it if you have uncommitted work you want to keep.
git reset --hard 0d1d7fc32

# Alternatively, if there's work to keep:
git stash
git reset --hard 0d1d7fc32
git stash pop
# This saves the modifications, then reapplies that patch after resetting.
# You could get merge conflicts, if you've modified things which were
# changed since the commit you reset to.

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

Отменить опубликованные коммиты с новыми коммитами

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

# This will create three separate revert commits:
git revert a867b4af 25eee4ca 0766c053

# It also takes ranges. This will revert the last two commits:
git revert HEAD~2..HEAD

#Similarly, you can revert a range of commits using commit hashes (non inclusive of first hash):
git revert 0d1d7fc..a867b4a

# Reverting a merge commit
git revert -m 1 <merge_commit_sha>

# To get just one, you could use `rebase -i` to squash them afterwards
# Or, you could do it manually (be sure to do this at top level of the repo)
# get your index and work tree into the desired state, without changing HEAD:
git checkout 0d1d7fc32 .

# Then commit. Be sure and write a good message describing what you just did
git commit

На самом деле git-revertсправочная страница в своем описании охватывает многое из этого. Еще одна полезная ссылка - это раздел git-scm.com, в котором обсуждается git-revert .

Если вы решите, что в конце концов не хотите возвращаться, вы можете отменить возврат (как описано здесь) или вернуться к предыдущему состоянию (см. Предыдущий раздел).

Вы также можете найти этот ответ полезным в этом случае:
Как я могу переместить HEAD обратно в предыдущее место? (Отдельная голова) & Отменить коммиты

Скажем, у вас есть следующие коммиты в текстовом файле с именем ~/commits-to-revert.txtgit log --pretty=oneline их получал)

fe60adeba6436ed8f4cc5f5c0b20df7ac9d93219
0c27ecfdab3cbb08a448659aa61764ad80533a1b
f85007f35a23a7f29fa14b3b47c8b2ef3803d542
e9ec660ba9c06317888f901e3a5ad833d4963283
6a80768d44ccc2107ce410c4e28c7147b382cd8f
9cf6c21f5adfac3732c76c1194bbe6a330fb83e3
fff2336bf8690fbfb2b4890a96549dc58bf548a5
1f7082f3f52880cb49bc37c40531fc478823b4f5
e9b317d36a9d1db88bd34831a32de327244df36a
f6ea0e7208cf22fba17952fb162a01afb26de806
137a681351037a2204f088a8d8f0db6e1f9179ca

Создайте сценарий оболочки Bash, чтобы вернуть каждый из них:

#!/bin/bash
cd /path/to/working/copy
for i in `cat ~/commits-to-revert.txt`
do
    git revert $i --no-commit
done

Это возвращает все обратно в предыдущее состояние, включая создание и удаление файлов и каталогов, фиксирует это в своей ветке, и вы сохраняете историю, но вы вернули ее обратно к той же файловой структуре. Почему у Git нет, не git revert --to <hash> понимаю.

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

git add . && git checkout master -f

Краткое описание:

  • Он НЕ будет создавать никаких коммитов, как это git revertделает.
  • Он НЕ отсоединит вашу ГОЛОВУ, как это git checkout <commithashcode>делает.
  • Он будет отменять все ваши локальные изменения и УДАЛЯТЬ все добавленные файлы с момента последней фиксации в ветке.
  • Он работает только с именами веток, поэтому таким образом вы можете вернуться только к последней фиксации в ветке.

Я нашел гораздо более удобный и простой способ добиться указанных выше результатов:

git add . && git reset --hard HEAD

где HEAD указывает на последнюю фиксацию в вашей текущей ветке.

Это тот же код, что и предложил boulder_ruby, но я добавил git add . раньше, git reset --hard HEAD чтобы стереть все новые файлы, созданные с момента последнего коммита, так как это то, чего, по моему мнению, ожидает большинство людей при возврате к последней фиксации.

Существует команда (не являющаяся частью ядра Git, но она находится в пакете git-extras ) специально для возврата и постановки старых коммитов:

git back

На странице руководства он также может использоваться как таковой:

# Remove the latest three commits
git back 3

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

git push -f ...

И не только git push .

Лучшим вариантом для меня и, вероятно, других является вариант сброса Git:

git reset --hard <commidId> && git clean -f

Для меня это был лучший вариант! Это просто, быстро и эффективно!


** Примечание: ** Как упоминалось в комментариях, не делайте этого, если вы делитесь своей веткой с другими людьми, у которых есть копии старых коммитов.

Также из комментариев, если вам нужен менее `` безумный '' метод, вы можете использовать

git clean -i

Здесь много сложных и опасных ответов, но на самом деле это просто:

git revert --no-commit 0766c053..HEAD
git commit

Это вернет все из HEAD обратно в хэш фиксации, то есть воссоздаст это состояние фиксации в рабочем дереве, как если бы каждая фиксация после 0766c053 была возвращена. Затем вы можете зафиксировать текущее дерево, и оно создаст совершенно новую фиксацию, по существу эквивалентную фиксации, к которой вы «вернулись».

( --no-commit Флаг позволяет git отменить все коммиты сразу - в противном случае вам будет предложено ввести сообщение для каждого коммита в диапазоне, засоряя вашу историю ненужными новыми коммитами.)

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

Выберите требуемый коммит и проверьте его с помощью

git show HEAD
git show HEAD~1
git show HEAD~2 

пока вы не получите требуемую фиксацию. Чтобы ГОЛОВА указала на это, выполните

git reset --hard HEAD~1

или git reset --hard HEAD~2 что-то еще.

Если вы хотите «отменить фиксацию», стереть последнее сообщение фиксации и вернуть измененные файлы в промежуточную стадию, вы должны использовать команду:

git reset --soft HEAD~1
  • --softуказывает, что незафиксированные файлы должны быть сохранены как рабочие файлы, в отличие от --hardкоторых они будут отброшены.
  • HEAD~1это последняя фиксация. Если вы хотите откатить 3 коммита, вы можете использовать HEAD~3. Если вы хотите вернуться к определенному номеру ревизии, вы также можете сделать это, используя его хэш SHA.

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

Источник: http://nakkaya.com/2009/09/24/git-delete-last-commit/

Вы можете сделать это с помощью следующих двух команд:

git reset --hard [previous Commit SHA id here]
git push origin [branch Name] -f

Это удалит ваш предыдущий коммит Git.

Если вы хотите сохранить свои изменения, вы также можете использовать:

git reset --soft [previous Commit SHA id here]

Тогда он сохранит ваши изменения.

Я считаю, что некоторые люди могут прийти к этому вопросу, желая знать, как откатить зафиксированные изменения, которые они сделали в своем главном устройстве, т.е. выбросить все и вернуться к origin / master, и в этом случае сделайте следующее:

git reset --hard origin/master

https://superuser.com/questions/273172/how-to-reset-master-to-origin-master

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

git reset <SHA>

Если изменения из предыдущего коммита в HEAD не требуются и просто отмените все изменения, выполните:

git reset --hard <SHA>

Чтобы полностью очистить каталог программиста от случайных изменений, мы использовали:

git add -A .
git reset --hard HEAD

Просто git reset --hard HEAD избавится от модификаций, но не избавится от «новых» файлов. В их случае они случайно перетащили важную папку куда-то наугад, и все эти файлы были обработаны Git как новые, поэтому reset --hard не исправил. Запустив git add -A . заранее, он явно отслеживал их всех с помощью git, чтобы они были уничтожены сбросом.

Это еще один способ напрямую сбросить до недавнего коммита.

git stash
git stash clear

Он напрямую очищает все изменения, которые вы делали с момента последней фиксации.

PS: Есть небольшая проблема; он также удаляет все недавно сохраненные изменения тайника. Что, я думаю, в большинстве случаев не имеет значения.

Предполагая, что вы говорите о мастере и о соответствующей ветке (при этом, это может быть любая рабочая ветка, которая вас интересует):

# Reset local master branch to November 3rd commit ID
git reset --hard 0d1d7fc32e5a947fbd92ee598033d85bfc445a50

# Reset remote master branch to November 3rd commit ID
git push -f origin 0d1d7fc32e5a947fbd92ee598033d85bfc445a50:master

Я нашел ответ в сообщении в блоге (сейчас больше не существует)

Обратите внимание, что это «Сброс» и «Принудительное изменение удаленного доступа», так что, если другие члены вашей команды уже выполнили git pull, вы создадите для них проблемы. Вы уничтожаете историю изменений, что является важной причиной, по которой люди вообще используют git.

Лучше использовать возврат (см. Другие ответы), чем сброс. Если вы команда из одного человека, это, вероятно, не имеет значения.

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

git reset --hard HEAD

Вы можете выполнить все эти начальные шаги самостоятельно и вернуться в репозиторий Git.

  1. Получите последнюю версию своего репозитория из Bitbucket с помощью git pull --allкоманды.

  2. Запустите команду Git log с -n 4вашего терминала. Число после -nопределяет количество коммитов в журнале, начиная с самого последнего коммита в вашей локальной истории.

    $ git log -n 4
    
  3. Сбросьте заголовок истории вашего репозитория, используя git reset --hard HEAD~Nгде N - количество коммитов, которые вы хотите вернуть. В следующем примере голова будет возвращена на одну фиксацию, до последней фиксации в истории репозитория:

  4. Отправьте изменение в репозиторий Git, используя, git push --forceчтобы принудительно отправить изменение.

Если вы хотите, чтобы репозиторий Git сохранял предыдущую фиксацию:

git pull --all
git reset --hard HEAD~1
git push --force

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


БЫСТРЫЙ И ГРЯЗНЫЙ : в зависимости от обстоятельств, быстрое и грязное на самом деле может быть очень ХОРОШИМ. Мое решение здесь НЕ заменяет безвозвратно файлы, которые у вас есть в вашем рабочем каталоге, файлами, извлеченными / извлеченными из глубин репозитория git, скрывающимся под вашим каталогом .git /, с использованием дьявольски умных и дьявольски мощных команд git, из которых есть много. ВАМ НЕ ОБЯЗАТЕЛЬНО ВЫПОЛНЯЕТСЯ ТАКОЕ ГЛУБОКОЕ ПОГРУЖЕНИЕ, ЧТОБЫ ВОССТАНОВИТЬ ситуацию, которая может показаться катастрофической, и попытка сделать это без достаточного опыта может оказаться фатальной .


  1. Скопируйте весь каталог и назовите его как-нибудь, например «мой проект - копия». Предполагая, что ваши файлы репозитория git («репо») находятся в каталоге «мой проект» (место по умолчанию для них, в каталоге с именем «.git»), вы теперь скопируете как свои рабочие файлы, так и файлы репо.

  2. Сделайте это в каталоге «мой проект»:

    .../my project $ git reset --hard [first-4-letters&numbers-of-commit's-SHA]
    

Это вернет состояние репо в «моем проекте» к тому, каким оно было, когда вы сделали эту фиксацию («фиксация» означает моментальный снимок ваших рабочих файлов). Все коммиты с тех пор будут потеряны навсегда в «моем проекте», НО ... они все еще будут присутствовать в репозитории в разделе «мой проект - копия», поскольку вы скопировали все эти файлы, включая те, что находятся в ... /. Git /.

Затем у вас есть две версии в вашей системе ... вы можете проверять, копировать или изменять интересующие файлы или что-то еще из предыдущего коммита. Вы можете полностью отказаться от файлов в разделе «мой проект - копия», если вы решили новую работу, так как восстановленная фиксация никуда не делась ...

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

Git - блестящее творение, но абсолютно никто не может просто «подобрать его на лету»: также люди, которые слишком часто пытаются объяснить это, предполагают предварительное знание других VCS [систем управления версиями] и копаются слишком глубоко слишком рано и совершают другие преступления, такие как использование взаимозаменяемых терминов для «проверки» - способами, которые иногда кажутся почти рассчитанными на то, чтобы сбить с толку новичка.

Чтобы избавиться от стресса, учись на моих шрамах. Вы должны в значительной степени прочитать книгу о Git - я бы порекомендовал «Контроль версий с Git» . Сделайте это раньше, чем позже. Если вы это сделаете, имейте в виду, что большая часть сложности Git возникает из-за ветвления, а затем повторного объединения: вы можете пропустить эти части в любой книге. Судя по вашему вопросу, нет причин, по которым люди должны ослеплять вас наукой .

Особенно, если, например, это безвыходная ситуация и вы новичок в Git!

PS: Еще одна мысль: (сейчас) на самом деле довольно просто хранить репозиторий Git в каталоге, отличном от того, где находятся рабочие файлы. Это означало бы, что вам не нужно копировать весь репозиторий Git, используя приведенное выше быстрое и грязное решение. См. Ответ Фрайера --separate-git-dir здесь . Однако будьте осторожны : если у вас есть репозиторий «отдельный каталог», который вы не копируете, и вы выполняете полный сброс, все версии, следующие за фиксацией сброса, будут потеряны навсегда, если у вас нет, как вам абсолютно необходимо, регулярно делайте резервные копии вашего репозитория, желательно в облаке (например, на Google Диске ) и в других местах.

Что касается «резервного копирования в облако», следующим шагом будет открытие учетной записи (конечно же, бесплатно) с помощью GitHub или (на мой взгляд, лучше) GitLab . Затем вы можете регулярно выполнять git push команду, чтобы «должным образом» обновлять репозиторий Cloud. Но опять же, говорить об этом можно слишком рано.

Дополнительные альтернативы решениям Jefromi

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

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

Альтернатива 1: жесткий и мягкий сброс

Это очень немного измененная версия решения Чарльза Бейли для возврата к фиксации с помощью хэша SHA в Git? :

# Reset the index to the desired commit
git reset --hard <commit>

# Move the branch pointer back to the previous HEAD
git reset --soft [email protected]{1}

# Commit the changes
git commit -m "Revert to <commit>"

Это в основном работает, используя тот факт, что мягкие сбросы оставляют состояние предыдущей фиксации, расположенной в области index / staging-area, которую вы затем можете зафиксировать.

Альтернатива 2: удалить текущее дерево и заменить новым

Это решение взято из решения svick для проверки старого коммита и сделать его новым :

git rm -r .
git checkout <commit> .
git commit

Подобно альтернативе №1, он воспроизводит состояние <commit> в текущей рабочей копии. Это необходимо сделать в git rm первую очередь, потому git checkout что файлы, которые были добавлены с тех пор, не будут удалены <commit> .

Вот гораздо более простой способ вернуться к предыдущей фиксации (и оставить ее в незадействованном состоянии, чтобы делать с ней все, что угодно):

git reset HEAD~1

Итак, идентификаторы фиксации и т. Д. Не нужны :)

Лучший способ:

git reset --hard <commidId> && git push --force

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

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

Прежде чем ответить, давайте добавим немного предыстории, объясняя, что это HEAD такое.

First of all what is HEAD?

HEAD просто ссылка на текущую фиксацию (последнюю) в текущей ветке. В HEAD любой момент времени может быть только один (исключая git worktree ).

Содержимое HEAD хранится внутри .git/HEAD и содержит 40 байт SHA-1 текущего коммита.


detached HEAD

Если вы не в последней фиксации - это означает, что HEAD это указывает на предыдущую фиксацию в истории, которую он вызвал detached HEAD.

Введите описание изображения здесь

В командной строке это будет выглядеть так - SHA-1 вместо имени ветки, поскольку HEAD не указывает на конец текущей ветки:

Введите описание изображения здесь


Несколько вариантов восстановления после отсоединенной HEAD:


git checkout

git checkout <commit_id>
git checkout -b <new branch> <commit_id>
git checkout HEAD~X // x is the number of commits t go back

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

На этом этапе вы можете создать ветку и начать работу с этого момента:

# Checkout a given commit.
# Doing so will result in a `detached HEAD` which mean that the `HEAD`
# is not pointing to the latest so you will need to checkout branch
# in order to be able to update the code.
git checkout <commit-id>

# Create a new branch forked to the given commit
git checkout -b <branch name>

git reflog

Вы всегда можете использовать reflog также. git reflog отобразит любое изменение, которое обновило HEAD файл, и проверка нужной записи в журнале рефлога HEAD вернет этот коммит.

Каждый раз, когда HEAD изменяется, в reflog

git reflog
git checkout [email protected]{...}

Это вернет вас к желаемой фиксации

Введите описание изображения здесь


git reset HEAD --hard <commit_id>

«Переместите» голову обратно к желаемой фиксации.

# This will destroy any local modifications.
# Don't do it if you have uncommitted work you want to keep.
git reset --hard 0d1d7fc32

# Alternatively, if there's work to keep:
git stash
git reset --hard 0d1d7fc32
git stash pop
# This saves the modifications, then reapplies that patch after resetting.
# You could get merge conflicts, if you've modified things which were
# changed since the commit you reset to.
  • Примечание. ( Начиная с Git 2.7 ) вы также можете использовать git rebase --no-autostash.

Эта схема показывает, какая команда что делает. Как видите, reset && checkout измените файл HEAD .

Введите описание изображения здесь

Хорошо, вернуться к предыдущей фиксации в Git довольно просто ...

Вернуть назад без сохранения изменений:

git reset --hard <commit>

Вернитесь назад с сохранением изменений:

git reset --soft <commit>

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

Но, как вы видите, разница заключается в использовании двух флагов --soft и --hard, по умолчанию, git reset использовании --soft флага, но рекомендуется всегда использовать флаг, я объясню каждый флаг:


--мягкий

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


--жесткий

Будьте осторожны с этим флагом. Он сбрасывает рабочее дерево и все изменения в отслеживаемых файлах, и все пропадет!


Я также создал изображение ниже, которое может произойти в реальной жизни при работе с Git:

Сброс Git до фиксации

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

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

Вот как я это сделал:

git reset --hard CommitId && git clean -f

Это вернется в локальный репозиторий, а здесь после использования git push -f обновит удаленный репозиторий.

git push -f

Например, если вы хотите полностью игнорировать фиксацию с именем enforce non-group manage policies из следующего изображения

введите описание изображения здесь

ты бы убежал

git reset --hard dd52eb9 && git clean -f

с последующим

git push -f

После этого вы не увидите там commit ( enforce non-group manage policies )

введите описание изображения здесь

Rogue Coder?

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

Работаете с другими? Git сложен. Прочтите комментарии под этим ответом, прежде чем делать что-то необдуманное.

Возврат рабочей копии к самой последней фиксации

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

git reset --hard HEAD

где HEAD - последняя фиксация в вашей текущей ветке

Возврат рабочей копии к более ранней фиксации

Чтобы вернуться к фиксации, более ранней, чем самая последняя фиксация:

# Resets index to former commit; replace '56e05fced' with your commit code
git reset 56e05fced 

# Moves pointer back to previous HEAD
git reset --soft [email protected]{1}

git commit -m "Revert to 56e05fced"

# Updates working copy to reflect the new commit
git reset --hard

Кредиты идут на аналогичный вопрос о переполнении стека: Вернуться к фиксации с помощью хэша SHA в Git? .