Являются ли операции git pull и push для всего репозитория или для конкретной ветки?

Попытка разобраться с git :) Является ли git pull широкомасштабной операцией репозитория? Это означает, что он обновляет ваши локальные ветки (которые отслеживают удаленные ветки) в репозитории, или он только извлекает и объединяет текущую проверенную ветку?

То же самое верно для толчка? Что делает --all для push и pull?

Любая помощь будет рок!

Кроме того, что делает выборка? Собирает ли он информацию (файлы внутри папки .git) для конкретной ветки? Или папка .git согласована во всем репо? Если я делаю выборку вместо клонирования, я ничего не могу после этого сделать, что мне делать после выборки?


person Costa Michailidis    schedule 12.03.2014    source источник
comment
Зависит от версии, от точной команды, локальной конфигурации... внимательно прочитайте свои руководства.   -  person vonbrand    schedule 13.03.2014


Ответы (2)


Резюме 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
comment
Большое спасибо, это очень тщательно! - person Costa Michailidis; 13.03.2014

git pull — это просто комбинация git fetch и git merge.

git fetch обновит все удаленные ветки, а git merge обновит текущую ветку, объединив соответствующую удаленную ветку.

Точное поведение простого git push зависит от вывода git config push.default. В последних версиях git для этого параметра установлено значение simple, которое будет толкать только текущую ветку.

Для точного значения параметров командной строки используйте git help push и git help pull.

git clone — это просто комбинация git init, git remote add, git fetch и git checkout.

Ваша папка .git является вашим локальным хранилищем, содержащим всю историю всех файлов. Файлы вне папки .git — это ваше «рабочее дерево». Рабочее дерево необходимо для изменения файлов, но не требуется для большинства других команд git, таких как git log.

person michas    schedule 12.03.2014
comment
Для тех, кто использует git 1.8.3 или более раннюю версию, есть заметная разница в том, как git pull работает git fetch: вы не видите обновлений своей удаленной ветки. Если вы просто запустите git fetch, он обновит все удаленные ветки. Это в конечном итоге просвечивает, и это может сбивать с толку. Ничего страшного, если у вас 1.8.4 или новее! :-) - person torek; 13.03.2014