В чем разница между git reset --mixed, --soft и --hard?

741

Я ищу разделить коммит и не уверен, какой вариант сброса использовать.

Я искал страницу На простом английском языке, что делает "git reset"? , но я понял, что не совсем понимаю, что такое индекс git или область подготовки, и поэтому объяснения не помогли.

Кроме того, варианты использования для --mixedи --softвыглядят одинаково для меня в этом ответе (когда вы хотите исправить и подтвердить). Может кто-нибудь сломать это еще больше? Я понимаю, --mixedчто, возможно, это вариант, но я хочу знать, почему . И наконец, как насчет --hard?

Может ли кто-нибудь дать мне пример рабочего процесса, как будет происходить выбор 3-х вариантов?

Майкл Чинен
источник
1
Я отредактирую свой ответ на этот другой вопрос, чтобы попытаться сделать его немного более понятным.
Каскабель
Ответ @mkarasek довольно хороший, но вам может быть интересно взглянуть и на этот вопрос .
brandizzi
3
Примечание для себя: В общем , soft: stage everything, mixed: unstage everything, hard: ignore everythingдо фиксации я сбросить с.
user1164937 22.10.16
еще одна хорошая статья David Zychс четким объяснением - davidzych.com/difference-between-git-reset-soft-mixed-and-hard
src3369

Ответы:

1491

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

git resetменяет, как минимум, текущую ветку ( HEAD). Разница между --mixedи --softзаключается в том, изменен или нет ваш индекс. Итак, если мы находимся на ветке masterс этой серией коммитов:

- A - B - C (master)

HEADуказывает на Cи индекс совпадает C.

Когда мы запускаем git reset --soft B, master(и, таким образом HEAD) теперь указывает на B, но индекс все еще имеет изменения от C; git statusпокажет их как постановочные. Поэтому, если мы запустим git commitэтот момент, мы получим новый коммит с теми же изменениями, что и C.


Итак, начиная с этого снова:

- A - B - C (master)

Теперь давайте сделаем git reset --mixed B. (Примечание: --mixedэто опция по умолчанию). Еще раз, masterи HEADукажите на B, но на этот раз индекс также изменяется, чтобы соответствовать B. Если мы запустим git commitв этот момент, ничего не произойдет, так как индекс совпадает HEAD. У нас все еще есть изменения в рабочем каталоге, но, поскольку они отсутствуют в индексе, они git statusотображаются как неперехваченные. Чтобы совершить их, вы бы, git addа затем совершить как обычно.


И, наконец, --hardто же самое, что и --mixed(он изменяет ваш HEADиндекс и индекс), за исключением того, что он --hardтакже изменяет ваш рабочий каталог. Если мы находимся Cи работаем git reset --hard B, то добавленные изменения C, а также любые незафиксированные изменения будут удалены, и файлы в вашей рабочей копии будут соответствовать коммиту B. Так как вы можете навсегда потерять изменения таким образом, вы должны всегда запускаться git statusперед выполнением аппаратного сброса, чтобы убедиться, что ваш рабочий каталог чист, или что вы в порядке потери ваших незафиксированных изменений.


И, наконец, визуализация: введите описание изображения здесь

mkarasek
источник
45
Другими словами, --soft отбрасывает последний коммит, --mix отбрасывает последний коммит и добавление, --hard отбрасывает последний коммит, добавление и любые изменения, сделанные вами в кодах, что аналогично git checkout HEAD
Джеймс Ван
11
@eventualEntropy Вы можете восстановить любые зафиксированные изменения с помощью reflog; незафиксированные изменения, которые были удалены reset --hard, исчезли навсегда.
mkarasek
2
@ Роберт Ни то, ни другое; --mixedизменяет ваш индекс, но не рабочий каталог, поэтому любые локальные изменения не затрагиваются.
mkarasek
3
Может быть полезно для визуальных людей, которые используют git на терминале с цветом: 1. 'git reset --soft A', и вы увидите вещи B и C в зеленом цвете (поэтапно) 2.'git reset --mixed A ', и вы посмотрите на вещи B и C в красном (не в формате) 3. 'git reset --hard A', и вы больше нигде не увидите изменений B и C (как если бы они никогда не существовали)
timhc22
2
@ user1933930 1 и 3 оставят вас - A - B - C′, где C 'содержит те же изменения, что и C (с другой отметкой времени и, возможно, сообщением о фиксации). 2 и 4 оставят вас - A - D, где D содержит объединенные изменения B и C.
mkarasek
216

