TL; DR
git pull
означает выполнить git fetch
, затем выполнить вторую Git команду . Первый шаг - git fetch
- не влияет ни на одну из ваших веток. Это ничего не меняет, с чем вы работаете, если вы работаете над чем-либо.
Шаг second , который по умолчанию запускает git merge
, влияет на текущую ветку . Он не создает новую ветвь, поэтому, как правило, любые новые имена ветвей, созданные в других Git, не имеют значения, если вы явно не назвали их в своей команде git pull
.
Предполагая, что вы запускаете git pull
без дополнительных аргументов, remote , на котором git pull
выполняет git fetch
, является удаленным, связанным с текущей ветвью, и фиксация, которая используется для rebase-or-merge, это связано с восходящим потоком текущей ветви, как обновлено на шаге git fetch
. Git налагает ограничения на настройки восходящего потока для имени ветви в вашем хранилище: в частности, если ваш Git еще не знает, что какое-то имя существует в другом Git, ваш Git не позволит вам установить это как вверх по течению. Таким образом, «новые» ветви, которые мы не определили должным образом, на самом деле, не имеют отношения.
Если вы добавите больше аргументов в командную строку git pull
, картина станет более сложной.
Long
Нет ли разницы для новых веток, тяну я или получаю?
Git pull всегда означает: run git fetch
, затем run вторая Git команда . Очевидно, что они разные, потому что git fetch
не запускает вторую команду Git. Здесь не имеет значения, видит ли шаг выборки имена ветвей, которых ваш Git не видел раньше.
И если да, то в чем отличие от Git внутренней точки зрения?
Здесь вы должны внимательно следить за тем, как на самом деле работает Git. Чтобы этот ответ был коротким (я sh), я скажу, чтобы увидеть много других моих ответов, но много:
- Каждый коммит имеет уникальный идентификатор ha sh, это длинное случайное имя коммита, которое
git log
показывает вам, например: commit 1c56d6f57adebf2a0ac910ca62a940dc7820bb68
. Каждый коммит хранит снимок всех ваших файлов. Файлы внутри каждого коммита имеют специальный, только для чтения, Git только сжатый формат, замороженный на все времена.
Каждый коммит также хранит некоторые метаданные: информация о коммите, которая не является файлом, сохраненным с коммитом, а содержит информацию о том, кто сделал коммит, когда и почему (их сообщение в журнале). В этих метаданных каждый коммит хранит идентификатор ha sh своего непосредственного родительского коммита (для большинства коммитов; некоторые хранят двух или более родителей, это коммитов слияния , и в по крайней мере один будет самым первым коммитом в репозитории и поэтому не будет иметь родителя).
A имя ветви подобно master
просто содержит необработанный ha sh ID последнего коммита в цепочке. Следовательно, если у вас есть ветвь с именем master
и некоторыми коммитами, master
содержит некоторое га sh ID H
, а коммит H
указывает на какой-то более ранний коммит G
, который указывает на еще более ранний коммит F
и т. д .:
... <-F <-G <-H <--master
К добавление коммита к ветви, мы выбираем это имя ветви, которое выбирает этот коммит. Это берет замороженные Git -только файлы из коммита в область, где мы можем работать с ними. Мы работаем с ними по желанию и в итоге сообщаем Git: сделать новый коммит . Git возвращает новую точку фиксации обратно к той, которую мы получили, сохраняя новый снимок всех наших файлов, а затем, сделав новый коммит, изменяет ветвь имя, чтобы оно указывало на новый коммит:
...--F--G--H--I <-- master
Имена ветвей - не единственный вид имен, которые могут запомнить коммиты ha sh ID. Более одного имени может идентифицировать любой отдельный коммит.
Команда git clone
работает, вызывая другой репозиторий Git. Вы говорите своей системе:
- Создайте новый пустой каталог / папку (или используйте пустую папку, на которую вы указываете
git clone
). - Создайте новый, пустой хранилище там:
git init
. - Сохраните URL-адрес для последующего использования под именем
origin
(или любым другим именем, которое вы скажете Git для использования): git remote add
. - Выполните любую другую конфигурацию, которую вы сказали Git, с помощью команды
git clone
. - Вызовите другой Git в
origin
- по сохраненному URL-адресу - и попросите перечислить его ветвь (и другие) имена и их необработанные идентификаторы ha sh. Затем попросите Git о коммитах ... в этом случае всех из них. Скопируйте все его коммитов в наш пустой репозиторий. Возьмите имена его ветвей и переименуйте их: например, сделайте его master
нашим origin/master
и сделайте его develop
нашим origin/develop
и т. Д. - Наконец, для одного из этих имен - вероятно,
master
- используйте переименованную origin/
версию имени, чтобы сделать branch name, и укажите это имя ветви на том же коммите, что и моя origin/
версия имени.
Таким образом, после начального git clone
у вас есть имена для удаленного отслеживания , обычно в форме origin/*
, для каждого другого Git филиал имена. Затем у вас есть одно собственное имя ветки, обычно master
, указывающее на такой же коммит, что и на origin/master
. Если у них есть master
и develop
, возможно, теперь у вас есть:
...--G--H <-- master, origin/master
\
I--J <-- origin/develop
Шаг 5 в шестиступенчатой последовательности git clone
, приведенной выше, на самом деле git fetch
. Однако вместо того, чтобы получать каждый коммит , git fetch
делает разговор с другим Git, чтобы увидеть, какие коммиты у них есть, а у вас нет. Во время первоначального клона у вас нет каких-либо коммитов, так что это просто автоматически все их коммиты. Позже это их новые единицы.
Когда вы запускаете git fetch
позже, если у них все еще есть master
идентифицирующий коммит H
и их develop
идентифицирующий коммит J
ваш Git будет смотреть в вашем хранилище, используя реальные идентификаторы ha sh, за которыми стоят H
и J
, и вы увидите, что они у вас уже есть. Ваш Git не должен получать новые коммиты. Если они добавили еще один коммит к их develop
, у них будет новый коммит K
, и вы получите его:
...--G--H <-- master, origin/master
\
I--J <-- origin/develop
\
K
и затем ваш git fetch
обновит ваше имя удаленного отслеживания origin/develop
, чтобы оно указывало на фиксацию K
:
...--G--H <-- master, origin/master
\
I--J--K <-- origin/develop
Если они сделают что-то необычное и заставят их develop
вернуться один шаг, и вы снова запускаете git fetch
, вы будете сохранять фиксировать K
некоторое время - обычно не менее 30 дней по умолчанию - но ваш Git настроит ваш origin/develop
в соответствии их develop
:
...--G--H <-- master, origin/master
\
I--J <-- origin/develop
\
K [no name: hard to find!]
Git в общем находит коммиты, начиная с какого-то имени - будь то имя вашей ветки, или имя вашего удаленного отслеживания, или любое другое имя - и затем работает в обратном направлении.
(Для каждого имени есть скрытые журналы ранее сохраненных идентификаторов ha sh, по которым вы можете найти K
. Записи в этих журналах в конце концов истекают, и вот где 30-дневный лимит исходит из: по истечении 30 дней срок действия записи, сохраняющей K
, истекает. at, Git s сборщик мусора , git gc
, выбросит K
по-настоящему, если никто не создал новое имя для его защиты.)
Запуск git fetch
как этот, без имени вообще - по умолчанию origin
, обычно - или только с именем пульта, таким как origin
, будет - до тех пор, пока вы не настроите настройки специально - получите все имен ветвей от других Git и соответственно создайте или обновите все ваших имен для удаленного слежения. Однако настройка так называемого клона с одной ветвью настраивает ваш Git по-другому, так что git fetch
обновляет только одно имя удаленного отслеживания. Вы можете перенастроить это позже или переопределить набор имен для обновления, используя refspe c, но мы не будем go более подробно здесь.
Пока это все о git fetch
; давайте начнем использовать имя ветви
Опять же, Git 'fetch
- это часть, которая получает новые коммиты от другого Git. Получив новые коммиты, если таковые были получены, git fetch
корректирует ваши имена для удаленного слежения . Это не влияет ни на одно из ваших названий веток . Все ваши имена ветвей не потревожены.
Если у вас никогда нет собственных имен ветвей, что было бы странно, хотя это возможно, и никогда не выполняйте работу на вашем own, что менее странно и разумно для определенных приложений (например, для хранения в архиве). Но вы, вероятно, действительно используете ветки.
Допустим, вы делаете своим собственным названием ветви, dave
или любым другим. Допустим, вы указали это имя для существующего коммита H
:
...--G--H <-- dave, master, origin/master
\
I--J--K <-- origin/develop
Теперь, когда у вас есть более одного имени ветки, мы бы хотели, чтобы Git запомнил, какое из них вы на самом деле используете , Мы добавим специальное имя HEAD
к одному из них:
...--G--H <-- dave (HEAD), master, origin/master
\
I--J--K <-- origin/develop
Так что теперь мы можем сказать, что вы используете name dave
и commit H
. Три имени, dave
и master
и origin/master
, все прямо сейчас определяют коммит H
.
Мы упоминали выше, что файлы, сохраненные в коммитах, находятся в специальном, только для чтения, Git -только сжатый и замороженный формат, который может использовать только Git. Итак, Git скопировал эти файлы в *1266* index и рабочую область для Git. Рабочая область - ваше рабочее дерево или рабочее дерево . В нем хранятся обычные файлы в обычном формате вашего компьютера.
Вы делаете новые коммиты - обычно в любом случае - манипулируя этими обычными файлами, а затем используя git add
, чтобы скопировать их обратно в индекс Git. Это повторно сжимает файл в замороженный формат, готовый к go в новый коммит. Когда вы запустите git commit
, Git упакует файлы, которые находятся в его индексе на тот момент. Следовательно, мы можем сказать, что основная функция индекса заключается в сохранении того, что вы предлагаете поместить в ваш следующий коммит . (У него также есть и другие функции, но мы не будем вдаваться в них здесь.)
В конечном итоге у вас есть файлы в форме и git add
-ed, и вы запускаете git commit
. Git собирает соответствующие метаданные и записывает новый коммит, который назначает новому коммиту его уникальный идентификатор sh. Git затем сохраняет идентификатор новой фиксации ha sh в текущую ветку name , давая нам:
L <-- dave (HEAD)
/
...--G--H <-- master, origin/master
\
I--J--K <-- origin/develop
Вы могли бы одинаково хорошо работать на master
или develop
который начинает указывать на коммит K
или что-то еще, но тем или иным образом вы делаете новый коммит, и он указывает на любой коммит, который вы сказали Git использовать для начала.
Теперь, если вы запустите git fetch
и они , кем бы они ни были, сделаны или иным образом приобрели новые коммиты, которые вы еще не видели, эти новые коммиты будут добавлены в их ветви. Ваш Git видит их в своем хранилище, видит, что у вас их еще нет, и получает их. Давайте нарисуем один (и перестанем рисовать I-J-K
, поскольку они в пути, но буквы израсходованы, поэтому я go с M
здесь далее):
L <-- dave (HEAD)
/
...--G--H <-- master
\
M <-- origin/master
Вы можете как включить их новый коммит каким-либо образом.
Точно как вы включите их новый коммит - решать вам. Например, вы можете:
git checkout master
, а затем git merge origin/master
git merge origin/master
прямо сейчас, когда совершаете коммит L
на ветке dave
или делайте любое другое.
Если вы:
git checkout master; git merge origin/master
, ваш Git будет делать то, что Git называет быстрой перемоткой вперед слияние . Это вовсе не слияние - оно несколько плохо названо, но оно имеет такой эффект:
L <-- dave
/
...--G--H--M <-- master (HEAD), origin/master
На самом деле, если вы запустите git checkout master; git rebase origin/master
, то же самое произойдет в этом частный случай. В других случаях могут происходить разные вещи.
Здесь git pull
входит
Как правило, после того, как вы принесли новые коммиты из других Git с git fetch
, вы, как правило, хотите что-то с ними сделать . Если вы используете master
, и они обновили свои master
, вам нужно обновить master
. Два наиболее распространенных способа сделать это - запустить git merge
или git rebase
.
. Команде git pull
может быть предписано запустить любой из них в качестве второй команды. По умолчанию он запускается git merge
. И git merge
, и git rebase
работают с текущей веткой. То есть они смотрят на специальное имя HEAD
. Пока это связано с каким-то именем ветви - как обычно, - это ваше имя ветви, на которое они будут влиять. Они вносят изменения в индекс Git и в ваше рабочее дерево; оба могут изменить то, какой коммит выбран текущим именем ветки; git merge
может сделать новый коммит слияния, или выполнить операцию ускоренной перемотки вперед, или иногда ничего не делать.
Одна из частей, которые мне не нравятся в git pull
, - это то, что вы не всегда знаете, , когда вы нажмете Enter , то, что фиксирует git fetch
, в итоге будет извлечено, и куда он может переместить любые имена для удаленного слежения. Но у вас мертвая установка на с git merge
или git rebase
с использованием этих новых коммитов и обновленных имен. (Технически это немного не так, как мы увидим - он не использует обновленные origin/*
имена напрямую, но здесь достаточно близко.)
Даже если новые коммиты не являются что-то, что вы хотите использовать, чтобы повлиять на вашу текущую ветвь, это произойдет. Вы не можете сказать , если это произойдет. Вы можете использовать какой-то просмотрщик, чтобы сначала проверить другой репозиторий Git, но что произойдет, если вы просмотрите его, а затем непосредственно перед тем, как нажать Введите , кто-то еще изменит вещи в этом другом хранилище? Тем не менее, людям это очень нравится и они используются постоянно, поэтому давайте перейдем к вашим подробным вопросам.
Я также снова открыл файл .git/FETCH_HEAD
и увидел строку для этой ветви: <sha-1> not-for-merge branch side_branch_2 of <url>.
Вот исторический секрет (или не очень секретный) о git fetch
и git pull
: они настолько стары, что существовали git pull
до того, как имена для удаленного слежения, такие как origin/master
, существовали , Remotes и имена для удаленного слежения были изобретены некоторое время между Git версиями 1.4 и 1.5, и некоторые возились с разными идеями. Команда git pull
продолжала работать так, как этого хотели люди, на протяжении всего переходного периода, когда разрабатывались новомодные пульты и имена для удаленного отслеживания.
Чтобы избежать необходимости слишком часто менять код, и / или из-за того, что имена удаленных пользователей и слежения за ними еще не существуют, git fetch
всегда записывал все в .git/FETCH_HEAD
Чтобы ранние сценарии git pull
выяснили, какой зафиксирует га sh ID , чтобы дать git merge
, git fetch
отмечает, какое из наших названий веток мы используем теперь - это проверка «где находится HEAD» - и какие имена использовать из other Git. Затем он помечает каждую .git/FETCH_HEAD
строку not-for-merge
или не помечает ее, в зависимости от аргументов, которые вы указали git fetch
.
Когда вы запускаете git pull
, вы можете задать кучу Аргументы команды git pull
:
git pull # no arguments at all
git pull origin # just a remote
git pull origin master # a remote and a branch name *on the remote*
Назад, когда git pull
буквально запустил git fetch
, он передал эти аргументы git fetch
. Теперь в него встроено git fetch
, но оно все равно работает. Если вы дадите здесь одно или несколько названий ветвей, то есть или те, которые git fetch
не помечают как not-for-merge
в файле .git/FETCH_HEAD
.
Точно так же, когда git pull
был еще сценарием оболочки - он был переписан в C относительно недавно - вот как git pull
решил, какой идентификатор ha sh передать git merge
или, если вы выберете git rebase
как ваша вторая команда, git rebase
. То, что он делает сейчас, более неясно. Поскольку часть выборки теперь встроена как вызовы функций C, она может просто сохранять необработанные идентификаторы ha sh в памяти.
В Git версии 1.8.4, Git люди решили, что git fetch origin master
должен обновить origin/master
. До этого git fetch origin
обновлял все имена для удаленного слежения, но git fetch origin master
обновлял нет . Начиная с Git 1.8.4 и далее git fetch origin master
обновляет origin/master
. Он по-прежнему не обновляет другие имена для удаленного слежения origin/*
, поскольку не переносит коммиты, соответствующие каким-либо обновленным именам. (В некоторых случаях он может обновлять имена удаленного отслеживания, но это не так.)
Заключение
git fetch
, который запускает git pull
:
- в основном получает аргументы, которые вы приводите: например,
git pull xyzzy one two three
запускает git fetch xyzzy one two three
. «В основном» здесь только потому, что некоторые опции влияют на то, какую вторую команду использовать, и / или съедаются самим git pull
и / или передаются второй команде, а не git fetch
. - извлекает данные из названного удаленного (или из заданного URL, но это многое меняет) и тем самым обновляет некоторый набор имен удаленного отслеживания ;
- записей все, что он сделал в
.git/FETCH_HEAD
на тот случай, если вы все еще используете старые сценарии оболочки git pull
.
В общем, git fetch
безопасно запускать в любое время. (Вы можете настроить его как небезопасный, если вы действительно используете sh, установив remote.<em>name</em>.fetch
ненадлежащим образом или передав небезопасный аргумент refspe c. Однако стоит отметить, что git fetch
имеет встроенные проверки безопасности , даже если вы делаете это . Старый сценарий pull
отключает их! )
Последующие git merge
или git rebase
работают в текущей ветке , и это не очень хорошая идея, если это случится, если вы не выполняете работу. Git обычно обнаруживает такой случай и вообще не позволяет запустить вторую команду для этих случаев. В далеком прошлом, однако, команда pull могла (и действительно) срывала текущую работу безвозвратно, потому что git pull
- старый сценарий, во всяком случае - отключил много проверок безопасности.
В любом В этом случае вторая команда - шаг слияния или ребазирования - получает кучу дополнительных аргументов, которые заставляют ее работать одинаково в течение переходного периода от Git 1,4 до 1,6, когда менялись имена удаленных и отслеживаемых удаленных объектов. Это было почти 15 лет go сейчас, но все равно работает так же. Если вы используете:
git fetch
git merge
и ваш Git делает коммит слияния, сообщение о слиянии по умолчанию будет выглядеть примерно так:
merge branch origin/dave into dave
, но если вы используете:
git pull
сообщение о слиянии по умолчанию будет больше похоже на:
merge branch dave of <url> into dave
«что-то вроде» происходит потому, что точное написание каждого сообщения здесь зависит от названий ветвей (очевидно), и от того, сливаются в master
- здесь пропущена часть into <branch>
- и вставляются некоторые кавычки, которые я не хотел бы здесь беспокоить. : -)