Як я можу сказати git завжди вибирати локальну версію для суперечливих злиттів у певному файлі?


100

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

Чи є для мене спосіб налаштувати місцеве репо, щоб не скаржитися на конфліктне злиття кожного разу, коли я git pull? Я б хотів завжди вибирати свою локальну версію під час об'єднання цього файлу.


1
Щойно додав просте рішення через .gitattributes і дуже базовий "драйвер злиття"
VonC

12
TD; LR:echo 'path/to/file merge=ours' >> .gitattributes && git config --global merge.ours.driver true
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

@CiroSantilli: працює як принадність у Linux. Цей драйвер досить простий, щоб його можна було
вбудувати

Чи бажаєте ви натиснути на файл зміни? Або це, наприклад, конфігураційний файл, де за замовчуванням зберігається git.
Ян Рінроуз

Коментар @CiroSantilli 新疆 改造 中心 六四 事件 法轮功 правильний, але змусить цю поведінку виникати для кожного репо в системі з --globalтегом. Якщо ви хочете лише такої поведінки для одного репо, залиште --globalпрапор:echo 'path/to/file merge=ours' >> .gitattributes && git config merge.ours.driver true
majorobot

Відповіді:


140

Що стосується конкретного примірника файлу config, я погодився би з відповіддю Рона :
конфігурація повинна бути "приватною" для вашої робочої області (отже, "ігнорується", як у "оголошеному у .gitignoreфайлі").
У вас може бути шаблон конфігураційного файлу з токенізованими значеннями та сценарій, який перетворює цей config.templateфайл у приватний (і ігнорований) конфігураційний файл.


Однак це конкретне зауваження не відповідає, що є більш широким загальним питанням, тобто вашим питанням (!):

Як я можу сказати git завжди вибирати локальну версію для суперечливих злиттів у певному файлі? (для будь-якого файлу чи групи файлів)

Цей вид злиття - це "злиття копії", в якому ви завжди будете копіювати "нашу" або "їхню" версію файлу, коли виникає конфлікт.

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

Для "файла" (файл взагалі, не кажучи про файл "config", оскільки це поганий приклад), ви могли б цього досягти за допомогою спеціального сценарію, викликаного через злиття.
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.

(Рядок, що починається з об’єднання, це доводить)


Ми можемо визначити, об'єднати та / або перезаписати драйвер злиття, як Git:

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

Під "комбінуванням" я маю на увазі "сукупність" драйверів з декількома об'єднаннями.
Нік Грін намагається, в коментарях , насправді комбайнери злиття: дивіться « Merge POM за допомогою пітона мерзотник драйвер ».
Однак, як згадувалося в його іншому питанні , він працює лише у випадку конфліктів (одночасні зміни в обох галузях).


1
Дякую за детальну відповідь! Я розумію, що не має сенсу керувати файлами конфігурації версій, але я був після прямого мотиваційного прикладу. Дійсно, саме широке питання мене зацікавило. Я ніколи раніше не чув про водії git merge, тому дякую, що просвітили мене.
saffd

6
cp -f $3 $2, Ймовірно , слід цитувати, тобто cp -f "$3" "$2".
Дуга

1
@VonC дякую за детальну відповідь! Проблема, яку я маю з цим, полягає в тому, що це залежить від встановлення драйвера у файлі .git / config. Я хотів би додати інформацію про драйвери до самого проекту, тому було б автоматичним і менше роботи з налаштуваннями. Якісь покажчики?
Хуан Дельгадо

2
@ulmangt: ви можете дуже добре зберігати цей скрипт і в git repo, ... поки ви знайдете спосіб додати його батьківський каталог до PATH(Unix або Windows PATH). Оскільки цей скрипт буде інтерпретований через оболонку bash Unix або через оболонку MingWin MsysGit Windows, він буде портативний.
VonC

5
@ VonC Дякую Ще одне питання. За певних обставин (якщо не відбулося жодних змін до місцевої гілки, в яку об'єднано), виявляється, що драйвер злиття ніколи навіть не викликається, що призводить до зміни локальних файлів (які, як передбачається, використовують користувацький драйвер злиття щоб не допустити їх зміни під час злиття). Чи є спосіб змусити git завжди використовувати драйвер злиття?
ulmangt

1

Як прокоментував @ ciro-santilli, простий спосіб це зробити, щоб використовувати його .gitattributesз налаштуваннями:

path/to/file merge=ours

та увімкніть цю стратегію за допомогою:

git config --global merge.ours.driver true

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


(Убік: якщо хтось дає відповідь як коментар і не додає відповіді, цілком нормально написати відповідь, що не стосується CW, і отримати заліки. Якщо вони все ще є активним учасником, ви можете надіслати їх, щоб додати відповідь, якщо ви хочете, але технічно вони вже мали свій шанс :-)).
півзахисник

0

У нас є кілька конфігураційних файлів, які ми ніколи не хочемо перезаписати. Однак .gitignore та .gitattributes не працювали в нашій ситуації. Нашим рішенням було зберігання конфігураційних файлів у конфігураційній гілці. Потім дозвольте змінити файли під час злиття git, але одразу після злиття використовуйте "гілку оформлення замовлення -." копіювати наші конфігураційні файли з конфігураційної гілки після кожного злиття. Детальна відповідь stackoverflow тут

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.