В чем разница между «git reset» и «git checkout»?


440

Я всегда думал об одном git resetи git checkoutтом же, в том смысле, что оба возвращают проект к определенной фиксации. Тем не менее, я чувствую, что они не могут быть точно такими же, поскольку это было бы излишним. Какова реальная разница между ними? Я немного сбит с толку, так как svn svn coнужно только отменить коммит.

ADDED

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

http://a.imageshack.us/img651/1559/86421927.png http://a.imageshack.us/img801/1986/resetr.png

ДОБАВЛЕНО 3

Из http://think-like-a-git.net/sections/rebase-from-the-ground-up/using-git-cherry-pick-to-simulate-git-rebase.html оформить заказ и сброс можно эмулировать ребаз.

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

git checkout bar 
git reset --hard newbar 
git branch -d newbar 

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



Re: "Это неправильно или слишком упрощенно?" Да, эта первая диаграмма вводит в заблуждение относительно разницы между проверкой и сбросом. (Это может быть хорошо в отношении -- filesвариантов; я не уверен.) На этой диаграмме видно, что основное различие заключается в том, влияют ли они на индекс или WD. Смотрите мой ответ по этому поводу. 2-я и 3-я диаграммы очень помогают увидеть реальную разницу. Четвертая и пятая диаграммы полезны для проверки того, понимаете ли вы, что делают эти команды, но на самом деле они вам не помогут.
LarsH

Я нашел раздел «Проверьте это» в «Git Tools Reset Demystified», чтобы дать наиболее полезную сводку.
Иосия Йодер

1
prosseek: Если вы согласны с @LarsH, что первая диаграмма вводит в заблуждение, вы можете удалить ее, пожалуйста?
Иосия Йодер

Обратите внимание, что извлечение и сброс только эмулируют 2-ю часть ребазинга, и think-like-a-git.netдля предотвращения потери данных требуются дополнительные шаги (приведенные в связанной статье).
Cowlinator

Ответы:


198
  • git resetконкретно об обновлении индекса , перемещая ГОЛОВУ.
  • git checkoutоб обновлении рабочего дерева (до индекса или указанного дерева). Он обновит ГОЛОВКУ только в том случае, если вы извлекаете ветку (если нет, то в конечном итоге вы получаете отдельный ГОЛОВ ).
    (на самом деле, с Git 2.23 Q3 2019, это будет git restoreне обязательно git checkout)

Для сравнения, поскольку svn не имеет индекса, только рабочее дерево svn checkoutскопирует данную ревизию в отдельный каталог.
Более близкий эквивалент для git checkout:

  • svn update (если вы находитесь в той же ветке, то есть тот же URL-адрес SVN)
  • svn switch (если вы извлекаете, например, ту же ветку, но с другого URL репозитория SVN)

Все эти три рабочие модификации дерева ( svn checkout, update, switch) есть только одна команда в мерзавца: git checkout.
Но так как у git есть понятие индекса («промежуточная область» между репо и рабочим деревом), у вас также есть git reset.


Thinkeye упоминает в комментариях статью « Сброс демистификации ».

Например, если у нас есть две ветви, ' master' и ' develop', указывающие на разные коммиты, и мы в настоящее время находимся на ' develop' (поэтому HEAD указывает на это), и мы запускаем git reset master, ' develop' сама теперь будет указывать на тот же коммит, который ' master'делает.

С другой стороны, если мы вместо этого бежим git checkout master, ' develop' не будет двигаться, HEADсамо будет. HEADтеперь будет указывать на ' master'.

Итак, в обоих случаях мы переходим HEADк указанию на фиксацию A, но то, как мы это делаем, сильно отличается. resetпереместит точки ветвления HEADна, checkout переместится HEADсам, чтобы указать на другую ветку.

http://git-scm.com/images/reset/reset-checkout.png

По этим пунктам, однако:

LarsH добавляет в комментариях :

Первый абзац этого ответа, тем не менее, вводит в заблуждение: « git checkout... обновит ГОЛОВКУ, только если вы извлечете ветку (если нет, у вас будет отключенный ГОЛОВ)».
Неверно: git checkoutобновит HEAD, даже если вы извлекаете коммит, который не является ветвью (и да, в итоге вы получаете отключенный HEAD, но он все еще обновляется).

git checkout a839e8f updates HEAD to point to commit a839e8f.

Де Ново соглашается в комментариях :

