Сантехнический эквивалент для `git checkout -.`

Я ищу подходящую для сценариев сантехническую команду (или команды), которые эквивалентны высокоуровневой фарфоровой команде git checkout -- .

Моя первоначальная мысль будет использовать git checkout-index --all --forceОднако это не полностью восстанавливает рабочий каталог в случае core.autocrlf = input:

#!/bin/bash
set -ex

rm -rf repo
git init repo
cd repo

git config --local core.autocrlf input

python3 -c 'open("foo", "wb").write(b"1\r\n2\r\n")'
git add foo
python3 -c 'open("foo", "wb").write(b"3\r\n4\r\n")'

git checkout-index --all --force
echo 'I expect this `git status` to have no modifications'
git status

Это дает следующий вывод:

+ rm -rf repo
+ git init repo
Initialized empty Git repository in /tmp/foo/repo/.git/
+ cd repo
+ git config --local core.autocrlf input
+ python3 -c 'open("foo", "wb").write(b"1\r\n2\r\n")'
+ git add foo
warning: CRLF will be replaced by LF in foo.
The file will have its original line endings in your working directory.
+ python3 -c 'open("foo", "wb").write(b"3\r\n4\r\n")'
+ git checkout-index --all --force
+ echo 'I expect this `git status` to have no modifications'
I expect this `git status` to have no modifications
+ git status
On branch master

Initial commit

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

    new file:   foo

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   foo

Обратите внимание, что git checkout -- . правильно восстанавливает рабочий каталог до содержимого indexдаже в этом случае.

1 ответ

Решение

Это похоже на ошибку в git checkout-index: он не знает, как справиться с тем фактом, что рабочее дерево в конце концов соответствует индексу.

Если мы добавим git ls-files --stage --debugмы видим, что после первого git addИндекс содержит:

100644 1191247b6d9a206f6ba3d8ac79e26d041dd86941 0   foo
  ctime: <ct>
  mtime: <mt>
  dev: <d>  ino: <i>
  uid: <u>  gid: <g>
  size: 6   flags: 0

(Я заменил нерелевантные значения переменных на <...> Вот). Обратите внимание, что указанный размер равен 6, а не 4: это размер файла рабочего дерева, который на самом деле имеет длину 6 байт, поскольку он содержит \r\n окончания строки.

Далее мы делаем:

python3 -c 'open("foo", "wb").write(b"3\r\n4\r\n")'

который заменяет файл, перезаписывая существующий индекс новыми метками времени и новым содержимым. Новое содержимое имеет длину 6 байт.

Тогда мы делаем:

git checkout-index [arguments]

который перезаписывает файл рабочего дерева содержимым индекса, так же как git checkout делает. Файл теперь имеет длину 4 байта... но индекс все еще говорит, что файл имеет длину 6 байтов.

Если мы переименуем foo, чтобы git checkout-index должен воссоздать foo с другим номером инода, мы находим, что stat информация в индексе все еще устарела. Другими словами, даже если git checkout-index переписывает fooникогда не обновляет кэшированную статистическую информацию. Так git statusВнутренний diff index-vs-work-tree использует быстрый путь (сравните кэшированные данные статистики с данными статистики для фактического файла в файловой системе) и предполагает, что он должен быть изменен.

(Довольно странно, git update-index --refresh -q я не буду касаться информации кеша, и я не уверен, почему нет.)

Казалось бы, решение заключается в использовании git checkout напрямую, по крайней мере, до git checkout-index фиксированный.

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