В чем разница между "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

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

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

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

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

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

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

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

Каждый объект - HEAD commit, index и work-tree - содержат три файла прямо сейчас, но каждый содержит свой набор файлов. Таблица всего состояния тогда выглядит так:

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

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

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 при использовании без имен путей сбросит все записи, в том числе и для .. а также ../builtin,

Другие вопросы по тегам