На простом английском языке, что делает "git reset"? - PullRequest
639 голосов
/ 27 марта 2010

Я видел интересных постов , объясняющих тонкости о git reset.

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

Я думаю, git reset близко к hg revert, но, похоже, есть различия.

Так что же делает git reset? Пожалуйста, включите подробные объяснения о:

  • варианты --hard, --soft и --merge;
  • странные обозначения, которые вы используете с HEAD, такие как HEAD^ и HEAD~1;
  • конкретные варианты использования и рабочие процессы;
  • последствия для рабочей копии, HEAD и вашего глобального уровня стресса.

Ответы [ 7 ]

942 голосов
/ 27 марта 2010

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

- A - B - C (HEAD, master)

и вы понимаете, что хотите, чтобы мастер указывал на B, а не на C, вы будете использовать git reset B, чтобы переместить его туда:

- A - B (HEAD, master)      # - C is still here, but there's no branch pointing to it anymore

Отступление: это отличается от проверки. Если вы запустите git checkout B, вы получите это:

- A - B (HEAD) - C (master)

Вы оказались в отключенном состоянии HEAD. HEAD, рабочее дерево, все индексы совпадают B, но основная ветвь осталась на C. Если вы сделаете новый коммит D в этот момент, вы получите это, что, вероятно, не то, что вы хотите:

- A - B - C (master)
       \
        D (HEAD)

Помните, сброс не делает коммитов, он просто обновляет ветку (которая является указателем на коммит), чтобы указывать на другой коммит. Остальное - только детали того, что происходит с вашим индексом и рабочим деревом.

Варианты использования

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

Вещи, которые нужно соблюдать

  • --hard может привести к потере работы. Он изменяет ваше рабочее дерево.

  • git reset [options] commit может привести к потере коммитов. В приведенном выше примере с игрушкой мы потеряли коммит C. Он все еще находится в репо, и вы можете найти его, посмотрев на git reflog show HEAD или git reflog show master, но на самом деле он больше недоступен из любой ветки.

  • Git навсегда удаляет такие коммиты через 30 дней, но до тех пор вы можете восстановить C, снова указав на него ветку (git checkout C; git branch <new branch name>).

Аргументы

Перефразируя man-страницу, чаще всего используется форма git reset [<commit>] [paths...], которая сбрасывает указанные пути в их состояние из данного коммита. Если пути не указаны, все дерево сбрасывается, а если фиксация не указана, она считается HEAD (текущая фиксация). Это общий шаблон для команд git (например, checkout, diff, log, хотя точная семантика различается), поэтому это не должно вызывать удивления.

Например, git reset other-branch path/to/foo сбрасывает все в пути / to / foo в его состояние в другой ветке, git reset -- . сбрасывает текущий каталог в его состояние в HEAD, а простой git reset сбрасывает все в его состояние в ГОЛОВА.

Основное дерево работы и параметры индекса

Существует четыре основных варианта управления тем, что происходит с вашим рабочим деревом и индексом во время сброса.

Помните, что индекс - это "область подготовки" git - это то, куда идут вещи, когда вы говорите git add при подготовке к коммиту.

  • --hard заставляет все совпадать с коммитом, к которому вы сбросили. Это проще всего понять, наверное. Все ваши локальные изменения сгущаются. Одно из основных применений - это сдувание вашей работы, но не переключение коммитов: git reset --hard означает git reset --hard HEAD, т.е. не меняйте ветвь, а избавляйтесь от всех локальных изменений. Другой - это просто перемещение ветви из одного места в другое и синхронизация индекса и рабочего дерева. Это то, что действительно может заставить вас потерять работу, потому что это изменяет ваше рабочее дерево. Будьте очень уверены, что вы хотите выбросить локальную работу, прежде чем запускать любую reset --hard.

  • --mixed является значением по умолчанию, т.е. git reset означает git reset --mixed. Сбрасывает индекс, но не рабочее дерево. Это означает, что все ваши файлы не повреждены, но любые различия между исходным коммитом и исходным коммитом будут отображаться как локальные изменения (или файлы без отслеживания) со статусом git. Используйте это, когда вы понимаете, что сделали плохие коммиты, но хотите сохранить всю работу, которую вы сделали, чтобы вы могли исправить ее и подтвердить. Для фиксации вам необходимо снова добавить файлы в индекс (git add ...).

  • --soft не касается индекса или рабочего дерева. Все ваши файлы не повреждены, как при --mixed, но все изменения отображаются как changes to be committed со статусом git (т. Е. Проверено при подготовке к фиксации). Используйте это, когда вы поймете, что сделали плохие коммиты, но работа хороша - все, что вам нужно сделать, это подтвердить это по-другому. Индекс не тронут, поэтому вы можете сразу же зафиксировать фиксацию, если хотите - итоговый коммит будет иметь все то же содержимое, что и до сброса.

  • --merge был добавлен недавно и предназначен для того, чтобы помочь вам прервать неудачное слияние. Это необходимо, потому что git merge фактически позволит вам попытаться выполнить слияние с грязным рабочим деревом (с локальными изменениями), если эти изменения находятся в файлах, не затронутых слиянием. git reset --merge сбрасывает индекс (например, --mixed - все изменения отображаются как локальные изменения) и сбрасывает файлы, затронутые объединением, но оставляет остальные в покое. Это, мы надеемся, восстановит все до того состояния, которое было до неудачного слияния. Вы обычно будете использовать его как git reset --merge (что означает git reset --merge HEAD), потому что вы хотите только сбросить слияние, а не перемещать ветвь. (HEAD еще не был обновлен, так как слияние не удалось)

    Чтобы быть более конкретным, предположим, что вы изменили файлы A и B, и вы пытаетесь объединить ветвь, которая изменила файлы C и D. По какой-то причине слияние не удалось, и вы решили прервать его. Вы используете git reset --merge. Он возвращает C и D к тому, как они были в HEAD, но оставляет ваши модификации для A и B в одиночку, поскольку они не были частью попытки слияния.

