Какой локальный репозиторий содержит мой код «origin / development» или «velop » - PullRequest
0 голосов
/ 04 мая 2020

Используя git -flow и VSTS, когда я «клонирую» из браузера «разрабатывать», обновляется ли моя локальная ветка: «происхождение / разработка» или «разработка»? Кажется, у меня противоречивые результаты ...

Ответы [ 2 ]

2 голосов
/ 04 мая 2020

origin/develop - это ветвь удаленного отслеживания . Он представляет состояние ветви develop в хранилище в восходящем направлении.

Когда вы выбираете, он переходит в origin/develop. Когда вы тянете, он извлекается в origin/develop, затем сливается origin/develop в develop. Вы должны работать в develop.

0 голосов
/ 05 мая 2020

Краткую версию см. Ответ Даниэля Манна . Я собираюсь угадать каков ваш настоящий вопрос:

, почему иногда git merge (или git pull) оставляют меня "на шаг впереди", а иногда не?

TL; DR: потому что git merge (или git merge, который запускается git pull) может выполнять одно из двух действий.

Фон (длинный )

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

Какой локальный репозиторий содержит мой код ...

Дословный ответ на этот вопрос ваш (один) локальный репозиторий . Это не правильный вопрос!

Возможно, вы захотите спросить, какая (локальная) ветвь принадлежит вам, но это опять не тот вопрос: в Git, всех именах ветвей являются местными. Хитрость в том, что имя origin/develop не является именем ветви . Вместо этого это имя для удаленного отслеживания (или то, что Git называет имя для удаленного отслеживания - я думаю, что это немного менее запутанно, если мы просто отбросим слово ответвление , которое из-за чрезмерного использования к этому моменту уже стало бессмысленным).

Чтобы попытаться сократить это, я пропущу здесь много деталей и неточностей:

  • Git на самом деле не о ветвях. Git это все о коммитах . Это единица хранения, с которой вы будете работать.

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

  • Gits делятся коммитами друг с другом по этим идентификаторам ha sh. Любой репозиторий Git может определить, есть ли у него фиксация, поскольку идентификатор ha sh уникален во всем мире. Либо у вас есть этот ha sh ID, в этом случае у вас есть коммит, либо у вас его нет, поэтому у вас его нет.

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

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

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

Если H - это last commit, мы можем используйте его, чтобы найти G, который мы можем использовать, чтобы найти F, и так далее. (Буквы здесь обозначают действительные идентификаторы ha sh, которые выглядят случайными и слишком сложными для работы с людьми.)

Эти обратные цепочки коммитов называются или могут быть названы ветви. Но ветви имена , такие как master и develop, также можно назвать ветвями. Имена удаленного слежения можно также назвать ветвями. См. Что именно мы подразумеваем под "ветвью"?

A имя ветви подобно master или develop просто содержит идентификатор ha sh последний коммит в цепочке. Чтобы добавить новый коммит, ваш Git запишет новый коммит, указывая назад на предыдущий последний коммит, а затем запишет идентификатор нового коммита ha sh в имя вашей ветви.

Ваша ветка все имена твои . Ваш develop является исключительно вашим . Ваш Git будет обновлять его по мере добавления новых коммитов. Ваш Git может также обновить имя вашей ветви с помощью git merge и других команд.

Другой используемый вами репозиторий Git имеет URL-адрес. Этот URL хранится под именем , которое Git называется remote . Стандартное имя первого удаленного пользователя всегда origin, поэтому origin - это remote . Команда git clone устанавливает это автоматически, поэтому origin является стандартным первым пультом.

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

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

Здесь есть еще один хитрый момент. Когда вы запускаете исходный git clone, ваш Git:

  • создает новый пустой репозиторий (без коммитов, без веток, ничего);
  • устанавливает имя origin для хранения URL;
  • использует git fetch для получения всех их коммитов и имен веток, переименовывая все их имена веток в ваши имена для удаленного слежения: это где ваши начальные origin/master и origin/develop родом из; и
  • последний, запускается git checkout. Это создает новую ветвь , потому что в данный момент у вас вообще нет ветвей.

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

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

