Как применить патч Git к файлу с другим именем и путем?

101

У меня два репозитория. В одном я вношу изменения в файл ./hello.test. Я фиксирую изменения и создаю патч из этого коммита с помощью git format-patch -1 HEAD. Теперь у меня есть второй репозиторий , который содержит файл , который имеет то же содержание , как hello.test но помещается в другом каталоге под другим именем: ./blue/red/hi.test. Как мне применить к hi.testфайлу вышеупомянутый патч ? Я пробовал, git am --directory='blue/red' < patch_fileно он, конечно, жалуется, что файлы имеют разные имена (я думал, что Git не заботится?). Я знаю, что, вероятно, мог бы отредактировать разницу для применения к этому конкретному файлу, но я ищу командное решение.

mart1n
источник
Связано с: stackoverflow.com/q/3367254/1959808
Иоаннис Филиппидис

Ответы:

97

Вы можете создать патч с помощью, git diffа затем применить его с помощью patchутилиты, которая позволяет указать файл, к которому вы хотите применить разницу.

Например:

cd first-repo
git diff HEAD^ -- hello.test > ~/patch_file

cd ../second-repo
patch -p1 blue/red/hi.test ~/patch_file
Джорджброк
источник
7
Ах, мило, не подумал об этом. Однако есть ли способ сделать это с помощью команд Git, чтобы данные фиксации (дата и время, автор фиксации, сообщение фиксации) оставались неизменными?
mart1n
2
Возможно, вы можете что-то сделать с amили apply, но я не могу этого найти. Если вы обнаружите, что часто дублируете изменения, может быть лучшее решение с использованием подмодулей или того, что ваш язык по выбору предоставляет для совместного использования кода (например, в Ruby вы можете извлечь повторяющийся код как драгоценный камень).
Джорджброк
1
На самом деле это связано с документацией (исходные файлы - это XML). Подмодули на самом деле не вариант, поскольку мне нужно было бы убедительно аргументировать их в нашей существующей инфраструктуре.
mart1n
52

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

В первом репозитории (это также может экспортировать диапазон фиксации, используйте, -1если вы хотите выбрать только одну фиксацию):

git format-patch --relative <committish> --stdout > ~/patch

Во втором репозитории:

git am --directory blue/red/ ~/patch

Вместо использования --relativein git format-patchдругим решением является использование -p<n>опции in git amдля удаления nкаталогов из пути патчей, как упоминалось в ответе на аналогичный вопрос .

Также можно запустить git format-patch --relative <committish>без --stdout, и он будет генерировать набор .patchфайлов. Эти файлы затем могут быть загружены напрямую с git amпомощью git am --directory blue/red/ path/to/*.patch.

Magiraud
источник
9
Это все еще зависит от того, что имена файлов одинаковы, верно?
mart1n
3
Следует отметить, что эта --directoryопция требует указать полный путь к каталогу относительно корня репо; что-то вроде --directory=./while chdir'd в подкаталог в репо не сработает.
Рид
1
Использование --3wayпомогает с does not exist in index:git am --3way --directory (relative-path) (patch)
Brent
Используйте -kключ в обеих командах, чтобы не удалять первую строку сообщения фиксации.
ruvim
Использование --3wayне только помогает с ошибками «не существует в индексе» (как указано в @nobar), но также позволяет вам аккуратно обрабатывать конфликты слияния. Вместо того, чтобы оставлять конфликтующие файлы нетронутыми, добавляется блок конфликта, который затем может быть разрешен.
Дэниел Вольф
11

Отвечая на свой вопрос с помощью скрипта, который делает именно это: https://github.com/mprpic/apply-patch-to-file

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

mart1n
источник
5

Основываясь на ответе @georgebrock, вот решение, которое я использовал:

Сначала создайте файлы патчей как обычно (например git format-patch commitA..commitB).

Затем убедитесь, что ваш целевой репозиторий чист (в нем не должно быть измененных или неотслеживаемых файлов), и примените такие патчи:

cd second-repo
git am ~/00*.patch

Для каждого файла патча вы получите сообщение об ошибке типа «ошибка: XYZ не существует в индексе». Теперь вы можете применить этот файл патча вручную:

patch --directory blue/red < ~/0001-*.patch
git add -a
git am --continue

Вы должны выполнить эти три шага для каждого файла исправления.

Это сохранит исходное сообщение фиксации и т. Д. Без необходимости использования какой-либо специальной git format-patchкоманды или редактирования файлов патча.

Оливер
источник
1
Хороший ответ, я думаю, что это лучшая основа для любых «нестандартных» манипуляций с патчами. Я делаю это за 3 шага. (1) Зафиксировать текст - git format-patch -1 commitA --stdout > thing.diff; (2) Отредактируйте файл патча, пока он не сделает то, что мне нужно; (3) Текст для фиксации, git am --3way thing.diff который имеет то преимущество, что вы можете принять части исправления, которые применяются чисто, и использовать gitстандартный процесс разрешения конфликтов для частей, которые не применяются .
We All Monica
2

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

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

Допустим, вы изменили файл A, обозначим его A~1как предыдущую версию, и вы хотите применить разницу между A~1к Aк файлу B.

Откройте инструмент трехстороннего слияния, например Beyond Compare, путь левой панели равен A, средняя панель является общим предком, так что путь есть A~1, путь правой панели B. Затем нижняя панель показывает результат применения различий между A~1к Aв файл B.

Следующий рисунок иллюстрирует эту идею.

введите описание изображения здесь

Gqqnbig
источник
0

К вашему сведению: недавно у меня были проблемы с попыткой загрузить патч из Github и применить его к локальному файлу (что было «переопределением» в новом месте).

git amне применил бы исправление, потому что файл был «не в индексе» или «грязный». Но я обнаружил, что простая patchкоманда может применить патч. Мне было предложено указать имя файла, который нужно исправить.

В любом случае, работа сделана ...

Майк Робинсон
источник