@LarsH правильно.
Второй пункт имеет неправильное представление о том, что в HEAD будет обновлять HEAD, только если вы извлекаете ветку.
ГОЛОВА идет везде, где ты, как тень.
Извлечение ссылки, не относящейся к ветке (например, тега) или фиксации напрямую, переместит HEAD. Отделенная головка не означает, что вы отсоединились от ГОЛОВКИ, это означает, что головка отделена от ссылки на ветку, которую вы можете видеть, например, из git log --pretty=format:"%d" -1.

  • Прикрепленный глава государства будет начинаться с (HEAD ->,
  • detached будет по-прежнему показывать (HEAD, но не будет стрелку на ветке ref.

7
Я бы сказал, что git resetречь идет об изменении «метки» ветки и, при желании, обновлении индекса или рабочего дерева в качестве побочного эффекта. git checkoutоб обновлении рабочего дерева и переключении текущей "выбранной" ветки (the HEAD).
Микко Ранталайнен

2
@MikkoRantalainen Нету. git resetна 100% о HEAD. Он работает даже в автономном режиме HEAD ( stackoverflow.com/a/3965714/6309 ), то есть там, где нет ветви (!). git checkout также работает в режиме отсоединенного HEAD или может быть использован для извлечения SHA1 в режиме отсоединенного HEAD: опять же, в этом случае ветвь отсутствует.
VonC

3
Дальнейшее чтение для всех потерянных душ, отправленных сюда поисковой системой, я думаю, это того стоит: git-scm.com/blog/2011/07/11/reset.html
Thinkeye

2
@ Thinkeye хорошая ссылка. Я включил его вместе с соответствующей выдержкой в ​​ответ для большей наглядности.
VonC

2
Объяснение от Сброс Демистифицировано отлично. Первый абзац этого ответа, тем не менее, вводит в заблуждение: «git checkout ... обновит HEAD только в том случае, если вы извлекаете ветку (в противном случае вы получите отключенный HEAD)». Не верно ... git checkout обновит HEAD, даже если вы извлекаете коммит, который не является ветвью (и да, вы в конечном итоге получаете отдельный HEAD, но он все еще обновляется). Может быть, я неправильно понимаю, что вы подразумеваете под "обновлением"? git checkout a839e8fобновляет HEAD, чтобы указать, чтобы совершить a839e8f.
LarsH

67

В своей простейшей форме resetсбрасывает индекс, не касаясь рабочего дерева, а checkoutизменяет рабочее дерево, не касаясь индекса.

Сбрасывает индекс для соответствия HEAD, оставляя рабочее дерево в покое:

git reset

Концептуально это проверяет индекс в рабочем дереве. Чтобы заставить его действительно что-то делать, вам придется использовать -fего для перезаписи любых локальных изменений. Это функция безопасности, которая гарантирует, что форма «без аргументов» не будет разрушительной:

git checkout

Как только вы начинаете добавлять параметры, это правда, что есть некоторое перекрытие.

checkoutобычно используется с веткой, тегом или коммитом. В этом случае он сбросит HEADи индекс для данного коммита, а также выполнит извлечение индекса в рабочее дерево.

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

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

Другие формы resetи commitпути снабжения.

Если вы предоставляете пути для resetвас, вы не можете предоставить --hardи resetтолько изменит индексную версию предоставленных путей на версию в предоставленном коммите (или HEADесли вы не укажете коммит).

Если вы указываете пути к checkout, как resetэто, он обновит индексную версию предоставленных путей, чтобы соответствовать предоставленной фиксации (или HEAD), но он всегда извлечет версию индекса предоставленных путей в рабочее дерево.


2
Неверно говорить, что «оформление заказа» не меняет индекс: оно меняет его, когда используется для перехода из одной ветви в другую.
wiki1000

В своей простейшей форме, сброс сбрасывает индекс, не касаясь рабочего дерева, а извлечение изменяет рабочее дерево, не касаясь индекса. : Насколько это сбивает с толку: |
Адитья Гупта

41

Один простой случай использования при отмене изменения:
1. Используйте сброс, если вы хотите отменить подготовку измененного файла.
2. Используйте checkout, если вы хотите отменить изменения в неустановленные файлы.


1
Идеальный ответ. Спасибо.
user358591

11

Ключевое отличие в двух словах состоит в том, что reset перемещает текущую ссылку на ветвь , а checkoutнет (перемещает HEAD).

Как объясняет книга Pro Git в разделе « Сбросить демистификацию» ,

Первое, что нужно resetсделать, это переместить то, на что указывает HEAD . Это не то же самое, что изменение самой HEAD (что и checkoutделает); reset перемещает ветку, на которую указывает HEAD. Это означает, что если для HEAD задана masterветвь (т. Е. Вы в данный момент находитесь на masterветке), запуск git reset 9e5e6a4начнется с masterуказания точки 9e5e6a4. [выделение добавлено]

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

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


2
Хорошие отзывы, в дополнение к моему старому ответу. +1
VonC

2

Две команды (сброс и проверка) совершенно разные.

checkout X НЕ ЯВЛЯЕТСЯ reset --hard X

Если X является именем ветви, checkout Xизменит текущую ветку, пока reset --hard Xне будет.


2
Но если X - это файл или папка, то они одинаковы.
Тед

1

краткая мнемоника:

git reset HEAD           :             index = HEAD
git checkout             : file_tree = index
git reset --hard HEAD    : file_tree = index = HEAD
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.