В самом деле, конкретный пример того, что объединение в Git легче, чем SVN? - PullRequest
58 голосов
/ 30 мая 2011

Вопрос переполнения стека Как и / или почему слияние в Git лучше, чем в SVN? - отличный вопрос с некоторыми отличными ответами . Однако ни один из них не показывает простой пример, где объединение в Git работает лучше, чем SVN .

На случай, если этот вопрос будет закрыт как дубликат, что такое

  1. Конкретный сценарий слияния
  2. Насколько это сложно в SVN?
  3. Как такое же объединение легче в Git?

Несколько баллов:

  • Нет философии или глубоких объяснений того, что такое DVCS, пожалуйста. Это здорово, правда, но я не хочу, чтобы их детали запутывали ответ на это (ИМХО) важное
  • Мне сейчас наплевать на "исторический SVN". Пожалуйста, сравните современный Git (1.7.5) с современным SVN (1.6.15).
  • Не переименовывайте, пожалуйста - я знаю, что Git обнаруживает переименования и перемещает, а SVN - нет. Это здорово, но я ищу что-то более глубокое и пример, который не включает переименования или перемещения.
  • Нет перебазирования или другой «продвинутой» операции Git. Просто покажи мне слияние, пожалуйста.

Ответы [ 7 ]

22 голосов
/ 30 мая 2011

С практической точки зрения слияние традиционно было "трудным" из-за того, что я называю проблемой "слияния большого взрыва". Предположим, что разработчик некоторое время работал над некоторым кодом и еще не завершил свою работу (возможно, разработчик привык работать в Subversion против trunk и не фиксирует незавершенный код). Когда разработчик наконец завершит коммит, будет много изменений, объединенных в один коммит. Для других разработчиков, которые хотят объединить свою работу с этим коммитом «большого взрыва», инструмент VCS не будет располагать достаточной информацией о том, как первый разработчик достиг цели, которую вы взяли на себя, поэтому вы просто получите «вот гигантский конфликт». во всей этой функции исправь это ".

С другой стороны, обычный стиль работы с Git и другими DVCS, имеющими дешевые локальные ветки, заключается в регулярной фиксации. После того, как вы выполнили одну часть работы, которая в значительной степени имеет смысл, вы делаете это. Оно не должно быть идеальным, но оно должно быть последовательной единицей работы. Когда вы возвращаетесь к слиянию, у вас все еще есть история меньших коммитов, которая показывает как вы перешли из исходного состояния в текущее состояние. Когда DVCS собирается объединить это с работой других, у него появляется намного больше информации о том, какие изменения были сделаны, и в итоге вы получаете меньше и меньше конфликтов.

Суть в том, что вы все еще можете усложнить объединение с Git, сделав один коммит большого взрыва только после того, как что-то закончите. Git призывает вас делать коммиты меньшего размера (делая их максимально безболезненными), что облегчает слияние в будущем.

16 голосов
/ 20 декабря 2012

Я могу лишь рассказать вам о небольшом эксперименте, в котором Git НЕ был лучше, чем Subversion (те же проблемы).

Мне было интересно об этом случае: Вы начинаете с двух веток "mytest1" и "mytest2", основанных на одном и том же коммите. У вас есть C-файл, который содержит функцию blub (). В ветке mytest1 вы перемещаете "blub ()" в другую позицию в файле и делаете коммит. В ветке mytest2 вы модифицируете blub () и делаете коммит. На ветке mytest2 вы пытаетесь использовать "git merge mytest1".

Кажется, дает конфликт слияния. Я надеялся, что Git распознает, что "blub ()" был перемещен в mytest1, и затем сможет автоматически объединить модификацию в mytest2 с ходом в mytest1. Но, по крайней мере, когда я пытался это не сработало автоматически ...

Поэтому, хотя я полностью понимаю, что Git намного лучше отслеживает то, что было объединено и что еще не было объединено, мне также интересно, есть ли «чистый» случай слияния, в котором Git лучше, чем SVN ...

Теперь, так как этот вопрос долго беспокоил меня, я действительно пытался создать конкретный пример, в котором Git лучше , тогда как объединение в SVN завершается неудачей.

Я нашел один здесь https://stackoverflow.com/a/2486662/1917520,, но это включает переименование, и вопрос здесь был для случая без переименования.

Итак, вот пример SVN, который в основном пытается это:

bob        +-----r3----r5---r6---+
          /                /      \
anna     /  +-r2----r4----+--+     \
        /  /                  \     \
