Как применить патч git из одного репозитория в другой?
-
06-09-2019 - |
Вопрос
У меня есть два репозитория: один — основной репозиторий библиотеки, а другой — проект, использующий эту библиотеку.
Если я внесу исправление в подчиненный проект, мне нужен простой способ применить этот патч обратно в исходную версию.
Местоположение файла в каждом репозитории разное.
- Основное репо:
www.playdar.org/static/playdar.js
- Проект:
playlick.com/lib/playdar.js
Я попробовал использовать git format-patch -- lib/playdar.js
в проекте playlick, а затем git am
в основном репозитории Playdar, но разные местоположения файлов в файле патча вызывали ошибку.
Есть ли простой способ применить патч из данного коммита к данному файлу к другому произвольному файлу в другом месте?
В качестве бонуса: что, если файла, к которому вы хотите применить патч, нет в репозитории git?
Решение
Если редактирование файла патча вручную исключено или невозможно, это можно сделать стандартными опциями (доступны в git apply
, git format-patch
и ГНУ patch
).
-p<n>
удаляетn
ведущие каталоги из путей в патче.После обработки
-p
,--directory=<root>
добавляетroot
к каждому пути в патче перед применением.
Пример
Итак, для вашего примера взять патч, который изначально стоял на static/playdar.js
и применить его к lib/playdar.js
, вы бы запустили:
$ cat patch_file | git am \
-p1 \ # remove 1 leading directory ('static/')
--directory='lib/' # prepend 'lib/'
Другие советы
Патч, созданный git format-patch
это просто текстовый файл. Вы можете редактировать заголовки различий, чтобы изменить другой путь.
Например, это привело бы к чему-то вроде этого:
diff --git a/lib/playdar.js b/lib/playdar.js
index 1234567..89abcde
-- a/lib/playdar.js
++ b/lib/playdar.js
Все, что вам нужно сделать, это изменить lib/playdar.js
к static/playdar.js
а затем запустите патч через git am"
Патч должен быть доступен для чтения стандартной утилитой GNU patch для людей, у которых нет git
--- но не беги format-patch
с -M
, -C
и т. д.в этом случае можно создавать патчи переименования, поскольку их поддержка не является универсальной.
Если предположить, что оба проекта являются git-проектами, это звучит так: субмодули идеально подойдет вам.Это позволяет проекту git динамически связываться с другим проектом git, по сути, создавая репозиторий git прямо внутри другого репозитория git, причем оба имеют свою собственную жизнь.
Другими словами, добавьте «основное репо» в качестве подмодуля в «проект».Всякий раз, когда вы фиксируете/отправляете новый материал в «основной репозиторий», вы просто git pull
их обратно в «проект».
Завершить Ответ Хенрика, и пойти за бонусным баллом
что, если файла, к которому вы хотите применить патч, нет в репозитории git?
Если у вас есть доступ к каталогам файла-кандидата для патча, поступающего из репозитория git, вы можете сначала преобразовать это дерево каталогов/файлов в сам репозиторий git!('git init
':в конце концов, репозиторий git — это всего лишь .git в корневом каталоге).
Затем вы установите этот репозиторий в качестве подмодуля вашего основного проекта.
Вы можете добавить новый пульт и пользоваться им. Статья с подробностями.
$ cd <path-to-repoB>
$ git remote add repoA <git-URL-for-repoA>
$ git pull repoA
Вы можете просто временно удалить (переименовать) основной репозиторий.
cd to/main/project
mv .git .git_
cd to/sub/project
git apply patchname
cd -
mv .git_ .git
Используя --relative
возможность format-patch
может улучшить абстракцию (скрыть ненужные сведения о репозитории, из которого был создан патч).
[repository-with-changes]
git format-patch --relative=(path-to-library) (base-commit-for-patch) ## 'HEAD~1'
Я нашел --3way
опция, которая потребуется при применении патча (во избежание does not exist in index
ошибка) – ваш пробег может отличаться.С использованием --directory=(...)
вероятно, необходим только в том случае, если ваш целевой путь не является корнем репозитория.
[repository-to-update]
git am --3way --directory=(path-to-library) (patch-file)
format-patch
создаст один файл патча для каждой фиксации в текущей ветке, начиная с «base».Документация для
--relative
вариант кажется отсутствует в некоторых случаях, но, похоже, он все равно работает (начиная с версии 2.7.4).