В общем, функция 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
.