Комментарий Кнуги содержит правильную причину: вы установили push.default
на matching
. Возможно, вы ожидали использовать current
или даже upstream
. Это TL; DR прямо там.
По умолчанию push.default
в современном Git - simple
, что большинство людей считает более безопасным - matching
- это то, что было по умолчанию до 2.0, и у многих людей было много головных болей. Но чтобы понять , почему это так, давайте посмотрим, что делает git push
на высоком уровне. Это также то, что делает git fetch
, так что стоит охватить оба в некоторой степени. Однако я опущу специальные правила, относящиеся к извлечению.
Длинное описание (необязательно)
Во-первых, ваш Git выбирает (одного) другого Git для контакта. (Если вы выбираете или нажимаете на несколько пультов, Git делает это по одному.) Этот другой Git находится по некоторому URL. Обычно вы используете имя типа origin
для предоставления URL, но вы можете указать его напрямую, если хотите. (Это редко хорошая идея - это в основном пережиток доисторического Git.) Или вы можете позволить Git понять это. Если есть только один пульт с именем origin
, Git сделает это правильно каждый раз. : -)
Затем ваш Git вызывает другой Git по этому URL (remote.origin.url
). Этот другой Git имеет своих собственных веток, тегов и других ссылок. У вашего Git есть свой Git список всех своих веток, тегов и других ссылок. Вы можете увидеть то, что видит ваш Git, запустив git ls-remote origin
, который выполняет эти два шага, распечатывает результат и останавливается.
Для git push
следующий шаг зависит от нескольких вещей:
Вы указали refspecs в командной строке? (Если это так, Git использует перечисленные вами refspecs.) Refspecs идет после удаленного имени: например, git push origin <refspec1> <refspec2> ...
. Если вы запустили git push
без дополнительных аргументов, вы не указали никаких refspecs.
Если вы не указали refspecs, есть ли специальное значение по умолчанию для этого пульта? (Если это так, это значение по умолчанию. Обратите внимание, что это refspec , а не строковый литерал, такой как push.default
!)
В противном случае push.default
является значением по умолчанию. Он имеет пять настроек, которые мы рассмотрим ниже.
Для git fetch
есть похожий шаблон, но Git почти всегда заводится с использованием настройки remote.<em>remote</em>.fetch
, например, remote.origin.fetch
, потому что всегда есть такая настройка, и вы, как пользователь, будете стремиться просто запустить git fetch origin
или даже просто git fetch
.
Это оставляет еще один очевидный вопрос: что за хрень в любом случае является refspec?
Refspecs
Вторая простейшая форма refspec выглядит как master:master
или jsm/logging:jsm/logging
- или для git fetch
, как master:origin/master
. То есть есть имя с левой стороны, символ двоеточия :
и имя с правой стороны.
Имя слева - источник , а имя справа - пункт назначения . Каждое имя является ссылкой или ref name, что означает, что вы можете записать полное имя наподобие refs/heads/master
. Если вы не произнесете имя по имени, Git обычно будет правильно угадывать, что master
- это имя ветви, а v1.2
- это имя тега (просматривая имеющиеся у вас ветви и имена тегов), но если он угадает неправильно, или вы хотите быть действительно уверенным, вы можете указать полное имя.
Но я сказал, что это вторая - самая простая форма. Самое простое - полностью опустить двоеточие и пункт назначения: master
или v1.2
или jsm/logging
. Здесь выборка и передача различаются в том, как они к ним относятся: они оба по-прежнему источник для операции, но для git push
, пункт назначения является копией источника. Для git fetch
назначение не сохранять - до отбрасывать - имя. Поскольку мы смотрим на git push
, мы можем пропустить специальную выборку и сосредоточиться на том, как git push
любит использовать одинаковое имя с обеих сторон.
Стоит отметить: вы можете добавить ведущий +
в refspec. Это устанавливает флаг силы только для этого refspec . Давайте рассмотрим простой пример ниже.
Выборки и push в основном о коммитах
TherЭто две вещи, которые нужно достичь и добиться. Первый и самый важный на сегодняшний день - это передача коммитов . Без коммитов у Git нет ничего: коммиты являются причиной существования Git. Они содержат (косвенно) файлы.
Итак, если вы запускаете git push
, ваш Git дает своим Git любые коммиты, которые у вас есть, а они нет, которые им понадобятся. Если вы запускаете git fetch
, ваш Git получает от их Git любые коммиты, которые у них есть, которые вам не нужны и которые вам понадобятся.
Набор коммитов, который вам или им понадобится, определяется достижимостью , что является довольно большой темой. Для действительно хорошего введения в это, см. Думайте, как (а) Git . Тем не менее, в одной строке кратко суммировано, что они будут нуждаться в коммитах, которые находятся в вашей ветви, когда вы нажимаете свою ветку; вам понадобятся коммиты, которые находятся на их ветке, когда вы получите их ветку.
Переместив правильный набор коммитов в правильный Git, ваши два Gits теперь должны взаимодействовать на одном последнем шаге: , задавая несколько имен . Если вы используете git push
, ваш Git попросит, чтобы его Git установил их имена: вы просите их обновить, например, master
, либо обновить или создать jsm/logging
. Если вы используете git fetch
, ваш Git устанавливает origin/master
на основании, например, их master
, и этот конкретный трюк переименования их master
в origin/master
происходит через refspecs настроен в remote.origin.fetch
.
С помощью refspecs Git отправляет или выбирает только те вещи, которые вы указали
Итак, если вы делаете имя некоторого набора refspecs в командной строке, ваш Git будет извлекать или выдавать коммиты, основываясь на именах источников, которые вы перечислили. Получающий Git будет помнить коммиты, извлеченные или выдвинутые путем установки некоторых имен - в вашем Git, если fetch
ing, в их имена, если push
ing - на основе имен получателей, которых вы перечислили.
Обратите внимание, что git push
может отправлять свои окончательные операции по установке имени в виде вежливых запросов - * пожалуйста, установите master
на a123456....
- или в качестве довольно убедительных команд: установите master
на a123456...
или прямо в постель без ужина! Их Git может по-прежнему отказывать в командах, но обычно по умолчанию проверяют вежливые запросы, чтобы увидеть, добавляют ли они просто новые коммиты, и подчиняются принудительным командам.
Без refspecs, git push
отступает, возможно, вплоть до push.default
Если вы просто запустите git push origin
или git push
- с или без флага force - ваш Git использует некоторые настройки по умолчанию. Если у вас нет конкретного refspec для пульта, ваш Git использует push.default
. Вот где его пять настроек:
nothing
: это приводит к сбою git push
, заставляя вас перечислить некоторые refspec (s). (Я сам попробовал это некоторое время, но мне было слишком больно.)
current
: это говорит вашему Git использовать вашу текущую ветку . Возможно, это было то, что вы ожидали. Это эквивалентно git push <em>remote</em> refs/heads/<em>branch</em>:refs/heads/<em>branch</em>
.
upstream
(он же tracking
): это говорит вашему Git использовать вашу текущую ветвь в качестве источника, но использовать его восходящее имя в качестве места назначения. То есть, если ваша текущая ветвь имеет значение B
, но восходящий поток B
равен origin/not-B
, это эквивалентно git push origin B:not-B
. 1
simple
: аналогично upstream
, но требует, чтобы вышестоящее имя совпадало с именем текущей ветви. То есть, если master
имеет origin/master
в качестве восходящего потока, и вы находитесь на master
, git push
подталкивает к origin/master
, как вы ожидаете, но если B
подталкивает к origin/not-B
, и вы находитесь на B
, git push
просто выходит из строя.
matching
: ваш Git просматривает список имен веток в Git (все имена git ls-remote
, начинающиеся с refs/heads/
). Для каждого имени ветви, которое у них есть, ваш Git выдвигает вашу ветку с тем же именем.
Обратите внимание, что если вы используете флаг --force
, это относится ко всем выдвинутым ветвям. Если ваш режим matching
, он применяется к соответствующим веткам. Вот почему ваш вывод гласил:
+ 5a649bc...8d320d2 develop -> develop (forced update)
Ваш мерзавец обнаружил, что и у вас, и у них refs/heads/develop
. Их было 5a649bc
, ваше было 8d320d2
, а 5a649bc
не предок 8d320d2
. Вежливый запрос - * пожалуйста, установите develop
на 8d320d2
- можно было бы отклонить, но с действующим флагом силы ваш Git отправил команду, и их Git повиновался. Это потеряло некоторые коммиты из их develop
, поэтому они сказали «принудительное обновление», и ваш Git напечатал это и три точки (обычный не принудительный push
показывает только две точки).
Если у вас все еще есть коммит 5a649bc
в вашем собственном репозитории, вы можете легко восстановиться после этого. Если нет, это сложнее. Чтобы восстановить, если у вас есть, рассмотрите запуск:
git push origin +5a649bc:refs/heads/develop
При этом используется refspec, в котором установлен +
(флаг принудительной установки), source - это хэш необработанного коммита 5a649bc
, а destination - имя ветви develop
. Обратите внимание, что мудро (и, возможно, даже необходимо) здесь прописать refs/heads/develop
, поскольку "name" источника - это необработанный хэш-идентификатор, поэтому ваш Git не знает, что это должна быть ветвь.
1 Это может или не может вызвать призрак Шекспира. (Или это король Гамлет?)