Резюме TL; DR: «это зависит».
Ответ "и то, и другое", на самом деле. Или "это зависит". Или что-то подобное!
Во-первых, необходимо рассмотреть две основные операции: fetch
и push
. (Операция pull
— это всего лишь сценарий оболочки, построенный поверх fetch
, поэтому, как только вы узнаете, это работает, мы сможем правильно объяснить pull
.)
И fetch
, и push
имеют доступ ко всем репозиториям. Но вообще они не работают, пересылая целые репозитории по проводу (или другому каналу связи). Они работают на основе ссылок.
Операции выборки и отправки обычно используют «refspecs», которые представляют собой пары ссылок (remote:local и local:remote соответственно) плюс необязательный префикс флага «force» +
. Однако им можно дать только простую ссылку, а принудительный флаг можно указать с помощью -f
или --force
.
Обе команды существуют уже давно и накопили много «старого хлама». «Современный» способ работы с удаленными репозиториями заключается в использовании так называемого «удаленного» репозитория с использованием git remote add
для их создания (а git clone
по умолчанию создает репозиторий с именем origin
). Они превращаются в записи в файле .git/config
:
[remote "origin"]
fetch = +refs/heads/*:refs/remotes/origin/*
url = ssh://...
Строка url =
содержит URL-адрес как для выборки, так и для отправки, хотя при необходимости может быть дополнительная строка pushurl =
, чтобы отправка отправлялась в другое место. (Существуют «старые способы» для запуска выборки, отправки и предоставления URL-адресов напрямую и т. д., но давайте просто проигнорируем их все... удаленные намного лучше!) Это также предоставляет спецификации ссылок — ну, в данном случае одну спецификацию ссылок — для git fetch
.
git ls-удаленный
С этим покончено, давайте начнем с другой команды, git ls-remote
. Это работает как fetch
, но фактически ничего не извлекает:
$ git ls-remote origin
676699a0e0cdfd97521f3524c763222f1c30a094 HEAD
222c4dd303570d096f0346c3cd1dff6ea2c84f83 refs/heads/branch
676699a0e0cdfd97521f3524c763222f1c30a094 refs/heads/master
d41117433d7b4431a188c0eddec878646bf399c3 refs/tags/tag-foo
Это говорит нам о том, что удаленное устройство с именем origin
имеет три ref-имени. Две ветки и одна тег. (Специальный HEAD
ref имеет тот же SHA-1, что и refs/heads/master
, поэтому git догадается, что удаленный сервер находится «на ветке master
», как мог бы сказать git status
. В удаленном протоколе есть своего рода ошибка: git должен иметь возможность сказать «HEAD
является символической ссылкой, указывающей на refs/heads/master
", так что вашей стороне не нужно угадывать. Это устранит случай, когда две ветки имеют тот же SHA-1, что и HEAD
.)
git fetch
Когда вы запускаете git fetch origin
, операция выборки начинается с того же ls-remote
, более или менее, и, таким образом, видит все ветки и теги. Если вы используете --tags
, он также переносит все теги, в противном случае он делает что-то довольно сложное1, которое переносит все ветки и некоторые теги. Он также видит все другие ссылки, но по умолчанию не переносит их: например, на удаленном сервере может быть refs/notes/commits
, который используется git notes
, но этот не приходит.
Однако когда вы изменяете refspecs, заданные для git fetch
, вы меняете то, что переносится. По умолчанию тот, что находится прямо в .git/config
, fetch = +refs/heads/*:refs/remotes/origin/*
. Эта refspec говорит, что необходимо перенести все ссылки refs/heads/*
— все ветки — и сохранить их локально под refs/remotes/origin/
, используя то же имя, что и имя ветки на удаленном компьютере. Использование --tags
добавляет одну дополнительную спецификацию: refs/tags/*:refs/tags/*
. Вот как git переносит все их теги: все, что соответствует refs/tags/*
, то есть всем тегам, попадает в ваш локальный refs/tags/
под соответствующим именем.
(Вы можете добавить больше fetch =
строк и добавить больше материала. См., например, этот ответ на "удаленные теги".)
Теперь просто перенос ссылки name не принесет много пользы, если только git также не перенесет все необходимые базовые объекты,2, как указано их ША-1с. Допустим, у вас уже есть 676699a...
, но нет 222c4dd...
. (Вы в курсе master
, но не branch
. Возможно, у вас еще даже нет имеет ветку branch
.) Операция выборки должна точно перенести эту фиксацию. Для этого коммита, вероятно, нужны различные файлы, предыдущие коммиты и так далее. Таким образом, ваш git fetch
связывается с вещью на удалении, которая просматривает другой репозиторий git, и они немного беседуют, где каждый рассказывает другому, какие SHA-1 у них есть сейчас, а какие им еще нужны. Если вашему нужно 222c4dd...
, он спрашивает другой конец "что еще мне нужно использовать 222c4dd...
", проверяет, есть ли они у него, добавляет их в свой список, если нет, проверяет их более подробно один раз добавлено и так далее.
Окончательно договорившись о том, что обменивать, их git отправляет вам объекты — обычно в «тонком пакете», если это возможно (детали зависят от транспорта), — и ваш git распаковывает и/или перепаковывает их по мере необходимости, а затем обновляет ваш локальный ссылки для любых новых ветвей, тегов или других ссылок. (По умолчанию ваш git просто хранит их ветки в ваших «удаленных ветках» — вашей копии «того, что у них было, когда я в последний раз разговаривал с ними», — но обновляет теги ваши. никаких «удаленных тегов», только «удаленные ветки».)
Важный частный случай git fetch
В особом случае, если вы укажете git fetch
какие-либо аргументы помимо имени пульта, например:
git fetch origin master
например, эти refspec переопределяют те, что указаны в файле конфигурации, и (в версиях git до 1.8.4) предотвращают обновление «удаленных ветвей». Это обычно ограничивает то, что извлекается, иногда совсем немного. (В 1.8.4 и более поздних версиях они по-прежнему ограничивают выборку, но удаленная ветвь все равно обновляется, что имеет больше смысла.) Здесь refspec, в котором отсутствует двоеточие, как в приведенном выше, является not< /em> обрабатывается так, как если бы у него было одно и то же имя с обеих сторон. Вместо этого "своя" ветка собирается как обычно, но SHA-1 и имя ветки записываются в .git/FETCH_HEAD
.
(Для этого есть очень веская причина: если git fetch origin master
обновит ваш master
, вы потеряете все новые сделанные вами коммиты! Поэтому вы хотите, чтобы он обновлял только origin/master
и/или FETCH_HEAD
.)
git push
Операция push
действительно очень похожа на fetch
. Однако это не совсем симметрично: вы не нажимаете на «удаленную ветку», в общем, вы просто нажимаете прямо на «ветку». Например, при отправке вашей ветки master
ваша локальная ссылка — refs/heads/master
, а их локальная ссылка — тоже refs/heads/master
. Это точно не refs/remotes/yoursystem/master
. Таким образом, refspecs, используемые для push, часто немного проще.
Если вы просто запустите git push
(или git push origin
), это все равно потребует некоторых спецификаций ссылок.
В конфигурационном файле git, push.default
, есть (своего рода новая) ручка управления, которая позволяет вам настраивать ссылки, которые git отправляет. В текущих версиях git по умолчанию используется matching
. В git 2.0 планируется изменить его на simple
. Всего существует пять возможных настроек:
nothing
: выдать ошибку
current
: нажмите ветку, в которой вы находитесь, на то же имя
upstream
: подтолкнуть ветку, в которой вы находитесь, к ее восходящему имени
simple
: аналогично восходящему потоку, но требуется, чтобы имя восходящего потока совпадало с локальным именем.
matching
: отправить все ветки с одинаковым именем
Некоторые из них требуют дополнительного пояснения. «Имя восходящего потока» — это имя ветки на другом конце. Допустим, у вас есть удаленная ветка с именем origin/feature
, и вы создали для нее локальную ветку отслеживания, но назвали ее feature2
, потому что вы уже работали над другой веткой feature
(еще не созданной в origin
). Таким образом, ваш локальный feature2
имеет remote/origin
в качестве восходящего потока (а ваш feature
вообще не имеет восходящего потока). Нажатие на upstream
будет следовать сопоставлению и подтолкнет ваш feature2
к их feature
. Нажатие с помощью simple
отклонит попытку.
Следовательно, если вы git push
не используете спецификацию ссылок, git найдет конфигурацию по умолчанию3 и создаст спецификацию ссылок на ее основе. Для случая matching
он подталкивает каждую ветку, которая есть у вас и у них обоих (так что, если у вас обоих есть master
и branch
, подтолкните свою master
к их master
, а вашу branch
к их branch
), но ничего не делает с ветвями только одна из вас есть.
Если вы указываете какие-то явные refspec(s), все это становится спорным: операция push проталкивает заданные вами refspecs. Кроме того, refspec без двоеточия означает «использовать одно и то же имя на обоих концах», поэтому master
— это сокращенный способ записи полной длинной версии, refs/heads/master:refs/heads/master
.
Как и в случае с выборкой, ваш git и их git взаимодействуют, чтобы выяснить, какие объекты репозитория, если таковые имеются, необходимо отправить для выполнения push.
git тянуть
Операция git pull
выполняет четырехсловную форму git fetch
.
Его первый шаг — выяснить, какой пульт использовать. Если вы назовете один:
git pull origin master
он принимает имя, которое вы ему даете; в противном случае он смотрит, на какой ветке вы находитесь (скажем, master
), затем ищет .git/config
, чтобы найти branch.master.remote
(вероятно, origin
).
Затем он выясняет, какую ветвь использовать. Если вы называете один, он использует его; в противном случае используется branch.master.merge
, имя ветки на другом конце (обычно снова просто master
). Затем он запускает git fetch
с этими аргументами.
Это означает, что выборка принесет только «интересную» ветвь, в данном случае master
, и поместит SHA-1 в FETCH_HEAD
. (Если у вас есть git 1.8.4 или новее, он также обновит origin/master
.)
Наконец, pull
запускает либо merge
, либо rebase
, опять же в зависимости от записей конфигурации и от того, запускаете ли вы его с --rebase
. Коммит, который вы объедините или перебазируете, это тот, чей SHA-1 теперь хранится в FETCH_HEAD
.
Обратите внимание, что это только объединяет или перемещает вашу текущую ветку.
1Как указано в руководстве, fetch по умолчанию использует прием "отслеживание тегов": он просматривает SHA-1 в тегах и определяет, есть ли они или будут ли они в вашем репозитории. Для тех, кто есть или будет, он переносит этот тег. Вы можете отключить это с помощью --no-tags
.
2Объекты — это то, что на самом деле хранится в репозитории: "блобы" (файлы), деревья (каталоги, заполненные файлами или несколькими каталогами), коммиты и "аннотированные теги". Каждый из них имеет уникальное имя SHA-1.
3Однако это можно переопределить с помощью конфигурации для каждой ветки, branch.name.pushremote
и remote.name.push
. Вы можете создать извилистую массу сложных для понимания эффектов, повернув множество ручек конфигурации.
person
torek
schedule
12.03.2014