В общем, 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 сбрасывается в его состояние в other-branch, git reset -- .
сбрасывает текущий каталог в его состояние в HEAD, а простой git reset
сбрасывает все в его состояние в HEAD.
Основное дерево работы и параметры индекса
Существует четыре основных варианта управления тем, что происходит с вашим рабочим деревом и индексом во время сброса.
Помните, что индекс - это «область подготовки» 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
.