Git pre-push hook, перечисляющий все неотталкиваемые коммиты

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

git rev-list BRANCH --not --remotes=origin отлично работает во всех случаях, кроме тех случаев, когда пульт, на который вы нажимаете, пуст. В таком случае команда ничего не вернет.

Можно ли предположить, что если удаленный sha arg равен 00000 и git rev-list BRANCH --not --remotes=origin возвращается пустым, то все коммиты должны быть перечислены git rev-list BRANCH?

Есть ли лучший способ получить информацию, которая будет работать во всех случаях?


person kylawl    schedule 20.12.2014    source источник


Ответы (1)


Мне не совсем понятно, чего именно вы собираетесь достичь, но каждый раз, когда вы запускаете 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
comment
Чтобы было ясно, то, что я здесь ищу, - это создать список коммитов, которые будут отправлены на удаленный компьютер изнутри pre-push hook. Ничего страшного, если это неточно, если оно консервативно. Так сказать. Некоторые из коммитов могли попасть на удаленный компьютер из другого источника до того, как мой push завершился. Это нормально. Спасибо за подробный ответ. - person kylawl; 20.12.2014
comment
Этот пример работает, кроме случая новой ветки. Он перечисляет ВСЕ коммиты. Меня вообще не волнуют ветки. Меня беспокоит только то, что удаленное репо выполняет или не выполняет фиксацию / файлы. - person kylawl; 20.12.2014
comment
Да, пример ловушки консервативен: если на пульте дистанционного управления нет метки B, и вы просите пульт установить новую метку B, указывающую на фиксацию C, вы потенциально выставили каждую фиксацию, доступную с идентификатора C. Как отмечалось выше, если вы хотите увидеть, что удаленное устройство уже может достичь из какой-либо существующей ветки, --not --remotes=origin сделает это (хотя это также может быть чрезмерно консервативным из-за тегов или других элементов в одном и том же push-сообщении). - person torek; 20.12.2014