Как вытащить изменения из ветки в master в подотрасль? - PullRequest
1 голос
/ 07 апреля 2020

Итак, у меня есть ветка B1, которая является веткой master. Позже я создал ветку B2 из ветви B1. Однако, когда я выполняю git извлечение в B2, я не извлекаю изменения из ветви B1. Как вытащить изменения из B1? Спасибо.

Ответы [ 2 ]

0 голосов
/ 07 апреля 2020

Я рекомендую избегать git pull полностью, по крайней мере (или особенно), если вы только начинаете с Git. Все, что git pull делает, это запускает git fetch, а затем запускает для вас вторую команду Git. Проблема в том, что это затемняет то, что происходит на самом деле, мешая вам узнать то, что вам нужно знать.

То, что вам нужно знать, начинается с того, что на самом деле делает и делает коммит, потому что Git это все о коммитах , Git не относится к файлам, хотя фиксация содержит файлов; и даже не совсем о ветвях, хотя ответвления names вроде b1 и b2 помогут вам - или, по крайней мере, Git - find commits. Но в то время как коммиты хранят файлы, а имена веток содержат коммиты с идентификаторами га sh, Git действительно все о коммитах .

Итак, каждый коммит:

  • Содержит снимок всех ваших файлов. Это полный снимок, а не изменения с момента предыдущего коммита.
  • Содержит некоторую дополнительную информацию - некоторые метаданные - такие как ваше имя и адрес электронной почты, и почему вы сделали сделанный вами коммит Большая часть этого предназначена только для других людей, но в метаданных есть ключевой элемент, предназначенный для Git, к которому мы вскоре перейдем.
  • Имеет идентификатор ha sh. Этот ха sh ID представляет собой большую уродливую строку букв и цифр. Это выглядит случайным, хотя это совершенно не случайно. Фактически, идентификатор ha sh - это криптографическая c контрольная сумма полного содержимого коммита: файлы-как-снимок и метаданные.

Результатом всего этого является что когда-то сделанный, никакая часть любого коммита никогда не может измениться. Вы можете взять все из коммита, внести в него изменения в другом месте, а затем сделать новый коммит из результата, но если вы изменили хотя бы один бит где-либо - ваше имя, ваше сообщение журнала, один бит какого-то источника файл, что угодно - что вы получаете, это новый и другой коммит. Так что commit ha sh ID гарантированно найдет один конкретный коммит, и каждый Git использует один и тот же алгоритм ha sh, поэтому все Git везде используют одинаковые идентификаторы ha sh для одного и того же commitits.

Как растут ветви, или нет такой вещи, как подветвление

Следующее, что вам нужно знать, это то, что нет такой вещи, как подветвь.

Учитывая, что каждый коммит имеет свой уникальный идентификатор ha sh, у нас все еще остается проблема: как нам найти коммитов? Идентификаторы ha sh выглядят случайными и, конечно, не в каком-либо полезном порядке. Именно здесь приходят внутренние метаданные Git и имена ветвей.

Каждый коммит содержит, как часть своих метаданных, список родительского коммита ха sh идентификаторов , Обычно здесь есть только один га sh ID. Этот идентификатор ha sh является (единственным) родителем этого коммита. Это связывает все коммиты, один за другим, в обратном направлении:

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

, где H обозначает идентификатор ha sh последнего последнего коммита в цепочке. Коммит H сам записывает фактический идентификатор ha sh предыдущего коммита G, поэтому, заглянув внутрь H, мы можем найти число - идентификатор ha sh, которое позволяет нам найти G. В коммите G хранится идентификатор ha sh предыдущего коммита F и т. Д.

Теперь мы уменьшили проблему до поиска коммита H. Мы делаем это с именем ветви , например master или b1. Мы сохраняем необработанный га sh идентификатор коммита H в имя , что дает нам:

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

Каждое имя ветви содержит ровно один коммит га sh ID. Если мы создадим вторую ветвь b2, мы должны выбрать один из различных существующих коммитов и заставить b2 сохранить этот идентификатор ha sh. Как правило, мы можем выбрать текущий коммит , т. Е. Тот, который имеет га sh H:

