Git не определяет «под-репозиторий», поэтому вы можете определить его так, как вам нравится.
Git действительно определяет, что означает перечисление каталога в .gitignore
,и этот конкретный шаблон работает отлично.То, что вы сделали, это установили два репозитория, которые вообще не связаны друг с другом.
В типичной установке эти два репозитория будут жить бок о бок:
$HOME/
[various dot files, personal files, etc]
src/
project-A/
.git/
[all of Git's files]
.gitignore
README.md
[various files]
project-B/
.git/
[all of Git's files]
.gitignore
README.md
[various files]
Все, что вы сделали, это переместились project-B
внутрь project-A
рабочего дерева .Помните, что любой стандартный репозиторий состоит из трех частей:
Сам репозиторий .git
, полный файлов, которые Git использует для собственных целей Git.Он содержит основные базы данных Git-репозитория, одна из которых содержит все коммиты и другие объекты Git.В нем есть все файлы, которые нужны Git для выполнения собственных задач Git.(Вы можете посмотреть на эти файлы в любое время, но в целом вам не следует редактировать их напрямую, если вы не знаете, что делаете. Но некоторые из них предназначены для очевидного, в этом случае прямого редактированиякак правило, тоже работает нормально. Возможно, вы захотите просмотреть файл .git/config
, например - он имеет довольно простой формат, напоминающий файлы INI Windows. Просмотр файла .git/HEAD
также поучителен.)
Рабочее дерево , в котором вы выполняете свою реальную работу.Это обычное дерево каталогов, содержащее обычные файлы.Git заполнит его файлами, извлеченными из коммита, чтобы вы могли работать с этими файлами.Здесь вы также можете хранить файлы, о которых Git ничего не знает: эти файлы называются неотслеживаемыми файлами .Git будет жаловаться на них, если вы не перечислите их - или их шаблоны имен - в файле .gitignore
, основная функция которого состоит в том, чтобы Git прекратил жаловаться на них (и убедитесь, что вы случайно не поместили их в индекс, длячто, см. следующий пункт).
Индекс , где Git сохраняет следующий коммит, который вы собираетесь сделать .В индексе есть копия каждого файла, не замороженного из текущего коммита, готового перейти в следующий коммит.Но эти копии находятся в специальном формате Git-only, как файлы внутри коммитов.Это не обычные файлы, подобные файлам в вашем рабочем дереве.
После работы с файлом в рабочем дереве вы можете в любой момент использовать git add
повторно скопировать этот файл из рабочего дерева обратно в индекс.Это превращает его обратно в специальный формат Git-only, так что он готов к переходу на следующий коммит.Если файл не был в индексе раньше, он копирует его в индекс (превращая его в формат Git-only), поэтому теперь он равен в индексе.
Наличиефайл в индексе - это то, что делает файл отслеживаемым , поэтому файл, который находится в рабочем дереве, но не в индексе, является неотслеживаемым файлом ,Git будет жаловаться на это, как мы только что сказали, если вы не скажете Git не жаловаться на этот файл через запись .gitignore
.
Вы можете запустить git add
с опцией(например, -a
) или аргумент (например, .
), который говорит добавить все или добавить много файлов .В этом случае git add
проверяет любой неотслеживаемый в настоящее время файл - любой файл, который не в индексе прямо сейчас - по настройкам .gitignore
, а не будет добавлятьфайл, так что он останется без отслеживания.
Таким образом, .gitignore
означает не игнорировать этот файл , а , если этот файл не отслежен, не жалуйтесь на него не отслеживается и не копирует его автоматически в индекс, когда я массово добавляю или обновляю множество файлов для индекса, чтобы они были готовы перейти в следующий коммит.
Индекс сам по себе является файлом, а иногда и более чем одним файлом, внутри .git
, но его стоит перечислить отдельно по двум причинам.Во-первых, это так важно и так в вашем лице, даже если вы не можете увидеть это.Во-вторых, Git теперь поддерживает git worktree add
для создания дополнительных рабочих деревьев.Каждое добавляемое вами рабочее дерево имеет свой собственный индекс .То есть индекс и рабочее дерево сопряжены: есть только один репозиторий , и с этим одним репозиторием вы получаете одно дерево индекса и работы бесплатно.Затем вы можете git worktree add
второе дерево индекса и работы, затем третье дерево индекса и работы и так далее.Очевидно, что индекс, по крайней мере, логически отличается от самого хранилища, тогда: он связывается с рабочим деревом, а не с хранилищем.
В любом случае, результат вышеупомянутого состоит в том, чтопоместив project-B
внутрь project-A
, все, что вы сделали, - это проект А, у которого есть тонн игнорируемых файлов:
$HOME/
[various dot files, personal files, etc]
src/
project-A/
.git/
[all of Git's files]
.gitignore <-- lists `/project-B/`
project-B/
.git/
[all of Git's files] <-- these files are untracked in A
.gitignore <-- and this is untracked in A
README.md <-- and so is this
[various files] <-- and all of these
README.md
[various files]
Это gitсубмодуль вроде того же, что я делал выше?
Не совсем, нет.Это существенно сложнее.Однако создает аналогичную структуру рабочего дерева .
При использовании подмодулей вы фактически связываете в двух репозиториях.Однако эта связь, по сути, односторонняя.
Обычно вы сначала создаете два отдельных, полностью независимых хранилища и заполняете их различными коммитами.Это похоже на параллельную настройку, описанную выше, когда проекты A и B располагаются рядом друг с другом, а не как вложенные, один внутри другого.На самом деле, очень часто вы вообще не создаете проект B: кто-то еще уже уже создал проект B, как, например, необычная библиотека, которую вы хотели бы использовать дляпроделать некоторую работу в вашем новом проекте A. Давайте рассмотрим это в качестве примера, поскольку он не только более распространен, но и того, как его ожидает использовать git submodule
- и если вы делаетеСам проект B, вы можете просто начать свой первый удар в проекте B, настроить его на GitHub или где бы вы ни собирались сохранить его для общей доступности, и получить все это, прежде чем приступить к созданию проекта A.
Итак, на данный момент у вас есть проект B, который - давайте предположим - имеет свой главный, общедоступный репозиторий открытого доступа, хранящийся в GitHub по URL git://github.com/someuser/B
/ ssh://git@github.com/someuser/B
/ https://github.com/someuser/B
.
Теперь вы собираетесь создать проект A. Вы можете использовать GitHub «создать репозиторий», нажимая кнопки, чтобы сначала создать его там, а затем клонировать на свой ноутбук или где бы вы ни работали:
<click some buttons in browser>
git clone <url> A # so now you have A/README.md and A/.git and so on
cd A
Или вы можете создать егокак пустой на GitHub или даже не создавать его на всех на GitHub, если вам нравится:
mkdir A
cd A
git init
В любом случае, вы сейчас находитесь в каталоге A/
, который имеет.git/
подкаталог, содержащий базы данных репозитория, индекс и рабочее дерево.Внутри этого рабочего дерева вы можете создавать и редактировать различные файлы, использовать git add
, чтобы скопировать их в индекс, чтобы они переходили к следующему коммиту, а затем запустить git commit
, чтобы сделать новые коммиты, которые замораживают содержимое индекса вновый снимок.
Теперь вы готовы ссылка хранилище B, которое все еще является независимым Git-хранилищем, само по себе в хранилище A.при этом вы выбираете один из URL-адресов, по которым находится основная версия репозитория B.Ваш Git запустит git clone
, чтобы поместить новый клон хранилища B в ваше рабочее дерево, поэтому вы также должны выбрать путь для проекта B - каталог, в который он войдет, в вашем текущем рабочем дереве.Давайте рассмотрим ssh://github.com/someuser/B
здесь как URL и project-B
как каталог:
git submodule add ssh://github.com/someuser/B project-B
Your Git теперь запускает git clone
, чтобы создать project-B
как клон ssh://github.com/someuser/B
.Этот клон является обычным клоном практически во всех отношениях.Например, у него есть .git
. 1 Мы просто называем его "подмодулем", потому что он используется другим хранилищем Git.Это не должно знать или заботиться об этом - что касается клона B, то это просто обычный клон.
Между тем, тот факт, что A теперь использует клон B, превращает A в то, что Git называет суперпроект .В вашем рабочем дереве A
команда git submodule add
создаст файл с именем .gitmodules
, поместив URL-адрес и путь для подмодуля B в этот файл.Он git add
отправит этот файл в индекс А, готовый к следующему коммиту.И, наконец, он git add
будет индексировать A специальную запись типа gitlink , используя git add project-B
, который является путем для подмодуля B.
Итак, теперь index в A
содержит две совершенно новые записи: одну для .gitmodules
, которая - если вы посмотрите на копию файла рабочего дерева - теперь перечисляет подмодуль, а одна - для «файла» -действительно, gitlink - с именем project-B
.Если вы сейчас запустите git commit
в рабочем дереве для проекта A, вы получите новый коммит в базе данных репозитория.В этом новом коммите есть все файлы, которые уже были в индексе (например, README.md
и т. Д.), Плюс этот новый файл .gitmodules
, плюс эта новая вещь gitlink.
1 В более старых версиях Git этот .git
в клоне подмодулей проекта B представляет собой обычный каталог, содержащий базу данных репозитория для этого клона, как и любое обычное рабочее дерево Git-репозитория с базой данных внутри его .git
,В современном Git это файл, который сообщает Git, где найти базу данных репозитория .git
для клона подмодуля проекта B, которую Git секретирует в каталоге .git
для проекта A (то есть в A/.git
).Такое скрытие хранилища submodule-B позволяет добавлять рабочие ветви для A к share хранилищу submodule-B, а не просто дублировать его.
Операция подмодуля
Помните, опять же, подмодуль не должен знать, что это подмодуль: это просто обычный, обычный репозиторий Git.Если вы сейчас cd project-B
зайдете в рабочее дерево для клона проекта B и запустите git log
и git status
и различные другие команды Git, все они работают, и они все расскажут вам, что происходит в этом клоне проекта B.
Вы можете работать здесь, если хотите!Тем не менее, есть загвоздка.Суперпроект Git, управляющий рабочим деревом для проекта A, к этому моменту дал команду подмодулю 1232 * Git, находящемуся здесь в рабочем дереве каталога project-B
,перейдите в режим detached HEAD .Если вы не знакомы с режимом отключенного HEAD, вам нужно узнать об этом.Если вы знакомы с ним - или после того, как вы ушли и узнали об этом и вернулись сюда - один один конкретный коммит , в котором ваше рабочее дерево подмодуля project-B имеетHEAD отключен - это хэш-идентификатор, который записывается в gitlink в суперпроекте.
Другими словами, когда вы работаете в проекте A и говорите ему иди манипулируйте другим репозиторием Git в проекте.-B каталог , способ, которым проект A Git знает , который обязуется использовать , это искать в gitlink , хранящемся в индексе для проекта A. Скажем, для иллюстрации,что это 0123456...
.
Если вы делаете перейдете в каталог project-B
, вы находитесь в клоне B, и вы можете git checkout
любой другой коммит иличетная git checkout
a ветвь в B. Это изменяет отсоединенный HEAD или даже присоединяет его к ветке, так что теперь в хранилище B есть другой извлеченный коммит.Допустим, вы делаете это:
cd project-B
git checkout develop
... do some work ...
git add ... some files ...
git commit -m 'do work on B to make it more useful for A'
Вы можете git push
новый фиксируется обратно в GitHub, поскольку проект B в конце концов является обычным старым хранилищем.Но теперь коммит HEAD
в project-B
(каталог рабочего дерева) больше не 0123456...
, теперь, скажем, 8088dad...
.Если вы вернетесь обратно к рабочему дереву проекта A и запустите git submodule status
, вы увидите, что управляющий Git A говорит: эй, субмодуль удалился из отсоединенного HEAD, который я хотел, он не включен 0123456...
больше!
Это правда, но если это то, что вы хотите , теперь пришло время использовать git add
до обновить запись gitlink вИндекс для проекта A:
git add project-B
, например.Теперь index , связанный с рабочим деревом проекта A, вызывает коммит 8088dad...
в подмодуле, и если вы запускаете git commit
в рабочем дереве проекта A, вы получаете новый коммит для проекта A, которыйговорит следующее:
git commit -m 'update submodule B to 8088dad...'
(Это на самом деле не лучшее сообщение о коммите - лучше сказать, какие функции подмодуля B вы используете сейчас, а не просто «Я переключился на коммит»8088dad "- но это пример, и я даже не знаю , какие функции вы используете.)
Существуют и другие способы танца обновления субмодуля, а затем записив новом коммите в суперпроекте, что суперпроект Git должен дать команду подмодулю Git проверить этот конкретный коммит.Команда git submodule
выражает многие из них.Но дело в том, что в репозитории superproject есть коммиты - многие со временем, каждый из которых говорит:
- Я использую подмодуль из
url
... - хранится по пути
path
... - , и когда я его использую, я запускаю субмодуль Git для проверки коммита
hash-ID
.
Первые два фрагмента информации записываются в коммитах в проекте A в файл с именем .gitmodules
.Каждый коммит имеет свою собственную копию этого файла (хотя, как обычно, если миллион коммитов использует одну и ту же версию файла, в хранилище будет только одна копия этой версии).Последний фрагмент информации записывается непосредственно в коммитах в проекте A: каждый сохраняет один необработанный хэш-идентификатор, давая коммит, который должен быть git checkout
-одан в пути субмодуля.
Summary
Цель git submodule
- дать вам возможность указать в вашем суперпроекте Git, что вы зависите от какого-то другого Git-репозитория.Вы записываете URL этого репозитория, который вы хотите, чтобы новые клоны использовали для git clone
субмодуля.Вы записываете путь , который вы хотите, чтобы ваш суперпроект Git использовал для сохранения клона.И с каждым коммитом в суперпроекте вы записываете конкретную фиксацию подмодуля для этого субмодуля, чтобы клонировать суперпроект, а затем проверить некоторую конкретную историческую фиксацию в этом клоне, также клонирует субмодуль и проверяет правильную историческую фиксацию в субмодуле-клоне.
Это означает, что суперпроект Git теперь зависит от подмодуля: хотя клон субмодуля:управляется с помощью команд Git суперпроекта (что делает подмодуль своего рода подчиненным), сам суперпроект больше не является независимым.Нужна помощь этого подмодуля Git, чтобы суперпроект чувствовал себя целым.И поскольку подчиненный подмодуль является клоном , это не мешает любому, кто управляет оригинальной версией подмодуля, делать все, что он хочет с этим хранилищем.Это даже включает в себя копирование коммитов из источника клона подмодуля, и если исходные коммиты источника удалили , хэш-идентификаторы, сохраненные в коммитах суперпроекта, теперь бесполезны.
Thэто не значит не делать этого , это просто означает знать, в какие зависимости вы попадаете, когда делаете это. Если вы хотите, чтобы ваш проект А был такимнезависимо от существования проекта B и точных хеш-идентификаторов коммитов, не используйте субмодуль.Если вы согласны с тем, что ваш проект A зависит от проекта B, и вы хотите, чтобы было удобно использовать новые функции из project-B по мере их появления, git submodule
хорошо подходит.