Почему я не могу объединить свою ветку git dev с master, хотя master содержит только пустой README? - PullRequest
1 голос
/ 26 марта 2019

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

Однако, когда я проверяю на master и запускаю git merge dev, я получаю фатальную ошибку refusing to merge unrelated histories. К сожалению, git pull origin master --allow-unrelated-histories не помогло устранить ошибку .

Это статус master:

  • git status показывает, что моя «ветка актуальна с« origin / master »».
  • Единственный файл в репо - пустой README.md.
  • GitLab пометил это как "устаревшую" ветвь.
  • Это защищенная ветка.

Помогите мне понять, почему я не могу слиться.

РЕДАКТИРОВАТЬ: я должен также упомянуть, что git log производит следующее:

commit ac22443836aeaf8abe38aa3761970a4cd3835587 (HEAD -> master, origin/master)
Author: // my info
Date:   Fri Dec 21 03:25:26 2018 -0700

    Initial commit
(END)

Ответы [ 4 ]

0 голосов
/ 26 марта 2019

Вы уже исправили (?) Это, используя --allow-unrelated-histories, и нет никакой реальной причины не оставлять это. Но если тебе все еще интересно ...

Что случилось и как вы (вероятно) попали сюда

Первый ключ к использованию Git - это понимание того, что Git - это commits . Конечно, для этого также необходимо, чтобы вы достаточно глубоко понимали, что такое коммит . Что это, довольно коротко и просто: это перманент (в основном) и неизменный (полностью) снимок плюс некоторые метаданные . Снимок содержит все ваши файлы - ну, все они на момент совершения фиксации - и метаданные имеют:

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

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

Git на самом деле находит фиксацию по хэш-идентификатору. Таким образом, идентификатор хеша имеет решающее значение. Конечно, людям также невозможно запомнить. Таким образом, Git дает нам способ запомнить последний идентификатор хэша, и таким способом будет имя ветви , подобное master или dev.

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

A <-B <-C   <-- master

Имя master запоминает хэш-идентификатор C. C сам - фактический коммит, полученный по хеш-идентификатору - запоминает хеш-идентификатор B, а B сам запоминает хеш-идентификатор A.

Когда что-то запоминает хэш-идентификатор какого-то другого коммита, мы говорим, что это что-то указывает на коммит. Таким образом, имя master указывает на C, C указывает на B, а B указывает на A. Эти три коммита - C, затем B, затем A - являются историей в хранилище.

Обратите внимание, что A никуда не указывает. Это буквально не может, потому что это был первый коммит. Не было более раннего коммита. Так что это просто не так, и Git называет это root commit. Все непустые репозитории должны иметь как минимум один корневой коммит. У большинства, наверное, ровно один ... а у тебя два.

Нормальное ветвление и слияние

Давайте кратко рассмотрим более обычный способ создания веток. Предположим, у нас есть только эти три коммита, на которые указывает master. Мы просим Git создать новое имя ветки, указывающее на тот же коммит, что и master:

A--B--C   <-- dev (HEAD), master

Оба имени обозначают фиксацию C, поэтому фиксация C включена и является tip commit of - обеих ветвей, и все три фиксации находятся в обеих ветвях. Но теперь мы делаем новый коммит. Процесс создания нового коммита - с обычным редактированием и git add и git commit - создает новый снимок, добавляет наше имя, адрес электронной почты, метку времени и т. Д., Использует текущий коммит C как сохраненный хеш, так и строит новый коммит. Новый коммит получает большой уродливый хэш-идентификатор, но мы просто назовем его D:

A--B--C   <-- dev (HEAD), master
       \
        D

С D парent C, D указывает на C. Но теперь происходит волшебство: Git записывает хэш-идентификатор D в текущее имя ветви - к которому присоединено HEAD - так что теперь у нас есть:

A--B--C   <-- master
       \
        D   <-- dev (HEAD)

и вуаля, у нас есть новая ветвь. (Ну, у нас было это раньше, указывая на C. Большинство людей не любят думать об этом, хотя, они хотят назвать D ветвью. Фактически, ветвь D-C-B-A!)

Со временем мы добавляем коммиты в обе ветви:

A--B--C-----J----K----L   <-- master
       \
        D--E--F--G--H--I   <-- dev

Мы git checkout master и git merge dev. Git найдет для нас коммит base merge , где dev и master расходятся. Это, очевидно, коммит C, поскольку именно здесь две ветви воссоединяются в прошлом. Git будет сравнивать C против L, чтобы увидеть, что мы изменили на master, сравнить C против I, чтобы увидеть, что мы изменили на dev, и объединить изменения. Git применяет объединенные изменения к снимку в C - к базе слияния - и делает новый коммит слияния M, который идет на текущая ветка HEAD, как обычно, обновляет имя этой ветви так, что master указывает на M:

A--B--C-----J----K----L--M   <-- master (HEAD)
       \                /
        D--E--F--G--H--I   <-- dev

Что особенного в M то, что он имеет две обратных ссылки: он возвращается к L, как и все коммиты, но у него есть второй родительский элемент I, который является текущим Тип коммит ветки dev. Однако, кроме двух родителей, он довольно обычный: у него, как обычно, есть снимок, а также наше имя, адрес электронной почты, отметка времени и сообщение журнала.

Аномальное ветвление

В Git нет ничего, что помешало бы вам делать дополнительные root-коммиты. Это просто немного сложно. Предположим, что вы каким-то образом сделали это:

A   <-- master

B--C--D--...--L   <-- dev (HEAD)

Как только у вас возникнет такая ситуация, git checkout master; git merge dev просто выдаст вам ошибку. Это связано с тем, что обычный метод поиска базы слияния - начиная с двух кончиков ветвей и работая в обратном направлении - никогда не находит общий коммит.

