Цитируя Линуса Торвальдса, когда его спросили, сколько файлов может обработать Git во время его Tech Talk в Google в 2007 году (43:09):
... Git отслеживает ваш контент. Он никогда не отслеживает ни одного файла. Вы не можете отслеживать файл в Git. Что вы можете сделать, так это то, что вы можете отслеживать проект с одним файлом, но если у вашего проекта есть один файл, обязательно сделайте это, и вы можете это сделать, но если вы отслеживаете 10 000 файлов, Git никогда не увидит их как отдельные файлы. Git думает все как полный контент. Вся история в Git основана на истории всего проекта ...
(стенограммы здесь .)
Тем не менее, когда вы погружаетесь в книгу Git , первое, что вам говорят, это то, что файл в Git может быть отслежен или не отслежен . Более того, мне кажется, что весь опыт работы с Git направлен на управление версиями файлов. При использовании git diff
илиgit status
вывод представлен отдельно для каждого файла. При использовании git add
вы также можете выбрать для каждого файла. Вы даже можете просмотреть историю на файловой основе и молниеносно.
Как следует толковать это утверждение? С точки зрения отслеживания файлов, чем Git отличается от других систем контроля версий, таких как CVS?
источник
Ответы:
В CVS история отслеживалась отдельно для каждого файла. Ветвь может состоять из различных файлов со своими собственными различными ревизиями, каждый со своим собственным номером версии. CVS был основан на RCS ( Revision Control System ), которая аналогичным образом отслеживала отдельные файлы.
С другой стороны, Git делает снимки состояния всего проекта. Файлы не отслеживаются и не имеют версий независимо; ревизия в хранилище относится к состоянию всего проекта, а не к одному файлу.
Когда Git ссылается на отслеживание файла, это просто означает, что он должен быть включен в историю проекта. В речи Линуса речь шла не об отслеживании файлов в контексте Git, а о том, как сравнивать модели CVS и RCS с моделью на основе снимков, используемой в Git.
источник
$Id$
в файле. То же самое не работает в Git, потому что дизайн отличается.Я согласен с Брайаном М. Ответ Карлсона : Линус действительно проводит различие, по крайней мере частично, между файлово -ориентированными и коммит-ориентированными системами контроля версий. Но я думаю, что это еще не все.
В моей книге , которая застопорилась и, возможно, никогда не закончится, я попытался придумать таксономию для систем контроля версий. В моей таксономии термин для того, что нас интересует, это атомность системы контроля версий. Посмотрите, что в данный момент находится на странице 22. Когда VCS имеет атомарность на уровне файлов, фактически существует история для каждого файла. VCS должен помнить имя файла и что происходило с ним в каждой точке.
Git этого не делает. Git имеет только историю коммитов - коммит является его единицей атомарности, а история - это набор коммитов в хранилище. Коммит запоминает данные - целое дерево, полное имен файлов и содержимого каждого из этих файлов, а также некоторые метаданные: например, кто сделал коммит, когда и почему, и внутренний хэш-идентификатор Git. коммит'S родителя коммита. (Именно этот родитель, и ориентированный ациклический граф, сформированный чтением всех коммитов и их родителей, является историей в хранилище.)
Обратите внимание, что VCS может быть ориентированным на принятие, но все же хранить данные файл за файлом. Это деталь реализации, хотя иногда и важная, и Git этого тоже не делает. Вместо этого каждый коммит записывает дерево с именами файлов, кодирующими объекты дерева , режимы (т. Е. Исполняемый файл или нет?) И указатель на фактическое содержимое файла . Сам контент хранится независимо, в объекте BLOB-объекта . Подобно объекту фиксации, BLOB-объект получает хеш-идентификатор, уникальный для его содержимого, но в отличие от коммита, который может появляться только один раз, BLOB-объект может появляться во многих коммитах. Таким образом, основное содержимое файла в Git сохраняется непосредственно в виде большого двоичного объекта, а затем косвенно в объекте дерева, чей идентификатор хеша (прямо или косвенно) записан в объекте коммита.
Когда вы просите Git показать вам историю файла, используя:
Git на самом деле просматривает историю коммитов , которая является единственной историей Git, но не показывает вам ни одного из этих коммитов, если только:
(но некоторые из этих условий могут быть изменены с помощью дополнительных
git log
опций, и очень сложно описать побочный эффект, называемый упрощением истории, который заставляет Git полностью пропустить некоторые коммиты из истории). История файлов, которую вы видите здесь, в определенном смысле не существует точно в хранилище: это всего лишь синтетическое подмножество реальной истории. Вы получите другую «историю файлов», если будете использовать разныеgit log
опции!источник
Запутанный бит здесь:
Git часто использует 160-битные хэши вместо объектов в своем репо. Дерево файлов - это в основном список имен и хэшей, связанных с содержимым каждого (плюс некоторые метаданные).
Но 160-битный хеш однозначно идентифицирует контент (в юниверсе базы данных git). Таким образом, дерево с хешами в качестве содержимого включает содержимое в своем состоянии.
Если вы измените состояние содержимого файла, его хеш-код изменится. Но если его хеш изменяется, хеш, связанный с содержимым имени файла, также изменяется. Что, в свою очередь, меняет хэш "дерева каталогов".
Когда база данных git хранит дерево каталогов, это дерево каталогов подразумевает и включает в себя все содержимое всех подкаталогов и все файлы в нем .
Он организован в виде древовидной структуры с (неизменяемыми, многократно используемыми) указателями на BLOB-объекты или другие деревья, но логически это единый снимок всего содержимого всего дерева. Представление в базе данных мерзавца не плоское содержание данных, но логически это все его данные и ничего другого.
Если вы сериализовали дерево в файловую систему, удалили все папки .git и сказали git добавить дерево обратно в его базу данных, вы ничего не добавите в базу данных - элемент уже будет там.
Это может помочь думать о хешах git как о подсчитанном указателе на неизменяемые данные.
Если вы построили приложение вокруг этого, документ представляет собой набор страниц, которые имеют слои, которые имеют группы, которые имеют объекты.
Когда вы хотите изменить объект, вы должны создать для него совершенно новую группу. Если вы хотите изменить группу, вам нужно создать новый слой, которому нужна новая страница, которой нужен новый документ.
Каждый раз, когда вы изменяете один объект, он порождает новый документ. Старый документ продолжает существовать. Новый и старый документ разделяют большую часть их содержимого - они имеют одинаковые страницы (кроме 1). Эта страница имеет одинаковые слои (кроме 1). Этот слой имеет те же группы (кроме 1). Эта группа имеет те же объекты (кроме 1).
И под тем же самым я подразумеваю логически копию, но с точки зрения реализации это просто еще один указатель с подсчетом ссылок на тот же неизменный объект.
Git РЕПО очень похоже на это.
Это означает, что данный набор изменений git содержит свое сообщение коммита (в виде хеш-кода), содержит свое рабочее дерево и содержит родительские изменения.
Эти родительские изменения содержат свои родительские изменения, все назад.
Часть git-репо, которая содержит историю, является той цепочкой изменений. Эта цепочка изменений на уровне выше дерева «каталогов» - из дерева «каталогов» вы не можете однозначно получить набор изменений и цепочку изменений.
Чтобы узнать, что происходит с файлом, вы начинаете с этого файла в наборе изменений. У этого набора изменений есть история. Часто в этой истории существует один и тот же именованный файл, иногда с одинаковым содержимым. Если содержимое совпадает, файл не изменился. Если это не так, то есть изменения, и нужно сделать работу, чтобы понять, что именно.
Иногда файл исчезает; но в дереве «каталогов» может быть другой файл с тем же содержимым (с таким же хеш-кодом), поэтому мы можем отслеживать его таким образом (примечание; именно поэтому вы хотите зафиксировать перемещение файла отдельно от фиксации для -редактировать). Или то же имя файла, и после проверки файл достаточно похож.
Так что git может совместить "историю файлов".
Но эта история файлов происходит из-за эффективного анализа «всего набора изменений», а не из ссылки на одну версию файла на другую.
источник
«мерзавец не отслеживает файлы» в основном означает , что коммиты Git и состоят из дерева файлов снимки , соединяющая путь в дереве к «сгустку» и совершающий график отслеживания истории коммитов . Все остальное восстанавливается на лету такими командами, как «git log» и «git blame». Эта реконструкция может быть объяснена с помощью различных опций, насколько сложно искать изменения на основе файлов. Эвристика по умолчанию может определять, когда большой двоичный объект изменяется в дереве файлов без изменений, или когда файл связан с другим большим двоичным объектом, чем раньше. Механизмы сжатия, используемые Git, не слишком заботятся о границах BLOB / файлов. Если содержимое уже где-то находится, это позволит сохранить небольшой размер хранилища, не связывая различные BLOB-объекты.
Теперь это хранилище. У Git также есть рабочее дерево, и в этом рабочем дереве есть отслеживаемые и неотслеживаемые файлы. Только индексированные файлы записываются в индекс (область подготовки «кэш»), и только то, что там отслеживается, попадает в хранилище.
Индекс ориентирован на файл, и есть некоторые ориентированные на файл команды для управления им. Но то, что заканчивается в репозитории, это просто коммиты в виде снимков дерева файлов и связанных с ними данных BLOB-объектов и предков коммитов.
Так как Git не отслеживает историю файлов и переименовывает, и ее эффективность не зависит от них, иногда вам приходится несколько раз пробовать разные варианты, пока Git не создаст интересующую вас историю / diffs / blames для нетривиальных историй.
Это отличается от таких систем, как Subversion, которые записывают, а не реконструируют истории. Если это не записано, вы не услышите об этом.
Я на самом деле создал разностный установщик, который сравнивал деревья релизов, проверяя их в Git, а затем создавал сценарий, дублирующий их эффект. Поскольку иногда целые деревья перемещались, это приводило к гораздо меньшим разностным установщикам, чем перезапись / удаление всего, что могло бы произойти.
источник
Git не отслеживает файл напрямую, но отслеживает снимки репозитория, и эти снимки состоят из файлов.
Вот способ посмотреть на это.
В других системах контроля версий (SVN, Rational ClearCase) вы можете щелкнуть правой кнопкой мыши файл и получить его историю изменений .
В Git нет прямой команды, которая делает это. Смотрите этот вопрос . Вы будете удивлены тем, как много разных ответов. Нет простого ответа, потому что Git не просто отслеживает файл , не так, как это делает SVN или ClearCase.
источник
git log
или какую-то программу, построенную поверх этого (или какой-то псевдоним, который делает то же самое). Но даже если бы было много разных способов, как говорит Джо, это также верно для отображения истории ветвей. (такжеgit log -p <file>
встроен и делает именно это)Кстати, отслеживание «контента» привело к тому, что пустые каталоги не отслеживались.
Вот почему, если вы нажмете последний файл папки, сама папка будет удалена .
Это не всегда так, и только Git 1.4 (май 2006 г.) применил эту политику «отслеживания контента» с коммитом 443f833 :
Это было отражено годами позже в январе 2011 года с коммитом 8fe533 , Git v1.7.4:
Тем временем, с Git 1.4.3 (сентябрь 2006 г.), Git начинает ограничивать неотслеживаемый контент непустыми папками с коммитом 2074cb0 :
Отслеживание контента - это то, что позволило git обвинить в самом начале (Git 1.4.4, октябрь 2006, commit cee7f24 ) быть более производительным:
Это (отслеживание содержимого) - это то, что добавило git add в Git API с Git 1.5.0 (декабрь 2006, commit 366bfcb )
Это то, что стало
git add --interactive
возможным, с тем же Git 1.5.0 ( commit 5cde71d )Вот почему, чтобы рекурсивно удалить все содержимое из каталога, вам нужно передать
-r
параметр, а не просто имя каталога как<path>
(все еще Git 1.5.0, commit 9f95069 ).Просмотр содержимого файла вместо самого файла позволяет сценарию слияния, подобному сценарию, описанному в коммите 1de70db (Git v2.18.0-rc0, апрель 2018 г.)
Коммит 37b65ce , Git v2.21.0-rc0, декабрь 2018 года, недавно улучшил разрешение конфликтов.
И фиксация bbafc9c еще раз иллюстрирует важность рассмотрения содержимого файла , улучшая обработку конфликтов переименования / переименования (2to1):
источник