мерзавец показывает, что он объединяет одну ветвь - PullRequest
2 голосов
/ 27 июня 2019

Я не уверен, какую команду git я выполнил, однако при запуске git log я получаю следующее сообщение

Слияние ветки 'xxx-2222' https://github.com/aaa/my_repo_nameв ххх-2222

Я думаю, что сделал следующее.

  1. У меня была ветвь функций (xxx-1111), и я создал из нее новую ветвь функций (xxx-2222).
  2. Затем я перебазировал свою ветку xxx-1111из моей ветки разработки, а затем ударил все коммиты, а затем слил в развитую.
  3. Я начал работать над моей новой веткой xxx-2222
  4. Я перебазировал xxx-2222 из ветви разработки и также сделал git pull.

Я думаю, что после этого я получаю следующее сообщение в журнале.

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

1 Ответ

2 голосов
/ 27 июня 2019

TL; DR

Вы запустили git pull. Команда git pull означает:

  1. Беги git fetch для меня.
  2. Запустите для меня вторую команду Git, как только выборка закончится.

По умолчанию вторая команда имеет значение git merge, и именно эта git merge вызывает вашу проблему. Прочитайте подробное обсуждение ниже, чтобы понять, почему.

Я советую новым пользователям Git избегать git pull. Запустите git fetch самостоятельно. Затем, если это уместно, запустите вторую команду Git - git merge, или git rebase, или любую вторую команду, которую вы, возможно, захотите использовать, если она есть - самостоятельно. Это дает вам возможность остановиться и посмотреть, что git fetch выбрано, прежде чем вы приступите к выполнению второй команды, которая, в конце концов, может быть неуместной.

Long

Во-первых, это не та же ветка. Это другая ветвь с тем же именем .

Аналогии - ужасные способы рассуждения, но иногда они полезны. Представьте, что вы на вечеринке, и всех там зовут Боб. Все они имеют одинаковые имена. Означает ли это, что они все человек? Конечно, нет, но они все "Боб". Вам просто нужно использовать какое-то другое имя, чтобы различать их.

Вот что пытается сделать Git:

merge branch ... of ... into xxx-2222

Два поля здесь заполняются:

  • что они называют их филиалом и
  • имя вы используете для разговора с ними

чтобы потом вы могли понять: О, это не было моим ххх-2222, это было их ххх-2222 . Это немного похоже на осознание того, что вы разговаривали не с Бобом Джонсом, а с Бобом Смитом.

Итак, давайте немного поговорим о наименовании вещей в Git.

Коммиты имеют уникальные (но безобразные) имена; люди используют названия веток

Существует ровно одно имя, на которое вы можете рассчитывать каждый раз, в любом и каждом репозитории Git. Это имя ID хэша . Хэш-идентификаторы - это большие уродливые строки шестнадцатеричных цифр, которые печатаются git log, например 8dca754b1e874719a732bc9ab7b0e14b21b1bc10. Эти идентификаторы уникальны и никогда не повторяются, так что вы либо do имеете коммит 8dca754b1e874719a732bc9ab7b0e14b21b1bc10 (который является коммитом в Git-репозитории для Git), либо нет (предположительно потому, что вы никогда не смешивались) ваш Git-репозиторий с одним для самого Git: и если вы не собираетесь писать код для модификации Git, зачем вам?).

Каждый коммит хранит снимок всех ваших файлов, а также некоторые метаданные. Метаданные включают в себя информацию о том, кто сделал коммит (имя, адрес электронной почты и отметку даты и времени), а также необработанный хэш-идентификатор коммита, который приходит до нового коммита. Это означает, что каждый коммит запоминает своего непосредственного предшественника или parent по необработанному хэш-идентификатору.

Что означает , что означает, что мы можем рисовать коммиты, используя стрелки, направленные назад, например:

... <-F <-G <-H

Здесь H обозначает идентификатор хеша последнего принятого нами коммита. Он запоминает хэш-идентификатор предыдущего коммита G. G в свою очередь запоминает хэш-идентификатор коммита F и т. Д.

Чтобы использовать коммит H, нам нужно запомнить его большой уродливый хеш-идентификатор. Нам не нужно больше помнить G, потому что это в H. Нам не нужно запоминать F, потому что мы можем использовать H, чтобы найти G, а G имеет хэш-идентификатор F. Этот шаблон продолжается и продолжается: все, что нам нужно сделать, это запомнить хэш-идентификатор last commit.

Но почему мы помним это, когда у нас есть компьютер? Мы можем * компьютер запомнить хэш-идентификатор. Например, давайте назовем имя master запомним идентификатор хеша коммита H, например:

...--F--G--H   <-- master

Сейчаскогда мы собираемся сделать новый коммит, мы начнем с git checkout master - который заставляет нас коммитить H для работы, и помнит, что мы «на ветке master», как git status сказал бы - и мы сделаем некоторую работу, git add и git commit.Git сохранит новый снимок всех наших файлов и создаст новый уникальный уникальный большой хадный идентификатор хеша, который мы просто назовем I.Новый коммит I запомнит хеш-идентификатор коммита H:

...--F--G--H
            \
             I

, а затем, как последний шаг создания коммита, Git будет сохранятьновый хэш-идентификатор в наше имя master, так что master указывает на I вместо H:

...--F--G--H--I   <-- master

I помнит H, который помнит G и так далее, так что все в порядке, чтобы наш master помнил только новый хэш-идентификатор для I.

Распространяется Git, что означает, что существует множество отдельных репозиториев

У вас есть два разных репозитория Git здесь.Одним из них является Git-репозиторий на вашем локальном компьютере, где вы запускаете git checkout и git add и git commit и так далее.Этот репозиторий действительно ваш: вы полностью контролируете его.

Другой на GitHub закончен.Технически это один их (GitHub's) репозиторий.Они передали вам большую часть контроля над ним, поэтому наиболее полезными способами он также принадлежит вам, но удобнее - и более технически точным - назвать его своим хранилищем, поэтому давайте сделаем это здесь.

Репозитории могут совместно использовать

Эти два репозитория не обязательно должны иметь одинаковые коммиты , но в целом вам, вероятно, нужны новые коммиты, которые вы делаете в вашем хранилище, чтобы войти в их хранилище.Если у них есть какие-то новые коммиты, которых нет у вас, вы, возможно, захотите включить их в свои.Чтобы сделать это, вы подключите свой Git к их Git и попросите их обмениваться коммитами.

Они будут делать это с помощью хеш-идентификаторов, потому что хеш-идентификаторы действительно универсальны.Их Git либо имеет некоторый хэш-идентификатор, либо его нет.Ваш Git имеет или не имеет некоторый хэш-идентификатор.Любой хеш-идентификатор, который у них есть, а у вас нет, - это какой-то объект, который вы можете получить от них, после чего вы оба получите его.Любое ваше удостоверение личности, которого у них нет, - это то, что вы можете им дать, после чего у вас обоих есть это.

Git очень сильно построен на идее добавления в репозитории.Очень легко добавить их вещи в ваш Git и добавить свои вещи в их Git.Чтобы получить их вещи в свой Git, вы запускаете git fetch (или git pull, который начинается с запуска git fetch).Чтобы добавить свои вещи в их Git, вы запускаете git push.

Иногда вам даже не хочется добавлять вещи вообще.Иногда вы хотите избавиться от некоторых коммитов!Вы можете сделать это, но Git не для этого построен , так что это сложнее.Мы увидим это чуть позже.

Имея все это в виду, давайте посмотрим на анатомию перебазировки

Что нужно знать о git rebase, это то, что он копирует фиксирует.То есть он берет какой-то существующий коммит, извлекает его, вносит некоторые изменения и делает новый коммит из этого.Этот новый коммит - это просто новый коммит, который оставляет старый коммит полностью незатронутым.

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

...--F--G--H--I   <-- master

, и вы сделали новую слегка отличающуюся копию I, родитель которой существует H, вы получите:

...--F--G--H--I
            \
             I'

где I'копия.Ни один из существующих коммитов не был затронут вообще.

Сложная часть: что происходит с именем ветви? Ну, в общем, причины, по которым мы используем git rebase:

  • , чтобы взять еСуществуют серии коммитов, которые в порядке, но они не основаны на правильной начальной точке, и копируют их так, чтобы они были на основе правильной начальной точки;или
  • , чтобы взять существующую серию коммитов, которые не совсем верны, и улучшить их.

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

То есть, у нас может быть:

...--F--G--H   <-- master
         \
          I--J--K   <-- feature

, где коммиты I до K просто хороши, но мыхотели бы, чтобы они пришли после H вместо G.Родители коммитов являются частью замороженных, неизменяемых коммитов, поэтому, чтобы получить то, что мы хотели бы получить, мы должны скопировать эти три в новые коммиты:

             I'-J'-K'   <-- feature
            /
...--F--G--H   <-- master
         \
          I--J--K   [abandoned in favor of the new improved feature]

Но предположим,что, прежде чем сделать это git rebase, мы поделились исходными тремя коммитами - с их уникальными большими уродливыми хэш-идентификаторами - с каким-то другим Git, например, с GitHub?Мы отправили им эти коммиты и сказали им установить их имя ветви feature, чтобы запомнить хеш-код K.Они сделали это, и у них в Git все еще есть эти три старых коммита, от которых мы бы хотели отказаться и от которых избавиться.

нашем Git, у нас, вероятно, еще естьимя прикреплено к коммиту K. Это имя origin/feature. График действительно должен быть:

             I'-J'-K'   <-- feature
            /
...--F--G--H   <-- master
         \
          I--J--K   <-- origin/feature

Но это не критическая часть.)

Предположим, что теперь у нас естьнаш Git вызывает Git - тот, что на GitHub, который мы называем origin или https://github.com/... или что-то еще - и говорит: Эй, вы, другой Git, скажите мне, какие у вас есть коммиты и какие имена выиспользуйте для них. Они скажут: У меня есть master, это коммит H.И у меня есть feature, это коммит K. Так что если мы действительно избавились от K, мы теперь повторно загрузим K (а также I и J, потому что мыдолжен получить всю цепочку).