Проще говоря:

  • --soft: незафиксированные изменения, изменения оставляются поэтапными ( индекс ).
  • --mixed (по умолчанию) : uncommit + unstage изменения, изменения остаются в рабочем дереве .
  • --hard: uncommit + unstage + удалить изменения, ничего не осталось.
Мо Али
источник
8
лучший ответ, потому что в ответе используются технические термины для предоставления полного ответа, который также является наиболее кратким
Тревор Бойд Смит
1
Когда я зафиксировал файл (не выгруженный) и у меня есть вновь созданный неотслеживаемый файл, то git reset --hard ничего не делает? Только когда я создаю неотслеживаемый файл, он удаляет его из моего рабочего каталога.
Майкл
1
@Nikhil Можете ли вы объяснить, где этот ответ не так?
Нед Бэтчелдер
1
@NedBatchelder Ни один из пунктов не является правильным: поскольку эти команды никогда не происходят uncommit.
Нихил
1
@Nikhil Возможно, вы имеете в виду, что оригинальный коммит все еще существует, и это правда. Но ветка была изменена, так что коммит больше не является частью ветки. Согласны ли мы с этим?
Нед Бэтчелдер
69

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

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


Для тех, кто использует Терминал с включенным цветом (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 (как если бы они никогда не существовали)


Или для тех, кто использует программу с графическим интерфейсом, как «Башня» или «SourceTree»

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

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

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

timhc22
источник
1
В лучшем случае это вводит в заблуждение: ваш ответ читается так, как будто git resetтолько меняет внешний вид git statusвывода России.
jub0bs
3
Я понимаю вашу точку зрения, но не согласен, потому что как визуальный ученик, то, как мой проект «выглядел» после использования 3 команд, наконец, помогло мне понять, что они делают!
timhc22
Я видел это скорее как «мерзавец для чайников», чтобы помочь людям разобраться в том, что на самом деле происходит. Можете ли вы придумать, как это можно улучшить, чтобы не вводить в заблуждение
timhc22
8
Нет, нам не нужно менять этот ответ. Это обеспечивает удобный "шпаргалка". Подумайте об этом: мягкий = зеленый, смешанный = красный, жесткий = ничего (значит, нет)! Как легко запомнить! Для тех новичков, которые даже не понимают, что на самом деле означают эти цвета, они слишком мало знают о мерзавцах, и в любом случае они будут брать тяжелые уроки в будущем, и это НЕ вина @unegma! Кстати, я просто одобряю этот ответ, чтобы противостоять предыдущему понижению. Хорошая работа, @unegma!
RayLuo
5
Это послужило хорошим дополнительным резюме для лучшего понимания внутренней работы, когда я читал их в другом месте. Спасибо!
spex
24

Все остальные ответы велики, но я считаю , что лучше понять их, разбивая файлы на три категории: unstaged, staged, commit:

  • --hard должно быть легко понять, он восстанавливает все
  • --mixed (по умолчанию) :
    1. unstagedфайлы: не меняются
    2. staged файлы: перейти к unstaged
    3. commit файлы: перейти к unstaged
  • --soft:
    1. unstagedфайлы: не меняются
    2. stagedфайлы: не меняйте
    3. commit файлы: перейти к staged

В итоге:

  • --softопция переместит все (кроме unstagedфайлов) вstaging area
  • --mixed опция переместит все в unstaged area
Хансен W
источник
22

Вот основное объяснение для пользователей TortoiseGit:

git reset --softи --mixedоставьте свои файлы нетронутыми.

git reset --hardна самом деле измените ваши файлы в соответствии с коммитом, который вы сбросили

В TortoiseGit концепция индекса очень скрыта в графическом интерфейсе. Когда вы изменяете файл, вам не нужно запускать, git addчтобы добавить изменение в промежуточную область / индекс. Когда просто имеем дело с изменениями существующих файлов, которые не меняют имена файлов, git reset --softа --mixedсовпадают! Вы заметите разницу только в том случае, если добавите новые или переименованные файлы. В этом случае, если вы запустите git reset --mixed, вам придется повторно добавить ваши файлы из списка « Файлы без версий» .

Джеймс Лаврук
источник
Этот ответ очень неясен относительно различий между мягким и смешанным. и даже пренебрежительно констатирует это. Этот следующий ответ более ясен на этом. stackoverflow.com/questions/2530060/…
штопор
2
Как пользователь Github Desktop , который также имеет такое же поведение, этот ответ дает мне некоторую ясность о том, почему я продолжаю путать о --mixedи --soft.
Чен Ли Ён
20

В этих случаях мне нравится визуал, который, я надеюсь, может объяснить это:

git reset --[hard/mixed/soft] :

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

Так что у каждого эффекта разные рамки

  1. Hard => WorkingDir + Index + HEAD
  2. Смешанный => Индекс + ГОЛОВА
  3. Soft => HEAD only (индекс и рабочий каталог без изменений).
Томер Бен Дэвид
источник
15

Три типа сожаления

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

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

A - B - C - D <- HEAD

Вот некоторые возможные сожаления и что с ними делать:

1. Я сожалею, что B, C и D не являются одним коммитом.

git reset --soft A, Теперь я могу немедленно зафиксировать и сделать все изменения, поскольку А - это один коммит.

2. Я сожалею, что B, C и D - это не десять коммитов.

git reset --mixed A, Фиксация исчезла, и индекс вернулся на А, но рабочая область все еще выглядит так же, как и после D. Так что теперь я могу добавлять и фиксировать в совершенно другой группе.

3. Я сожалею, что B, C и D произошли на этой ветви ; Хотелось бы, чтобы я разветвился после А, и они произошли в этой другой ветке.

Сделай новую ветку otherbranch, а потом git reset --hard A. Текущая ветвь теперь заканчивается в точке А, otherbranchвытекающей из нее.

(Конечно, вы также можете использовать аппаратный сброс, потому что хотите, чтобы B, C и D никогда не случались.)

матовый
источник
5

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

1. Внесите некоторые изменения.

2.git добавить.

3.gc -m "Я сделал что-то"

Soft, Mixed и Hard - это способ, позволяющий вам отказаться от операций, которые вы делали от 3 до 1.

Софт "притворился", что никогда не видел, что ты сделал "gc -m".

Смешанный «сделал вид», чтобы никогда не видеть, что вы сделали «git add».

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

qinmu2127
источник
4

Прежде чем перейти к этим трем вариантам, нужно понять 3 вещи.

1) История / ГОЛОВА