Хотите узнать больше?

Я думаю, что man git reset действительно хорош для этого - возможно, вам нужно немного понять, как работает git, чтобы они действительно погрузились. В частности, если вы уделите время их тщательному прочтению, эти таблицы с подробным описанием состояний файлов в индексе и рабочем дереве для всех различных вариантов и вариантов очень полезны. (Но да, они очень плотные - они передают очень много вышеуказанной информации в очень сжатой форме.)

Странные обозначения

Упоминаемые вами "странные обозначения" (HEAD^ и HEAD~1) - это просто сокращение для указания коммитов без необходимости использовать хеш-имя, например 3ebe3f6. Он полностью задокументирован в разделе «Указание ревизий» справочной страницы для git-rev-parse, с множеством примеров и связанным синтаксисом. Карета и тильда на самом деле означают разные вещи :

  • HEAD~ - это сокращение от HEAD~1 и означает первого родителя коммита. HEAD~2 означает первого родителя первого коммита. Думайте о HEAD~n как о «n коммитит перед HEAD» или «предок n-го поколения HEAD».
  • HEAD^ (или HEAD^1) также означает первого родителя коммита. HEAD^2 означает второй родительский коммит. Помните, что у обычного коммита слияния есть два родителя - первый родитель - это коммит слияния, а второй родитель - коммит, который был слит. В общем случае у слияний может быть сколько угодно родителей (слияния осьминогов).
  • Операторы ^ и ~ могут быть соединены вместе, как в HEAD~3^2, втором родителе предка третьего поколения HEAD, HEAD^^2, втором родителе первого родителя HEAD или даже HEAD^^^, что эквивалентно HEAD~3.

caret and tilde

78 голосов
/ 27 марта 2010

Помните, что в git у вас есть:

  • указатель HEAD , который сообщает вам, над каким коммитом вы работаете
  • рабочее дерево , которое представляет состояние файлов в вашей системе
  • промежуточная область (также называемая index ), которая «меняет» этапы, чтобы впоследствии их можно было зафиксировать вместе

Пожалуйста, включите подробные объяснения о:

--hard, --soft и --merge;

В порядке возрастания степени опасности:

  • --soft перемещается HEAD, но не касается области подготовки или рабочего дерева.
  • --mixed перемещает HEAD и обновляет область подготовки, но не рабочее дерево.
  • --merge перемещает HEAD, сбрасывает область подготовки и пытается переместить все изменения в вашем рабочем дереве в новое рабочее дерево.
  • --hard перемещается HEAD и настраивает вашу область подготовки и рабочее дерево в соответствии с новым HEAD, отбрасывая все.

конкретные варианты использования и рабочие процессы;

  • Используйте --soft, если вы хотите перейти к другому коммиту и исправить все без «потери своего места». Это довольно редко, что вам нужно это.

-

# git reset --soft example
touch foo                            // Add a file, make some changes.
git add foo                          // 
git commit -m "bad commit message"   // Commit... D'oh, that was a mistake!
git reset --soft HEAD^               // Go back one commit and fix things.
git commit -m "good commit"          // There, now it's right.

-

  • Используйте --mixed (по умолчанию), когда вы хотите увидеть, как выглядят другие коммиты, но вы не хотите терять какие-либо изменения, которые у вас уже есть.

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

  • Используйте --hard, чтобы уничтожить все и начать новый лист с новым коммитом.

35 голосов
/ 19 июля 2011

Сообщение Сбросить демистификацию в блоге Pro Git дает очень объяснений объяснения git reset и git checkout.

После всего полезного обсуждения в начале этого поста автор сводит правила к следующим трем простым шагам:

Вот и все. Команда reset перезаписывает эти три дерева в определенном порядке, останавливаясь, когда вы указываете это.

  1. Переместить любую ветвь, на которую указывает HEAD (остановка, если --soft)
  2. ТО, сделайте так, чтобы указатель выглядел так (остановитесь здесь, если не --hard)
  3. ТОГДА сделайте так, чтобы рабочий каталог выглядел

Есть также опции --merge и --keep, но я бы предпочел пока упростить ситуацию - это будет для другой статьи.

25 голосов
/ 07 июля 2014