...--F--G--H   <-- b1, b2

Теперь нам нужно запомнить, какое имя мы Используешь. Git использует для этого специальное имя HEAD (во всех прописных буквах). Есть несколько способов сделать это; здесь я просто прикреплю HEAD в скобках к одному из следующих названий веток:

...--F--G--H   <-- b1 (HEAD), b2

или:

...--F--G--H   <-- b1, b2 (HEAD)

На этом этапе давайте сделаем новый коммит, в обычный способ: изменить что-то, git add и git commit. Git создает снимок для нового коммита из git add индекса Git - кое-что, что мы не будем здесь описывать должным образом, но этот индекс - то, почему вы должны запускать *1099* все время - и соответствующие метаданные, включая сообщение журнала, которое вы должны написать. Этот новый коммит получает новый уникальный идентификатор ha sh. Данные для нового коммита - это новый моментальный снимок, а метаданные - это то, что вы сказали, кроме родителя: это происходит из того, что знает Git, а именно: текущий коммит равен H в в этот раз. Таким образом, новый коммит I указывает на существующий коммит H:

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

, а теперь Git обновляет любое имя ветви HEAD присоединяется к . Если это b2, то имя b2 теперь содержит га sh идентификатор коммита I:

...--F--G--H   <-- b1
            \
             I   <-- b2 (HEAD)

Обратите внимание, что коммиты через H теперь находятся на в обеих ветвях , в то время как commit I только на b1. Имя HEAD остается прикрепленным к текущей ветви, но теперь текущий коммит является коммитом I.

Слияние

Предположим, вы начинаете с:

...--G--H   <-- master (HEAD)

и затем создайте имена b1 и b2, оба указывают на существующий коммит H. Затем вы выбираете b1 для работы с git checkout b1 и делаете два коммита:

          I--J   <-- b1 (HEAD)
         /
...--G--H   <-- master, b2

Обратите внимание, что b2 еще не перемещен. Теперь вы запускаете git checkout b2, чтобы снова выбрать коммит H. Снимки, которые вы сделали для I и J, все еще там, навсегда заморожены, но теперь вы снова работаете со снимком из H. Теперь вы можете сделать пару коммитов здесь на b2, что дает:

          I--J   <-- b1
         /
...--G--H   <-- master
         \
          K--L   <-- b2 (HEAD)

Обратите внимание, что на данный момент вы можете git checkout освоить и сделать новые коммиты там:

          o--o   <-- b1
         /
...--o--o--o--o--o--o   <-- master (HEAD)
         \
          o--o   <-- b2

Мы пока не будем беспокоиться об этом, но учтите, что ничего не произошло с любым существующим коммитом или любым другим именем ветки . Каждый раз, когда мы добавляли новые коммиты, текущее имя ветви перемещалось. Новые коммиты всего лишь добавлены к существующему графу пока что все коммиты. Абсолютно невозможно изменить какой-либо существующий коммит, но мы этого не сделали. Все, что мы сделали, это добавили новые коммиты, которые ссылаются на существующие, а затем изменили имена ветвей .

Давайте go вернемся к идее слияния , хоть. В конце концов, мы не сделали никаких новых master коммитов, и если мы git checkout b1 и временно прекратим рисовать в name master (потому что это будет мешать), мы имеем:

          I--J   <-- b1 (HEAD)
         /
...--G--H
         \
          K--L   <-- b2

Теперь мы можем запустить git merge b2 или git checkout b2; git merge b1. Git теперь сделает все возможное, чтобы объединить работу , потому что git merge - это объединение работы.

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

Здесь совершенно очевидно, какой коммит является best common commit. Это коммит H. Поэтому Git теперь будет сравнивать снимок в H со снимком в том коммите, который мы выбрали с помощью нашего git checkout - к которому прикреплен коммит HEAD. Если мы предположим, что это b1, фиксация для этого сравнения будет J:

git diff --find-renames <hash-of-H> <hash-of-J>   # what we changed

