Разница между двумя методами git rebasing - PullRequest
0 голосов
/ 25 апреля 2018

Я хотел бы выполнить обновления от мастера в мою локальную ветвь, которая ранее была разветвлена ​​из мастера на временной оси (из изменения M2). Мастер обозначен M изменениями, мой локальный филиал L изменениями

Создана новая ветвь из мастерской M2:

 M1->M2-->M3->M4
      \
       L1->L2

Полагаю, мой результат для моего местного филиала должен быть следующим:

M1->M2->M3->M4->L1->L2

Что означает воссоздание моей локальной ветки, чтобы сначала были внесены все основные изменения, и только потом моя локальная ветвь меняется поверх нее, как указано в:
https://www.atlassian.com/git/tutorials/merging-vs-rebasing (поправьте меня, если я ошибаюсь)

У меня вопрос, не является ли один из следующих методов созданием вышеуказанного желаемого потока, и если да, то почему?

git checkout master
git pull --rebase 
git checkout branch_to_update
git rebase master` (method mentioned in attlasian tautorial)

VS

git checkout branch_to_update
git pull --rebase origin master

Ответы [ 3 ]

0 голосов
/ 25 апреля 2018
git checkout branch_to_update
git rebase master

или

git checkout branch_to_update
git pull --rebase origin master

это один и тот же результат, но разные способы pull --rebase параметр кратко с использованием rebase

0 голосов
/ 25 апреля 2018

Как и в обоих других ответах, эффект, как правило, практически одинаков. Мсэнфорд указал на одну определенную и одну разность потенциалов , но есть и другие. Чтобы увидеть, что и почему, мы должны разобрать git pull на составляющие.

Все привередливые детали (предупреждение: длинные)

За некоторыми незначительными исключениями (такими как запуск в совершенно пустом хранилище), git pull означает:

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

Вторая команда обычно git merge, но вы можете указать Git использовать git rebase. Параметры и аргументы, передаваемые двум командам, зависят от параметров, переданных в git pull, и других параметров конфигурации, а также от результата или результатов выборки на шаге 1.

Однако, как своего рода общее правило, аргументы, переданные в git pull, передаются в git fetch, поэтому это означает, что ваша вторая последовательность команд, которая передает origin master в git pull, передает origin master в git fetch. Если вы запускаете git pull без этих аргументов, как в вашей первой последовательности команд, Git извлекает remote (обычно origin) и имя восходящей ветви (обычно совпадает с текущим именем ветви) из вашей конфигурации, в частности, по результатам этих двух команд: 1

git config --get branch.$branch.remote
git config --get branch.$branch.merge

(где $branch - текущая ветвь). Если текущая ветвь имеет значение master, в качестве пульта используется branch.master.remote. Это то, что мы имеем в виду, предполагая, что есть только один пульт. merge имя, вероятно, master, но если нет, это еще одно предположение, которое мы должны сделать, прежде чем мы сможем утверждать, что они делают то же самое.


1 , если ваш Git достаточно стар, git pull - это скрипт оболочки, который буквально выполняет различные другие команды Git. Если он более новый, git pull был преобразован в программу на языке C, и он встроен непосредственно в него.


Rebase копии фиксирует, затем переключается на новые копии

То, что делает git rebase, усложняется, если мы углубляемся во все детали, но на высоком уровне его работа заключается в copy commits. Чтобы увидеть, какие коммиты будут копироваться, вы должны нарисовать график коммитов или использовать git log --graph, чтобы Git нарисовал его для вас. (Некоторые графические пользовательские интерфейсы всегда рисуют его, а некоторые веб-интерфейсы * кашель * GitHub * кашель * никогда не позволяют просматривать его!) С графическим рисунком это легко - ну, иногда легко - сказать, какие коммиты копируются:

...--A--B--C--D   <-- master
         \
          E--F--G   <-- br

Перебазирование вашей ветви br на ваш мастер копирует три коммита, здесь E - G, размещая копии после коммита D. Это похоже на то, что вы нарисовали.

Предположим, мы добавили origin/ имен для удаленного слежения и покажем, что ваш собственный master в данный момент указывает на фиксацию B, в то время как origin/master в данный момент указывает на фиксацию D, например:

          C--D   <-- origin/master
         /
...--A--B   <-- master
         \
          E--F--G   <-- br

Теперь мы видим, что мы должны перебазировать br на origin/master, чтобы копии шли после коммита D. Перебазирование на master приведет к копированию после B, где находятся оригиналы, поэтому копировать все равно не нужно. (То, является ли rebase копированием или повторным использованием оригиналов, является одной из самых хитроумных деталей: например, это зависит от опции -f.)

Как только копирование завершено, git rebase просто повторно указывает имя ветки, чтобы указать на окончательно скопированный (или повторно использованный) коммит, который мы можем назвать здесь G', чтобы отметить, что это копия * 1096. *. Первоначальные коммиты фактически отменяются, хотя записи журнала для HEAD и исходной ветки и имя ORIG_HEAD временно сохраняют их:

               E'-F'-G'  <-- br
              /
          C--D   <-- origin/master
         /
...--A--B   <-- master
         \
          E--F--G   [abandoned, but see ORIG_HEAD and reflogs]

По умолчанию записи журнала сохраняют оригиналы доступными не менее 30 дней. В конечном итоге ORIG_HEAD перемещается в другое место из-за других операций, и срок действия записей reflog истекает, а исходные коммиты собираются сборщиком мусора.

Теперь мы можем посмотреть на ваши оригинальные последовательности команд

LeПредположим, что у нас есть приведенный выше график (как у вас, но с еще одним коммитом на ветке br, и мы уже запустили git fetch, чтобы обновить origin/master). Затем последовательность команд Atlassian начинается с этих двух команд:

git checkout master
git pull --rebase 

Это прикрепит наш HEAD к нашему master, проверяя коммит B; затем, предполагая, что восходящий поток равен origin/master, запустите git fetch origin master, чтобы обновить наш origin/master, что в этом случае оставляет origin/master, указывая на D. Если бы мы еще не запустили git fetch, то получило бы коммитов C и D и указало бы origin/master на D.

Последнее, это будет работать git rebase <hash-of-commit-D>. Операция rebase использует хеш-идентификатор, потому что использует трассировки, которые git fetch оставляет в .git/FETCH_HEAD, и, в зависимости от точной версии Git и других деталей, которые мы здесь проигнорируем, также использует git merge-base --fork-point, чтобы найти хеш коммита, чтобы оправиться от перебазирования вверх по течению. (Этот процесс иногда идет не так, в зависимости от вашего рабочего процесса, и я не уверен, что мне нравится поведение по умолчанию.)

Как только это будет сделано, мы перейдем к двум последним командам:

git checkout br
git rebase master

Первый присоединяет HEAD к имени br, проверяя коммит G. Затем ребаз копирует последовательность коммита E-F-G, которая идет после коммита, на который теперь указывает master. Итак, игнорируя все записи reflog, мы получаем график:

                E'-F'-G'  <-- br (HEAD)
               /
...--A--B--C--D   <-- master, origin/master
         \
          E--F--G   [abandoned]

Сравните это с вашей более короткой последовательностью команд:

git checkout br
git pull --rebase origin master

Оформление заказа прикрепляет HEAD к br. pull запускает git fetch origin master, что гарантирует, что у нас есть коммиты C-D (если мы их еще не получили) и обновляет origin/master (если наш Git по крайней мере 1.8.4), затем запускается git rebase <hash-of-D>, который копирует цепочку E-F-G, давая:

               E'-F'-G'  <-- br
              /
          C--D   <-- origin/master
         /
...--A--B   <-- master
         \
          E--F--G   [abandoned]

Итак, ключевое отличие в том, что ваше собственное имя, master, никогда не обновляется, чтобы указывать на коммит D.

Что я рекомендую вместо

Важно отметить (и знать), что если вы запускаете git fetch самостоятельно - это мой предпочтительный метод - это скажет вашему Git вызывать другой Git по URL-адресу пульта и иметь другой список Git для вашего Git, все его (мы предполагаем, происхождения) ветвей. Затем ваш Git получит все коммитов, которых у вас нет, и поместит их в репозиторий вашего Git, и обновит все ваших имен для удаленного отслеживания, таких как origin/master и origin/develop и т. Д.

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

Когда git pull запускает git fetch, он запускает его с ограничивающим параметром. Например, если ваш git pull работает:

git fetch origin master

, который говорит вашему Git вызвать Git по URL-адресу для origin и попросить их передать только фиксирует новое для их master. Если у них есть обновления к develop и production и feature/tall и т. Д., Вы не получите ни одного из них - вы получите только новые коммиты, которые находятся на их master. Ваш Git обновляет ваш origin/master для запоминания новых коммитов, 2 , но оставляет остальные ваши имена для удаленного отслеживания без изменений.

Во второй последовательности команд вы запускаете явный git pull origin master (также с --rebase), так что это ограничивает ваш Git обновлением origin/master. В вашей последовательности команд first вы запускаете git pull без аргументов, но git pull вставляет origin и master, предполагая, что это настроенные параметры для вашей ветви master, так что это также ограничивает ваш Git обновлением только вашего origin/master.

Я упоминаю все это, потому что я рекомендую вообще не использовать git pull. Запустите git fetch самостоятельно - вы можете разрешить ему по умолчанию извлекать все из origin - и затем запускать любые команды git rebase, которые вы хотите! После fetch у вас есть все коммиты и все соответствующие origin/* имена; Затем вы можете запустить:

git checkout <whatever-name>
git rebase origin/<whatever-other-name>

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


2 Это предполагает, что ваш Git по крайней мере версии 1.8+0,4.Если нет, то git fetch не может обновиться даже origin/master.Вы должны запустить git fetch или git fetch origin, чтобы обновить имена для удаленного отслеживания!

0 голосов
/ 25 апреля 2018

Если есть только один удаленный репозиторий , эти два будут иметь одинаковый эффект.

В первом случае вы обновляете локальную копию master, а затем перебираете.

Во втором случае вы перебираете непосредственно из удаленного репозитория .

Используйте второй вариант, если вам не нужно обновлять локальную копию.ветки, из которой вы отказываетесь.

Например, у нас есть основная ветка develop, из которой мы создаем тематические ветки, например, feature/0001.Во время работы я проверяю feature/0001 и время от времени просто git pull -r origin develop.В этом случае локальная актуальная копия develop не имеет значения.

После моя функциональная ветвь объединена, я извлекаю и извлекаю develop, а затем создаюновая ветвь feature/0002 из этой обновленной копии.


Кроме того, обратите внимание, что она фактически создаст this в результате:

M1 -> M2 -> M3 -> M4 -> L1' -> L2'

Что делатьЯ имею в виду под L1'?Грубо говоря, он создаст новый коммит - с новым идентификатором SHA - с тем же содержимым.Так что это не тот же коммит как таковой .

...