Какую проблему пытается решить репозиторий Git --bare?

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

Напомним, что практическая разница (т. е. с точки зрения файлов) между обычным и голым репо должна быть следующей:

  • репозиторий не голый представляет собой комбинацию:

    1. папка .git, то есть «специальная» папка, которую Git как программное обеспечение использует для хранения всех данных (например, больших двоичных объектов файлов проекта с версиями) и метаданных (например, истории коммитов), чтобы иметь возможность работать должным образом .
    2. рабочее дерево, то есть фактические файлы и папки, представляющие наш проект. Следует помнить одну важную вещь: рабочее дерево — не единственное место, где хранится содержимое вашего проекта. Данные вашего проекта сохраняются Git также внутри папки .git в специальном формате с использованием некоторых внутренних инструментов, таких как .git/объекты. Рабочее дерево существует потому, что с его помощью все, что не является Git, может работать (отсюда и название рабочее дерево) и редактировать файлы и папки проекта.
  • репозиторий голый:

    1. содержимое подкаталога .git прямо в самом основном каталоге
    2. нет рабочего дерева

Вопрос: зачем мне нужно промежуточное голое репо, чтобы удобно синхронизировать два не голых репо? Многие потоки и статьи отвечают, говоря, что отсутствие центрального голого репо приведет к рассинхронизации центрального рабочего дерева (см. здесь). Хорошо, но почему? Может ли кто-нибудь привести конкретный пример?

Я могу себе представить следующую ситуацию:

  1. А — локальное репо, Б — локальное репо, а С — центральное удаленное репо. Все не голые.
  2. А делает коммит c и отправляет его на С. С обновляет свою папку .git и рабочее дерево в соответствии с изменениями в c. Важный: «обновление рабочего дерева», на мой взгляд, означает замену рабочего дерева С (то есть файлов и папок С) рабочим деревом А.
  3. Б извлекает изменения из С и обновляет свою папку .git и рабочее дерево в соответствии с изменениями в c

Как может ситуация, подобная описанной выше, привести к рассинхронизации рабочего дерева C? Что вообще означает, что C выходит из синхронизации?

Единственное истинное преимущество, которое я пока понял, это то, что для таких сервисов, как Github или Gitlab, не поддерживающих рабочее дерево (т.е. наличие голого репо) для каждого репо и для каждой ветки очень удобно экономить место для хранения. Они могут реконструировать рабочее дерево на лету, используя инструменты Git.

Какую проблему здесь пытается решить ты?

Romain Valeri 04.05.2022 18:21

@RomainValeri понимает, почему Git внедрил концепцию голого репо

toioski 04.05.2022 18:22

Разве вашего последнего абзаца не было бы достаточно, чтобы оправдать их существование? Кроме того, что вы подразумеваете под «логической» разницей? Почему реальной практической разницы как-то не хватило бы? Честно спрашивал, мог что-то упустить.

Romain Valeri 04.05.2022 18:27

@RomainValeri да, моего последнего абзаца было бы достаточно, если бы не было много тем и статей, в которых упоминается, что это не единственная мотивация. «Настоящая» мотивация, по-видимому, заключается в том, чтобы избегать управления несинхронизированным центральным рабочим деревом, но я действительно не понимаю, как это могло произойти. Под «логичным» я имею в виду: «откуда возникает необходимость реализации концепции --bare репо? Какую проблему он пытается решить?»

toioski 04.05.2022 18:32

@RomainValeri также в этом ответе stackoverflow.com/a/24115534 упоминается случай «рассинхронизации рабочего дерева». Он говорит: «Каждый раз, когда вы нажимаете на не голое репо, у вас нет гарантии, что его рабочее дерево будет отражать то, что вы нажимаете»… почему?

toioski 04.05.2022 18:39

Поскольку я не являюсь частью downvoters, я могу только предполагать. Может быть, это было прочитано как вопрос мнения? Были случаи, когда люди рассказывали о технологии в форме вопроса (я не думаю, что это ваш случай), может ли это быть кому-то?

Romain Valeri 05.05.2022 13:42
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
6
61
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

What does even mean that [the non-bare central repo] goes out of sync?

Давайте обойдемся без третьей машины: нам нужен только client, не голый репозиторий, и server, репозиторий, который должен быть, но не голый.

На server ветка main активно проверяется. Кто-то может войти или не войти в server и редактировать там файлы.

Между тем, в client вы сделали новую фиксацию, запускаете git push и отправляете новую фиксацию в server. Если сервер примет эту фиксацию, теперь есть две возможности:

  1. server Git-репозиторий не обновляет извлеченное рабочее дерево или
  2. server Git-репозиторий делает обновляет проверенное рабочее дерево.

Обе ситуации могу дают плохой результат. Прежде чем мы начнем, давайте немного изучим работу Git.


