Git перебазирует неожиданный результат слияния

#git #git-rebase

#мерзавец #git-перебазирование #git #git-перебазировать

Вопрос:

Предположим, что следующая структура фиксации:

 A ---- B(HEAD)
  
   C
  

Содержимое файла в этих ревизоинах.

A:

 1
  

B:

 1
2
  

C:

 0
1
  

Я хочу объединить B с C. Если я сделаю это через git merge C , я получу ожидаемый конфликт. Но если я сделаю git rebase C , то git сделает это без каких-либо конфликтов, но результат будет неожиданным:

 0
12
  

Кто-нибудь может, пожалуйста, объяснить мне, почему это происходит? Могу ли я вообще полагаться на перебазирование? В документации говорится, что rebase повторно применяет коммиты на конце другого коммита. Итак, как получается, что коммит, добавляющий «2» ко второй строке, не вызывает никаких конфликтов и «съедает» разрыв строки?
Вы можете протестировать его на этом репозитории https://gitlab.com/timofei.davydik/rebase_problem.git

 git checkout test
git rebase master
  

Комментарии:

1. Я не могу это объяснить, но я могу подтвердить поведение. git checkout test amp;amp; git rebase master была команда, которая создала 12 в файле.

Ответ №1:

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

Я воспроизвел поведение в вашем образце репозитория — спасибо, что предоставили его! — и затем создал другую изолированную среду с теми же файлами, но с новой строкой в последней строке в каждом состоянии.

С окончательными символами новой строки в файле были созданы как слияние, так и перебазирование:

 0
1
2
  

как и следовало ожидать.

Я думаю, git merge что в вашем примере наблюдается конфликт, потому что он не распознает 1 и 1n как одно и то же, тогда как git rebase видит, что «вставить 0 впереди 1 » продолжается, потому что он находит 1 в первой строке, даже если новая строка отсутствует в 1 .

Тот факт, что git rebase произведено 0n12 , предполагает, что он взял 0n и 1 из C, а затем 2 из B, при этом новая строка отсутствует в 1 и 2 , потому что они отсутствуют в вашем файле.

Я бы сказал, что это ошибка, из-за которой перебазирование учитывало 1 == 1n , но в остальном вывод является разумным на основе входных данных.

Комментарии:

1. На самом деле, здесь ожидается конфликт, потому что ревизии B и C добавляют содержимое в одну и ту же строку (вторую).

2. @AlbertoTrindadeTavares Нет, B добавляет строку после 1 , в то время как C добавляет строку перед 1 . Если файлы имели новую строку в последней строке, как обычно и должно быть, конфликта быть не должно.

3. @joanis Мне нравится ваше объяснение, спасибо. Я думаю, следует избегать создания файлов без новой строки в конце.

4. Некоторые из вас, ребята, могли бы создать проблему / PR в github.com/git/git

Ответ №2:

На самом деле, merge и rebase не будут перемещать вещи одинаково. Итак, предполагая, что вы в настоящее время находитесь на ветке B:

  • git merge C приведет к переходу верхнего состояния ветки C в вашу текущую ветку B. Это полезно, когда вы работаете над главной веткой и пытаетесь объединить результаты локальных ветвей разработки, например.
  • git rebase C переместит вашу полную ветвь B поверх C (в качестве целевого объекта). Для этого git фактически найдет все коммиты, относящиеся к B, просматривая вашу ветку назад, пока не обнаружит, что первый коммит является общим для обеих ваших ветвей (здесь точка «A»). Затем он будет воспроизводить каждую фиксацию ветви B поверх C, то есть повторно применять каждый набор изменений.

Во-первых, это означает, что rebase операция не является операцией слияния. Тогда набор изменений, введенный B, на самом деле «добавляет 2 сразу после 1».

 @@ -1  1,2 @@
1
 2
  

В «A» ваш файл содержит только «1», поэтому в итоге вы получите «1» «2».
В «C» ваш файл содержит «0» «1», поэтому вы получите «0» «1» «2».

Комментарии:

1. Ваше описание хорошее, но набор изменений с A на C должен содержать 0, а не 2.

2. Извините, набор изменений подходит для команды, которую вы показали. Вставка 0 предназначена для git checkout C; git rebase B . Ваш ответ был бы более полезным, если бы вы также показали это, поскольку это вопрос OP.