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

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

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

Ответ 1

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


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

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

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

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

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

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


Позвольте проверить, что в простом сценарии, с 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 и описан в Merge Strategies " Настройка git - git Атрибуты, вы можете замените оболочку script командой оболочки true.

git config merge.keepMine.driver true

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

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.

Под "объединением" я имею в виду "совокупный" множественный драйвер слияния.
Nick Green пытается, в комментариях, чтобы фактически объединить драйверы слияния: см. " Объединить pom через драйвер python git.
Однако, как упоминалось в его другом вопросе, он работает только в случае конфликтов (одновременная модификация в обеих ветвях).

Ответ 2

У нас есть несколько файлов конфигурации, которые мы никогда не хотим перезаписывать. Однако и .gitattributes не работали в нашей ситуации. Наше решение состояло в том, чтобы сохранить конфигурационные файлы в ветке configs. Затем разрешите изменять файлы во время слияния git, но сразу после слияния используйте "w21 > checkout branch -.". чтобы скопировать наши файлы конфигурации из ветки configs после каждого слияния. fooobar.com/info/7307/...