Что -m делать в git remote add - PullRequest
       2

Что -m делать в git remote add

0 голосов
/ 15 декабря 2018

Я надеялся, что

git remote add -m <local branch> <origin> <url> 

позволит мне создать «ярлык» для

git push <origin> <local branch>/master

через git push <origin>

  1. Что делает git remote add -m <branch name> do?
  2. Есть ли способ сделать команду "псевдоним", например git push <origin> = git push <origin> <local branch>/master?

1 Ответ

0 голосов
/ 15 декабря 2018

TL; DR

Вы не можете получить то, что вы хотите здесь, потому что git push занимает refspecs .

Long

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

Я надеялся, что [git remote add -m ...] позволит мне создать «ярлык» для [push-команды, которая неВо-первых, не имеет смысла]

Не будет, но в основном потому, что эта команда git push на самом деле не имеет особого смысла.Мы можем понять, для чего git remote add -m полезен , но перед этим нам понадобится хороший фон.

Некоторые основные факты о git push

Давайте начнем спросматривая git push сам.Что нужно git push, как минимум, две части информации:

  • как связаться с другим Git;и
  • что сказать этому другому Git, как только будет установлен контакт.

"Что сказать" - это на самом деле список одной или нескольких вещей, которыеGit звонит refspecs , и мы скоро выясним это.Первая часть - как связаться с другим Git - имеет длинную и несколько грязную историю, но современный способ сделать это - использовать remote .

Это более или менее естественно приводит к синтаксису:

git push <em>remote refspec</em> [<em>refspec2 refspec3</em> ...]

Обратите внимание, что аргументы здесь позиционные , в том что первый аргумент 1 после ключевого слова push - удаленный. Удаленный - это просто короткое имя, такое как origin, которое Git использует для нескольких целей, наиболее важной из которых является хранение длинного URL-адреса, ну, предположительно, одного длиннее шести букв origin, например.Если вы случайно введете имя ветки в этой позиции, Git все равно будет рассматривать его как удаленное имя:

$ git push master
fatal: 'master' does not appear to be a git repository

Другими словами, все, что здесь выглядит для Git, как обычное слово - ненеобработанный URL-адрес, который разрешен, а не что-то определенное с использованием того, что я назвал «несколько грязной историей», - должен быть определен как удаленный, к которому приходит команда git remote. Мы доберемся до этого, но мысначала нужно завершить команду git push.

Деталь после удаленного имени origin представляет собой список из некоторого числа refspecs .Я использовал слово «refspec» здесь неоднократно, не определяя его. Глоссарий Git определяет его, довольно бесполезно, следующим образом:

"refspec" используется fetch и push для описания сопоставления между удаленным ref и локальным ref.

Я хотел бы сказать, что во второй простейшей форме это просто пара ссылок , разделенных двоеточием , с прекрасными примерами master:master или develop:develop.A reference или ref здесь также определено, на этот раз лучше, в Глоссарии Git.Вы можете увидеть это сейчас для получения более подробной информации, или подождите: я расскажу об этом подробнее в другом разделе ниже.

Я говорю, что эта форма master:master является самой простой формой second , потому что самая простая форма refspec удаляет двоеточие и использует только одно имя.Это означает, что master, который является допустимым именем ветви, также является действительным refspec!Когда вы пишете git branch master или git checkout master, вы используете слово master в качестве имени ветви , но когда вы пишете git push origin master, вы используете слово master в качестве refspec .

Это стоит сесть и подумать: Способ использования слова влияет на его значение. Это верно для многих человеческих языков, 2 а также на компьютерных языках, таких как мини-компьютерный язык, используемый командной строкой Git.В случае с Git положение аргументов git push определяет, как Git будет их использовать: первый - удаленный, а остальные - refspecs.

Мы вернемся к этому позже.Теперь давайте перейдем к удаленным и git remote.


1 Точнее, это первый не флаговый аргумент после ключевого слова push,так как вы можете, например, запустить git push -f origin ... например.

