Почему git (иногда) клонирует коммит при слиянии?

Я знаю, что есть некоторые сценарии, которые приводят к дублированию коммитов Git, например, git cherry-pick. Если коммит выбран и «объединен» с другой веткой, он появляется дважды в графе коммитов с двумя хэшами коммитов.

Возможно ли, что Git дублирует коммит во время операции git merge?

Причина, по которой я спрашиваю, заключается в следующем графике коммитов (сгенерированном в TortoiseGit):

введите здесь описание изображения

Коммиты перечислены здесь в порядке даты (дата автора).

После того, как я совершил efb916.. в зеленой ветке (которая тогда была моей master веткой), я объединил зеленую ветку с красной веткой (которая является локальной боковой веткой). Это выглядит как обычное слияние на графике. Все идет нормально.

После этого я нажал кнопку «Синхронизировать» в Github для Windows, чтобы синхронизировать мой локальный master с удаленным origin/master. Это вытащило фиксацию 09067c.. из удаленной ветки, а затем объединило или перебазировало локальную master, поэтому за фиксацией 09067c.. последовала фиксация efb916... Однако вместо настоящего слияния efb916.. с 09067c.. Git продублировал коммит efb916.. и присвоил ему новый хэш 48e314...

В конце концов, моя master указывала на 48e314.. (черная ветвь на графике), а моя локальная боковая ветвь указывала на 68b78a.. (красная ветвь). Содержание, дата, автор и сообщение коммитов efb916.. и 48e314.. точно такие же.

Это случалось несколько раз, иногда с дублированием только одного коммита, иногда с несколькими коммитами.

Почему git продублировал фиксацию efb916..? Как я могу предотвратить это?

EDIT: В качестве дополнительного примечания: мне кажется странным, что мой master изначально указывал на efb916.., но после Github Sync efb916.. больше не было в истории коммитов master.


person cheesus    schedule 17.09.2014    source источник


Ответы (4)


а затем объединил или перебазировал локальный

Он перебазировался. Когда вы перебазируете, вы создаете дубликаты shas. При слиянии вы создаете единую фиксацию слияния.

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

Вручную передать --rebase для вытягивания

git pull --rebase

Настройте pull для выполнения перебазирования по умолчанию вместо слияния (этот параметр может быть в вашем глобальном, репо или системном файле конфигурации)

git config pull.rebase true

Кроме того, в конфигурации репо может быть настройка для каждой ветки, например

branch.<branch>.rebase true 
person Andrew C    schedule 17.09.2014

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

Простой вид

Проблема в том, что вы не понимаете, что такое коммит на самом деле. Хэш коммита — это не хэш вашего патча, это скорее хеш системы в целом. Таким образом, когда вы выполняете слияние, оно функционально применяет все исправления в обеих (или более) ветвях, которые вы объединяете, создаете новое полное дерево кода и создаете его в дереве объектов с хэшем, представляющим текущее состояние. .

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

Создайте новый репозиторий с одним файлом:

# echo "a" >> file
# git init
Initialized empty Git repository in /home/hardaker/tmp/h/test/.git/
# (master #): git add file
# (master #): git commit -m "new file"
[master (root-commit) 9617f27] new file
 1 file changed, 1 insertion(+)
 create mode 100644 file

Теперь давайте разветвим его:

# (master): git checkout -b new-branch
Switched to a new branch 'new-branch'

Добавьте новый файл в ветку:

# (new-branch): echo "file2" > file2
# (new-branch): git add file2 
P# (new-branch +): git commit -m "new file2" file2 
[new-branch 04f3fdf] new file2
 1 file changed, 1 insertion(+)
 create mode 100644 file2

Снова проверьте мастер и внесите изменения:

# (new-branch): git checkout master
Switched to branch 'master'
# (master): echo "b" >> file 
# (master *): git commit -m "added b" -a
[master 3b3de88] added b
 1 file changed, 1 insertion(+)

Теперь давайте отобразим журнал в виде дерева и посмотрим, что у нас есть на данный момент:

# (master):  git log --oneline --graph --all --decorate
* 3b3de88 (HEAD, master) added b
| * 04f3fdf (new-branch) new file2
|/  
* 9617f27 new file

Красивый вид на дерево с двумя ветвями. Обратите внимание на идентификатор фиксации новой ветки, потому что он изменится, когда мы перебазируем новую ветку на главную:

# (master): git checkout new-branch 
Switched to branch 'new-branch'
# (new-branch): git rebase master
 file | 1 +
 1 file changed, 1 insertion(+)
First, rewinding head to replay your work on top of it...
Applying: new file2

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

# (new-branch):  git log --oneline --graph --all --decorate
* d9918d4 (HEAD, new-branch) new file2
* 3b3de88 (master) added b
* 9617f27 new file

Теперь, чтобы повеселиться и узнать, что такое коммит на самом деле и что такое объекты в дереве, прочтите Веб-страница Git Objects. Вы узнаете так много всего за несколько страниц.

person Wes Hardaker    schedule 17.09.2014
comment
Спасибо. Вы отвечаете на вопрос Почему один и тот же коммит имеет разные хеш-значения?. Но я хотел бы знать, почему Git/Github для Windows иногда создает дублирующую фиксацию с тем же содержимым (одинаковая разница), а иногда нет. - person cheesus; 17.09.2014
comment
@cheeesus, коммиты имеют разные хэш-значения, потому что они не являются одним и тем же коммитом :-). Хэши коммитов генерируются на основе многих атрибутов коммита, включая его содержимое, его сообщение коммита, его временную метку и его родителя(ей). Даже если вы приложите усилия, чтобы убедиться, что все остальное идентично, включая временные метки, если родительские коммиты отличаются, хэш будет другим. - person Chris; 17.09.2014
comment
Крис прав. Прочитайте веб-страницу в самом низу моего ответа, и вы точно увидите, что входит в набор коммитов. На самом деле это не дубликат. Патч может показаться дубликатом, но набор изменений, хранящийся в git, не является дубликатом другого. - person Wes Hardaker; 18.09.2014

git merge никогда не дублирует коммиты, он только создает новые (коммиты слияния)

git rebase дублирует коммиты

git cherry-pick ДОПОЛНЯЕТСЯ ли коммиты

Если вы не используете git rebase и git cherry-pick, вы будете уверены, что в вашей истории нет повторяющихся коммитов. !, единственное, что вам нужно, это как их использовать

person dseminara    schedule 17.09.2014

Нашел его в настройках Github для Windows:

введите здесь описание изображения

person cheesus    schedule 18.09.2014