2) Стадия / индекс

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

reset --soft: История изменена, HEAD изменен, Рабочий каталог не изменен.

reset --mixed: история изменена, HEAD изменена, рабочая директория изменена с неподготовленными данными.

reset --hard: История изменена, HEAD изменен, Рабочий каталог изменен с потерянными данными.

С Git --soft всегда безопасно. Нужно использовать другой вариант в сложном требовании.

Суреш Шарма
источник
3

Здесь есть несколько ответов с неправильным представлением о git reset --soft. Хотя есть определенное условие, в котором git reset --softбудут только изменяться HEAD(начиная с состояния отсоединенной головки), обычно (и для предполагаемого использования), оно перемещает ссылку ветвления, которую вы в настоящее время извлекли. Конечно, он не может этого сделать, если у вас нет проверенной ветки (отсюда и конкретное условие, которое git reset --softбудет меняться только HEAD).

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

В этом суть resetизменения ветки на нечто иное, чем новый коммит, а не изменение HEAD. Вы можете увидеть это в примере документации:

Отменить коммит, сделав его веткой тем

          $ git branch topic/wip     (1)
          $ git reset --hard HEAD~3  (2)
          $ git checkout topic/wip   (3)
  1. Вы сделали некоторые коммиты, но понимаете, что они были преждевременны, чтобы быть в "основной" ветке. Вы хотите продолжить полировать их в ветке темы, поэтому создайте ветку "topic / wip" вне текущей HEAD.
  2. Перемотайте ветку master, чтобы избавиться от этих трех коммитов.
  3. Переключитесь на ветку "topic / wip" и продолжайте работать.