2 Например, английское слово пишется let может быть глаголом, означающим , чтобы разрешить , или глаголом, означающим для аренды («квартира для аренды» или «комнаты для аренды»).Этот конкретный пример поразителен, потому что это также существительное, означающее препятствие и глагол, означающий для предотвращения , что делает его автоантонимом или контронимом a la расколоть , чтобы разрезать или, со вспомогательным словом до , цепляться вместе.Для забавы, смотрите ссылку на Википедию, чтобы найти много других противоречий.


Использование git remote для определения или изменения удаленных

Как мы видели, удаленное - это короткое имя, которое хранит URL,Когда вы запускаете git clone, чтобы скопировать какой-либо другой репозиторий Git в вашу собственную систему, ваш Git устанавливает для вас пульт, чтобы сохранить URL, который вы передали git clone, чтобы вам больше не приходилось вводить его снова.Ваш Git сохраняет этот URL под именем origin, поэтому у вас есть пульт с именем origin. 3

Если вы используете git init, а не git clone, длясоздайте свой репозиторий, у вас не будет пульта с именем origin.В этом случае вы можете создать его.Вы можете назвать это origin, если хотите, потому что вы можете называть это как хотите.

Чтобы добавить новый пульт - как вы хотите это называть - в свой существующий репозиторий, вы можете использовать git remote add <em>name url</em>,Здесь требуются как name, так и url, поскольку удаленный в основном является именем для URL.Имя и URL теперь объединены в пару, и на этом этапе вы можете использовать git remote, чтобы внести в него изменения .

Опция, о которой вы спрашивали, -m <em>branch</em>, этопросто вариант выполнения git remote add, за которым следует git remote set-head, в качестве одной команды.Это означает, что его полное описание приведено в разделе set-head документации git remote :

Устанавливает или удаляет ветвь по умолчанию (т. Е. Цель symbolic-refrefs/remotes/<name>/HEAD) для названного пульта.Наличие ветки по умолчанию для удаленного не требуется, но позволяет указать имя удаленного вместо определенной ветви....

К сожалению, эта информация, хотя и правильная, сама по себе бесполезна.Чтобы увидеть, что set-head - и, следовательно, -m - хорошо, нам нужно посмотреть, как Git обрабатывает преобразование имени в хэш-идентификатор, что является концепцией, независимой от git push и git fetch.


3 Фактически, git clone - это, по сути, git init в новом каталоге, затем git remote add, затем git fetch, затем git checkout.Есть несколько дополнительных, необязательных шагов, которые я пропустил в этом коротком списке здесь, и git clone также обрабатывает особый случай прерывания и удаления любого неполного хранилища, которое он мог сделать, но на самом деле, эти четыре шага - пять, еслиВы рассчитываете сделать новый каталог как шаг - получите рабочий клон.


Ссылки: имена ветвей, имена для удаленного отслеживания, теги и т. д.

Как яКак уже говорилось в другом месте, Git в первую очередь касается commits , и коммиты - и фактически все объекты Git - идентифицируются по их хэш-идентификаторам.Идентификатор хэша любого объекта Git представляет собой большую уродливую строку букв и цифр, например 5d826e972970a784bd7a7bdf587512510097b8c7 (это фактический коммит в Git-репозитории для Git).Эти вещи слишком неудобны для использования людьми, тем более что они кажутся совершенно случайными (хотя они не случайны).

Учитывая, что идентификаторы хешей проблематичны, и что мы постоянно расширяем репозитории, добавляя new фиксирует их (сохраняя старые), у Git есть решение.Это имена ветвей, имена тегов и т. Д.В совокупности Git называет эти имена ссылками .Чтобы заставить эти разные имена взаимодействовать, Git помещает их в пространства имен , определение которых я собираюсь оставить в Википедии.Здесь достаточно сказать, что master - это сокращение от refs/heads/master, а тег v2.3 - это сокращение от refs/tags/v2.3.

Git использует these ссылки для хранения хеш-идентификаторов.У вашего Git есть большой стол, который вы можете распечатать в любое время, используя git for-each-ref.Эта сантехническая команда 4 печатает всю таблицу или некоторое подмножество, если вы так говорите, и позволяет вам давать директивы форматирования, сообщающие ей , как распечатать ее, ноВот пример вывода по умолчанию:

