Клонирование репозитория git-svn приводит к «исчезновению» веток - PullRequest
1 голос
/ 31 октября 2019

Предисловие

У нас есть большой репозиторий SVN (200k + коммитов и сотни веток и тегов). Большой, зловещий, неприемлемый, расстраивающий беспорядок. Чтобы работать более эффективно, около года назад я сделал клон git svn на своей машине для разработки, поэтому я локально развиваюсь на GIT, а затем перехожу к SVN.

Сейчас мы думаем о разделении репозитория и перемещенииосновные ветви разработки для git или, по крайней мере, для переноса нашей ветви разработки на git.

Так как у меня есть локальный репозиторий git, я хотел провести некоторый тест, клонируя его часть и передав его в нашу компанию. GitLab, но без особого успеха, возможно потому, что мне не хватает знаний о некоторых механизмах Git

Давайте начнем

Для того, чтобы провести несколько быстрых тестов, не загружая весь репозиторий на 30 ГБЯ хотел сделать мелкий клон моего локального репозитория Git и отправить его с помощью следующей команды:

git clone --depth=1 --no-single-branch file:///path/to/repo

Я хотел клонировать ревизию HEAD каждой ветви, но клон включал только главную ветвьи наша ветка разработки, больше ничего (я не уверен насчет тегов, я не проверял). Через некоторое время я понял, что в клон включена только наша ветка разработчика, потому что она была единственной, которую я когда-либо проверял (хотя репозиторий git svn является полным клоном репозитория SVN).

Затем я попыталсясделать

git clone file:///path/to/repo

, и я снова получил только master и мою ветку разработки, ничего больше.

В этих двух попытках я заметил, что клон был намного меньше (200-700MB)чем оригинальный репозиторий git (30GB). Во второй попытке я ожидал хранилище того же размера, что и оригинал.

Итак, я понял, что git клонирует только проверенные ветки, а не удаленные (remotes / svn / *). Почему, поскольку git svn repo является полной копией svn repo? Почему он не клонирует все ветви? Они есть (иначе git svn repo не будет таким большим), их просто не проверяют. И ... Как мы можем говорить об «удаленных» ветвях? Разве они не являются частью репозитория git svn и должны считаться локальными?

Так как я могу сказать git учитывать все эти ветви при клонировании репозитория git svn? Я не хотел бы проводить массовую проверку всех веток в git svn repo, для меня это звучит как неуклюжее и грязное решение.

Обновление

Спасибо за ваш ответ. Прошу прощения, что не отвечаю вам раньше, но вы оставили мне достаточно много документации для чтения, плюс мне пришлось провести какое-то другое исследование самостоятельно!

Так что, если мое понимание верно, мой мерзавец-svn-репозиторий содержит все коммиты исходного svn-репозитория, и он знает, что svn-репозиторий содержит ветви и теги, но локально он не имеет связи между SHA1 коммита и меткой, которая является именем ветви, и я должендобавьте эти ассоциации вручную.

Ваш фрагмент - очень полезная отправная точка, спасибо!

Я также обнаружил магический аргумент --mirror для команды клонирования, которая также импортировала пульты, поэтому ямне не нужно было трогать репозиторий git-svn, но позже я создал ветки непосредственно в клонированном репозитории git.

1 Ответ

0 голосов
/ 31 октября 2019

TL; DR: вам нужно создать фактические имена ветвей для каждой ветви, которую вы хотите иметь в качестве ветви. Имена удаленного слежения просто не учитываются при клонировании (обычно). Это может быть очень дешево! Прочитайте подробное объяснение.