Это говорит Git, что "мы" изменили на b1 относительно общей начальной точки. Затем Git различает другую пару:

git diff --find-renames <hash-of-H> <hash-of-L>   # what they changed

Это сообщает Git, что "они" изменились на b2 относительно той же начальной точки.

Операция объединения Теперь объединяет эти изменения, файл за файлом. Если мы изменили строку 42 main.py, а они вообще не коснулись main.py, Git потребует нашего изменения. Если они тоже коснулись main.py, но не строки 42, Git принимает , их также меняется. Если бы мы оба коснулись строки 42, мы бы лучше изменили ее таким же образом . Если нет, Git объявляет конфликт слияния и оставляет нас в беспорядке.

Предполагая, что конфликтов слияния нет - приведенный выше список не является полным списком возможных конфликтов, только очевидным kind - Git сможет объединить два набора изменений и применить все эти изменения ко всем файлам, как они появляются в commit H. Таким образом, новый коммит, который Git собирается сделать, имеет наши изменения с J, но также имеет их изменения с L.

В этот момент Git делает новый коммит. Как обычно, новый коммит имеет моментальный снимок: это тот, который Git построен путем объединения изменений. Как обычно, новый коммит имеет родителя; так как мы находимся на ветке b1, этот родитель J. Но - не как обычно - новый коммит также имеет второго родителя. Так как мы сказали Git слить коммит L, этот второй родительский элемент L.

Сделав коммит, Git перетаскивает наше текущее имя ветви вперед:

          I--J
         /    \
...--G--H      M   <-- b1 (HEAD)
         \    /
          K--L   <-- b2

и это результат запуска git merge.

Git распространяется

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

Мы уже видели, что в любом хранилище мы находим последний коммит какой-либо ветки по имени ветки. Таким образом, их Git - тот, что на GitHub, или что-то еще - имеет некоторые имена веток:

...--o--o   <-- master
         \
          o--o--o   <-- develop

и так далее. Когда вы клонируете их хранилище, вы получаете все их коммитов с их уникальными идентификаторами ha sh, которые остаются с этими идентификаторами ha sh. Но вы не получите их названий ветвей . Это их . Ваш Git берет их имена и изменяет их, так что вы можете иметь свои собственные имена ветвей.

Изменения, которые ваш Git вносит в свои имена ветвей, превращают их имена в ваши имена для удаленного отслеживания. . Эти имена выглядят как origin/master и origin/develop. Они помнят, в вашем хранилище, где их ответвления имена были:

...--D--E   <-- origin/master
         \
          F--G--H   <-- origin/develop

Для последнего шага вашего git clone, ваш Git создает новое имя ветви, указывая на тот же коммит, что и одно из их имен ветви. Вы можете выбрать, какая это ветка, но обычно это просто master, так что вы получите:

...--D--E   <-- master (HEAD), origin/master
         \
          F--G--H   <-- origin/develop

Если вы сейчас go делаете новые коммиты, вы получаете это:

          I--J   <-- master (HEAD)
         /
...--D--E   <-- origin/master
         \
          F--G--H   <-- origin/develop

Теперь предположим, что они , кем бы они ни были, сумели создать два новых коммита на их master. Ваш Git еще не имеет этих коммитов, но вы можете их получить.

Вы запускаете:

git fetch origin

(или просто git fetch, по умолчанию origin). Ваш Git снова вызывает их Git. Они сообщают вашему Git об именах их ветвей (и других имен), а ваш Git обнаруживает, что gosh, есть новые коммиты K-L, которые можно получить от них. Так делает ваш Git, а затем ваш Git обновляет ваш origin/master соответственно:

          I--J   <-- master (HEAD)
         /
...--D--E--K--L   <-- origin/master
         \
          F--G--H   <-- origin/develop