$ git for-each-ref
5d826e972970a784bd7a7bdf587512510097b8c7 commit refs/heads/master
5d826e972970a784bd7a7bdf587512510097b8c7 commit refs/remotes/origin/HEAD
98cdfbb84ad2ed6a2eb43dafa357a70a4b0a0fad commit refs/remotes/origin/maint
5d826e972970a784bd7a7bdf587512510097b8c7 commit refs/remotes/origin/master
bc1bbc6f855c3b5ef7fcbd0f688f647c4e5b208b commit refs/remotes/origin/next
dfcf84ebfa17eb0bb3b57806fa530e87d8c8f1b8 commit refs/remotes/origin/pu
b2cc3488ba006e3ba171e85dffbe6f332f84bf9a commit refs/remotes/origin/todo
[massive snippage]
e8f2650052f3ff646023725e388ea1112b020e79 tag    refs/tags/v2.17.0
8548c552c627322ac6e8a221d6fe9be531c3aeb1 tag    refs/tags/v2.17.0-rc0
53e83f73a113f0dbfec850d222681ae21eadd834 tag    refs/tags/v2.17.0-rc1
5c35c7ea8cfdb6951a2e3923309d46138e1724b4 tag    refs/tags/v2.17.0-rc2
5b62a68cad663be4cd19fd59d053c57d88811c80 tag    refs/tags/v2.17.1
7f8020239f3b8ebc28299a97ba7db23d74e65447 tag    refs/tags/v2.17.2

Большая уродливая вещь слева - это хэш-идентификатор, а имя справа, начиная с refs/, - это полное имя ссылки.

Существует еще одна базовая сантехническая команда Git, git rev-parse, которая преобразует ссылку - или краткую форму этой ссылки - в хэш-идентификатор:

$ git rev-parse master
5d826e972970a784bd7a7bdf587512510097b8c7
$ git rev-parse v2.17.0
e8f2650052f3ff646023725e388ea1112b020e79

Некоторые команды Git, напримерgit checkout и git branch, просто естественно ожидайте имени ветви и ожидайте, что вы будете использовать одну без ведущей refs/heads/ части.Другие команды Git, такие как git log, не делают: они обычно запускают git rev-parse, чтобы превратить имя в хэш-идентификатор.

Когда git rev-parse выполняет эту работу, превращатьимя в хеш-идентификаторе, которое действительно нужно большинству команд Git, оно имеет шестистадийную процедуру, которой следует.Эта процедура описана в документации gitrevisions .<refname> здесь - это буквенная строка, введенная вами в команде, например, master или v2.3 или origin/master:

  1. Если существует $GIT_DIR/<refname>, это то, что вы имеете в виду (это обычно полезно только для HEAD, FETCH_HEAD, ORIG_HEAD, MERGE_HEAD и CHERRY_PICK_HEAD);

  2. в противном случае, refs/<refname> если он существует;

  3. в противном случае, refs/tags/<refname>, если он существует;

  4. в противном случае, refs/heads/<refname>, если он существует;

  5. в противном случае refs/remotes/<refname>, если он существует;

  6. в противном случае, refs/remotes/<refname>/HEAD, если он существует.

Эта довольно длинная последовательность на самом деле очень важна: она многое объясняет о Git, особенно в некоторых плохих или сложных случаях.В частности, если вы случайно создаете тег с именем master, Git начинает вести себя странно: git checkout master все еще работает, но git rev-parse master начинает рассказывать вам о теге , а неверхушка ветви.

Давайте вернемся к идее стола на мгновение.В таблице записан текущий идентификатор хеша каждой ссылки.Предполагается, что имена тегов должны быть удобочитаемыми именами для одного конкретного хеш-идентификатора, и поэтому никогда не меняются, но имена branch должны быть удобочитаемыми именами для последнего коммита наветка.Это означает, что сопоставление имени ветви с идентификатором хэша постоянно меняется!Каждый раз, когда вы добавляете новый коммит в ветку master, ваш Git изменяет результат поиска имени master.