trunk  r1-+-------------------r7-- Conflict

Идея здесь такова:

  • Анна и Боб являются разработчиками со своими ветками (созданными в r2, r3).
  • Анна делает некоторые модификации (r4),
  • Боб делает некоторые модификации (r5).
  • Боб сливает модификации от Анны в свою ветку; это дает конфликты, которые Боб исправляет, а затем фиксирует (r6).
  • Модификации Анны объединяются обратно в сундук (r7).
  • Боб пытается слить свою модификацию обратно в ствол, и это снова вызывает конфликт.

Вот скрипт Bash , который создает этот конфликт (с использованием SVN 1.6.17, а также SVN 1.7.9):

#!/bin/bash
cd /tmp
rm -rf rep2 wk2
svnadmin create rep2
svn co file:///tmp/rep2 wk2
cd wk2
mkdir trunk
mkdir branches
echo -e "A\nA\nB\nB" > trunk/f.txt
svn add trunk branches
svn commit -m "Initial file"
svn copy ^/trunk ^/branches/anna -m "Created branch anna"
svn copy ^/trunk ^/branches/bob  -m "Created branch bob"
svn up 
echo -e "A\nMA\nA\nB\nB" > branches/anna/f.txt
svn commit -m "anna added text"
echo -e "A\nMB\nA\nB\nMB\nB" > branches/bob/f.txt
svn commit -m "bob added text"
svn up
svn merge --accept postpone ^/branches/anna branches/bob
echo -e "A\nMAB\nA\nB\nMB\nB" > branches/bob/f.txt
svn resolved branches/bob/f.txt
svn commit -m "anna merged into bob with conflict"
svn up
svn merge --reintegrate ^/branches/anna trunk
svn commit -m "anna reintegrated into trunk"
svn up
svn merge --reintegrate --dry-run ^/branches/bob trunk

Последний «--dry-run» говорит вам, что будет конфликт. Если вместо этого вы сначала попытаетесь объединить реинтеграцию Анны с филиалом Боба, вы также получите конфликт; так что если вы замените последний svn merge на

svn merge ^/trunk branches/bob

это также показывает конфликт.

Вот то же самое с Git 1.7.9.5:

#!/bin/bash
cd /tmp
rm -rf rep2
mkdir rep2
cd rep2
git init .
echo -e "A\nA\nB\nB" > f.txt
git add f.txt
git commit -m "Initial file"
git branch anna
git branch bob
git checkout anna
echo -e "A\nMA\nA\nB\nB" > f.txt
git commit -a -m "anna added text"
git checkout bob
echo -e "A\nMB\nA\nB\nMB\nB" > f.txt
git commit -a -m "bob added text"
git merge anna
echo -e "A\nMAB\nA\nB\nMB\nB" > f.txt
git commit -a -m "anna merged into bob with conflict"
git checkout master
git merge anna
git merge bob

Содержимое файла f.txt изменяется следующим образом.

Начальная версия

A
A
B
B

Анны, модификации

A
MA
A
B
B

модификации Боба

A
MB
A
B
MB
B

После слияния Анны с веткой Боба

A
MAB
A
B
MB
B

Как уже указывало много людей: проблема в том, что Subversion не может вспомнить, что Боб уже разрешил конфликт. Поэтому, когда вы попытаетесь теперь слить ветвь Боба в ствол, вам придется заново разрешить конфликт.

Git работает совершенно по-другому. Вот некоторое графическое представление того, что делает git

bob         +--s1----s3------s4---+
           /                /      \
anna      /  +-s1----s2----+--+     \
         /  /                  \     \
master  s1-+-------------------s2----s4

s1 / s2 / s3 / s4 - это снимки рабочего каталога, которые делает git.