Вот дешевый способ создания локальных ветвей из каждого refs/remotes/svn/* имени:

git for-each-ref --format='%(refname)' refs/remotes/svn |
    while read name; do
        local=${name#refs/remotes/svn/}  # remove the icky part from the name
        [ "$local" == HEAD ] && continue
        git branch $local $name
    done

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

... Итак, я понял, что git клонирует только проверенные ветви, а не удаленные ...

Нетна самом деле не существует такой вещи, как «удаленная ветвь». Ну, если вы не определите «удаленную ветку» таким образом, чтобы она существовала. Что в конечном итоге ставит нас перед проблемой определения «ветви» в первую очередь: см. Что именно мы подразумеваем под «ветвью»? Когда мы осторожны с этим - в отличие от повседневного разговора - мне нравится бытьОбязательно используйте фразу из двух слов имя ветки для обозначения таких имен, как master, которые на самом деле уже сокращены: см. ниже.

То, с чем Git имеет дело с commitits , как найдено names и другими коммитами. См. Думайте как (а) Git для правильного определения достижимости и множества связанных вещей, 1 , но общая идея заключается в том, что имена - полные именакак refs/heads/master или refs/remotes/svn/foo - каждый из них содержит хэш-идентификатор одного коммита. Этот коммит запоминает, какой коммит (ы) пришел прямо перед ним. Эти коммиты - родительский коммит - помнят их коммиты предшественника, бабушка и дедушка запоминают их предшественников и т. Д.

Что делает git clone:

  1. создать новый пустой каталог (или использовать тот, который вы указали для использования);
  2. создать новый пустой репозиторий в этом каталоге, с помощью git init;
  3. добавить remote , который состоит из простого имени, такого как origin и URL-адреса (и некоторой конфигурации - его можно перейти к шагу 4 или считать частью шага 3);
  4. сделать любоедополнительная необходимая конфигурация;
  5. run git fetch;и в последний раз
  6. введите git checkout для имени, которое вы предоставляете, или для других ресурсов Git, или - наихудший запасной вариант - попробуйте git checkout master.

Шаг 5вот самый важный для вас здесь, потому что git fetch - это то место, где находятся все основные действия.

Почему он не клонирует все ветви?

Когдаgit fetch запускается, он получает список из other Git, в котором другой Git сообщает ему о всех его имен. Другой Git скажет, например: У меня есть refs/heads/master, то есть commit a123456...;У меня есть refs/remotes/svn/foo, это коммит b789abc... и т. Д.

Ваш Git затем выбрасывает любое имя, которое не начать с refs/heads/ или refs/tags/. Результирующий список имен представляет собой имена веток Git и тегов . Все остальные имена попадают в другие категории. В частности, любое имя, начинающееся с refs/remotes/, представляет собой имя для удаленного отслеживания , 2 , поэтому оно выбрасывается.

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

Как только ваш Git получает коммитобъекты и другие внутренние объекты, если / при необходимости, ваш Git затем копирует свои ветви имен - их refs/heads/master и т. п. - в ваши имена для удаленного слежения. Их refs/heads/master становится вашим refs/remotes/origin/master. Их refs/heads/develop (если таковой существует) становится вашим refs/remotes/origin/develop.

Все это происходитs во время шага git fetch (шаг 5). Такие параметры, как --single-branch или --no-single-branch, влияют на то, какие имена их ветвей совпадают, но не на преобразование имени ветви в имя удаленного отслеживания. Опция --mirror влияет на преобразование , полностью исключая его, но иногда имеет и нежелательный побочный эффект:

Последний шаг, git checkoutна шаге 6 имеет один очень большой побочный эффект. Новый клон, который вы только что создали, имеет нет названий ветвей. 3 Так что git checkout master или любое другое имя явно обречено на провал, верно? Но это не подводит. Вместо этого Git использует хитрый (?) Трюк: Когда вы просите проверить имя ветви, которая не существует, Git просматривает имена удаленного отслеживания , чтобы увидеть, есть ли такая, котораябудет соответствовать. Если это так, Git создаст (локальное) имя ветви, используя идентификатор хеша фиксации, сохраненный в соответствующем имени удаленного отслеживания.

Так что это создает ту ветку, о которой вы просили - или в этом случае, поскольку вы не указали одну ветку, другой Git сообщает вашему Git, какое имя ветви рекомендует другой Git. (Обычно это просто master.) Это создаёт шаг 6.

Если у вас есть теги в репозитории origin, у вас будет некоторое их количество - от нуля до всех - в новомклон тоже. Вы можете явно запросить теги позже, или нет, с более поздним git fetch. Вы можете явно попросить , а не иметь теги в вашем новом клоне во время клонирования. Теги, которые у вас есть на данный момент, просто копируются из тегов в другом хранилище. Идея заключается в том, что, в отличие от имен веток, которые являются полностью частными для каждого репозитория, имена тегов будут общими для всех репозиториев, распределенных путем присоединения к репозиторию, почти как какой-то вирус. 4

Поскольку ваш исходный репозиторий имеет в основном только имена для удаленного отслеживания, а не ветви, ваш клон - мелкий или нет - пропускает эти имена и коммиты, которые доступны только из техnames.


1 Это немного отличается от SVN, в котором есть один центральный сервер, который может просто нумеровать каждую ревизию последовательно. Git буквально не может полагаться на последовательную нумерацию, потому что могут быть отдельные клоны, которые последовательно-но-параллельно-извиняются (извинения за не-слово здесь ?), получающие разные коммиты,То есть предположим, что клоны A и B идентичны и каждый имеет 500 коммитов. Затем Алиса, работающая в клоне А, создает коммит # 501. Тем временем Боб, работающий в клоне B, создает коммит # 501. Эти два коммита разные - возможно, в разных ветках - и оба они # 501. Последовательные номера здесь не могут работать.

2 Git называет это именем удаленного отслеживания ветви . Раньше я использовал эту фразу, но теперь я думаю, что слово branch здесь больше вводит в заблуждение, чем полезно. Вы можете называть это как хотите: просто помните, что это не имя branch , так как они на самом деле начинаются с refs/heads/.

Примечание: Git обычно удаляет refs/heads/, refs/tags/ и refs/remotes/ разделяются здесь при печати имен, при условии, что вывод все еще будет достаточно четким. Иногда Git удаляет только refs/: попробуйте git branch -r, затем попробуйте git branch -a. (Чем они отличаются? Это загадка.)

3 Если вы использовали --mirror, у вашего нового клона есть все имена ветвей, но затем git clone пропускает шаг 6. Ваш новый клон пуст, поэтому нет рабочего дерева, и git checkout не может быть использовано.

4 Это также способ распространения коммитов. Предположим, у вас есть коммиты W, X и Y подряд, которых у них нет. Вы подключаетесь к их Git как операция push, и вы даете им все эти три коммита и просите их установить одно из их имен для запоминания коммита Y, который запоминает X, который запоминает W, который запоминаеткоммит у них уже есть.

или: у них есть тон совершает, а ты нет. Вы подключаетесь к их Git как fetch операция, они дают вам все три, и ваш Git устанавливает ваш origin/whatever для запоминания коммита Y сейчас.

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

Коммит или другой объект Git, у которого хэш-идентификатор не может найти его, в конечном итоге собирается сборщиком мусора. и выбросил. Для пустых репозиториев это имеет тенденцию быть быстрее, и, начиная с Git 2.11, сервер «получает коммиты и другие объекты Git» сначала помещает их в область карантина, прежде чем решить, что они хороши, и принять их или решить, что ониплохо и отвергая их. Принятые затем переносятся из карантина в реальную базу данных хранилища, а отклоненные быстро отбрасываются. До 2.11 полученные объекты сразу отправлялись, временно вздувая серверы, которые, например, отклоняли большие файлы (вспомните ограничения размера файла GitHub в 100 МБ).

Мелкие клоны изменяют (некоторые из) эти правила: с мелкимклон, получающий Git имеет специальный файл, полный хеш-идентификаторов. Ему не хватает этих фактических коммитов, но делает вид, что утверждает, что они есть, поэтому, когда отправитель спрашивает «у вас есть коммит X», ответом будет «да», так что отправитель никогда не отправляет коммит X.

...