Как в Git в организовано хранение файлов?
Помогите пожалуйста разобраться:
1. Я сделал коммит файла. После этого изменил его 3 раза и сделал 3 коммита. Git сохраняет первоначальную копию файла и все 3 его измененных копии полностью (т. е. если я в файле изменил всего 1 % данных он 99% просто дублирует в следующую копию), или хранит первоначальную копию и отдельно 3 последующих изменения (т. е. только то, чего нет в изначальной копии)?
2. Когда я переключаюсь с ветки на ветку, Git копирует в рабочий каталог файлы из базы данных. Я правильно понимаю, что он также при этом копирует те же самые файлы в индекс, так как Git понимает, что нету измененных файлов, сравнивая рабочий каталог с индексом?
3. И тоже самое происходит, когда я клонирую репозиторий? Сначала копируется база данных из удаленного репозитория, потом данные копируются в рабочий каталог и индекс?
Дополнительно:
1. Это же легко проверить. Добавьте объемный текстовый файл и сделайте пару коммитов с изменениями в одной строке. Каждый раз измеряйте объем папки с репозиторием (.git)
Вообще-то как раз гит хранит целые копии файла.
Для каждого файла создается его хеш, и файл-объект хранится под именем с этим кешом.
If you again examine your objects directory, you can see that it now contains a file for that new content. This is how Git stores the content initially — as a single file per piece of content, named with the SHA-1 checksum of the content and its header. The subdirectory is named with the first 2 characters of the SHA-1, and the filename is the remaining 38 characters.
То есть любое изменение файла - создает еще один файл-объект.
Два одинаковых файла не будут занимать два места, даже если они хранятся под разными именами.
Каждый коммит - содержит список файлов и хеш для содержимого.
А ветка - это просто ссылка на конкретный коммит и немного метаданных.
Также файлы-объекты хранятся упакованными, а периодически файлы-объекты могут быть объеденены в отдельный пакет.
Чтобы посмотреть содержимое любого гит-объекта, юзай
git cat-file -p ID_объекта (где айди объекта это как раз его хеш)
И собственно именно эта фича - хранение каждого изменения файла отдельным объектом и позволило создать легковесные ветки, где переключение на любой коммит любой ветки - быстрая проверка и копирование файлов, в отличие от CVS и SVN, где любое переключение ветки - куча пересчетов диффов как назад так и вперед.
Но, поскольку SVN - централизированная система, где все изменения хранятся только на сервере, можно менять формат хранения между версиями, так как это не нужно согласовывать со всеми пользователями репозитория.
Например кроме диффов, в поздних SVN периодически сохраняются полные снепшоты, например каждые 1000 коммитов делается полный слепок, что ускоряет перерасчеты.
- Спасибо большое, я разобрался как хранятся файлы в git. Спасибо Сергей Соловьев ссылку на источник, спасибо Saboteur за разъяснение.
Поладнее, скажите пожалуйста, я правильно понимаю:
1. Мы постоянно взаимодействуем с тремя областями, рабочий каталог, индекс и база данных, где хранится вся история. Я правильно понимаю, что, когда мы используем команду git status, git обнаруживает измененные файлы путем сравнения SHA-1 файлов в рабочем каталоге и SHA-1 файлов в индексе?
2.Когда мы переключаемся на ветку, git переключается на указатель HEAD этой ветки, там он находит ссылку на дерево, откуда получает ссылки на все файлы в базе данных и копирует их в рабочий каталог. И самое главное, также он копирует эти данные также в индекс? Если я правильно понимаю, то он должен это сделать, чтобы команда git status показала, что измененных файлов нет, так как файлы в индексе идентичны файлам в рабочем каталоге. -
Когда мы переключаемся на ветку, Git переключается на указатель HEAD этой ветки
Вы не поняли что такое ветка. Нет, ветка в гите это уже указатель на некоторый коммит в цепочке коммитов.
Указатель HEAD в репозитории один, и он показывает какое состояние в данный момент распаковано в рабочем каталоге. Соответственно у веток нет собственного указателя HEAD.Когда мы переключаемся на ветку
Перед переключением на другую ветку и вообще на другое состояние, желательно убедиться, что ваш рабочий каталог чист. В том смысле, что нет незакоммиченных изменений файлов. Также желательно чтобы индекс был вообще пуст. В противном случае гит будет пытаться сохранить вашу незакоммиченную работу. Он не будет молча удалять то, что вы ещё не закоммитили.
- Сергей Кузнецов, что Вы понимаете под пустым индексом? Если я правильно понимаю, он технически не может быть пустым. Там всегда находятся файлы. Иначе как git понимает, что есть измененные файлы?
Как я уже писал:
Я правильно понимаю, что, когда мы используем команду git status, git обнаруживает измененные файлы путем сравнения SHA-1 файлов в рабочем каталоге и SHA-1 файлов в индексе?
Может Вы имеете ввиду под пустым индексом то, что файлы в рабочем каталоге полностью идентичны файлам в индексе?
- Семен,
путем сравнения SHA-1 файлов в рабочем каталоге и SHA-1 файлов в индексе?
На самом деле тут даже сравнивать не нужно. Каждый раз при попытке коммита, создается новый hash-объект, и сохраняется. Но так как имя состоит из этого SHA-1, то он или сохраняется как новый объект, или не сохраняется, так как объект уже есть.
Если же сравнивать то, что в репозитории и то, что в рабочей директории - работает обычный дифф. Там microsoft подкатила не так давно много изменений для ускорения как раз именно работы с рабочей директорией, если в ней очень много файлов, так что как именно проверяется что было изменено, не подскажу. Ну а как найдены изменения, обычный diff
- Семен, индекс это черновик коммита, поэтому он очень даже может быть пустым.
Гит статус сравнивает файлы в рабочем каталоге с файлами в репозитории, чтобы узнать что изменилось. - Сергей Кузнецов, я прочитал в книге Раджу Ганди "Head First Git", что в некоторые моменты (когда все файлы находятся в состоянии "unmodified"), все файлы рабочего каталога имеют еще 2 копии: в индексе и базе данных. Т. е. 3 копии файлов. Git сравнивает копию в рабочем каталоге и индексе и тем самым понимает, что есть измененные файлы и сравнивает копию в индексе и базе данных и так он понимает, что есть файлы подготовленные для коммита. Скажите, может я не правильно понял, но тогда получается, индекс никогда не пуст, там всегда есть копии файлов рабочего каталога.
- в рабочем каталоге файл хранится в единственном экземпляре.
Только в том случае, когда ты делаешь git add, создается новый гит-объект для файлов, который хранится в "stage", еще до коммита.Если ты просто отредактировал файл, гит ну никак не делает еще две копии, он не висит в памяти чтобы следить за изменениями.
Ну и следует точнее оперировать терминами - что такое "база данных", что такое "индекс"?
Есть репозиторий с git-objects. Все. В других местах файлы не лежат, только метаданные. Нет никакого индекса с файлами в нем.
- Saboteur, я не говорил, что данные хранятся в рабочем каталоге в нескольких экземплярах. В рабочем каталоге 1 экземпляр, в индексе 1 экземпляр, в базе данных 1 экземпляр. Если я отредактировал файл, git и не должен ничего самостоятельно сравнивать. Но когда я вызывают команду git status, он сравнивает копию в рабочем каталоге и копией в индексе и тем самым помечает измененные файлы и сравнивает копию в индексе с копией в базе данных, тем самым помечает файлы, подготовленные для коммита.
Не могли бы Вы прокоментировать в противном случает:
Прошу прощения, что приходится отправлять выдержки из книги, просто я хочу окончательно разобраться.
- Нет никакого "индекса", где лежат файлы.
И вообще я бы рекомендовал читать англоязычную документацию, потому что своеобразный перевод терминов может немного страдать.В гите только одно хранилище гит-объектов (по вашему это хранилище файлов - база), все остальнео - метаданные, которые ссылаются на эти гит-объекты.
У вас же тут даже прямо написано что "гит сохраняет свое содержимое в своей объектной базе".
Разница только в том, что єти файлы еще не принадлежат никакому коммиту.
- Saboteur, я правильно Вас понял, что этой книге не стоит "доверять"? Потому что там прямо написано, про 3 копии, в рабочем каталоге, индексе и базе (хранилище) и тут дело не в искаженном переводе.
- Семен, я думаю что это искареженный перевод.
Индекс, коммит, бренч - это все метаданные.
Сами файлы хранятся непосредственно как гит-объекты в единой базе.
Да просто возьмите и поковыряйте что лежит в .git, как оно там лежит. Найдите как хранятся коммиты, референсы. Все ж под рукой.
1. Гит в истории целые файлы, уже измененные, но сжатые (изменения хранит SVN, но у него другая модель работы)
2. Сравнение файлов происходит через сравнение хэша файла. История изменений хранится в виде графа, поэтому переключение - это по сути: 1. Найти общего предка 2. Откатиться до этого предка 3. Применить изменения до новой ветки. Дополнительно, если у тебя есть незакоммиченные текущие изменения, то гит тебе об этом скажет
3. Скачивается вся история изменений из удаленного репозитория и потом HEAD выставляется на ветку, которую клонировал (в основном это main/master)
Почитай как работает git
UPD: спасибо Saboteur за важное замечание
- Вот только полностью наоборот.
Это именно гит хранит копии, а SVN хранит диффы, о чем можно как раз почитать по вашей ссылке, а именно тут: https://git-scm.com/book/en/v2/Git-Internals-Git-O...
Ответы:
└─$ find .git .git .git/config .git/info .git/info/exclude .git/HEAD .git/refs .git/refs/tags .git/refs/heads .git/branches .git/hooks .git/hooks/pre-rebase.sample .git/hooks/post-update.sample .git/hooks/pre-commit.sample .git/hooks/pre-merge-commit.sample .git/hooks/update.sample .git/hooks/pre-receive.sample .git/hooks/commit-msg.sample .git/hooks/applypatch-msg.sample .git/hooks/pre-applypatch.sample .git/hooks/pre-push.sample .git/hooks/push-to-checkout.sample .git/hooks/prepare-commit-msg.sample .git/hooks/fsmonitor-watchman.sample .git/objects .git/objects/info .git/objects/pack .git/description |
└─$ find .git .git .git/config .git/info .git/info/exclude .git/HEAD .git/refs .git/refs/tags .git/refs/heads .git/branches .git/hooks .git/hooks/pre-rebase.sample .git/hooks/post-update.sample .git/hooks/pre-commit.sample .git/hooks/pre-merge-commit.sample .git/hooks/update.sample .git/hooks/pre-receive.sample .git/hooks/commit-msg.sample .git/hooks/applypatch-msg.sample .git/hooks/pre-applypatch.sample .git/hooks/pre-push.sample .git/hooks/push-to-checkout.sample .git/hooks/prepare-commit-msg.sample .git/hooks/fsmonitor-watchman.sample .git/objects .git/objects/info .git/objects/pack .git/description
Git Repository Layout
https://git-scm.com/docs/gitrepository-layout
objects
Хранилище объектов, связанное с этим репозиторием. Обычно хранилище объектов самодостаточно (т. е. все объекты, на которые ссылается найденный в нем объект, также находятся в нем), но есть несколько способов его нарушить.
- Вы можете получить неполный, но пригодный для локального использования репозиторий, создав поверхностный клон.
- Вы можете использовать механизмы objects/info/alternates или $GIT_ALTERNATE_OBJECT_DIRECTORIES для заимствования объектов из других хранилищ объектов. Репозиторий с таким неполным хранилищем объектов не подходит для публикации для использования с тупыми транспортами, но в остальном все в порядке, если objects/info/alternates указывают на хранилища объектов, из которых он заимствован.
...
и так далее.
- Дополнение к ответу выше.
Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.
Пока нет других ответов. Будьте первым, кто поможет автору.
Ответить на вопрос

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