Почему синтаксис git push для создания новой удаленной ветки от отдельно стоящей HEAD настолько отличается?

Недавно у меня был сценарий, в котором я находился в отдельном состоянии HEAD. Я хотел подтолкнуть это к вилке на github, чтобы поделиться некоторым кодом работы с партнером по команде. Я не нуждался в имени локальной ветки для этого конкретного коммита.

Ясно, что это не сработало бы:

git push sandy-github HEAD

Это имеет смысл, поскольку я не указывал имя для удаленной ветки.

Но я не понимаю, почему это не сработало:

git push sandy-github HEAD:mynewbranch

Это привело к следующей ошибке:

ошибка: невозможно нажать на неквалифицированный адрес: mynewbranch Пункт назначения refspec не совпадает с существующим ссылкой на удаленный начинается с refs/, и мы не можем угадать префикс на основе исходного кода ref. error: не удалось нажать некоторые ссылки на git @github.com: sandyarmstrong/myreponame.git '

Мне пришлось делать:

git push sandy-github HEAD:refs/heads/mynewbranch

Это сработало. Из документов:

git push origin master: refs/heads/experimental

Создайте экспериментальную ветвь в исходном репозитории, скопировав текущую главную ветвь. Эта форма нужна только для создания нового ветки или тега в удаленном репозитории, когда локальное имя и удаленное имя отличается; в противном случае имя ссылки само по себе будет работа.

Я просто не понимаю, почему это было необходимо. Я предполагаю, что там что-то важное о git Я не понимаю здесь. Почему этот сложный синтаксис необходим только потому, что имена не совпадают? Почему синтаксис HEAD:mynewbranch не достаточен, чтобы позволить git знать, что он должен сгенерировать новую ветку на удаленном носителе с именем "mynewbranch"?

Ответ 1

В разделе refspec git push документации есть (основное внимание):

<dst> сообщает, какой ref на удаленной стороне обновляется этим нажатием. Здесь нельзя использовать произвольные выражения, фактический ref должен быть назван. Если :<dst> опущен, будет обновлен тот же ref, что и <src>.

В нормальном состоянии git может определить, что вы хотели нажать на определенную ветвь (или тег) на основе отслеживания ветвления и/или значений push.default в config.

В отключенном состоянии HEAD git не может догадаться, хотите ли вы создать новую ветку или новый тег (оба могут быть разумными здесь).

Можно предположить, что ветвь создается путем создания локальной ветки первой:

git checkout -b mynewbranch
git push -u sandy-github mynewbranch

Если вы не хотите проверять ветку, которую вы нажимаете, вы можете использовать префикс refs/heads/, как вы упомянули в своем вопросе:

git push sandy-github HEAD:refs/heads/mynewbranch

Ответ 2

Git имеет множество "магии" или "DWIM" * во многих командах. "Push" имеет это: если вы нажмете локальную ветвь b на пульт дистанционного управления, а пульт имеет b, он использует часть magic/DWIM для согласования локального ref (чье "реальное" имя refs/heads/b) с удаленный (имя которого на пульте дистанционного управления также равно refs/heads/b). Но вы также можете нажать refs/tags/t, или теперь, когда существуют примечания, refs/notes/commits.

У этого уже есть magic/DWIM, чтобы понять, что локальный ref y, который разрешает локальное полное имя refs/x/y, должен создать тот же самый refs/x/y на удаленном компьютере, когда вы используете y:y, даже если y не существует на пульте дистанционного управления. Просто, если локальное полное имя не разрешает что-то подобное, это не удается. Он может даже разрешать HEAD, когда это символическое имя ветки, но когда HEAD "отсоединен", нет символического имени.

Можно предположить, что git push HEAD:foo "означает" для создания refs/heads/foo. Вероятно, это, скорее всего, то, что вы имели в виду. Но это еще не все.

* DWIM: "Делайте то, что я имею в виду" (в отличие от того, что я говорю).

Ответ 3

Это действительно отлично работает для меня. Вы используете более старую версию git или что-то еще?

Для справки я выполнил следующее:

MacBook:AndroidAsync[master*]$ git commit -a -m gitignore
[master 69851e1] gitignore
 1 file changed, 1 insertion(+)
MacBook:AndroidAsync[master]$ git push origin HEAD:master
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 343 bytes, done.
Total 3 (delta 1), reused 0 (delta 0)
To ssh://[email protected]/koush/AndroidAsync
   04b2f79..69851e1  HEAD -> master

Версия:

MacBook:AndroidAsync[master]$ git --version
git version 1.8.2.1 (Apple Git-45)

Также подтверждено, что он работает от отдельной главы:

MacBook:AndroidAsync[(no branch)]$ git push origin HEAD:master
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 332 bytes, done.
Total 3 (delta 1), reused 1 (delta 0)
To ssh://[email protected]/koush/AndroidAsync
   69851e1..ae2d1be  HEAD -> master