Примечания:

  • Когда Анна и Боб создают свои ветви развития, это НЕ создавать любые коммиты под git. мерзавец просто запомнит это обе ветви изначально ссылаются на один и тот же объект коммита мастер ветка. (Этот коммит в свою очередь будет ссылаться на снимок s1).
  • Когда Анна реализует свою модификацию, это создаст новый снимок "s2" + объект коммита. Объект фиксации включает в себя:
    • Ссылка на снимок (s2 здесь)
    • Сообщение коммита
    • Информация о предках (других объектах коммита)
  • Когда Боб реализует свою модификацию, это создаст еще один снимок s3 + объект коммита
  • Когда Боб объединяет модификации Анны в свою ветку разработки это создаст еще один снимок s4 (содержащий слияние его изменения и изменения Анны) + еще один объект коммита
  • Когда Анна объединяет свои изменения с главной веткой, этобыть слияния "ускоренной перемотки" в показанном примере, потому что мастер не изменился за это время.Что означает «ускоренная перемотка вперед», так это то, что мастер просто укажет на снимок s2 из анны, ничего не сливая.При таком «перемотке вперед» не будет даже другого объекта коммита.Ветвь "master" прямо сейчас будет ссылаться на последний коммит из ветки "anna"
  • Когда bob теперь объединяет свои изменения в ствол, произойдет следующее:
    • git найдетвыяснилось, что коммит от anna, который создал снимок s2, является (прямым) предком коммита bobs, который создал снимок s4.
    • из-за этого git снова "перемотает" ветку master до последнегоcommit ветви "bob".
    • снова, это даже не создаст новый объект commit.Ветвь "master" будет просто указана на последний коммит ветки "bob".

Вот вывод "git ref-log", который показывает всеэто:

88807ab HEAD@{0}: merge bob: Fast-forward
346ce9f HEAD@{1}: merge anna: Fast-forward
15e91e2 HEAD@{2}: checkout: moving from bob to master
88807ab HEAD@{3}: commit (merge): anna merged into bob with conflict
83db5d7 HEAD@{4}: commit: bob added text
15e91e2 HEAD@{5}: checkout: moving from anna to bob
346ce9f HEAD@{6}: commit: anna added text
15e91e2 HEAD@{7}: checkout: moving from master to anna
15e91e2 HEAD@{8}: commit (initial): Initial file

Как вы можете видеть из этого:

  • когда мы идем в ветку разработки Анны (HEAD @ {7}), мы не перейти на другой коммит, мы сохраняем коммит;git просто помнит, что мы сейчас находимся в другой ветви
  • В HEAD @ {5} мы переходим к начальной ветви bob;это переместит рабочую копию в то же состояние, что и основная ветвь, потому что bob еще ничего не изменил
  • В HEAD @ {2} мы возвращаемся в главную ветвь, поэтому к тому же объекту фиксации все запущеноfrom.
  • Head @ {1}, HEAD @ {0} показывают слияния "ускоренной перемотки", которые не создают новые объекты фиксации.

С "git cat-В файле HEAD @ {8} -p "вы можете просмотреть полную информацию о начальном объекте коммита.Для приведенного выше примера я получил:

tree b634f7c9c819bb524524bcada067a22d1c33737f
author Ingo <***> 1475066831 +0200
committer Ingo <***> 1475066831 +0200

Initial file

Строка «tree» идентифицирует снимок s1 (== b634f7c9c819bb524524bcada067a22d1c33737f), к которому относится этот коммит.

Если я сделаю «git cat-file HEAD @ {3} -p "Я получаю:

tree f8e16dfd2deb7b99e6c8c12d9fe39eda5fe677a3
parent 83db5d741678908d76dabb5fbb0100fb81484302
parent 346ce9fe2b613c8a41c47117b6f4e5a791555710
author Ingo <***> 1475066831 +0200
committer Ingo <***> 1475066831 +0200

anna merged into bob with conflict

Это выше показывает объект коммита, bob, созданный при слиянии ветки разработки Анны.Снова строка «дерево» относится к созданному снимку (здесь s3).Дополнительно обратите внимание на «родительские» строки.Второй, который начинается с «parent 346ce9f», позже сообщает git, когда вы пытаетесь объединить ветку разработки bob с веткой master, что этот последний коммит bob имеет последний коммит анны как предка.Вот почему git знает, что слияние ветки разработки bob с master веткой - это «ускоренная перемотка вперед».

6 голосов
/ 30 мая 2011

У меня нет конкретных примеров, но любой вид повторного слияния затруднен, в частности так называемое перекрестное слияние .

   a
  / \
 b1  c1
 |\ /|
 | X |
 |/ \|
 b2  c2

слияние b2 и c2


Страница вики на Subversion Wiki, описывающая различия между слиянием info на основе ассиметричного слияния Subversion (с направлениями 'sync' и 'reintegrate') и слиянием отслеживания На основе симметричного слияния в DVCS имеется раздел « Симметричное слияние с слиянием Крисса-Кросса »

4 голосов
/ 29 мая 2013

Самый конкретный пример, который я могу придумать, - это простейшее слияние, которое не приводит к конфликтам слияния.Однако (TL; DR) с этим примером Git все еще по своей природе более простая процедура, чем с Subversion.Давайте рассмотрим, почему:

