Преобразовать подмодуль git в обычный каталог и сохранить историю в главном дереве? - PullRequest
0 голосов
/ 07 сентября 2018

У меня есть проект, который состоит из множества подмодулей. Однако некоторые из этих подмодулей в ретроспективе не должны быть подмодулями, поскольку они не предназначены или никогда не будут использоваться в другом проекте, и я иногда передаю код между ними. Этот проект удваивается как эксперимент в подмодулях, поэтому я немного схожу с ума.

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

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

Ответы [ 3 ]

0 голосов
/ 08 сентября 2018

Это сложная проблема в целом. Существуют конкретные случаи или вырожденные способы применения содержимого подмодуля, которые облегчают его. Один компромисс - который может быть или не быть достаточно хорошим - состоит в том, чтобы просто объединить две истории коммитов в один репозиторий, а затем сделать несколько слегка пугающих преобразований, используя либо git filter-branch, либо просто автоматический git replace (хотя с использованием или злоупотреблением). , git replace как это может привести к проблемам с производительностью).

Вот основная ситуация, то есть то, что вам нужно знать, как ментальные инструменты, прежде чем задумываться об обобщении проблемы. Каждый репозиторий содержит граф коммитов: DAG коммитов с различными точками входа в граф, найденные и, следовательно, сохраненные по именам ветвей. В каждом коммите, который использует субмодуль, коммиты суперпроекта имеют ссылку на один из коммитов субмодуля. Эти ссылки находятся в объекте «дерево», как записи типа gitlink . Git на самом деле не проверяет их, когда дело доходит до сохранения коммитов, поскольку предполагается, что они идентифицируют коммиты в некотором другом хранилище (подмодуле).

Вы можете легко использовать git fetch для извлечения всего графика подмодуля в хранилище суперпроекта, изменяя имена ветвей подмодуля на разные имена в суперпроекте. (По умолчанию git fetch используется для создания имен для удаленного слежения, но с некоторой хитростью вы можете легко использовать альтернативное пространство имен. Для решений, которые я предлагаю, имена для удаленного слежения подойдут в любом случае.) Результат, однако, просто у вас есть два отключенных DAG. У коммитов суперпроекта все еще есть деревья с записями gitlink, которые ссылаются на коммиты в другой группе обеспечения доступности баз данных. Эти записи gitlink не сохранят коммиты достижимые , поэтому вы должны сохранить оба набора имен. За исключением того, что все коммиты содержатся в одной базе данных репозитория, на самом деле это вообще не улучшение (и может быть хуже, так как с ним теперь трудно работать).

Вот общая проблема : что Git хранит (есть?) Эти коммиты. Нет отдельного пункта, который мы можем назвать «историей»; история в репозитории Git - это (есть?) коммиты в репозитории. Мы можем визуально увидеть проблему, если нарисовать коммиты. Упростим это до пяти коммитов, от A до E в суперпроекте. Буквы в верхнем регистре обозначают фактические хэш-идентификаторы (которые бесполезны для людей):

A--B--C   <-- master
    \
     D--E   <-- dev

Теперь давайте поместим шесть коммитов в подпроект, используя строчные буквы, так как это подпроект:

a--b--c--d   <-- master
       \
        e--f   <-- issue213

