Интерактивное слияние файлов отслеживаемых с помощью git и неотслеживаемых локальных файлов
Я использую несколько программных пакетов (например, gitlab), которые вы устанавливаете путем клонирования из их репозитория git. Они обычно поставляются с некоторыми config.example (под управлением версий), которые вы копируете в свой собственный файл config (не под управлением версий или даже игнорируете в .gitignore) и адаптируете к вашим потребностям.
При обновлении вышестоящего пакета и, например, изменении параметров конфигурационного файла, которые, очевидно, будут отражены только в config.example.
Есть ли цепочка команд git, которую я пропускаю, которая может помогите мне сравнить изменения config.example с новыми в upstream/HEAD и, возможно, даже объединить их в интерактивном режиме в мой локальный файл config?
Было бы здорово, если бы я мог получить что-то вроде интерактивного режима патча в
git add/commit --interactive. 7 ответов:
git checkout --patchвыбирает разные куски, простейшим здесь может быть поместить ваш контент на восходящий путь, сделать это и очистить после:cp config config.example git checkout -p upstream config.example mv config.example config git checkout @ config.exampleЭто даст вам выбор двух разных кусков из
git add --patch.
- вы можете использовать
vimкак инструмент слияния сvimdiff.Emacsможет сделать это также сediff-mode.
Вероятно, вы могли бы подойти к этому по-разному. Я мог бы подумать о двух:
git-merge-fileи старый добрыйpatch. Методgit-merge-fileпредлагает некоторую интерактивность, в то время как методpatchне предлагает ничего.Решение с
Предположим, у вас есть исходный файлgit-merge-fileconfig.example, который вы используете для создания локального неверсированного файла,config.local. Теперь, когда апстрим обновляетconfig.example, вы можете выполнить что-то вроде следующих шагов, чтобы объединить любые новые изменения.$ git fetch $ git show master:config.example > config.example.base $ git show origin/master:config.example > config.example.latest $ git merge-file config.local config.example.base config.example.latestЭто обновит
config.localс обычными маркерами конфликтов, которые вам затем придется разрешить с помощью вашего любимого инструмента слияния (ediffв Emacs хорошо, я уверен, что есть аналогичные режимы для Vim). Например, следующие три файла,Конфиг.образец.основание :
Original: some configКонфиг.образец.последние :
Original: some config Upstream: new upstream configКонфиг.local :
Original: some config My changes: some other configБудут объединены следующим образом:
Original: some config <<<<<<< config.local My changes: some other config ======= Upstream: new upstream config >>>>>>> config.example.latestВы, вероятно, могли бы написать это без особых усилий. Кстати,
git-merge-fileможет работайте с любыми 3 файлами, они не должны управляться версией подgit. Это означает, что можно использовать его для объединения любых трех файлов!Решение с
patch:Предполагая те же имена файлов,
config.localиconfig.example, должно работать следующее.$ git fetch $ git diff master..origin/master -- config.example | sed -e 's%\(^[-+]\{3\}\) .\+%\1 config.local%g' > /tmp/mypatch $ patch < /tmp/mypatch
Если вы хотите полное слияние git, вы можете заставить git сделать это с произвольным содержимым, настроив запись индекса, как это делает
git read-tree, а затем вызвав обычный драйвер слияния git только для этой записи. Git относится к различным версиям контента как к "этапам"; это 1: оригинал, 2: Ваш, 3: их. Слияние сравнивает изменения от 1 до 2 и от 1 до 3 и делает свое дело. Чтобы настроить его, используйтеgit update-index:orig_example= # fill in the commit with the config.example you based yours on new_upstream= # fill in the name of the upstream branch ( while read; do printf "%s %s %s\t%s\n" $REPLY; done \ | git update-index --index-info ) <<EOD 100644 $(git rev-parse $orig_example:config.example) 1 config 100644 $(git hash-object -w config) 2 config 100644 $(git rev-parse $new_upstream:config.example) 3 config EODИ вы организовали слияние пользовательского контента для этого пути. Сейчас делать это:
git merge-index git-merge-one-file -- configИ он либо автоматически исчезнет, либо оставит обычный помет конфликта, исправьте его, как вам нравится, и
git rm --cached --ignore-unmatch(или сохраните) запись индекса, если хотите.Путь, который вы вводите в индекс ("config" во всех трех записях здесь), кстати, не должен уже существовать или иметь какое-либо отношение к чему-либо. Вы можете назвать его "wip" или "deleteme" или как угодно. Слияние происходит с идентификатором содержимого в записи индекса.
Я думаю, что это, вероятно, сделает то, что вы хотите здесь. Если вы действительно хотите выбирать из вышестоящих изменений, вы можете поместить свой собственный контент в config.пример и сделайтеgit checkout -p upstream -- config.example, что делает обратноеgit add -p, а затем верните все на свои места.
Для diff просто
config.exampleв вашем локальном РЕПО с соответствующим файлом вupstream/HEADВы можете запустить:git diff upstream/HEAD config.exampleК сожалению, я не знаю способа заставить git напрямую применить изменения к файлу, который git не отслеживает.
Есть инструмент под названием sdiff, который может делать то, что вы хотите.
Вызовите его (в вашем случае) с помощью
sdiff -o config config.example config
Должно работать следующее:
git diff <some-args> | perl -pe 's/path\/to\/changes\/file/path\/other/g' > t patch -p1 < t rm t
Comments