Добавление --allow-unrelated-histories говорит Git , притворяясь , что существует специальная пустая фиксация перед обеими ветвями:

 A   <-- master (HEAD)
0
 B--C--D--...--L   <-- dev

Теперь Git может различать 0 против A, чтобы увидеть, что вы изменили на master, и 0 против L, чтобы увидеть, что они изменили на dev. На master вы добавили каждый файл. На dev вы также добавили каждый файл. Поскольку это разные файлы, способ объединить эти два изменения состоит в том, чтобы добавить master файлы из commit A в dev файлы из commit L, применить эти изменения к пустой нулевой коммит и фиксация результата, когда родители возвращаются к A и L:

A---------------M   <-- master (HEAD)
               /
B--C--D--...--L   <-- dev

Как вы (вероятно) попали сюда

Существует опция git checkout, git checkout --orphan, которая устанавливает это состояние. Но это, вероятно, не то, что вы сделали. Состояние, которое это устанавливает, является таким же состоянием, в котором вы находитесь, когда создаете новый пустой репозиторий с git init:

[no commits]   <-- [no branches]

Нет веток, и все же Git скажет, что вы on branch master. Вы не можете быть на master: его не существует. Но вы, , хотя его не существует. Способ, которым Git управляет этим, заключается в том, что он помещает имя master в HEAD (на самом деле .git/HEAD) без предварительного создания ветви с именем master. Он не может создать ветвь, потому что имя ветки должно содержать действительный идентификатор хеша, а их нет.

Итак, когда вы запускаете git commit, Git обнаруживает это аномальное состояние: HEAD говорит master, но master не существует. Это то, что вызывает Git, чтобы сделать наш корневой коммит A. Затем Git записывает хеш-код A в ветвь, которая создает ветвь, и теперь мы имеем:

A   <-- master (HEAD)

это именно то, что мы хотели.

Но предположим, что пока мы находимся в этом странном состоянии без коммитов, мы запускаем:

git checkout -b dev

Это говорит Git: Поместите имя dev в HEAD. Он делает это без жалоб, хотя master тоже нет. Затем мы делаем наш первый коммит, но без видимой причины мы выберем B в качестве однобуквенного заменителя для его хеш-идентификатора:

B   <-- dev (HEAD)

Meanwhиль, запустив git init здесь, затем git checkout -b dev, затем что-то сделал и git commit, мы перейдем к $ WebHostingProvider - будь то GitHub, или GitLab, или Bitbucket, или что-то еще - и используя его , сделайте меня новый репозиторий кликающие кнопки. У них обычно есть опция: создать начальный коммит с файлами README и / или LICENSE и такими . Если этот параметр установлен - или параметр не не отмечен, - они делают первый коммит и master:

A   <-- master (HEAD)

Теперь вы подключаете свой репозиторий к их и заставляете ваш Git загружать любые свои коммиты, которых у вас нет:

A   <-- origin/master

B   <-- dev (HEAD)

Теперь вы можете добавлять множество коммитов, не замечая, что ваша ветка dev не связана с их веткой master (которую ваш Git вызывает origin/master).

Позже вы запускаете:

git checkout master

Ваш Git замечает, что у вас нет a master, но у вас есть origin/master. Поэтому ваш Git создает для вас a master, указывая на тот же коммит, что и origin/master, и присоединяет ваш HEAD к вашему новому master:

A   <-- master (HEAD), origin/master

B--C--D--...--L   <-- dev

и вуаля , вы в рассоле, в котором вы были.

0 голосов
/ 26 марта 2019

Если, как было сказано, master и dev не имеют общего (родительского) коммита, я бы попытался создать новую ветку из master и выбрать все коммиты из dev в виде вишниэто: git cherry-pick startHash^..endHash Например git cherry-pick 1234^..5678.Тогда вы сможете объединить эту новую ветку в master

0 голосов
/ 26 марта 2019

Вы, вероятно, отметили "[] Создать хранилище с README", когда создавали хранилище.

Когда master действительно пусто, я вижу следующие опции

  • зайдите в gitlab, измените ветку по умолчанию на что-то другое и удалите основную ветку в веб-интерфейсе (там можно удалить защищенные ветки).Или,

  • снять защиту master

Теперь вы можете принудительно толкнуть свою ветвь dev в gitlab (например, git push -f dev:master).

ПРИМЕЧАНИЕ. Эта опция запрещена для веток, соответственно.хранилища, которые используются другими народами.В этом случае выполните:

git fetch gitlab master
git merge -s ours FETCH_HEAD

и нажмите эту ветку.

0 голосов
/ 26 марта 2019

Если --allow-unrelated-hhistories было необходимо, это означает, что у вас фактически есть эквивалент двух разных репозиториев, которые вы объединили.

Я подозреваю, что это произошло, потому что вы не clone исходное хранилище сначала, прежде чем создавать свою ветку разработки и выполнять работу. Мой вывод из вашего описания состоит в том, что вы выполнили git init, извлекли ветку dev, а затем добавили удаленное хранилище как origin, когда хотели объединиться (это работало, потому что в вашем локальном хранилище не было удаленных).

Попытка слияния в этот момент приведет к «несвязанным историям», потому что два репо фактически не связаны; оригинал был создан и отправлен в GitLab, а совершенно отдельный был создан, когда вы сделали git init для создания локального. Клонирование оригинала создаст локальный репозиторий, который был связан , потому что он разделял этот начальный README commit.

Это, действительно, единственный способ решить эту проблему, и как только вы перейдете на GitLab, локальный и удаленный будут синхронизированы - но вы всегда должны клонировать, чтобы избежать проблемы в первую очередь.

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