(я предположил, что они не добавляли новые коммиты в свои develop, в противном случае мы ' теперь у нас есть новые коммиты origin/develop.

Итак, вот в чем смысл git fetch: ваш Git вызывает некоторые другие Git и получает новых коммитов с этого Git (если есть), а затем обновляет ваши имена для удаленного отслеживания . После этого git fetch у вас могут быть новые коммиты. Что если вы хотите использовать их?

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

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

git merge origin/master

Это сделает новый коммит слияния M самостоятельно, если это возможно, объединяя ваши J с их L, точно так, как мы видели выше. Неважно, что коммит L найден по имени remote-tracking вместо имени branch . Git не заботится о именах; Git заботится о коммитах . Итак, мы получаем:

          I--J--M   <-- master (HEAD)
         /     /
...--D--E--K--L   <-- origin/master
         \
          F--G--H   <-- origin/develop

Здесь git pull входит

Команда pull просто объединяет две команды в одну:

  • run git fetch получать от них новые коммиты; затем
  • выполните вторую команду Git для включения этих новых коммитов.

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

Запутанная часть заключается в том, что когда вы указываете, что использовать в команде git pull, вы должны используйте их имена ветвей, даже если ваш Git будет работать с вашими именами удаленного отслеживания в ближайшее время. Поэтому, если вы хотите извлечь из origin, затем объединить с тем, что они называют b2, вам нужно:

git pull origin b2

Это запускает ограниченный вид git fetch, а затем сливается с любым коммитом, который они ' переименование через их b2, что ваши Git имена через ваш origin/b2. Пропавший sla sh здесь, потому что первый шаг - git fetch - должен знать , что Git, чтобы вызвать (тот, в origin) и второй step должен знать , какое из названий ветвей использовать .

Оба эти шага, git fetch и git merge, могут завершиться неудачно. Весьма маловероятно, что git fetch потерпит неудачу (и git pull остановится, если это произойдет), но если вы оставите их как две отдельные команды, вы сможете определить, какая из них завершилась неудачей, если одна из них сделала это. Более того - и для меня, что гораздо важнее - вы можете посмотреть, что git fetch принес до , на котором вы запускаете git merge. И, наконец, не так важно, как только вы это узнаете, вы поймете, что это две совершенно отдельные операции в Git: fetch получает коммиты; merge объединяет работу .

Безопасно запускать git fetch в любое время из любой ветки. Fetch просто вызывает другой Git и получает от них коммиты. Это не касается ничего, что вы делаете сейчас. Не так безопасно запускать git merge в любое время: это объединяет что-то с вашей текущей веткой , и у вас есть только одна текущая ветка в любой момент. Если у вас есть много веток для обновления, вы можете git fetch и обновить все свои имена для удаленного отслеживания сразу, но затем вам придется, по одной, git checkout ветки, которые вы хотите обновить, и git merge каждая во-первых, с commit , с которым вы хотите объединиться - возможно, из имени удаленного отслеживания для этой ветви, но поскольку вы можете посмотреть, вы можете сначала проверить, действительно ли вы это делаете хочу это слияние.

Отправка коммитов

Теперь у вас есть коммиты I-J-MM, имеющим двух родителей, первый из которых J, а второй L) что они не имеют. Чтобы отправить им ваши коммиты, вы можете использовать git push, если они дают вам разрешение. (Примечание: эти разрешения контролируются программным обеспечением хостинга, а не самим Git.) Запуск:

git push origin master

имеет ваш Git вызов их Git, дайте им коммиты, которые у вас есть, что они не нужно, что им понадобятся - в данном случае I, J и M - и затем попросят указать им ответвление имя. Обратите внимание, что вы не просите их установить имя для удаленного отслеживания. В этом направлении нет имен для удаленного слежения! Вы просто просите их установить имя branch напрямую.

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

          I--J--M   <-- master (HEAD)
         /     /
...--D--E--K--L--N   <-- origin/master
         \
          F--G--H   <-- origin/develop

, что означает, что их master теперь именуют коммит N, который не существовал ни минуты. go. Возможно, вам понадобится слить снова, или, возможно, вы захотите удалить ваш коммит M полностью (что иногда немного сложно) и сделать новое слияние O, которое включает N на этот раз.

0 голосов
/ 07 апреля 2020

Вы можете объединить или переместить ветвь b1 в ветвь b2.

git checkout b2
git merge b1

или

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