Как мне сообщить git, чтобы он всегда выбирал мою локальную версию для конфликтующих слияний в определенном файле?

100

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

Есть ли способ настроить мое локальное репо, чтобы не жаловаться на конфликтующее слияние каждый раз, когда я git pull? Я хотел бы всегда выбирать мою локальную версию при объединении этого файла.

Saffsd
источник
1
Просто добавил простое решение через .gitattributes и очень простой «драйвер слияния»
VonC
12
TD; LR:echo 'path/to/file merge=ours' >> .gitattributes && git config --global merge.ours.driver true
Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
@CiroSantilli: Прекрасно работает в Linux. Этот драйвер достаточно прост, чтобы его можно было
встроить
Вы хотите внести изменения в файл? Или это, например, файл конфигурации, в котором значение по умолчанию хранится в git.
Ян Рингроуз
Комментарий @CiroSantilli 新疆 改造 中心 六四 事件 法轮功 правильный, но он будет вызывать такое поведение для каждого репо в вашей системе с --globalтегом. Если вам нужно такое поведение только для одного репо, не --globalecho 'path/to/file merge=ours' >> .gitattributes && git config merge.ours.driver true
указывайте

Ответы:

140

В отношении конкретного экземпляра файла конфигурации я согласен с ответом Рона :
конфигурация должна быть «частной» для вашей рабочей области (следовательно, «игнорироваться», как в «объявленной в .gitignoreфайле»).
У вас может быть шаблон файла конфигурации с токенизированными значениями в нем и сценарий, преобразующий этот config.templateфайл в частный (и игнорируемый) файл конфигурации.


Однако это конкретное замечание не отвечает на более широкий вопрос, то есть на ваш вопрос (!):

Как мне сообщить git, чтобы он всегда выбирал мою локальную версию для конфликтующих слияний в определенном файле? (для любого файла или группы файлов)

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

(как отмечает Брайан Ванденберг в комментариях , ' ours' и ' theirs' здесь используются для слияния .
Они меняются местами для перебазирования : см. " Why is the meaning of “ours” and “theirs” reversed with git-svn", который использует перебазирование, " git rebaseотслеживая" локальные "и" удаленные " " )

Для «файла» (файла в целом, не говоря уже о файле «конфигурации», поскольку это плохой пример), вы можете добиться этого с помощью специального сценария, вызываемого посредством слияния.
Git вызовет этот скрипт, потому что вы определите значение gitattributes , которое определяет настраиваемый драйвер слияния .

«Пользовательский драйвер слияния» в данном случае представляет собой очень простой скрипт, который в основном сохраняет неизменной текущую версию, что позволяет вам всегда выбирать локальную версию.

. IE, Как отмечено на Ciro Сантилли :

echo 'path/to/file merge=ours' >> .gitattributes
git config --global merge.ours.driver true

Давайте проверим это на простом сценарии с msysgit 1.6.3 в Windows, в простом сеансе DOS:

cd f:\prog\git\test
mkdir copyMerge\dirWithConflicts
mkdir copyMerge\dirWithCopyMerge
cd copyMerge
git init
Initialized empty Git repository in F:/prog/git/test/copyMerge/.git/

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

echo a > dirWithConflicts\a.txt
echo b > dirWithCopyMerge\b.txt
git add -A
git commit -m "first commit with 2 directories and 2 files"
[master (root-commit) 0adaf8e] first commit with 2 directories and 2 files

Мы введем «конфликт» в содержимое обоих этих файлов в двух разных ветках git:

git checkout -b myBranch
Switched to a new branch 'myBranch'
echo myLineForA >> dirWithConflicts\a.txt
echo myLineForB >> dirWithCopyMerge\b.txt
git add -A
git commit -m "add modification in myBranch"
[myBranch 97eac61] add modification in myBranch