ваш Git просто созданный ваш master (и прикрепленный HEAD к нему), указывающий на фиксацию E, некоторые коммиты Вы попали в первоначальный выбор. Их master, в своем хранилище, фиксируют имена E, поэтому ваши новые master также фиксируют имена E.

Ваш Git переименовал все их имена ветвей в сделайте ваши origin/* имена для удаленного слежения. Таким образом, ваши origin/master баллов за фиксацию E, а ваши origin/develop баллов за фиксацию H.

(Кстати, коммиты до E теперь на оба ветки. Это то, что свойственно Git: большинство систем контроля версий говорят, что коммит находится на одной ветви, но в Git любая фиксация может быть на много ветки. Фиксация найдена, начиная с имени ветки, переходя к его последнему коммиту, а затем работая в обратном направлении. Фиксация E - это последняя фиксация master, но она также определяется путем перехода к H - последний коммит develop - и затем работает в обратном направлении на три шага, поэтому E находится на обеих ветвях. Все коммиты до E также находятся на обеих ветвях.)

Если вы сейчас запускаете git checkout develop Ваш Git создает новое develop имя. Ваш Git пытается проверить существующий develop, видит, что его нет, и выдает ошибку - но подождите, есть origin/develop! Ваш Git теперь делает ставку на то, что вы хотели создать develop, используя тот же коммит, обозначенный origin/develop. Таким образом, ваш Git делает это, а затем присоединяет ваш HEAD к этому имени:

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

Ваш Git извлечет все файлы из снимка для коммита H в рабочее пространство, которое Git вызывает ваше рабочее дерево . (Я опускаю все, что касается Git index , что действительно важно.)

Теперь вы можете делать новые коммиты, если хотите, обычным способом. , (Это касается индекса Git, что является одной из причин, по которой это так важно.) Допустим, вы делаете - что вы делаете один новый коммит, который мы назовем I.

Родитель нового обычный коммит I будет коммитом H: I будет указывать на H. Git запишет I действительный идентификатор ha sh в имя develop, поскольку HEAD присоединен к develop. Результат выглядит следующим образом:

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

Если вы добавите новый коммит, который мы назовем J, Git продолжит добавлять как обычно:

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

На этом этапе Вы можете задаться вопросом, сделал ли кто-нибудь какую-то работу и отправил ли новые коммиты другому Git в origin. У вас их еще нет, но теперь вы можете Git вызвать их Git и получить их:

git fetch origin

Ваш Git вызывает их Git (используя URL-адрес, сохраненный в имени origin), спрашивает их об именах веток и последних коммитах, находит, что их develop указывает на новый коммит с идентификатором ha sh, который есть у вашего Git никогда не слышал, и просит их для новых коммитов. Они говорят У меня есть какой-то коммит ______ (какой-то большой уродливый ха sh ID): мы назовем это L. Они говорят Родитель L - это _____ (какой-то большой уродливый га sh ID): мы назовем это K. Они говорят Родитель K - H, и, эй, у нас уже есть коммит H. Таким образом, у вашего Git есть Git отправка по коммитам K и L (только), которые ваш Git помещает в ваш репозиторий.

Затем ваш Git обновляет ваш origin/develop помнить, что их develop указывает на фиксацию L. Итак, теперь у вас есть:

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

в вашем хранилище. Обратите внимание, что ваш develop не затронут . Операция git fetch обновляет только ваши имена для удаленного отслеживания . Это не влияет ни на одно из ваших названий ветвей , потому что ваши имена ветвей ваши , а не их.

Обработка расхождений с помощью git merge

Теперь, когда у вас есть такая ситуация:

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

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

Если вы хотите запустить git merge, вы можете сделать это сейчас:

git merge origin/develop

Это объединяет работу и, если возможно, делает новый коммит слияния самостоятельно:

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

Новый коммит идет на вашу ветку, как любой другой новый коммит. У него есть снимок, как и у любого другого нового коммита. (Снимок - это тот, который Git сделан путем выяснения того, что вы сделали с H, и того, что они сделали с H, и объединения их и выполнения с снимком с H.) У него есть автор и метаданные коммиттера, как и любой другой коммит. Автором и коммиттером, а также отметками даты и времени являются you: вы сделали этот коммит слияния. Он имеет сообщение журнала, как и любой обычный коммит. Здесь есть сообщение низкого качества по умолчанию; Вы можете добавить лучшую, но на практике никто не беспокоится:

merge branch 'origin/develop' into develop

Единственное, что особенного в этом новом коммите слияния M, это то, что он имеет не один, а два родители. Один из родителей - ваш предыдущий коммит J. Это тоже первый родитель, что важно позже (но не сейчас). второй родитель - это фиксация ваших origin/develop имен, то есть фиксация L.

Слияние не всегда должно объединяться

Иногда вы этого не делаете есть расхождение. Например, предположим, что вы не не сделали никаких новых коммитов - с которых вы начали:

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

и застряли с этим. Затем вы набрали git fetch, и они сделали два новых коммита, которые мы назовем I-J, как и раньше:

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

Теперь вы можете запустить git merge origin/develop, как и раньше. Ваш Git заметит, что основа для этого слияния, коммит H, также является верхушкой вашего develop. То есть, если бы мы сделали настоящее слияние, мы бы объединили то, что мы сделали, H -vs- H (ничего), с тем, что они сделали, H * 1357-vs *. Результат, очевидно, будет соответствовать J: ничего не делать, а затем что-то делать, что приведет к тому, что "что-то сделал".

Ваш Git по умолчанию не сделает что-нибудь, чтобы объединить «ничто» с «чем-то». Вместо этого он будет просто проверять J напрямую и сдвигать ваше имя develop вперед. Git называет это ускоренным слиянием , но фактического слияния нет. Результат выглядит следующим образом:

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

, который мы можем нарисовать проще:

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

Мы вернулись к тому, что ваша develop и ваша origin/develop указывают на тот же коммит, в данном случае, коммит J.

Нижняя строка о слиянии

Конечный результат здесь такой: git merge может сделать fast-forward , который на самом деле не является слиянием и не делает новый коммит. Или он может выполнить истинное слияние , которое действительно является слиянием, а делает новый коммит.

Git может выполнять только ускоренную перемотку вперед объединить при двух условиях:

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

  • Во-вторых, вы должны сообщить git merge, что это разрешено или требуется. Но «разрешено» по умолчанию. Использование git merge --ff-only говорит Git, что требуется , а использование git merge --no-ff говорит Git, что запрещено . По умолчанию: разрешено; сделайте это, если возможно.

Если быстрая перемотка возможна и разрешена или требуется, это то, что делает Git. В противном случае Git необходимо из-за реального слияния. Если это запрещено - если вместо этого требуется перемотка вперед - команда git merge выдает ошибку и ничего не делает. Только когда реальное слияние и разрешено, и необходимо, или принудительно --no-ff, мы получаем реальное слияние.

Примечания к git pull

Команда git pull просто Он выполняет две команды Git для вас, в качестве своего рода вспомогательной оболочки:

  1. Сначала он запускает git fetch. Это работает так, как git fetch всегда работает.
  2. Во-вторых, он запускает git merge (или, если вы скажете это вместо git rebase).

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

Здесь есть одно специальное изменение по сравнению с просто выполнением git fetch && git merge самостоятельно. Низкое качество стандартного сообщения слияния для git merge, которое вы делаете самостоятельно:

merge branch 'origin/develop' into develop

Низкое качество стандартного сообщения слияния, которое git pull поставляет:

merge branch 'develop' of <url> into develop

где часть url взята из URL, сохраненного под именем origin.

Git Слияние не всегда само по себе

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

Окончательное объединение, конфликтное или нет, будет иметь снимок всех ваших файлов. Если Git выполняет слияние самостоятельно, Git сделал снимок. Если возникают конфликты слияния, вы сами контролируете окончательный снимок.

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