Subversion

Рассмотрим следующий сценарий в Subversion;транк и ветвь функции:

   1  2  3
…--o--o--o              trunk
          \4  5
           o--o         branches/feature_1

Для объединения вы можете использовать следующую команду в Subversion:

# thank goodness for the addition of the --reintegrate flag in SVN 1.5, eh?
svn merge --reintegrate central/repo/path/to/branches/feature_1

# build, test, and then... commit the merge
svn commit -m "Merged feature_1 into trunk!"

При объединении Subversion изменения требуют еще одного коммита.Это должно опубликовать изменения, которые слияние произвело с применением изменений в виртуальном каталоге ветви функций обратно в транк.Таким образом, каждый, кто работает со стволом, теперь может использовать его, и график ревизий выглядит примерно так:

   1  2  3      6
…--o--o--o------o       /trunk
          \4  5/
           o--o         /branches/feature_1

Давайте посмотрим, как это делается в git.

Git

В Git этот коммит слияния действительно не нужен, так как ветви - это прославленные закладки на графе ревизий.Таким образом, с такой же структурой графа ревизий это выглядит примерно так:

         v-- master, HEAD

   1  2  3
…--o--o--o
          \4  5
           o--o

              ^-- feature_branch

С головкой, находящейся в настоящее время в главной ветви, мы можем выполнить простое объединение с ветвью объектов:

# Attempt a merge
git merge feature_branch

# build, test, and then... I am done with the merge

... и он перемотает вперед ветвь к коммиту, на который указывает ветвь функции.Это стало возможным, потому что Git знает, что целью слияния является прямой потомок, и текущая ветвь должна принимать только все произошедшие изменения.График ревизий в конечном итоге будет выглядеть следующим образом:

   1  2  3  4  5
…--o--o--o--o--o
               ^-- feature_branch, master, HEAD

Изменения не требуют новой фиксации, поскольку все, что сделал git - это переместил ссылки на ветки дальше вперед.Осталось только опубликовать это в общедоступном репозитории, если у вас есть:

# build, test, and then... just publish it
git push

Заключение

Учитывая этот простой сценарий, вы можете утверждать две вещи в разнице между Subversion и Git:

  • Для SVN требуется как минимум пара команд , чтобы завершить слияние, и заставляет опубликовать слияние как коммит.
  • Git требует только одну команду и не заставляет опубликовать ваше слияние.

Учитывая, что это самый простой сценарий слияния, трудно споритьэта подрывная деятельность проще, чем git.

В более сложных сценариях слияния git также предоставляет вам возможность перебазировать ветку , которая создает более простой граф ревизий за счет переписывания истории.Однако, как только вы освоитесь с этим и избежите переиздания истории публикации уже опубликованных вещей, это не так уж и плохо.

Интерактивные перебазировки выходит за рамкио вопросе, но быть честным;это дает вам возможность переставлять, сдавливать и удалять коммиты.Я бы не хотел возвращаться к Subversion, так как переписывание истории не возможно по проекту.

2 голосов
/ 12 февраля 2018

Похоже, это миф о том, что объединение в Git проще, чем в SVN ...

Например, Git не может объединиться в рабочее дерево с изменениями, в отличие от SVN.

Рассмотримследующий простой сценарий: у вас есть некоторые изменения в вашем рабочем дереве и вы хотите интегрировать удаленные изменения без фиксации вашего собственного .

SVN :update, [разрешать конфликты].

Git : stash, fetch, rebase, stash pop, [разрешать конфликты], [stash drop при наличиибыли конфликты].

Или вы знаете более простой способ в Git?


Кстати, этот вариант использования кажется настолько важным, что IntelliJ даже реализовал отсутствующую функциональность «Обновить проект»для Git (аналог обновления SVN), который может автоматизировать ручные шаги, описанные выше:

IntelliJ IDEA - Update Project

2 голосов
/ 30 мая 2011

Сокращая ответ - В DVCS, поскольку у вас есть локальный источник контроля, если что-то напортачит в процессе слияния (что, вероятно, произойдет при больших слияниях), вы всегда можете выполнить откат к предыдущей локальной версии, которая имеет изменения, которые вы сделали до слияния, а затем повторите попытку.

Таким образом, вы можете выполнять слияние, не опасаясь, что ваши локальные изменения могут быть повреждены во время процесса.

1 голос
/ 21 декабря 2012

Если вы исключите «Ад с рефакторингом слияния», вы не получите честных образцов, потому что их просто не существует

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...