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

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

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

Ответы [ 2 ]

135 голосов
/ 31 мая 2009

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


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

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

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

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

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

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


Давайте проверим это в простом сценарии, с 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.

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

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

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 через драйвер python git ".
Однако, как упоминалось в его другом вопросе , он работает только в случае конфликтов (одновременное изменение в обеих ветвях).

0 голосов
/ 18 октября 2016

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

...