Но - за исключением некоторых команд, которые знают они 'Имея дело с ответвлением name - поиск имени master проходит через этот шестиэтапный процесс, потому что Git думает, что master может быть tag name.Итак, на шаге 3 этого процесса Git проверяет таблицу: есть ли refs/tags/master?Если нет, Git переходит к шагу 4 и ищет refs/heads/master.

Это означает, что если вы случайно создадите тег с именем master, some * 1352Команды Git останавливаются на шаге 3 и получают хэш-идентификатор для тега .Другие команды Git, такие как git checkout master, сначала попробуйте ввести имя как branch , начиная с шага 4 и находя хеш-идентификатор из имени ветви!Итак, теперь некоторые команды Git показывают вам тэг commit, а другие работают с branch .


4 A plumbing command в Git - это любая команда, предназначенная для использования другими командами для выполнения какой-то конкретной работы, а не для использования обычными людьми каждый день.Это не значит, что вы не можете использовать их каждый день, но они, как правило, не надуты: они, как правило, не используют цвета или пропускают вещи через пейджер, если они длинные,например.Ориентированные на пользователя команды Git называются фарфор , так как они предназначены для того, чтобы быть чистыми и блестящими, в отличие от отвратительной сантехники за фарфором.


Ваш собственный Git обновляет ваш удаленныйотслеживание имен автоматически

Некоторые из приведенных выше ссылок начинаются с refs/remotes/, за которым следует удаленное имя, например origin, и еще один слеш.Это ваши собственные Git имена для удаленного отслеживания . 5 Они действуют как метод вашего Git'а по запоминанию хеш-идентификаторов в другом Git .

То есть ваш Git имеет вашу большую таблицу, полную ссылок типа refs/heads/master и refs/heads/develop.Но так же их Git: у Git по URL-адресу, указанному в origin, есть свой refs/heads/master и, возможно, собственный refs/heads/develop.Ваш Git хочет, по нескольким причинам, в том числе быть полезным для вас в целом, вспомнить, что их Git записал как их мастер ветвь.Таким образом, ваш Git обновляет ваш refs/remotes/origin/master, чтобы запомнить хэш-идентификатор , который они имели под их refs/heads/master.

Обратите внимание, что эти удаленныеимена трекинга могут быть сокращены до origin/master.Это из-за шага 5 выше: если вы наберете origin/master, и ваш Git еще не нашел хеш-идентификатор на любом из предыдущих этапов, он перейдет к шагу 5, вставит refs/remotes/ впереди, заглянет в таблицу вашего Gitи находит refs/remotes/origin/master.Поэтому, когда вы пишете origin/master, ваш Git превращает его в refs/remotes/origin/master, который содержит хэш-идентификатор, который ваш Git сохранял / обновлял в последний раз, когда ваш Git общался с другим Git в origin.(Вот так!)

