Мне не совсем понятно, чего именно вы собираетесь достичь, но каждый раз, когда вы запускаете git push
:
- ваш git вызывает их git (на пульте дистанционного управления) и выясняет, что у него есть;
- вы сообщаете своему git - часто неявно - какие имена ветвей (и / или другие ссылки) он должен смотреть на вашей стороне, и какие имена ветвей он должен пытаться передать на «их» стороне, используя «refspecs» (пары имен с двоеточием между ними).
То есть вы можете запустить:
git push origin mybranch:master
or:
git push origin branch1:branch1 branch2:branch2 branch3:newname
или даже:
git push origin 'refs/heads/*:refs/heads/*'
Вы также можете запустить:
git push origin refs/tags/v1.2:refs/tags/v1.2
или (с --tags
) включить пару refs/tags/*
, скорее как строку refs/heads/*
.
Другими словами, вы можете нажимать не просто ветку (вы можете нажать несколько), или вы можете нажимать не ветку вообще, а тег, или вы можете нажимать ветки и теги. (В этом отношении есть также «заметки». Заметки находятся в refs/notes/
, который представляет собой несколько новое пространство имен, которое обычно не переносится, но обратите внимание на слово «обычно».)
В хук pre-push вы должны прочитать несколько строк из стандартного ввода. Для каждого ссылочного имени, которое вы предлагаете создать, удалить или обновить на пульте дистанционного управления, будет одна строка.
В каждой строке вы получаете (как примечания документации) локальное ссылочное имя, 1 локальный SHA-1, удаленное ref-name и удаленное SHA-1, все в указанном порядке. Вы можете определить, просили ли вы свой git создать или удалить удаленное ref-name, проверив два SHA-1. Максимум один из них будет 40 0
с. Для нормального обновления ни один из них не будет полностью нулевым.
Может не быть новых коммитов или даже не быть новых объектов, 2, участвующих в поставленном обновлении ref-name. Например, при создании нового тега, указывающего на существующую фиксацию, больше нечего делать: вы просто спрашиваете удаленный «пожалуйста, создайте этот новый тег, указывающий на существующую фиксацию 1234567890123456789012345678901234567890
» или что-то еще. Однако, если вы просто удаляете какую-то историю коммитов (с принудительным нажатием), это тоже не имеет новых коммитов: вы просто просите удаленный «пожалуйста, измените branch
, чтобы он указывал на этот новый ID».
Чтобы узнать, какие новые объекты (если таковые имеются) будут отправлены, не следует смотреть на свои собственные имена, поскольку они могут быть устаревшими. Вместо этого вы должны сделать то же самое, что и git: сконцентрироваться на идентификаторах SHA-1.
Однако здесь есть небольшая проблема. Предположим, например, что вы просите удаленный компьютер обновить ref-name refs/heads/branch
с 1234567...
до 9abcdef...
, чтобы удаленный SHA-1 был 1234567...
, а локальный SHA-1 - 9abcdef...
. Это может быть - действительно, обычно - движение "вперед":
... <- 1234567... <- 5555555... <- 9abcdef... <-- refs/heads/branch
(где числа здесь - это идентификаторы SHA-1 фактических объектов фиксации, и вы просто просите удаленный компьютер переместить свою ветку branch
вперед на две фиксации). Однако возможно, что на пульте ДУ уже есть коммиты 5555555...
и 9abcdef...
, но не branch
:
... <- 1234567... <-- branch
\
5555555... <- 9abcdef... <-- develop
В этом случае, когда вы обновляете их branch
, перемещая его вперед на две фиксации, это две фиксации, которые уже были где-то в репозитории (фактически, в ветке develop
).
Тем не менее, это два коммита, которых не было branch
раньше, и которые будут выполнены позже, если push завершится успешно (ваш pre-push
хук может остановить его, но может и удаленный: он может запускать свои собственные хуки и принимать решение отклонить ваш push).
Чтобы перечислить эти две фиксации, просто используйте git rev-list
с необработанными значениями SHA-1, как в этом образец ловушки, который я нашел на github.
Если вы спрашиваете, как можно избежать перечисления этих двух коммитов, ответ будет таков: не существует 100% надежного метода. Вы можете подойти достаточно близко, запустив git fetch
3 перед запуском git push
. Это позволит вам найти все ссылочные имена, которые пульт дистанционного управления хочет вам экспортировать, и их значения SHA-1. Любой объект фиксации, который можно найти по их ссылочным именам, обязательно находится в удаленном репозитории.
Здесь git rev-list ... --not --remotes=origin
действительно в основном 4 правильных вещи: после запуска git fetch
, чтобы получить свою копию их ссылок, вы можете использовать необработанный SHA-1, чтобы найти достижимые коммиты, а также использовать все эти копии для исключить коммиты, доступные из любой удаленной ветки. Недостаток здесь не только в том, что указан в сноске четыре (теги), но и в том, что независимо от того, насколько быстрой будет ваша последовательность fetch
-затем-push
, ссылки, которые вы копируете, могут быть устаревшими к моменту выполнения push
. Вы можете сделать это окно очень маленьким, но не устранять его (используя только git).
1 Здесь есть предостережение, также отмеченное в документации: у локального SHA-1 может не быть имени. Это, очевидно, тот случай, когда вы просите пульт удалить ссылку, поскольку вы запрашиваете это с помощью git push :ref-to-delete
: в левой части refspec нет name. Однако это также верно, если вы нажимаете необработанный SHA-1 или относительную ссылку, как в gitrevisions. В общем, это не так уж и важно, поскольку локальное ref-name, если оно есть, не влияет на удаленный: все действия происходят из-за двух SHA-1 и удаленного ref-name.
2 Помните, git push
подталкивает все необходимые объекты, а не только фиксирует: фиксация указывает на дерево, поэтому, если есть новая фиксация, вероятно, есть новое дерево; деревья указывают на другие деревья и капли, поэтому могут быть дополнительные деревья и капли; а аннотированный тег - это отдельный тип объекта. Все это можно передать во время толчка.
3 Вы можете использовать git ls-remote
для получения текущих сопоставлений имен ссылок, но проблема здесь в том, что если в вашем локальном репозитории отсутствуют соответствующие объекты, вы не можете связать их со своей собственной историей репозитория, чтобы найти какие именно предметы у них есть, а у вас нет. Единственный способ узнать, что у них есть, - это использовать git fetch
, чтобы получить не только объекты, на которые указывают эти ссылки, но и сами объекты, чтобы построить граф фиксации.
4 Это, конечно, полностью исключает теги.
Коммиты на пульте дистанционного управления могут быть доступны через теги. Однако, если вы переносите их пространство имен тегов, вы (и git) обычно делаете это, копируя все эти теги в свое пространство имен. Эти теги не помечены по их происхождению, поэтому невозможно определить, является ли тег v1.2
вашим тегом, их тегом или и тем, и другим. Если вы исключите коммиты, доступные по тегам, вы можете исключить слишком много коммитов.
Чтобы правильно отличить теги пульта дистанционного управления от ваших собственных или любых других удаленных тегов, вам необходимо (заново) изобрести "удаленные теги" .
person
torek
schedule
20.12.2014