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
:
Если существует $GIT_DIR/<refname>
, это то, что вы имеете в виду (это обычно полезно только для HEAD
, FETCH_HEAD
, ORIG_HEAD
, MERGE_HEAD
и CHERRY_PICK_HEAD
);
в противном случае, refs/<refname>
если он существует;
в противном случае, refs/tags/<refname>
, если он существует;
в противном случае, refs/heads/<refname>
, если он существует;
в противном случае refs/remotes/<refname>
, если он существует;
в противном случае, 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, но довольно современный.)