Возможно ли исправление подмодуля в Git из родительского проекта?
У меня есть main
проекта, которая содержит подмодуль foo
. Для этого конкретного проекта я хотел бы внести небольшое изменение в foo
которое применимо только к этому конкретному main
проекту.
main/
+ .git
+ main.c
+ lib/
| + bar.c
+ foo/ # My 'foo' submodule
+ .git
+ config.h # The file I want to patch from 'main'
+ ...
Общим решением было бы перейти к моему подмодулю, сделать фиксацию Applied patch for main
в новом ветки, называемом main-project
, а затем нажать его. К сожалению, это очень плохой подход, потому что я вношу изменения в foo
что имеет значение только для main
. Кроме того, когда я обновляю foo
до последней версии, мне придется чересчур-выбрать патч, который вводит много шума в историю foo
.
Другое решение - иметь реальный файл исправления на main
который применяется к foo
непосредственно перед сборкой. К сожалению, поскольку это изменяет содержание подмодуля, и я буду иметь uncommitted измененный на foo
, так что это тоже нехорошее решение.
Идеальное решение - отслеживать мой патч с помощью Git, но на верхнем уровне (например, непосредственно на main
, а не на foo
). Теоретически, можно было бы добавить blob
в tree
Git, который указывает на местоположение подмодуля:
blob <sha> main.c
tree <sha> lib/
commit <sha> foo
blob <sha> foo/config.h
С этой идеей исправленный файл config.h
принадлежащий foo
будет отслеживаться по main
.
Как это можно сделать?
Ответы
Ответ 1
Я по-прежнему буду иметь второй вариант (иметь основной файл исправления на главной странице), но приспособить процесс сборки к:
- сделать копию
config.h
в подмодуле - применить патч
- строить
- восстановить
config.h
в исходное содержимое.
Таким образом, я сохраняю статус подмодуля неизменным.
OP добавляет в комментарии:
Но ваше решение не работает в среде IDE, Intellisense будет запутано -
Истинный: для этого я автоматически применяю патч для проверки и удаляю его при проверке с помощью драйвера фильтра smudge/clean content.
Таким образом, патч остается на месте во время всего сеанса, но исчезнет с любого состояния git/diff/checkin.
Однако это не идеальный вариант, и, похоже, не существует родного Git-способа справиться с этим.
Ответ 2
Самый простой способ перенести специфичные для проекта патчи в историю вендора - это клонировать репо вендора, перенести изменения в виде ветки проекта и объявить этот клон как вышестоящий .gitmodules
.
Это делает работу над вашими изменениями в исходном проекте вендора совершенно обычной, git clone --recurse-submodules yourproject
работает отлично, изменения вашего подмодуля могут быть перенесены обратно в ваш восходящий поток вашего проекта (субмодуль repo origin
remote), все работает.
Единственным дополнительным заполнением является обновление версии вашего субмодуля до последней версии кода поставщика, которую кто-то должен получить и объединить из (далее по течению) репо поставщика
... но это также совершенно нехарактерно: способ получить и объединить с репо вендора, сделать это. git remote add vendor u://r/l; git fetch vendor; git merge vendor/master
. Или, если вы предпочитаете rebase для слияния, сделайте это. Как только вы это сделаете, отправьте результаты в свой подмодуль origin
, версию своего проекта, как обычно.
Ответ 3
Идеальным решением было бы отслеживать мой патч с помощью Git, но на верхнем уровне. (например, непосредственно на основной, а не на foo). Теоретически, было бы возможно Добавьте BLOB-объект в дерево Git, указывающий на местоположение субмодуля:
Даже если это выполнимо, лично я нахожу это очень запутанным. Если это не было
реализовано изначально и включает в себя пользовательские команды, которые облегчают
понять и справиться, я бы держался от этого подальше.
Альтернатива 1: патч
Есть ли более элегантный/обтекаемый способ сделать патч в Git субмодуль, кроме принятого ответа?
Если вы хотите вообще не связываться с подмодулем, я бы предложил
копирование/проверка рабочего дерева в другом месте и использование только этого
во время сборки. Таким образом, подмодуль всегда "чистый" (и, возможно,
"неизменяемый", с точки зрения main
, и вам остается только беспокоиться об этом
в каталоге сборки.
Пример упрощенного процесса сборки:
cd main
mkdir -p build
cp -R foo/ build/
cp myconfig.patch build/
cd build
patch <myconfig.patch
make
Обратите внимание, что это сборка только foo
, и что процесс сборки main
не требуется
следует изменить, кроме того, чтобы указывать на build/
вместо foo/
.
Если вы не собираетесь модифицировать сам TG46/предпочли бы сохранить его "нетронутым",
Вы также можете превратить его в пустой репозиторий и использовать
GIT_WORK_TREE="$PWD/build" git checkout HEAD
вместо cp
, так что это
только проверено во время сборки. Это похоже на то, как
makepkg (8) делает это (по крайней мере, по моему опыту работы с AUR) по порядку
чтобы избежать изменения оригинальных источников (массив $source
против $srcdir
). Это
также разделяет извлечение источника из самой сборки (prepare()
vs build()
).
Смотрите также PKGBUILD (5) и Создание пакетов.
В вашем случае, разработка и IDE также участвуют, так что это может быть сложнее
если вы хотите проверить как исходные файлы, так и файлы сборки одновременно.
Доводы:
- Исходники отделены от файлов сборки
main
не влияет на foo
- Не зависит от git/делает это просто проблемой автоматизации сборки
- Требуется только файл патча
Против:
- Необходимо постоянно обновлять файл патча (вместо изменений в базисе)
- Нужно изменить процесс сборки
Я бы пошел с этим, если ваши патчи маленькие и/или очень специфичные для main
.
P.S.: Можно пойти еще дальше и отследить версию foo
непосредственно в процессе сборки вместо использования подмодулей, если вы хотите:
Переместите foo
на один каталог вверх, затем в процессе сборки:
cd build
GIT_DIR='../../foo/.git' git checkout "$myrev"
patch <myconfig.patch
make
Альтернатива 2: отдельная ветвь
Кроме того, когда я обновлю foo до последней версии, мне придется выбрать патч, который вносит много шума в историю foo.
Вы действительно не должны выбирать это, вы можете просто сохранить изменения в
вместо этого свою ветку и сливайте master
время от времени.
Лично я бы этого избежал, если бы ваши изменения не были намного более значительными
чем шум, вызванный его синхронизацией (то есть: слияния и конфликты).
Я считаю коммиты слияния очень непрозрачными, особенно когда речь идет о конфликтах,
поскольку несвязанные/случайные изменения труднее обнаружить.
Перебазирование ваших коммитов на master
также возможно.
Доводы:
- Нет необходимости в отдельном хранилище
- Держит рабочее дерево в одном месте (не нужно связываться с вашей IDE)
Против:
- Репозиторий Pollutes
foo
с несвязанными коммитами (при слиянии)
- Репозиторий Pollutes
foo
с несвязанными объектами фиксации (при перебазировании)
- Мрачная история эволюции ваших изменений в
config.h
(при перебазировании)
Альтернатива 3: мягкая вилка
Кроме того, когда я обновлю foo до последней версии, мне придется выбрать патч, который вносит много шума в историю foo.
К сожалению, это очень плохой подход, потому что я делаю изменения в Foo это имеет значение только для
Если вы хотите изменить foo
для соответствия main
, но не связываться с foo
вверх по течению,
почему бы не создать софт-форк из foo
? Если вы не слишком заботитесь о
foo-fork
история, вы можете просто зафиксировать свои изменения на main-project
ответвление и синхронизируйте его с foo
master
через rebase:
Создание форка:
cd foo
git remote add foo-fork 'https://foo-fork.com'
git branch main-project master
git push -u foo-fork main-project
Синхронизация:
git checkout main-project
git pull --rebase foo/master
# (resolve the conflicts, if any)
git push foo-fork
Pros:
- Легко синхронизировать с восходящим потоком (например, с помощью
pull --rebase
)
- Держит рабочее дерево в одном месте (не нужно связываться с вашей IDE)
Против:
- Мрачная история эволюции ваших изменений в
config.h
(из-за
перебазирования)
Дополнительным преимуществом использования патча вместо перебазирования является то, что вы сохраняете
История патча. Но если вы хотите, чтобы все было очень просто,
Я полагаю, что так оно и есть.
Альтернатива 4: хард-форк
Если вы обнаружите, что foo
меняется слишком сильно/слишком часто и/или вам нужно слишком много исправлять
многие вещи, ваш лучший выбор, вероятно, это создание полного форка и сбор вишни
их изменения вместо.
Ответ 4
Вы можете обнаружить, что имеет смысл использовать поддерево git, а не подмодуль. Поддерево копирует другой проект в местоположение в локальном репозитории, а не управляет им как отдельным репо внутри текущего репо.
Вы можете применить свое изменение локально как часть основной ветки master, а затем периодически обновлять, объединяя изменения из основной ветки разработки.