Различия между git fetch и git fetch origin master

Я делал выборки / слияния и хотел знать, есть ли разница между

git fetch

а также

git fetch origin master

У меня нет никаких других веток и точек происхождения к моим remote repository на GitHub.

Когда я делаю:

git fetch origin master
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From github.com:XXXXXXXXXXXXXXX
 * branch            master     -> FETCH_HEAD

Но просто:

git fetch
From github.com:XXXXXXXXXXXXXXX
   531d466..aaf6df0  master     -> origin/master

Обратите внимание, что мастер указывает на разные вещи; в одном случае FETCH_HEAD а в другом случае origin/master? Они разные?

1 ответ

Решение

Вот версия "TL;DR" (которая закрывает множество особых случаев): git fetch всегда обновления FETCH_HEAD с более чем одной строкой в ​​разных случаях. Иногда обновляются "удаленные ветки", которые являются ссылками, полное имя которых начинается с refs/remotes/, Остальное в основном касается "иногда", которое варьируется в зависимости от количества аргументов git fetch и версия git.


У меня был шанс проверить это. Давайте выделим три случая, каждый из которых предполагает выполнение git fetch без дополнительных опций, таких как -a или даже --all, Давайте также исключим более странные варианты git fetch например, использование URL-адреса напрямую или insteadOf записи или файлы, перечисленные в .git/remotes или же .git/branches, (Признаюсь, я просто догадываюсь, но я думаю, что это остатки за несколько дней до [remote "name"] записи вошли в конфигурационные файлы git.)

  1. git fetch и других аргументов нет.

    Git определяет вашу текущую ветку (обычным способом, читая HEAD, но вы, конечно, можете увидеть, что это такое git branch или же git status). Затем он ищет запись конфигурации для этой ветви, называя ее remote, Например, предположим, что вы находитесь на ветке dummy а также .git/config имеет (среди других записей):

    [branch "dummy"]
        remote = remote-X
    

    В этом случае git fetch эквивалентно git fetch remote-X, После этой точки это эквивалентно случаю 2, который:

  2. git fetch remote (и больше никаких аргументов кроме этого).

    Git не смотрит на вашу текущую ветку на этот раз. Удаленный для использования тот, который указан в командной строке. Он ищет раздел конфигурации для данного пульта. Допустим, вы используете remote-X в этом случае он ищет:

    [remote "remote-X"]
        url = ...
    

    Если этот раздел не существует, или нет url = запись, вы получаете ошибку: fatal: 'remote-X' does not appear to be a git repository, 1 В противном случае, который дает URL, и git fetch будет пытаться подключиться к там. Предполагая, что это может соединиться...

    Обычно есть также по крайней мере одна запись конфигурации, возможно, больше, читая:

        fetch = +refs/heads/*:refs/remotes/remote-X/*
    

    (название пульта здесь жестко запрограммировано). Предполагая, что есть...

    Следующий, git fetch спрашивает у пульта, какие ссылки у него есть (в основном ветки и теги, хотя вы можете получить все ссылки, но большинство людей просто заботятся о ветках и тегах). Вы можете сделать то же самое самостоятельно с git ls-remote remote-X, который разливает такие вещи:

    676699a0e0cdfd97521f3524c763222f1c30a094        HEAD
    222c4dd303570d096f0346c3cd1dff6ea2c84f83        refs/heads/branch
    676699a0e0cdfd97521f3524c763222f1c30a094        refs/heads/master
    

    Лечение HEAD ref не совсем последовательный (я видел, что он ведет себя странно), но обычно здесь он просто сбрасывается. 2 Остальные ветви переименованы и обновлены в соответствии с fetch = refspec. (Если есть несколько fetch = refspecs, они переименованы и обновлены в соответствии со всеми ними. Это в основном полезно для привлечения refs/notes/ или сделать ваше собственное пространство имен "удаленных тегов" под refs/rtags/, например.)

    В этом случае fetch перенесет любые объекты, необходимые для двух веток. branch а также master и обновите (локальные) имена "удаленных филиалов", refs/remotes/remote-X/branch а также refs/remotes/remote-X/master, по мере необходимости. Для каждого, который обновляется, fetch печатает строку как это:

       22b38d1..676699a  master     -> remote-X/master
    

    Если fetch = линии отсутствуют, вы получаете что-то совсем другое. Вывод будет читать:

     * branch            HEAD       -> FETCH_HEAD
    

    В этом случае это как будто (отсутствует) fetch = линия была там и содержала fetch = HEAD,

  3. git fetch remote refspec (refspec part - это один или несколько refspecs, как описано ниже).

    Это похоже на случай 2, только на этот раз "refspecs" указывается в командной строке, а не из fetch = записи конфигурации для пульта. Тем не менее, поведение выборки здесь совсем другое.


Давайте на минуту остановимся и опишем refspec должным образом, в данном конкретном случае. (Refspecs также встречаются для git push но, как обычно в git, детали реализации просачиваются и работают там немного по-другому.) У refspec есть дополнительный начальный плюс (+) знак, который я здесь проигнорирую; 3 затем две части, разделенные двоеточием (:). Оба часто являются просто именем ветви, но вы можете (и fetch = строки делают) прописать "полное" ref-имя, refs/heads/branch в случае названия филиала.

Для операций извлечения имя слева - это имя самого пульта (как показано git ls-remote например). Имя справа - это имя, которое будет сохранено / обновлено в вашем локальном git-репозитории. Как особый случай, у вас может быть звездочка (*) после косой черты как последний компонент, вроде refs/heads/*, в этом случае соответствующая часть слева заменяется справа. следовательно refs/heads/*:refs/remotes/remote-X/* это то, что вызывает refs/heads/master (как видно на пульте, с git ls-remote) становиться refs/remotes/remote-X/master (как видно из вашего локального хранилища, и в более короткой форме, на правой стороне -> линия git fetch печать).

Если вы не вставите в :, хоть, git fetch не имеет хорошего места, чтобы положить копию "ветки там". Допустим, это принесет с собой пульт refs/heads/master (master ветка на пульте). Вместо обновления вашего refs/heads/master - очевидно, что было бы плохо, если бы у вас были свои коммиты в ветке master - он просто сбрасывает обновление в FETCH_HEAD,

Вот где дела обстоят особенно коротко. Допустим, вы бежите git fetch remote-X master branch т. е. дать хотя бы один, а может и несколько, refspecs, но все без двоеточий.

  • Если ваша версия git старше 1.8.4, обновление входит только в FETCH_HEAD, Если вы дали два рефспека без двоеточия, FETCH_HEAD теперь содержит две строки:

    676699a0e0cdfd97521f3524c763222f1c30a094        branch 'master' of ...
    222c4dd303570d096f0346c3cd1dff6ea2c84f83        branch 'branch' of ...
    
  • Если ваша версия git 1.8.4 или новее, обновление идет туда - эта часть не изменяется - но также выборка использует возможность для постоянной записи этих веток в их надлежащие удаленные ветви, как указано fetch = линии для пульта.

    По какой-то причине, однако, git fetch только распечатывает обновление -> линия для удаленных веток, которые фактически обновляются. Поскольку он всегда записывает все обновления в FETCH_HEAD здесь всегда печатаются названия веток.

    (Другой проблемой, помимо необходимости использования git 1.8.4 или новее, с обновлением удаленных веток, является то, что fetch = линии должны существовать. Если они этого не делают, нет сопоставления, с помощью которого выборка знает, чтобы переименовать refs/heads/* в refs/remotes/remote-X/*.)

Другими словами, git 1.8.4 и новее действительно "обновляют" все удаленные ветви. Старые версии git делают это на git push так что это было противоречивым раньше. Даже в git 1.8.4 все равно несовместимо с git pull Я думаю (хотя я не использую git pull достаточно заметить:-)); это должно быть исправлено в git 1.9.

Теперь давайте вернемся к разнице между git fetch remote а также git fetch remote refspec ...,


  • Если вы бежите git fetch remote то есть, если опустить все refspecs, выборка возвращается к fetch = линии как обычно. Операция извлечения переносит все ссылки из fetch линий. Все это входит в FETCH_HEAD, но на этот раз они помечены как "не для слияния" (с вкладками, которые я изменил на один пробел, чтобы лучше разместить на веб-страницах):

    676699a0e0cdfd97521f3524c763222f1c30a094 not-for-merge branch ...
    

    Ссылки, которые не являются ветвями, например, refs/notes/ ссылки, которые приводятся, читайте вместо:

    f07cf14302eab6ca614612591e55f7340708a61b not-for-merge 'refs/notes/commits' ...
    

    Между тем, ссылки на удаленные ветки обновляются, если необходимо, с сообщениями, сообщающими, какие из них были обновлены:

       22b38d1..676699a  master     -> remote-X/master
    

    Опять все сбрасывается в FETCH_HEAD, но только ссылки, которые "нуждаются в обновлениях" обновляются и печатаются. На новых ветвях напечатана "новая ветвь", а на старых напечатаны сокращенные "старые и новые" SHA-1, как для master -> remote-X/master выше.

  • Если, с другой стороны, вы запускаете git fetch remote refspec ..., выборка приносит только указанные refspecs. Все это входит в FETCH_HEAD как обычно 6, но на этот раз каждый из них напечатан. Затем, если ваш git 1.8.4 или новее, любые ссылки-обновления, которые могут быть сопоставлены (через разумный fetch = строки) и нуждаются в обновлении, также обновляются и печатаются:

     * branch            master     -> FETCH_HEAD
     * branch            branch     -> FETCH_HEAD
       22b38d1..676699a  master     -> remote-X/master
    

    Если ваша версия git старше 1.8.4, обновление remote-X/master не происходит в этом случае, или, скорее, это не происходит, если один из ваших refspecs командной строки не был refs/heads/master:refs/remotes/remote-X/master, или же refs/heads/*:refs/remotes/remote-X/* или варианты с плюсиками впереди.


1 Это не большое сообщение об ошибке. remote-X Аргумент никогда не должен был быть "хранилищем", он должен был быть "удаленным"! Было бы неплохо, если бы Git сказал что-то более информативное.

2 В git remote protocol есть недостаток: HEAD обычно является косвенным ref, поскольку это текущая ветка на пульте, поэтому он должен называться, например, "ref: refs/head /master", но вместо этого он появляется как полностью решен SHA-1. Хотя бы одна команда git (git clone) пытается "угадать" текущую ветку на удаленном компьютере, сравнивая этот SHA-1 с тем из каждого из ветвей. Например, в приведенном выше примере ясно, что удаленный узел находится "на главном филиале", так как HEAD а также refs/heads/master имеют тот же SHA-1. Но если несколько имен веток указывают на один и тот же коммит, и HEAD соответствует этому коммит-идентификатору, нет способа узнать, какая ветвь (если есть) HEAD включен Пульт дистанционного управления также может находиться в состоянии "отсоединенный HEAD", и в этом случае он не находится ни в одной ветви, независимо от значений SHA-1.

3 Знак "плюс" означает "принимать принудительные обновления", то есть принимать обновления, которые будут отклонены правилом "ничего, кроме перемотки вперед" 4 для ветвей, или "никогда не менять теги" 5 для тегов.

4 "Перемотка вперед" для метки, изменение ее со старого SHA-1 на новый, возможна, когда старый SHA-1 в коммит-ориентированном ациклическом графе является предком нового SHA-1.

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

6 Но без not-for-merge этот раз. В основном, когда вы предоставляете refspecs без двоеточия, git fetch предполагает, что они "для слияния" и помещает их в FETCH_HEAD чтобы git merge FETCH_HEAD могу найти их. (Я не проверял, что происходит с ссылками не из ветвей.)

Другие вопросы по тегам