Каков наиболее эффективный способ найти общих предков в большом удаленном репозитории GitHub?

Методы, которые я исследовал:

Итак, есть десятки способов взаимодействия с репозиторием GitHub. Методы включают Github GraphQL, GitHub REST API, git clone/fetch-ing и прямое взаимодействие с базовыми службами Github.

Я нашел различные способы формирования дерева коммитов SHA, но почти каждый метод, который я нашел, имеет неэффективность, которую я не могу решить.

Цель:

Я просто хочу найти SHA общего предка двух ветвей.

Что я знаю:

Ветки в git подобны указателям на коммит shas. Сама ветка на самом деле не отслеживает, какие коммиты находятся в ветке, а только какие коммиты находятся на кончике ветки. Затем коммиты отслеживают своих родителей. Таким образом, обычный способ определить общего предка двух ветвей состоит в том, чтобы перебирать каждую родительскую фиксацию, начиная с кончика ветки, пока вы не найдете фиксацию, которая является общей для обеих ветвей. Вот как работает git merge-base.

Проблема с git merge-base в том, что вам нужно как минимум выполнить git fetch в хранилище. Это неэффективно, потому что git fetch загружает больше, чем просто SHA коммита из источника.

REST и проблема GraphQL:

Итак, я думал об использовании GitHub REST и/или API GraphQL. Проблема с обоими:

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

Конкретный репозиторий, который я пытаюсь запустить merge-base, — это chromium/chromium, который является абсолютно огромным репозиторием.

Неэффективность, вызванная крупномасштабными репозиториями:

Я пытаюсь найти общих предков основной ветки и веток функций, связанных с каждым тегом Git. Звучит довольно просто, за исключением того факта, что в репозитории chromium/chromium есть почти 27 000 тегов и связанных с ними веток функций.

Таким образом, с учетом сказанного, попытка вручную запустить базу слияния в репозитории с помощью простого запроса GraphQL по-прежнему слишком неэффективна. С ограничением скорости 500 000 (5000 баллов) при 27 000 ветвей функций я теоретически мог бы выполнить только более 20 коммитов на ветвь, прежде чем достичь ограничения скорости GraphQL. Что еще хуже, я ожидаю, что количество тегов в этом репозитории со временем будет увеличиваться.

О REST не может быть и речи (насколько мне известно во всяком случае), потому что ограничения скорости для REST еще более ограничительны.

Возможное альтернативное решение:

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

Я считаю, что git взаимодействует на прикладном уровне, используя HTTP для связи с репозиториями с пользовательскими заголовками HTTP. Этот метод позволил бы мне взаимодействовать не только с зеркалом GitHub Chromium, , но и с реальным репозиторием, размещенным Piper/Gitiles .

Однако я не уверен, есть ли способ вручную объединить базу с этим. Мне было интересно, можно ли просто загрузить SHA фиксации (и их родителей) с учетом целевой ветки и без других метаданных, но я понятия не имел, как это сделать и будет ли это эффективно. Кажется, я не могу найти никакой хорошей документации по базовым службам связи, предлагаемым репозиториями git.


4
86
1

Ответ:

Решено

На моей машине это занимает около минуты:

git clone --bare --filter=tree:0 https://github.com/chromium/chromium chromium-bare

Сейчас репозиторий занимает около 764M.

--filter=tree:0 избегает деревьев и капель (предложено Бенджамином В. ). --bare означает, что нам не нужно проверять рабочее дерево.

Это занимает 19 секунд:

git commit-graph write

Попробуем найти старый тег:

git tag --sort=version:refname

Я не знаю, какая версия у Chromium, поэтому я просто предполагаю, что эти теги могут быть отсортированы таким образом. Тег 3.0.195.25 с 2014 года.

Это занимает около 1,3 с:

git merge-base main 3.0.195.25

Допустим, это худший случай. С примерно 27 000 тегов он должен потребуется не более 1.3 * 27000 = 35100 секунд, чтобы вычислить все слияния базы (при условии, что время выполнения остального кода незначительно), или 9¾ часов.

Сравнение xargs с (GNU) parallel

Вычисление базы слияния между master и двумя сотнями тегов занимает около 2м18с:

git for-each-ref --format='%(refname:short)' 'refs/tags/**' --count=200 | xargs -n 1 git merge-base master

Я разогрел диск (надеюсь), выполнив команду один раз перед призывом.

Та же процедура с parallel вместо этого:

git for-each-ref --format='%(refname:short)' 'refs/tags/**' --count=200 | parallel -n 1 git merge-base master

Это занимает 28 секунд.

Мой процессор имеет четыре ядра и восемь потоков.

Это сокращает общее время работы с 9¾ часов до примерно 1 часа 10 минут.