Какой смысл в этой серии команд? Вы хотите переместить ветку здесь master, поэтому, пока вы masterпроверили, вы бежите git reset.

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

Измени свою ветку

git reset --soft <ref>: Сбрасывает указатель филиала в настоящее время извлеченной ветви к совершающему в указанной ссылке, <ref>. Файлы в вашем рабочем каталоге и индексе не изменены. Фиксация с этого этапа вернет вас туда, где вы были до git resetкоманды.

Измените свой индекс тоже

git reset --mixed <ref>

или эквивалентно

git reset <ref>:

Делает то, что --softделает AND, также сбрасывает индекс в соответствии с фиксацией по указанной ссылке. Хотя git reset --soft HEADкоманда ничего не делает (потому что говорит, что перемещает извлеченную ветвь в извлеченную ветвь) git reset --mixed HEADили, что то же самое git reset HEAD, является обычной и полезной командой, поскольку она сбрасывает индекс до состояния вашего последнего коммита.

Измените свой рабочий каталог тоже

git reset --hard <ref>: делает то, что --mixedделает И также перезаписывает ваш рабочий каталог. Эта команда аналогична git checkout <ref>, за исключением того, что (и это ключевой момент reset) все формы git resetперемещения, на которые HEADуказывает ссылка ref .

Примечание о "такая-то команда перемещает ГОЛОВУ":

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

Де Ново
источник
2
"перемещение ветки ref": хорошая точка. Мне пришлось обновить stackoverflow.com/a/5203843/6309 .
VonC
1

Краткий ответ, в каком контексте используются 3 варианта:

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

  • soft: Вы можете зафиксировать все сразу и создать новый коммит с новым описанием (если вы используете torotise git или любой другой графический интерфейс, это тот, который нужно использовать, так как вы можете пометить, какие файлы вы хотите в коммите и сделать несколько фиксирует таким образом с различными файлами. В Sourcetree все файлы будут помещены для фиксации.)
  • mixed: Вам нужно будет снова добавить отдельные файлы в индекс, прежде чем делать коммиты (в Sourcetree все измененные файлы будут неизменными)

Чтобы фактически потерять ваши изменения в коде:

  • hard: вы не просто переписываете историю, но и теряете все изменения до момента сброса
Nickpick
источник
Я не становлюсь мягким и смешанным в этом случае. Если вы должны совершить, то что было отменено? вы
Джон Литтл
Подтверждение изменений. Обратного коммита не будет.
Nickpick
1

Основное различие между различными опциями команды git reset приведено ниже.

  • --soft: Сбрасывает только HEAD в выбранный вами коммит. Работает в основном так же, как git checkout, но не создает отдельное состояние головы.
  • --mixed (опция по умолчанию): сбрасывает заголовок в коммит, выбранный вами в истории, и отменяет изменения в индексе.
  • --hard: сбрасывает заголовок в коммит, выбранный вами в истории, отменяет изменения в индексе и отменяет изменения в вашем рабочем каталоге.
Вишвас Абхьянкар
источник
1

--soft: Говорит Git сбросить HEAD для другого коммита, так что индекс и рабочий каталог никак не будут изменены. Все файлы, измененные между оригинальным HEAD и коммитом, будут размещены.

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

--hard: Это сбрасывает все - он сбрасывает HEAD обратно в другой коммит, сбрасывает индекс, чтобы соответствовать ему, и сбрасывает рабочий каталог, чтобы соответствовать ему.

Основное различие между --mixedи --softзаключается в том, изменен или нет ваш индекс. Проверьте больше об этом здесь .

Неша Зорич
источник
0

Ответ mkarasek велик, простыми словами мы можем сказать ...

  • git reset --soft: установите HEADдля предполагаемого коммита, но сохраняйте свои изменения с момента последнего коммита
  • git reset --mixed: это то же самое, git reset --softно единственное отличие состоит в том, что он не вносит изменения с момента последнего коммита
  • git reset --hard: установите свой HEADкоммит, который вы указали, и сбросьте все ваши изменения с последних коммитов, включая не зафиксированные изменения.
Вивек Мару
источник