#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.