понимание эффекта git reset на индекс - PullRequest
0 голосов
/ 02 декабря 2018

У меня небольшой конфликт при чтении документации / учебных пособий по сбросу git: например, для git reset --mixed документация гласит:

Следующее, что нужно сделать для сброса, - обновить индексс содержимым любого снимка HEAD теперь указывает на

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

1 Ответ

0 голосов
/ 02 декабря 2018

TL; DR

Индекс всегда обновляется.Индекс содержит следующий коммит, который вы намереваетесь сделать , поэтому он никогда не будет пустым.(Что, никогда? Ну, вряд ли когда-либо: он пуст в новом репозитории, который вы только что создали, у которого нет файлов, и он ничего не зафиксирует, если вы запустите git commit прямо сейчас. Он также пуст, если вы git rm все .)

Длинный

Ваша путаница здесь почти наверняка связана с комментарием, сделанным PetSerAl .Новичкам в Git часто говорят или показывают, или, по крайней мере, заставляют поверить, что коммиты и / или индекс Git содержат изменений , но это неверно!Как только вы избавитесь от этого неверного убеждения, некоторые из загадок Git станут более понятными.(Не все Git имеют смысл для всех, даже для меня. ? Так что не волнуйтесь, если потребуется grok Git.)

В Git commit содержит полный снимок всех ваших файлов .Он также содержит некоторые метаданные - информацию о самого коммита, например ваше имя, адрес электронной почты и метку времени.В метаданные включен хэш-идентификатор коммита родительского коммита - или, для коммита слияния, нескольких родителей, множественного числа - и сравнивая коммиты с их родителями, Git показывает вамизменения.Каждый коммит имеет свой уникальный хэш-идентификатор, такой как 8858448bb49332d353febc078ce4a3abcc962efe (это идентификатор фиксации в Git-репозитории для Git).Этот коммит является моментальным снимком, но этот коммит имеет родителя (в данном случае 67f673aa4a...), поэтому Git может показать вас 8858448bb4..., извлекая обоих ранее 67f673aa4a и 8858448bb4, затем сравниваем их.Команда git show делает именно это, поэтому вы видите, что изменилось в 8858448bb4, а не то, что равно в 8858448bb4.

(Это все равно, что сказать вам, что сегодня на 5 градусов теплее или прохладнее, чем вчера, и более или менее ветрено, вместо того, чтобы указывать погоду в виде набора чисел. База данных хранит абсолютные значения, но в основном мы хотим знать, будет ли это лучше.)

В индексе хранится следующий коммит, который вы можете сделать

Вы можете просматривать коммиты Git различными способами и, конечно, называть их по их хэш-идентификаторам, как я делал выше.Вы можете видеть ваше рабочее дерево - именно там Git позволяет вам просматривать и редактировать ваши файлы - напрямую: там, на вашем компьютере, в их обычной повседневной форме.Но вы не можете увидеть индекс очень хорошо.Это невидимо.Это проблема, потому что она также критична.

Большинство систем контроля версий вообще не имеют индекса, или, если у них есть что-то подобное, держите его настолько хорошо скрытым , что выникогда не должен знать об этом.Но Git делает эту странную вещь: заставляет вас понимать индекс Git, и при этом держать его немного скрытым.

Если вы действительно хотите увидеть список файлов, которые находятся вИндекс прямо сейчас, вы можете использовать git ls-files:

$ git ls-files | head
.clang-format
.editorconfig
.gitattributes
.github/CONTRIBUTING.md
.github/PULL_REQUEST_TEMPLATE.md
.gitignore
.gitmodules
.mailmap
.travis.yml
.tsan-suppressions
$ git ls-files | wc -l
    3454

В этом индексе почти 3500 файлов, в этом Git-репозитории для Git.Это много файлов!Это , почему Git держит его в основном скрытым: там слишком много вещей для понимания.

Но это также , почему Git показывает нам коммиты, сравнивая ихсвоим родителям.Показывать все содержимое 8858448bb4 было бы слишком много, поэтому git show 8858448bb4 показывает нам, что изменило в 8858448bb4, по сравнению с его родителем.Git делает то же самое с индексом, показывая нам, что у нас есть изменилось , а не выбрасывает все целиком.

Я думаю, именно это заставляет людей думать, что Git хранитизменения.Git показывает изменений, поэтому Git должен хранить их ... но это не так!Git хранит целые снимки.Git вычисляет изменения, каждый раз, когда вы просите Git показать вам что-то.

Имея это в виду, давайте посмотрим, как мы видим индекс.

Индex сидит между текущим коммитом и рабочим деревом

Теперь мы знаем, что каждый коммит является полным снимком.Если бы Git создавал новую копию каждого файла каждый раз, когда мы делали коммит, хранилище становилось очень большим и очень быстрым.Так что он этого не делает, и одна часть способа это не делает, это действительно просто.Хотя каждый коммит является полным снимком, файлы внутри каждого коммита полностью, полностью, доступны только для чтения.Ни один из них не может когда-либо меняться.Это означает, что каждый коммит может поделиться некоторыми или всеми своими файлами с каким-либо ранее коммитом!

Git просто нужно убедиться, что каждый раз, когда мы запускаем git commit, он зависает все содержимое файла, навсегда - или, если не навсегда, по крайней мере, до тех пор, пока этот новый коммит продолжает существовать.Поэтому файлы внутри каждого коммита замораживаются.Они также сжаты в специальный формат Git-only (который действительно хорошо работает для текстовых файлов, но часто не так хорош для бинарных файлов, как изображения).Это сжатие занимает время, иногда много времени, но делает хранилище небольшим.