Некоторые коммиты суперпроекта - возможно, все они, но для простоты, скажем, просто C и E - имеют внутри себя ссылки на некоторые коммиты подпроекта, поэтому, если мы сгруппируем все коммиты подмодуля в суперпроект , используя имена sub/*, чтобы запомнить подсказки веток, мы получаем это:

A--B--C   <-- master
    \ :
     D÷-E   <-- dev
      : :
     :  :
    :    :
   :     :
a--b--c--d   <-- sub/master
       \
        e--f   <-- sub/issue213

Предположим, что теперь мы каким-то образом заменим коммиты C (с его gitlink на b) и E (с его gitlink на d) на коммиты, деревья которых имеют фактические прямые ссылки на объекты дерева для совершает b и e. Давайте назовем эти коммиты C' и E'. Технически это возможно в Git - мы просто делаем новые коммиты C' и E' с нужными деревьями, которые используют деревья в b и d соответственно, затем меняем имена master и dev для ссылки на коммиты C' и E'. Если мы отбросим sub/* имен, у нас будет это:

A--B--C'  <-- master
    \
     D--E'  <-- dev

и если мы теперь git checkout master, мы получим хорошее рабочее дерево, полное того, что было в оригинальном C плюс того, что было из подмодуля, полученного из его коммита b, что используется оригинальный C, как видно из нашей диаграммы.

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

The деревья в этом новом измененном хранилище содержат все источники снимка, которые вы получите, проверив C -and-submodule или E -and-submodule. Но фиксирует , которые были в подмодуле, т. Е. История d, ведущая к c, возвращающаяся к b, ведущая к a, плюс вся ветвь issue213, состоящая из f, ведущая к e, ведущая к c ... ну, эти коммиты ушли! Нет ничего, чтобы представлять их больше.

Более того, нет места, куда вы могли бы вставить их . Где в графе, содержащем коммиты с A по E (все прописные), коммиты с a по f (все строчные) подходят? Единственный ответ - "никуда": есть нет места, куда они не могут пойти.

Теперь, в определенных случаях, мы можем придумать ответ. Мы можем вставить new коммитов между существующими коммитами, чтобы новые коммиты сохраняли файлы суперпроекта на месте при обновлении файлов субмодуля. Это практично, когда существует топологический вид графа подмодуля, который «вписывается» в топологический вид графа суперпроекта. (Если имеется несколько подмодулей, нам нужна полная топо-сортировка объединения всех графов.) Нет гарантии, что такая ситуация существует, и легко нарисовать случай, когда ее нет:

A--B--C   <-- master
 :   :
  : :
   :
  : :
 :   :
a--b--c   <-- sub/master

Здесь коммит суперпроекта A относится к последнему коммиту в подпроекте, тогда как коммит суперпроекта C ссылается на первый коммит в подпроекте. Эти топологии графа не являются композируемыми. 1 Но это может быть тот случай, когда ваши топологии имеют место, и в этом случае вы можете вставлять узлы коммитов по мере необходимости, если вы хотите создать новый граф, который действует как соответствующий надстройка. Я не знаю ни одной программы для этого.


1 Я не уверен, является ли "составным" хорошим термином для этого, но у меня нет времени на поиск литературы. Я имею в виду, что объединение групп DAG может привести к циклам, и я называю такие репозитории «несложимыми». См. Также Эффективный алгоритм объединения двух групп DAG, например, .


Выполнение более сложной работы с компонуемыми подмодулями

Вам нужно будет написать код. ? Это нетривиально и требует немного теории графов. Это не особенно сложно, но я определенно не собираюсь делать это здесь.

Выполнение более простой работы, если усеченная история приемлема

Более простое задание, которое в приведенном выше примере состоит в замене коммита C на C' и E на E', является автоматизируемым: перебирать все коммиты, находить их подмодули gitlinks и использовать git replace заменить объект дерева, который имеет подмодуль, на объект дерева, который использует дерево подмодуля. Это на самом деле заменяет объект дерева, а не объект фиксации, так что история действительно остается такой же, какой была раньше, но теперь у вас будет очень большая коллекция заменяющих объектов. Более того, клонирование хранилища не приведет к клонированию замещающих объектов, поэтому теперь пришло время переписать все коммиты, используя git filter-branch.

У меня нет удобного рецепта для использования git replace, как этот, но вы, вероятно, захотите автоматизировать git replace --edit, установив переменную GIT_EDITOR в сценарий, который найдет и заменит запись gitlink. (Написание такого сценария будет немного утомительным, но технически не сложным.)

Поскольку git filter-branch учитывает замены, 2 и никаких других изменений не требуется, вы можете просто запустить git filter-branch --tag-name-filter cat -- --branches --tags, чтобы выполнить все замены фиксации. (Примечание: сделайте это на клоне, который вы создали специально для экспериментов с replace и filter-branch, чтобы вы могли начать все сначала, если испортили его.) Затем вы можете удалить все ссылки для замены (git for-each-ref --format='delete %(refname)' | git update-ref --stdin) так как они больше не нужны и теперь делают Git медленным.


2 Хорошо, если только не запустить как git --no-replace-objects filter-branch.

0 голосов
/ 20 декабря 2018

Довольно просто использовать git subtree, если вы просто хотите сохранить историю отдельной ветки для каждого подмодуля:

git fetch <path/to/submodule> HEAD
git rm <path/to/submodule>
git commit -m "Prepare to integrate Git submodules' history into repository"
git subtree add --prefix=<path/to/submodule> FETCH_HEAD 

Будет интегрирована история текущей проверенной ревизии субмодуля. Прежде чем быть уверенным, что вы находитесь в чистом состоянии, запустите, например, git submodule update и двойная проверка с git status.

Вы получите два коммита: первый удаляет субмодуль, второй интегрирует предыдущую историю (теперь хранится в FETCH_HEAD) в хранилище. Нет простого способа (по крайней мере, я не знаю об этом) сделать это с помощью «атомарного» коммита. Вам нужно будет поиграть с командой Git, установленной для этого.

Если вам нужно объединить историю нескольких подмодулей, я рекомендую поместить все операции удаления в первый коммит, а все операции интеграции - во второй. В этом случае вам нужно запомнить выбранные ГОЛОВА другими способами.


Примечание: Хотя git subtree находится в пределах ./contrib в Git вверх по течению, он, кажется, доступен (по крайней мере) в Debian начиная с версии 1.9.1 (март 2014 г.).

0 голосов
/ 07 сентября 2018

У меня нет большого опыта работы с подмодулями, но вот что я бы сделал:

  • удалить подмодуль из проекта **. Добавьте репо "оригинальный субмодуль" в качестве удаленного для вашего проекта и получите.
  • Объедините любую ветвь, которую вы хотите привнести в ваш проект. Если бы я хотел поместить файлы из этого другого проекта в отдельную директорию моего основного проекта, то я бы, вероятно, извлек бы ветку подмодуля (больше не подмодуль, теперь это действительно удаленная ветвь), переименуйте файлы там в каталог, который я имею в виду (чтобы он не конфликтовал ни с чем из основного проекта), тогда я бы слил эту новую редакцию в свой основной проект.

Возможно, не самый лучший подход, но я бы поступил так, если бы хотел перенести другой проект в свой проект, оставив при этом разные вещи.

** Это вообще возможно? Мне определенно нужно больше попрактиковаться с подмодулями и тому подобным.

...