1Этот был был истинным до того, как был добавлен git worktree add, а теперь это не так. Таким образом, простота существовала до Git 2.5, а теперь ее нет.

2Эта был верна в оригинальном Git, до изобретения различных элементов конфигурации. Теперь это не так. Итак, когда-то простота была, а теперь ее нет. (Вещи receive.denyCurrentBranch произошли до команды git worktree, но я не помню навскидку, какая это была версия.)


Git предназначен для коммитов; коммиты пронумерованы; имена веток находят коммиты

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

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

Сам коммит содержит две вещи:

  • полный снимок каждого файла, в состоянии, в котором он должен иметь за этой фиксации; и
  • метаданные: информация о самом коммите, например, кто его сделал, когда и почему (сообщение журнала).

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

Чтобы иметь возможность получить коммит самый последний для любой заданной ветки, Git хранит в имени ветки (например, refs/heads/main) необработанный хэш-идентификатор коммита самый последний. Этот коммит содержит в своих метаданных хэш-идентификатор предыдущего (родительского) коммита, который, в свою очередь, содержит другой хэш-идентификатор для другого родителя и так далее.

Когда мы используем git checkout или git switch с именем ветки, мы сообщаем Git: извлеките коммит самый последний для этой ветки. Это тот, чей хэш-идентификатор хранится в названии ветки. Таким образом, с помощью git switch main Git ищет refs/heads/main, находит хэш-идентификатор, такой как a123456..., и ищет эту фиксацию в базе данных. Этот коммит имеет набор связанных с ним файлов. Git копирует эти файлы из коммита — те, что в коммита, как правило, не используются ОС, поскольку они доступны только для чтения, сжаты, доступны только для Git и дедуплицированы — в ваше рабочее дерево.

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

Когда все это будет готово, Git установит специальное имя HEAD, содержащее название филиала. (В оригинальном Git это была символическая ссылка на файл refs/heads/main, но, как и во многих частях Git, с этим покончили более десяти лет назад.)

Теперь у нас есть группа четко определенных, тщательно скоординированных данных:

  • HEAD содержит название текущей ветки;
  • имя ветки содержит хэш-идентификатор;
  • Индекс Git содержит имена файлов и идентификаторы больших двоичных объектов для фиксации следующий, отслеживая файлы в рабочем дереве; и
  • рабочее дерево содержит файлы, скопированные из коммита.

Вы работаете с файлами, запускаете git add, чтобы сказать Git обновить то, что находится в индексе Git, и, в конце концов, запускаете git commit. На данный момент Гит:

  • читает HEAD, а затем имя ветки, чтобы найти текущую фиксацию;
  • собирает все необходимые метаданные;
  • упаковывает содержимое индекса;
  • записывает все это в коммит новый, который получает новый уникальный хеш ID; и
  • записывает хэш-идентификатор в имя ветки.

Тщательно скоординированные данные позволяют Git делать все это, и все еще тщательно координируются.

Если кто-то другой сделает коммит, пока вы работаете...

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

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

Однако вместо этого скажем, что мы принимаем толчок. Сервер Git обновляет refs/heads/main для хранения хэш-идентификатора коммита их. Две наши возможности:

  1. не индекс обновления и рабочее дерево;
  2. делать индекс обновления и рабочее дерево.

Если мы выберем вариант №1, то у нас будет «устаревшая проверка»: наши файлы взяты из предыдущего коммита. Но название филиала содержит новый идентификатор хэша коммита. Итак, мы не синхронизированы. Если мы обновим какие-либо файлы, а затем зафиксируем, мы получим отменить работу другого парня (помните, что наше программное обеспечение Git использует то, что находится в нашем индексе, что соответствует нашему рабочему дереву). Это не очень хорошо, поэтому давайте перейдем к варианту 2.

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

Таким образом, обновление рабочего дерева репозитория сервера является, возможно, лучше, чем не делать этого, и это то, что делает настройка receive.denyCurrentBranch в updateInstead. Однако это не идеально. «Идеально» — это просто не иметь рабочего дерева, так что ничего не может пойти не так, и мы получаем это с помощью --bare.

Спасибо БОЛЬШОЕ, это именно то, что я искал и даже больше. Теперь это кристально чисто. Я не совсем понял, потому что я был предвзят из-за того, как мы привыкли использовать Git с такими сервисами, как GitHub. Я всегда связывал GitHub с «сервером» вашего примера и, следовательно, с репозиторием, с которым вы не можете взаимодействовать (например, оформить заказ, зафиксировать и т. д.). Однако, если мы думаем о «сервере» как о машине, к которой у нас есть доступ, тогда ситуация рассинхронизации имеет смысл!

toioski 04.05.2022 20:01

Другие вопросы по теме