git checkout master
Switched to branch 'master'
git checkout -b hisBranch
Switched to a new branch 'hisBranch'
echo hisLineForA >> dirWithConflicts\a.txt
echo hisLineForB >> dirWithCopyMerge\b.txt
git add -A
git commit -m "add modification in hisBranch"
[hisBranch 658c31c] add modification in hisBranch

Теперь давайте попробуем объединить hisBranch с myBranch с:

  • ручное разрешение конфликтующих слияний
  • за исключением того, на dirWithCopyMerge\b.txtкотором я всегда хочу , чтобы сохранить свою версию b.txt.

Поскольку слияние происходит в ' MyBranch', мы вернемся к нему и добавим gitattributesдирективы ' ', которые будут настраивать поведение слияния.

git checkout myBranch
Switched to branch 'myBranch'
echo b.txt merge=keepMine > dirWithCopyMerge\.gitattributes
git config merge.keepMine.name "always keep mine during merge"
git config merge.keepMine.driver "keepMine.sh %O %A %B"
git add -A
git commit -m "prepare myBranch with .gitattributes merge strategy"
[myBranch ec202aa] prepare myBranch with .gitattributes merge strategy

У нас есть .gitattributesфайл, определенный в dirWithCopyMergeкаталоге (определенный только в ветке, где будет происходить слияние :) myBranch, и у нас есть .git\configфайл, который теперь содержит драйвер слияния.

[merge "keepMine"]
        name = always keep mine during merge
        driver = keepMine.sh %O %A %B

Если вы еще не определили keepMine.sh и все равно запускаете слияние, вот что вы получите.

git merge hisBranch
sh: keepMine.sh: command not found
fatal: Failed to execute internal merge
git st
# On branch myBranch
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   dirWithConflicts/a.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

type dirWithConflicts\a.txt
a
<<<<<<< HEAD:dirWithConflicts/a.txt
myLineForA
=======
hisLineForA
>>>>>>> hisBranch:dirWithConflicts/a.txt

Это нормально:

  • a.txt готов к слиянию и в нем есть конфликт
  • b.txtостается нетронутым, поскольку предполагается, что драйвер слияния позаботится об этом (из-за директивы в .gitattributesфайле в его каталоге).

Определите keepMine.shгде угодно в вашем %PATH%(или $PATHдля нашего друга Unix. Я, конечно, делаю и то, и другое: у меня есть сеанс Ubuntu в сеансе VirtualBox)

Как отметил по lrkwz , и описано в разделе « Объединить Strategies разделе» из пользовательской настройки Git - Атрибуты Git , вы можете заменить скрипт с командой оболочки true.

git config merge.keepMine.driver true

Но в общем случае вы можете определить файл сценария:

keepMine.sh

# I want to keep MY version when there is a conflict
# Nothing to do: %A (the second parameter) already contains my version
# Just indicate the merge has been successfully "resolved" with the exit status
exit 0

(это был один простой драйвер слияния;) (даже проще в этом случае, использование true)
(Если вы хотите сохранить другую версию, просто добавьте перед exit 0строкой:
cp -f $3 $2.
Вот и вы сливаться водитель будет Визитки держать версию приходя от другого. ветвь, отменяя любое локальное изменение)

Теперь давайте повторим слияние с самого начала:

git reset --hard
HEAD is now at ec202aa prepare myBranch with .gitattributes merge strategy

git merge hisBranch
Auto-merging dirWithConflicts/a.txt
CONFLICT (content): Merge conflict in dirWithConflicts/a.txt
Auto-merging dirWithCopyMerge/b.txt
Automatic merge failed; fix conflicts and then commit the result.

Слияние не удается ... только для a.txt .
Отредактируйте a.txt и оставьте строку из hisBranch, затем:

git add -A
git commit -m "resolve a.txt by accepting hisBranch version"
[myBranch 77bc81f] resolve a.txt by accepting hisBranch version

Давайте проверим, что b.txt был сохранен во время этого слияния

type dirWithCopyMerge\b.txt
b
myLineForB

Последний коммит действительно представляет собой полное слияние:

