Pull to refresh

Конфликты при слиянии csproj файлов

Reading time 5 min
Views 9.5K
Original author: Phil Haack


В текущей версии GitHub для Windows, мы сделали небольшое изменение, которое имеет едва заметный эффект, который вы, вероятно, уже заметили. Мы изменили подход к слиянию *.csproj и похожих файлов, используемый по умолчанию.
Если вы измените .csproj файл в ветке и затем объедините ее с другой веткой, то вы возможно столкнетесь с большим количеством конфликтов слияния, нежели вы могли иметь раньше.

Почему?

Чтож, раньше мы бы использовали union для слияния *.csproj файлов.
git merge-file документация описывающая эту функцию:

Вместо того, чтоб оставить конфликт в файле, разрешать конфликты в пользу нашей (или их или обоих) стороны.

В общем когда происходит конфликт, система пытается решить эту проблему, стараясь применить все изменения.
В принципе, это компромисс. Этот подход можно настроить в файле .gitattributes, поэтому если вы действительно хотите использовать такое поведение для своего репозитория, добавьте следующее:
*.csproj  merge=union

А теперь давайте я расскажу, почему возможно вы не захотите это делать, и почему мы в итоге изменили это.

Слияние объединением обернулось катастрофой


Допустим мы начнем со следующего упрощенного foo.csproj файла в нашей ветке master вкупе файлом .gitattributes:
<?xml version="1.0" encoding="utf-8"?>
<Project>
  <PropertyGroup>
    <Page Include="AAA.cs">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="DDD.cs">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
  </PropertyGroup>
</Project>


После создания этого файла давайте убедимся, что мы закоммитили его
git init .
git add -A
git commit -m "Initial commit of gittattributes and foo.csproj"


Затем мы создаем ветку (git checkout -b branch) под оригинальным названием «branch» и вставляем указанный ниже кусок в foo.csproj между элементами AAA.cs и DDD.cs.
   <Page Include="BBB.cs">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>


Для тех у кого слабенькое воображение, ниже указан результат того, что мы закоммитим в эту ветку.
<?xml version="1.0" encoding="utf-8"?>
<Project>
  <PropertyGroup>
    <Page Include="AAA.cs">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="BBB.cs">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="DDD.cs">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
  </PropertyGroup>
</Project>


Не забудьте закоммитить это, если действуете по порядку.
git commit -a "Add BBB.cs element"


Теперь давайте вернемся обратно в ветку master
git checkout master


И добавим туда же следующий кусок:
<Page Include="CCC.cs">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>


В итоге ветка master должна содержать следующее:
<?xml version="1.0" encoding="utf-8"?>
<Project>
  <PropertyGroup>
    <Page Include="AAA.cs">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="CCC.cs">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="DDD.cs">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
  </PropertyGroup>
</Project>


И все это закоммитим:
git commit -a "Add CCC.cs element"


Вы всё еще здесь?
Хорошо, теперь давайте сольем нашу ветку branch в ветку master
git merge branch


В результате использования слияния объединением мы получим следующее:
<?xml version="1.0" encoding="utf-8"?>
<Project>
  <PropertyGroup>
    <Page Include="AAA.cs">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="CCC.cs">
    <Page Include="BBB.cs">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="DDD.cs">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
  </PropertyGroup>
</Project>


Блин(иууу), получилось не совсем то, что мы хотели. Заметьте, что “BBB.cs” встроилось в “CCC.cs” и у нас больше нет закрывающего тэга . Это мягко говоря ужасно.
При отсутствии файла .gitattributes в репозитории и использовании стандартного подхода слияния, стандартная команда слияния привела к конфликту слияния, который требует исправления. В нашем понимании это лучше, чем не бросающаяся в глаза ошибка, которая останется в вашем проекте.
<?xml version="1.0" encoding="utf-8"?>
<Project>
  <PropertyGroup>
    <Page Include="AAA.cs">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
<<<<<<< HEAD
    <Page Include="CCC.cs">
=======
    <Page Include="BBB.cs">
>>>>>>> branch
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="DDD.cs">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
  </PropertyGroup>
</Project>


Очевидно, что в какой-нибудь идеальной параллельной вселенной git поместил бы весь элемент «ссс» после элемента «bbb» без отсебятины и не беспокоя нас по поводу решения этих противных конфликтов слияния. Мы не живем в этой вселенной, но быть может наша может стать чуть более похожей на идеальную. Я слышал там круто.

Что должно быть сделано в Visual Studio?


Я недавно попросил подписчиков в Твитере проголосовать за вопрос запрос к команде Visual Studio о добавлении поддержки шаблонов файлов в проектных файлах. MSBuild уже поддерживает символы-джокеры в файлах .csproj, но у Visual Studio пока с ними не все в порядке.

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

С другой стороны, можно написать качественный драйвер слияния XML для Git, но это достаточно серьезное испытание, и мой коллега Marcus Olsson может это подтвердить. Если бы это было просто, или даже сложно но в меру, это было бы уже сделано. Любопытно, если мы ограничим это до обычной проблемы .csproj файлов, сможем ли мы сказать, что хоть это и не отличное, но достаточно хорошее решение для контроля обычных конфликтов слияния? Возможно.

Даже если мы напишем этот драйвер слияния, он сможет решить проблему только одной конкретной системы контроля версии, которая только и важна для нас. :trollface:

Было предположение, что если Visual Studio сначала отсортирует эти элементы, то это может помочь частично решить проблему. Это поможет уменьшить несущественные конфликты возникшие по вине условно недетерминированной сортировки элементов в Visual Studio. Но это не решит проблему слияния как таковую. В примере, который я представил, каждый элемент сохраняет сортировку на протяжении всего примера. Получается, что каждый раз когда в двух разных ветках добавляются файлы смежные файлы, вы рискуете получить конфликт. Что и происходит достаточно часто.

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

p.s.
Волею судеб наткнулся на эту статью, решил что Вам тоже будет интересно знать, лично я сталкиваюсь с этим постоянно.
Это мой первый перевод поэтому пожалуйста ответьте на вопрос в опроснике, хотелось бы знать стоит дальше тратить ваше/мое время или нет.
Only registered users can participate in poll. Log in, please.
Понравился ли / Понятен ли вам перевод?
26.47% Да 27
41.18% Нормально 42
16.67% Три с минусом 17
8.82% Садись, два 9
6.86% Нет 7
102 users voted. 62 users abstained.
Tags:
Hubs:
+6
Comments 4
Comments Comments 4

Articles