Это не имеет ничего общего с внутренним форматом хранения больших двоичных объектов, а также с фактическими командами, которые вы выполняли.
В частности, вы сделали:
git init
echo hello world > readme.md
git add readme.md
Пока что хорошо: вы Git скопировали файл readme.md
в индекс Git. Поскольку индекс на самом деле не содержит копий файловых данных, Git должен был создать внутренний объект blob, который вы упомянули; что теперь в индексе, если вы выгрузите его с помощью git ls-files --stage
или git ls-files --debug
, это одна запись с режимом 100644
(обычный файл), именем readme.md
и BLOB-объектом ha sh.
Но теперь вместо git mv readme.md readyou.md
или какой-либо другой эквивалентной серии команд вы запустили:
mv readme.md readyou.md
Это переименовало вашу рабочую елку копию файла. Файл в вашем рабочем дереве управляется вашей операционной системой, а не Git. Копия в вашем рабочем дереве - это не то, что go войдет в следующий коммит: то, что go войдет в следующий коммит, это то, что содержится в индексе Git. Это совершенно без изменений.
Ваша команда git status
работает следующим образом:
Вывод информации о текущей ветви и подобных вещах. В настоящее время вы используете ветку нерожденный или ветку orphan (Git использует для этого оба термина): имя master
хранится в .git/HEAD
, но ни одна ветка не называется master
на самом деле существует, потому что никаких коммитов еще не существует. Итак, первая строка git status
говорит об этом.
Сравните текущий коммит с индексом. Пока нет текущего коммита, поэтому Git использует вместо Git пустое дерево в качестве левой части этого сравнения. Результатом сравнения является объявление о появлении нового файла с именем readme.me
, который будет go в следующем коммите. Это ваши изменения, которые нужно зафиксировать .
Сравните индекс с рабочим деревом. Индекс содержит файл с именем readme.md
, а рабочее дерево - нет. Так что readme.md
удалено. Рабочее дерево содержит файл с именем readyou.md
, а индекс - нет; Git держит это на мгновение. В результате этого сравнения объявляется, что файл readme.md
удален, но это ваши изменения, не подготовленные для фиксации .
Наконец, поскольку есть Файлы рабочего дерева, которых нет в индексе, Git фильтрует этот список по записям .gitignore
. (Технически это действительно происходит при сканировании рабочего дерева: нет смысла помещать файлы в список, а затем выбрасывать их, когда Git может вообще не включать их в список.) Поскольку в .gitignore
записей нет *, 1231 * не не замолкает об этом: он печатает имена этих файлов в виде неотслеживаемых файлов .
Ключ ко всем это означает, что Git имеет эту дополнительную копию 1 каждого файла в индексе Git, и что git commit
создаст новый коммит из индекса. Git даже не будет смотреть на ваше дерево работы здесь. Это то, что в индексе имеет значение. Команда git status
не показывает, что находится в индексе; вместо этого он показывает, что отличается в индексе, дважды: один раз по сравнению с текущим коммитом (или пустым деревом в этом специальном состоянии) и один раз по сравнению с вашим рабочим деревом, если предположить что вы действительно можете видеть, что находится в вашем рабочем дереве.
Если индекс соответствует рабочему дереву, вы ничего не сможете скопировать из рабочего дерева, в индекс, чтобы изменить его. Так что это неинтересно, и последние два git status
выходных раздела - изменения не подготовлены и неотслеживаемые файлы - пусты. Если индекс соответствует текущему commit , нет никакой причины делать новый коммит, так что это не интересно, и раздел выходных данных изменений, подготовленных для фиксации, пуст.
1 Опять же, индекс действительно содержит кортежи (mode, name, blob-ha sh). Он также имеет кучу данных кеша, поэтому его иногда называют кеш , хотя в наши дни кеш должен быть зарезервирован для структуры данных в памяти, которая Git строит, читая файл .git/index
. Поскольку вы используете индекс для "подготовки" обновленных файлов для фиксации, индекс также называется промежуточной областью .
Обратите внимание, что git mv
ничем не особенным
Если вы используете git mv readme.md readyou.md
, Git:
- переименовывает файл рабочего дерева, а
- удаляет старый
readme.md
индексировать запись и вставить вместо нее readyou.md
запись.
git status
теперь просто скажет, что существует новый файл readyou.md
, готовый для фиксации. Но предположим, что вы сначала фиксируете файл readme.md
. Теперь git status
скажет, что переименовано готово к фиксации.
Технически, все, что произойдет, если вы сделаете второй коммит, это то, что у второго коммита будет файл с именем readyou.md
и не имеют файл с именем readme.md
. Это механизм различий Git - программа, которую вы можете вызвать с помощью git diff
, которая также вызывает git status
и другие команды Git, - решающие, что этот файл был «переименован».
Если вместо git mv
вы запустите:
mv readme.md readyou.md
git add readme.md readyou.md
, отдельный шаг git add
здесь удалит readme.md
из индекса и скопирует readyou.md
в индекс ( снова см. сноску 1 - она действительно просто сохранит существующий объект BLOB-объекта). Как ни странно, git add <em>path/to/file</em>
удаляет path/to/file
из индекса, если он есть в индексе и , которого нет в рабочем дереве. Чтобы понять это, нужно понять, что git add
означает не просто копировать файл из рабочего дерева в индекс , но вместо этого означает нечто более простое: сделать индексное соответствие рабочему дереву . «Совпадение» в случае переименованного или удаленного файла означает удаление старого имени.
Когда Git сравнивает два коммита, или один коммит и индекс, или индекс и ваше рабочее дерево или что-то еще, Git обнаружит переименование только при определенных условиях. К ним относятся:
Детектор переименования должен быть включен . Когда вы запускаете git diff
, вы можете включить его вручную с помощью -M
или --find-renames
. По умолчанию он был выключен в старых версиях Git и переключен на на по умолчанию в Git 2.9. Вы можете настроить собственное частное значение по умолчанию с помощью ручки настройки diff.renames
.
Файл content должен либо точно соответствовать - точное совпадение для Git - либо быть "достаточно похожим". Это сходство выражается в процентах: точное совпадение на 100% похоже, а файл без общих байтов похож на 0%. Файлы, которые не идентичны, но имеют несколько общих байтов, что определяется быстрым и грязным сканером файлов, в некоторой степени похожи: больше нуля, меньше 100. При использовании --find-renames
или -M
вы можете установить сходство порог. По умолчанию 50%. Файлы, которые по крайней мере не равны 50 или любому другому процентному значению, считаются «не переименованными».
Чтобы Git обнаружил переименование, должен быть файл на «левая сторона», которой просто нет на «правой стороне», и наоборот. Например, в этом случае левая сторона - первый коммит - будет иметь файл с именем readme.md
, а правая сторона - текущее содержимое индекса - не будет. Правая сторона имеет readyou.md
, а левая сторона - нет. Git объединяет файлов левой и правой сторон с одинаковым именем . Любые оставшиеся файлы, то есть не спаренные на данный момент, являются кандидатами для обнаружения переименования.
При использовании git diff
есть еще кое-что, что вы можете сделать с помощью -B
(перерыв -пары) вариант. Вы не можете сделать это с помощью git status
, поэтому мы не будем вдаваться в подробности go.
В команде git status
детектор переименования включен и установлен на 50 % по умолчанию. В более старых версиях Git изменить это невозможно, кроме Git 2. 18 добавил status.renames
в качестве опции и заставил ее следовать diff.renames
по умолчанию, если она не установлена. Так что теперь diff.renames
также контролирует git status
, если только вы не установили diff.renames
и status.renames
.
Смысл всего этого словоблудия в том, что git mv
не совсем специальный . Он просто заботится о двух операциях переименования одновременно: одна в индексе и одна в вашем рабочем дереве. Если вы забыли, просто используйте git add
, чтобы удалить старое имя и добавить новое. Эффект точно такой же: сделанный вами следующий коммит, который хранится сейчас в индексе Git, теперь корректно обновляется.