git show -v 77bc81f5e
commit 77bc81f5ed585f90fc1ca5e2e1ddef24a6913a1d
Merge: ec202aa 658c31c
git merge hisBranch
Already up-to-date.

(Строка, начинающаяся с Merge, действительно доказывает это)


Считайте, что вы можете определить, объединить и / или перезаписать драйвер слияния, поскольку Git будет:

  • исследуйте <dir>/.gitattributes(который находится в том же каталоге, что и рассматриваемый путь): будет преобладать над другим .gitattributesв каталогах
  • Затем он проверяет .gitattributes(который находится в родительском каталоге), устанавливает только директивы, если они еще не установлены
  • Наконец он исследует $GIT_DIR/info/attributes. Этот файл используется для переопределения настроек в дереве. Это перезапишет <dir>/.gitattributesдирективы.

Под «объединением» я подразумеваю «объединение» нескольких драйверов слияния.
Ник Грин пытается в комментариях фактически объединить драйверы слияния: см. « Слияние pom через драйвер git python ».
Однако, как упоминалось в его другом вопросе , он работает только в случае конфликтов (одновременное изменение в обеих ветвях).

VonC
источник
1
Спасибо за подробный ответ! Я понимаю, что нет смысла использовать файлы конфигурации для управления версиями, но мне был нужен простой мотивирующий пример. На самом деле меня интересовал более широкий вопрос. Я никогда раньше не слышал о драйверах слияния git, так что спасибо за то, что просветили меня.
saffsd
6
cp -f $3 $2, Вероятно , следует цитировать, т.е. cp -f "$3" "$2".
Арк,
1
@VonC спасибо за подробный ответ! Проблема в том, что это зависит от людей, устанавливающих драйвер в своем файле .git / config. Я хотел бы добавить информацию о драйвере в сам проект, чтобы это было автоматическим и меньшим объемом настройки. Есть указатели?
Хуан Дельгадо
2
@ulmangt: вы также можете сохранить этот скрипт в репозитории git, ... пока вы найдете способ добавить его родительский каталог в PATH(Unix или Windows PATH). Поскольку этот сценарий будет интерпретироваться через оболочку Unix bash или через оболочку MingWin bash MsysGit Windows, он будет переносимым.
VonC
5
@VonC Спасибо. Еще одна проблема. При определенных обстоятельствах (если не было никаких изменений в объединяемой локальной ветке) кажется, что драйвер слияния даже не вызывается, что приводит к изменению локальных файлов (которые должны использовать настраиваемый драйвер слияния. чтобы предотвратить их изменение во время слияния). Есть ли способ заставить git всегда использовать драйвер слияния?
ulmangt
1

Как @ Сиро-Сантилли прокомментировал, простой способ сделать это , чтобы использовать .gitattributesс настройками этого:

path/to/file merge=ours

и включите эту стратегию с помощью:

git config --global merge.ours.driver true

(Я добавляю это в качестве ответа, чтобы сделать его более заметным, но делаю это вики сообщества, чтобы не пытаться получить для себя больше, чем пользователь. Пожалуйста, проголосуйте за его комментарий под Q здесь, чтобы воздать ему должное!)

Грег Дубицки
источник
(Кроме того: если кто-то дает ответ в виде комментария и не добавляет ответ, совершенно нормально написать ответ без CW и получить кредиты. Если он все еще является активным участником, вы можете пинговать его, чтобы добавить ответ, если вы хотите, но они технически уже имели свой шанс :-)).
халфер
0

У нас есть несколько файлов конфигурации, которые мы никогда не хотим перезаписывать. Однако .gitignore и .gitattributes не работали в нашей ситуации. Нашим решением было хранить файлы конфигурации в ветке конфигурации. Затем разрешите изменение файлов во время слияния git, но сразу после слияния используйте «ветку проверки git -». для копирования наших файлов конфигурации из ветки конфигурации после каждого слияния. Подробный ответ stackoverflow здесь

HamletHub
источник