Интерактивно объединять файлы, отслеживаемые с помощью git и не проверенных локальных файлов
Я использую пару программных пакетов (например, gitlab), которые вы устанавливаете, клонируя из своего репозитория git. Обычно они поставляются с некоторыми config.example
(под контролем версий), которые вы копируете в свой собственный config
файл (не под управлением версиями или даже игнорируемым в .gitignore
) и адаптируетесь к вашим потребностям.
Когда обновляемый пакет обновляется и, например, изменяет параметры файла конфигурации, которые, очевидно, будут отображаться только в config.example
.
Есть ли цепочка из git команд, которые мне не хватает, которые могут помочь мне сравнить изменения config.example
с новым в upstream/HEAD
и, возможно, даже объединить их в интерактивном режиме в мой локальный файл config
?
Было бы здорово, если бы я мог получить что-то вроде режима интерактивного патча в git add/commit --interactive
.
Ответы
Ответ 1
git checkout --patch
выбирает diff hunks, простейшим здесь может быть размещение вашего контента по восходящему пути, выполнение этого и очистка после:
cp config config.example
git checkout -p upstream config.example
mv config.example config
git checkout @ config.example
вы получите выбор из двух столбцов из git add --patch
.
Ответ 2
- Вы можете использовать
vim
как инструмент слияния с vimdiff
.
-
Emacs
может сделать это также с помощью ediff-mode
.
Ответ 3
Вероятно, вы могли бы подойти к этому по-разному. Я мог бы подумать о двух: git-merge-file
и добром patch
. Метод git-merge-file
предлагает некоторую интерактивность, тогда как метод patch
не имеет значения.
Решение с git-merge-file
Допустим, у вас есть исходный файл config.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 существуют аналогичные режимы). Например, следующие три файла,
config.example.base
Original:
some config
config.example.latest
Original:
some config
Upstream:
new upstream config
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
Вы могли бы script сделать это без особых усилий. Btw, 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
Ответ 4
Если вы хотите полное слияние 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" или "deleteeme" или что угодно. Слияние состоит из содержимого id'd в записи индекса.
Я думаю, что вы, вероятно, будете делать то, что хотите. Если вы действительно хотите выбрать и выбрать из вышеперечисленных изменений, вы можете поместить свое собственное содержимое в config.example и сделать git checkout -p upstream -- config.example
, что делает обратное к git add -p
, а затем вернет вещи так, как они были.
Ответ 5
Чтобы развернуть только config.example
в вашем локальном репо с соответствующим файлом в upstream/HEAD
, вы можете запустить:
git diff upstream/HEAD config.example
К сожалению, я не знаю, как сделать git напрямую применить изменения к файлу, который не отслеживает git.
Ответ 6
Существует инструмент, называемый sdiff, который может делать то, что вы хотите.
Вызовите его (в вашем случае) с помощью sdiff -o config config.example config
Ответ 7
Следующее должно работать:
git diff <some-args> | perl -pe 's/path\/to\/changes\/file/path\/other/g' > t
patch -p1 < t
rm t