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

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

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

2 ответа

Решение

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


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

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

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

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

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

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


Давайте проверим это в простом сценарии с 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 и описал в разделе " Стратегии слияния " настройки атрибутов Git - Git, вы можете заменить сценарий оболочки командой shell 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.

Слияние не удается... только для.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 директивы.

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

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

path/to/file merge=ours

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

git config --global merge.ours.driver true

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

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

Другие вопросы по тегам