Очевидно, что замороженные файлы только для Git полезны только для самого Git, поэтому нам нужна копия каждого файла из текущий коммит извлечен, разморожен, распакован и полезен.Эти полезные копии попадают в рабочее дерево , где мы выполняем свою работу.

Другие системы контроля версий делают то же самое.В гипотетической системе управления версиями XYZ вы запускаете xyz checkout <em>commit</em>, и он копирует фиксацию из хранилища глубокой заморозки, оттаивает его, распаковывает и сохраняет в своем рабочем дереве.Вы делаете некоторую работу, и в конечном итоге вы запускаете xyz commit.Теперь он просматривает все ваше рабочее дерево, повторно сжимает каждый файл, замораживает его и проверяет, есть ли у него уже замороженная версия на складе или нужно ли ее тоже туда поместить.Каждый из этих шагов занимает много секунд или минут, пока вы идете за кофе или чем-то еще.

То, что делает Git со своим индексом, очень умно: индекс - это промежуточная область , междухранилище глубокой заморозки (хранилище, полное коммитов) и полезная форма (размороженные файлы в вашем рабочем дереве).Первоначально он содержит тех же файлов, которые были в глубокой заморозке.Они размораживаются (вроде как), но все еще находятся в специальной форме только для Git, и они соединены с полностью размороженной, разжатой версией в вашем рабочем дереве.

Когда вы изменить файлы в вашем рабочем дереве или добавить и / или удалить файлы, индексные копии не синхронизируются с рабочим деревом.Теперь Git может сравнивать индексную копию с копией рабочего дерева и сообщать вам, что вы изменили, но еще не сделали .

Как только у вас есть файл,так, как вы хотите, вы запускаете git add <em>file</em>. тут же сжимает файл в специальный формат только для Git и помещает эту копию в индекс.Теперь индексная копия, которая является полной копией, только что сжатой, соответствует копии рабочего дерева , но отличается от совершенной копии.

В любое времяGit может сравнить совершенную (HEAD) копию каждого файла с index copy:

git diff --cached

Для одинаковых файлов:Гит ничего не говорит.Для файлов, которые отличаются, Git выводит список файлов и показывает разницу.

Аналогично, вы можете в любое время заставить Git сравнивать index копию каждого файла с work-tree copy:

git diff

Для одинаковых файлов Git ничего не говорит.Для файлов, которые отличаются, Git выводит список файлов и показывает разницу.

(Примечание: при добавлении --name-status в git diff отображаются имена файлов с префиксом M для измененных, если они изменены. Git использует A для вновь добавленного файла, D дляудаленный файл и т. д. Файл удален в индексе, просто полностью удалив его из индекса. Файл добавил в индексе, если он находится в индексе, но не находится вHEAD.)

Команда git status выполняет оба эти сравнения с ограничителем --name-status.Для файлов, которые отличаются между HEAD и индексом, это , подготовленные для фиксации .Для файлов, которые отличаются между индексом и рабочим деревом, они не подготовлены для фиксации .


Наглядно:

   HEAD         index        work-tree
----------    ----------    ----------
README.txt    README.txt    README.txt
main.py       main.py       main.py

HEADкопия заморожена, потому что она находится в коммите.Индекс и копии рабочего дерева могут измениться, но изначально все три соответствуют .Вы изменяете копию рабочего дерева и используете git add, чтобы скопировать его обратно в индекс, сжимая и добавляя его (если «en-Git-ing» - это слово, которое«т).Если вы в конце концов не хотели изменять его в индексе, вы используете git reset (с его действием по умолчанию --mixed или с тем, как он работает с любым отдельным файлом), чтобы скопировать замороженный файл обратно в индекс.

Именно поэтому git commit такой быстрый по сравнению с xyz commit

Когда вы запускаете git commit, Git уже имеет все файлы, которые будут добавлены в новом коммите, вправильная форма.Не нужно повторно сжимать все файлы рабочего дерева и проверять, соответствуют ли они замороженным зафиксированным версиям.У index есть все, что нужно сделать: все, что ему нужно сделать, это заморозить копию индекса, и если это то же самое, что и предыдущий коммит, поделиться файлом с предыдущим коммитом.

Более того, поскольку индекс «знает», какие файлы соответствуют рабочему дереву, а какие нет, 1 и имеет дополнительную информацию о том, что находится в хранилище, это делает git checkout тоже быстрее.Предположим, что вы используете master с его файлами -3500, а вы git checkout - какую-то другую ветвь с примерно 3300 файлами, которые абсолютно одинаковы.Около 200 файлов различаются между двумя коммитами (может быть, несколько новых или удалены).Git может использовать index , чтобы узнать, что ему может понадобиться в рабочем дереве 1239 *, и вообще не трогать эти файлы about-3300.

Следовательно,вместо сканирования системы XYZ и, возможно, трогательных 3500 файлов, Git сканирует и, возможно, касается 200 файлов, сохраняя более 94% работы.


1 Для этого часто требуетсясканирование рабочего дерева.Индекс хранит копии ( кэшей ) данных о рабочего дерева, чтобы ускорить это.Вот почему индекс иногда называют кеш .Другие VCS, такие как Mercurial, имеют кэш рабочего дерева (Mercurial называет это dirstate ), но в отличие от индекса Git, он должным образом скрыт: вам не нужно знать об этом.

...