Когда вы делаете что-то для мерзавца, вы сначала должны внести изменения (добавить в индекс). Это означает, что вам нужно добавить в git все файлы, которые вы хотите включить в этот коммит, прежде чем git считает их частью коммита. Давайте сначала посмотрим на изображение git-репо: enter image description here

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

Но внезапно мы узнали, что при фиксации у нас есть еще один дополнительный файл, который мы добавили в индекс, не требуется для добавления в репозиторий git. Это означает, что мы не хотим этот файл в индексе. Теперь вопрос заключается в том, как удалить этот файл из индекса git. Поскольку мы использовали git add для помещения их в индекс, было бы логично использовать git rm ? Неправильно! git rm просто удалит файл и добавит удаление в индекс. И что теперь делать:

Использование: -

git reset

Он очищает ваш индекс, оставляет ваш рабочий каталог нетронутым. (просто unstaging все).

Может использоваться с рядом опций. Существует три основных варианта использования git reset: --hard, --soft и --mixed . Они влияют на то, что сбрасывается в дополнение к указателю HEAD при сбросе.

Сначала - жесткий сбрасывает все. Ваш текущий каталог будет точно таким же, как если бы вы следили за этой веткой все время. Рабочий каталог и индекс изменяются на этот коммит. Это версия, которую я использую чаще всего. git reset --hard это что-то вроде svn revert .

Далее, полная противоположность, —soft , не сбрасывает ни рабочее дерево, ни индекс. Он только перемещает указатель HEAD. Это оставляет ваше текущее состояние с любыми изменениями, отличными от коммита, на который вы переключаетесь, на месте в вашем каталоге, и «готовым» для фиксации. Если вы делаете коммит локально, но не отправляете коммит на git-сервер, вы можете сбросить предыдущий коммит и повторить с хорошим сообщением коммит.

Наконец, - смешанный сбрасывает индекс, но не рабочее дерево. Таким образом, все изменения все еще есть, но они «неустановлены» и должны быть добавлены в git add или git commit -a . мы используем это иногда, если мы зафиксировали больше, чем намеревалось, с помощью git commit -a, мы можем отменить коммит с помощью git reset --mixed, добавить вещи, которые мы хотим зафиксировать, и просто зафиксировать их.

Разница между Git Revert и Git Reset : -


Проще говоря, git reset - это команда для "исправления незафиксированных ошибок" и git revert - это команда для "fix- совершил ошибку ".

Это означает, что если мы допустили какую-то ошибку в каком-либо изменении, зафиксировали и отправили то же самое в git repo, то git revert - это решение. И если в случае, если мы обнаружили ту же ошибку до нажатия / подтверждения, мы можем использовать git reset , чтобы исправить эту проблему.

Надеюсь, это поможет вам избавиться от путаницы.

6 голосов
/ 13 июля 2015

TL; DR

git reset сбрасывает Staging до последнего коммита. Используйте --hard для сброса файлов в вашем рабочем каталоге до последнего коммита.

ДОПОЛНИТЕЛЬНАЯ ВЕРСИЯ

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

Если git revert является «безопасным» способом отмены изменений, вы можете подумать о git сбросить как опасный метод. Когда вы отменяете с помощью git reset (и коммиты больше не ссылаются ни на какие ссылки или рефлоги), есть нет способа восстановить оригинальную копию - это постоянная отмена. Уход должен следует использовать при использовании этого инструмента, поскольку это одна из единственных команд Git, которая может потерять вашу работу.

С https://www.atlassian.com/git/tutorials/undoing-changes/git-reset

и это

На уровне фиксации сброс - это способ перемещения кончика ветви в другой коммит. Это можно использовать для удаления коммитов из текущей ветви.

С https://www.atlassian.com/git/tutorials/resetting-checking-out-and-reverting/commit-level-operations

2 голосов
/ 21 ноября 2014

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

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


Для тех, кто использует Терминал с включенным цветом (git config --global color.ui auto):

git reset --soft A, и вы увидите, что материалы B и C выделены зеленым цветом (поставлены и готовы к фиксации)

git reset --mixed A (или git reset A), и вы увидите вещи B и C в красном (не подготовленные и готовые к постановке (зеленые), а затем зафиксированные)

git reset --hard A и вы больше нигде не увидите изменений B и C (как если бы они никогда не существовали)


Или для тех, кто использует программу с графическим интерфейсом, такую ​​как 'Tower' или 'SourceTree'

git reset --soft A и вы увидите вещи B и C в области 'готовые файлы', готовые к фиксации

git reset --mixed A (или git reset A), и вы увидите вещи B и C в области 'unstaged files', готовые для перемещения в инсталляцию и затем зафиксированные

git reset --hard A и вы больше нигде не увидите изменений B и C (как если бы они никогда не существовали)

1 голос
/ 16 августа 2018

Оформление заказа указывает на конкретный коммит.

Сброс указывает ветку на конкретный коммит. (Ветвь - это указатель на коммит.)

Кстати, если ваша голова не указывает на коммит, на который также указывает ветка, то у вас оторванная голова. (оказалось, что это неправильно. Смотрите комментарии ... )

...