Если мы запустим git fetch, мы обязательно получим I-J-K и обновим нашу метку origin/feature, чтобы мы знали, что их feature names commit K, которые мы сейчас / снова / до сих пор делимся.Но git pull не просто запускает git fetch.

git pull запускает git merge (по умолчанию в любом случае)

Второй шаг git pull по умолчаниюбежать git merge.Команда git merge принимает несколько аргументов, чтобы указать, что объединять, и git fetch предоставляет их.В этом конкретном случае - если приведенная выше иллюстрация соответствует вашему случаю - аргументы git merge будут следующими:

  • объединить в коммит K;
  • установить сообщение в результирующемобъединить с merge branch 'xxx-2222' of https://github.com/aaa/my_repo_name into xxx-2222

Эта часть - слить коммит K сейчас часть git pull - потому что после git fetch, ваш Git, имеет в вашем репо эти коммиты:

             I'-J'-K'   <-- xxx-2222
            /
...--F--G--H   <-- master
         \
          I--J--K   <-- origin/xxx-2222

Так что ваш Git покорно находит базу слияния коммитов K и K' (что является коммитом * 1326)*) и выполняет всю работу для выполнения и совершения слияния, давая вам:

             I'-J'-K'-------M   <-- xxx-2222
            /              /
...--F--G--H   <-- master /
         \       ________/
          I--J--K   <-- origin/xxx-2222

Теперь вы увидите, как выглядят две копии коммитов I, J и K- потому что K' действительно является копией K, а J' действительно является копией J и т. Д.

Вы, по сути, сказалиGit: Да, мне нравятся мои новые и улучшенные коммиты ... и мне нравятся мои старые, так что сделайте меня объединением, которое связывает оба набора и делает моим именем ветви xxx-2222указать на новый коммит слияния M.

То, что вы, вероятно, хотели, это запустить git push --force-with-lease

Вместо запуска git pull - или его двух компонентов, git fetch иgit merge - то, что вы, вероятно, хотели сделать, когда у вас было:

             I'-J'-K'   <-- xxx-2222
            /
...--F--G--H   <-- master
         \
          I--J--K   <-- origin/xxx-2222

должен был получить вашGit вызывает их Git (origin / GitHub's) и предлагает им ваши новые коммиты I', J' и K'.Это замены, которые вы сделали с git rebase, которые каким-то образом улучшают исходную последовательность I-J-K.Тогда вы хотели бы, чтобы ваш Git сказал этому другому Git: А теперь, я думаю, ваш xxx-2222 помнит K.Если это так, я командую , чтобы вы сделали свое имя xxx-2222, запомните commit K'!.

Если у вас есть Git, завершите эту операцию git push с: Теперь я вежливо прошу вас, GitHub-Git, установить ваше имя xxx-2222, чтобы оно запомнило commit K', они скажут: Нет, я не буду этого делать, потому что если я сделаю эточто, я откажусь от своих I-J-K коммитов. Но, конечно, это именно то, что вы хотите, чтобы они делали.

Риск здесь в том, что у них теперь может быть I-J-K-L, в их хранилище.То есть их xxx-2222 может помнить какой-то новый коммит L, который запоминает коммит K и так далее.Вы можете справиться с этим риском, используя эту опцию --force-with-lease.Это использует ваш origin/xxx-2222 - память вашего Git'а о его Git's xxx-2222 - чтобы сказать Я думаю, что ваш xxx-2222 это ... .

Вы можете использовать git push --force, чтоотбрасывает я думаю ... если так ... часть команды.Это более опасный, но даже более сильный тип git push, который может заставить их идти вперед и подчиняться вашей команде.

Заключение

Всегда важно помнитьнесколько вещей:

  • Как выглядит график коммитов?
  • Кто (какой Git) запоминает, какие хэш-идентификаторы коммитов, под какими именами?

Команда git push отправляет коммиты из вашего Git в другой Git, а затем просит или дает им команду установить некоторые из их имен для запоминания некоторого хеш-идентификатора коммита (по одному хеш-идентификатору на имя).Команда git fetch получает коммиты в ваш Git от другого Git, а затем устанавливает ваши origin/* или другие remote-tracking имена на основе того, что ваш Git видел из их Git.

Этине полностью симметричны!С git fetch ваши имена для удаленного отслеживания обновляются, но это никак не влияет на ваши имена ветви.С git push их имена branch обновляются - или они отклоняют ваш вежливый запрос, потому что обновление потеряло бы некоторые коммиты.

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

Обратите внимание, что когда вы делаете это - когда вы используете git push --force или git push --force-with-lease -вы говорите один Git-репозиторий, чтобы потерять некоторые коммиты.Что если эти коммиты распространились в больше Git-репозиториев?Каждый, кто запускал git fetch в репозиторий GitHub, подобрал все новые коммиты, которые они могут получить из этого репозитория.Ваши старые и не очень хорошие коммиты могут теперь распространяться повсюду, и от них трудно избавиться.Убедитесь, что все, кто может их использовать, понимают, что вы намеревались отозвать и заменить их!

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