В чем разница между "git checkout -." и "git reset HEAD - hard"?

Это не общий вопрос о том, что "-" делает, как в отмеченном дубликате. Это git -специфический вопрос, требующий ясности в отношении того, какие операционные различия между упомянутыми командами.

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

git reset HEAD --hard
git clean -fd

Сотрудник также упомянул об этой команде:

git checkout -- .

Это сложная команда для google, и мне не понятно из документации git, что на самом деле делает эта команда. Это, по-видимому, одно из упомянутых выше способов использования в руководстве.

Угадав, что он реплицирует git reset HEAD --hard, но что именно он делает по сравнению с командами, которые я уже использую?
Реплицирует ли она одну или обе команды, или она похожа, но тонко отличается?

Ответ 1

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

Git в основном использует это в одобренный POSIX способ (см. Руководство 10), чтобы указать разделительную линию между аргументами опций и необязательные аргументы. Поскольку git checkout принимает имена ветвей, как в git checkout master, а также имена файлов (пути), как в git checkout README.txt, вы можете использовать --, чтобы заставить Git интерпретировать все, что приходит после --, как имя файла, даже если оно в противном случае было бы допустимым именем ветки. То есть, если у вас есть ветка и файл с именем master:

git checkout master

проверит ветку, но:

git checkout -- master

проверит файл (сбивчиво, с текущего индекса).

Ветви, индекс и файлы, о мой

Далее нам нужно обратиться к quirk git checkout. Как видно из документации, существует множество "режимов" git checkout (в документации перечислены шесть отдельных вызовов в synposis!), Существуют различные тирады (различного качества: Стив Беннет действительно полезен, на мой взгляд, хотя, естественно, я не согласен с ним на 100%: -)) о Git плохой "пользовательской" модели, включая тот факт, что git checkout имеет слишком много режимов работы.

В частности, вы можете git checkout ветку (для переключения ветвей) или git checkout один или несколько файлов. Последний извлекает файлы из определенного фиксатора или из индекса. Когда Git извлекает файлы из фиксации, он сначала копирует их в индекс, а затем копирует их из индекса, в дерево работы.

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

Это хорошая идея, я думаю, сделать трехстороннюю диаграмму или таблицу, иллюстрирующую текущий или HEAD -коммит, индекс и дерево работы. Предположим, что:

  • есть два обычных, преданных файла README.md и file.txt;
  • существует новый, git add -ed, но незафиксированный new.txt;
  • есть файл с именем rmd.txt, который был git rm -ed, но не зафиксирован;
  • и есть файл без следа с именем untr.txt.

Каждая сущность - фиксация HEAD, индекс и дерево работы - содержит три файла прямо сейчас, но каждый содержит другой набор файлов. Затем таблица всего состояния выглядит следующим образом:

  HEAD       index    work-tree
-------------------------------
README.md  README.md  README.md
file.txt   file.txt   file.txt
           new.txt    new.txt
rmd.txt
                      untr.txt

Есть гораздо больше возможных состояний, чем только эти: на самом деле для каждого имени файла существует семь возможных комбинаций "in/not-in" HEAD, index и work-tree (восьмая комбинация "нет" во всех трех ", и в каком случае, о каком файле мы говорим в первую очередь?!).

Команды checkout и reset

Две команды, о которых вы спрашиваете, git checkout и git reset, могут выполнять многие вещи. Конкретные призывы каждого, однако, уменьшают "дела" до одного из двух, к которому я добавлю еще несколько:

  • git checkout -- .: копии из индекса, в дерево работы, только
  • git checkout HEAD -- .: копии из HEAD, для индексации, а затем для рабочего дерева
  • git reset --mixed: сбрасывает индекс из HEAD (а затем оставляет только дерево)
  • git reset --hard: сбрасывает индекс из HEAD, затем сбрасывает дерево работы из индекса

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

Рассмотрим, в частности, файл с именем new.txt выше. Это в индексе прямо сейчас, поэтому, если мы скопируем из индекса, в дерево работы, мы заменим копию рабочего дерева на копию индекса. Это то, что делает git checkout -- new.txt, например.

Если вместо этого мы начнем с копирования из HEAD в индекс, ничего не произойдет с new.txt в индексе: new.txt не существует в HEAD. Следовательно, явный git checkout HEAD -- new.txt просто терпит неудачу, а git checkout HEAD -- . копирует файлы, находящиеся в HEAD, и оставляет две существующие версии new.txt невозмутимыми.

Файл rmd.txt ушел из индекса, поэтому, если мы git checkout -- ., Git не увидим его и ничего не делает. Но если мы git checkout HEAD -- ., Git копируем rmd.txt из HEAD в индекс (теперь он обратно), а затем из индекса в дерево работы (а теперь и обратно туда).

Команда git reset имеет ключевое различие при использовании без аргументов имени пути. Здесь он буквально переустанавливает индекс в соответствии с фиксацией. Это означает, что для new.txt он замечает, что файл не находится в HEAD, поэтому он удаляет запись индекса. Если используется с --hard, он также удаляет запись рабочего дерева. Между тем rmd.txt находится в HEAD, поэтому он копирует его обратно в индекс и с помощью --hard в дерево работы.

Если есть неустановленный, т.е. только дерево работы, изменяется на два других файла README.md и file.txt, обе формы git checkout и --hard формы git reset уничтожают эти изменения.

Если в эти файлы внесены изменения, которые были скопированы в дерево работы, а затем git reset, они будут деактивированы. Также существует вариант git checkout, где вы указываете ему имя HEAD. Однако вариант git checkout, где вы копируете индексные файлы обратно в дерево работы, сохраняет эти поэтапные изменения!

Верхний уровень и текущий каталог

Наконец, стоит отметить, что ., то есть текущий каталог, может в любое время отличаться от "top of Git repository":

$ git rev-parse --show-toplevel
/home/torek/src/kernel.org/git
$ pwd
/home/torek/src/kernel.org/git/Documentation
$ git rev-parse --show-cdup
../

Здесь я находится в подкаталоге Documentation каталога верхнего уровня git, поэтому . означает все в Documentation и его подкаталогах. Использование git checkout -- . будет проверять (из индекса) все файлы Documentation и Documentation/RelNotes, но не любые файлы ../builtin, например. Но git reset при использовании без имен путей будет reset всех записей, в том числе для .. и ../builtin.