Каждый раз, когда вы запускаете git fetch origin (без ссылок), ваш Git обновляет все ваши origin/* имена.Когда вы запускаете git push origin <em>refspec</em>, ваш Git обновляет ваше единственное нажатие на origin/* имя , если толчок успешен, потому что если толчок успешен, ваш Git только что спросил origin Git на установите одно из его имен веток, и они приняли, так что ваш Git знает, какой хэш-идентификатор они только что установили.


5 Git традиционно называет эти удаленнымиотслеживание имен ветвей , что, на мой взгляд, является плохим именем, поэтому я решил назвать их имена для удаленного отслеживания .Это лишь небольшое улучшение, но я думаю, что это улучшение.


Собираем все вместе

ОК , можно сказать, но тег-мастер против ветерана, ну, это дело "Я сломал что-то".Если я ничего не сломал, как это связывается с git remote set-head?

Хорошо, еще раз посмотрите на описание того, что делает git remote set-head (помните, что git remote add -m ... делает git remote set-head):

Устанавливает ... refs/remotes/<name>/HEAD

Теперь рассмотрим шаг 6 процесса gitrevisions (git rev-parse и большинство команд Git):

в противном случае, refs/remotes/<refname>/HEAD, если он существует.

Что делает git remote set-head, так это настраивает так, чтобы шаг 6 делал что-то полезное или, по крайней мере, потенциально полезное.Посмотрите также на вывод, который я получил для своего Git-репозитория для Git, особенно эту строку:

5d826e972970a784bd7a7bdf587512510097b8c7 commit refs/remotes/origin/HEAD

Имя origin/HEAD уже разрешается для конкретного коммита, а именно того же коммита, что и origin/master. 6 . Я могу изменить это так, чтобы origin/HEAD вместо этого ссылался на тот же коммит, как, скажем, origin/next.Для этого я могу использовать git remote set-head.

Если я использую имя origin - удаленное имя - в месте, где Git требуется идентификатор хеша коммита и где Git вызывает git rev-parse, мой Git будет проходить полный шестиэтапный процесс.Мы можем предположить, что мой Git не найдет, например, ветку с именем origin, чтобы он перешел к шагу 6. ​​Дойдя до шага 6, мой Git прочитает мой origin/HEAD и увидитчто я сказал моему Git читать мой origin/next.Таким образом, использование git remote set-head origin next делает мою origin/HEAD ссылку на мою origin/next.

Но ничего из этого не имеет большого значения для git push, потому что когда вызапустите git push, здесь вы не даете простую ссылку, вы даете refspec .Refspec состоит из двух частей, разделенных двоеточием.Если вы запустите:

git push origin origin

ваш refspec буквально origin, что в переносном смысле означает origin:origin.Это не делает ничего полезного.(На самом деле, он делает что-то странное, что, вероятно, является ошибкой. 7 )


6 Здесь есть важная деталь, которую я подчеркиваю:это имя является символьной ссылкой .В сущности, он говорит: «У меня нет хеш-идентификатора, у кого-то другого, а именно refs/remotes/origin/master, есть хеш-код. Иди ищи это имя сейчас».Это означает, что всякий раз, когда мой Git обновляет мой origin/master, он также обновляет значение, которое git rev-parse произведет для моего origin/HEAD.

7 refs/remotes/origin/HEAD:refs/remotes/origin/HEAD, что делает то, чего на самом деле не должно происходить вообще.

Я установил хранилище с помощью крюка предварительного нажатия:

$ cat .git/hooks/pre-push
#! /bin/sh
echo pre-push begins
while read line; do printf '%s\n' "$line"; done
echo pre-push ends

Это предварительное нажатиеHook показывает нам, какую ссылку мой локальный Git попросит установить сервер Git, какой хеш-идентификатор и какой существующий хеш-идентификатор у сервера есть для этой ссылки.Вот что произошло:

$ git push origin origin
pre-push begins
refs/remotes/origin/HEAD 11ae6ca18f6325c858f1e3ea2b7e6a045666336d refs/remotes/origin/HEAD 0000000000000000000000000000000000000000
pre-push ends
Total 0 (delta 0), reused 0 (delta 0)
To [url]
 * [new branch]      origin/HEAD -> origin/HEAD

На сервере Git там на самом деле создал ссылку refs/remotes/origin/HEAD.В этом сервере Git bare репозиторий вообще нет удаленного origin, поэтому он не должен создавать эту ссылку.Все это должно было быть отклонено.

Для моего локального Git было бы логично попросить сервер установить server 'master - чтомой Git представляет собой мой origin/master, с которым связана моя символическая ссылка origin/master, но на самом деле это не то, что происходит.И, даже если это было , как это было переведено, это запрос от моего Git к серверу Git, запрашивающий у сервера установить master сервера для хеш-идентификатора, который мой Git записывает как последнее, что мойGit видел на сервере master.Это не очень полезно.Таким образом, даже если локально разрешить git push origin/HEAD немного лучше, вся идея далеко не уйдет.

(Мой локальный Git на тестовой машине, которая также является здесь сервером, ssh-в себя)немного устарел, на 2.19.1, но довольно современный.)

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