Как я могу ограничить git rev-list определенной веткой и удаленной? - PullRequest
0 голосов
/ 08 ноября 2018

Я хочу иметь возможность искать git rev-list для определенных комбинаций удаленных / ответвлений. Меня смущает документация git rev-list , касающаяся параметров --branches и --remotes, которые, как мне кажется, я и хочу использовать.

Для --remotes документация гласит:

- пультов ДУ [= шаблон]

Сделайте вид, что все ссылки в refs / remotes перечислены в командной строке как [commit]. Если задан [шаблон], ограничьте ветви удаленного отслеживания ветвями, соответствующими заданному глобусу оболочки. Если шаблон отсутствует?, * Или [, / * в конце подразумевается.

Я не понимаю, что это значит, когда он говорит, что они перечислены в командной строке, как "commit".

Часть моей путаницы заключается в том, что я не уверен, какой формат строки --branches и --remotes просматривается, поэтому я оставляю попытки использовать подстановочные знаки в различных местах, чтобы посмотреть, работает ли это. Я бы предположил, что ответвления будут похожи на «remotes / remote_name / branch_name», а для удаленных - «remote_name», но когда я пытаюсь выполнить поиск с учетом этого, я не получаю ожидаемых результатов.

Например, вот базовый запрос, который я хочу выполнить:

 git rev-list --after='timestamp' --author="Name" --format='%h,%aI,%cI'

Я использовал варианты --branches=*remote_name/branch_name --remotes=remote_name с различными конфигурациями подстановочных знаков, но это никогда не работает. Под этим я подразумеваю то, что я знаю, что в определенных комбинациях удаленных / ветвлений, которые существуют в коммите А, существует, но иногда все ветви для конкретного пульта находят коммит А, даже если это не так, иногда нет ветвей на удаленном не-источнике. вернет коммит А, хотя я знаю, что он существует там и т. д.

Думаю, если бы я просто лучше понял, как искать с помощью --branches и --remotes, я бы смог собрать его воедино. Может быть, rev-list не предназначен для поиска с обоими этими опциями.

Любая помощь будет принята с благодарностью.

Спасибо.

Ответы [ 2 ]

0 голосов
/ 09 ноября 2018

Решение

Решение для поиска как на удаленном компьютере, так и в филиале заключается в использовании следующей опции:

git rev-list ... --remotes=*remote_name/branch_name

Использование здесь опции --remotes и --branches может работать, но только если вы отметили соответствующую ветку.

Объяснение

Использование --branches=*branch_name ищет ветви с указанным именем в refs/heads, это папка, в которой хранится информация о ваших извлеченных ветвях. Это означает, что он может только искать историю изменений git на основе веток, которые вы уже извлекли.

При использовании --remotes=*remote_name выполняется поиск пультов с этим именем, если они были добавлены в ваш локальный список пультов (refs/remotes - это папка, в которой хранится информация обо всех добавленных вами пультах).

Если вы используете эти две опции вместе, rev-list может найти ваш пульт, но не сможет найти вашу ветку , если вы не проверили его .

Однако refs/remotes на самом деле хранит информацию о всех филиалах на этих пультах, так что это все, что вам нужно для поиска, используя --remotes=*remote_name/branch_name.

Вы можете увидеть, какая информация у вас есть в refs, перейдя в фактическую папку в вашей скрытой папке .git. Из вашей папки git просто cd .git/refs.

0 голосов
/ 09 ноября 2018

TL; DR

git rev-list - это обход графа коммитов (DAG). Вы даете это отправные точки. --branches - это один из способов дать ему различные отправные точки; --remotes это другое. В отличие от git log, который по умолчанию использует HEAD в качестве начальной точки, вы должны дать некоторую фиксацию начальной точки для git rev-list. Затем в нем перечисляются достижимые коммиты: имена актуальны только с точки зрения определения местоположения начальных точек для обхода графа.

Long

В Git вы должны помнить, что такое ветвь (или нет). Без этой концепции вы сильно заблуждаетесь. Для сравнения давайте сначала рассмотрим Mercurial, где branch - более конкретная вещь (хотя свободное и небрежное определение Git branch в наши дни также просачивается в использование Mercurial).

В Mercurial вы создаете ветку, используя:

hg branch <name>

и затем сделайте коммиты на нем. Эти коммиты на этой ветви, навсегда (ну, до тех пор, пока они не будут лишены , но если игнорировать возможность удалять коммиты, они всегда там). Если commit C находится на ветке X на X, а не на любой другой ветке. Спросите, какие коммиты находятся на ветке X, и она всегда будет включать C. Имеет смысл, верно? Комитеты сшиваются навсегда в их филиал; ветвь - это коллекция всех коммитов, когда-либо сделанных в этой ветке.

В Git это не так. Вы делаете коммит C на ветке X, а он на ветке X, но через несколько минут (или дней или месяцев) он также на ветках Y и Z и master. На этом этапе полностью удалите ветку X, и коммит C все еще там, все еще в ветвях Y и Z и master. Вы можете даже переместить имя X, чтобы фиксация C была , а не на X больше, но все еще на master.

Другими словами, в Git коммиты не прикреплены к их ветвям. То, находится ли коммит в какой-либо ветви, является динамическим свойством, которое может измениться в будущем. Коммиты существуют с или без веток, содержащих их. коммиты имеют значение; ветки только для слабых людей. 1 Но коммиты постоянны (ну, в основном) и доступны только для чтения, замороженные навсегда. Так что коммиты исправлены; это имена ветвей , которые перемещаются.

Имея это в виду, давайте нарисуем несколько графиков.


1 Имена ветвей или другие ссылки служат в Git нескольким другим целям, но лучше начать с этого как своей модели. Как только вы поймете эту концепцию, все остальное станет на свои места.


Git commit graphs

Каждый коммит записывает своего непосредственного предшественника или parent , commit (s). В простейшем случае один коммит имеет одного родителя, а родитель этого коммита имеет одного родителя и т. Д. Начиная с конца цепи, мы можем легко работать в обратном направлении:

A  <-B  <-C  ...  <-G <-H   <--master

Ветка name , в данном случае master, хранит необработанный хэш-идентификатор last commit, который должен считаться частью этой ветви. В коммите H хранится еще один хэш-идентификатор коммита для его коммита-предшественника G. G хранит хеш-идентификатор своего родителя и т. Д. Обратно через commit B. В коммите B хранится хэш-идентификатор A, но A - самый первый коммит в истории, поэтому у него нет родителя.

(Поскольку сами коммиты исправлены, больше нет необходимости рисовать внутренние стрелки в виде стрелок, что помогает в следующем шаге, потому что рисование стрелок в тексте довольно ограничено. Поэтому теперь я буду использовать:

A--B--C   <-- master

например. Однако стрелки с названиями ветвей много двигаются, поэтому полезно сохранять их в виде стрелок.)

Добавление нового коммита в ветку в Git - это вопрос замораживания исходного снимка и создания объекта коммита. Новый объект фиксации записывает текущий хеш-идентификатор фиксации, поэтому мы поддерживаем эту обратную цепочку. Новый коммит затем получает свой новый уникальный хэш-идентификатор, и Git записывает новый хэш-идентификатор в имя ветви:

A--B--C   <-- master

становится:

A--B--C--D   <-- master

Но мы можем добавить больше названий веток. Прежде чем мы сделаем еще один новый коммит, давайте сделаем это: давайте сделаем имя ветви develop также , указывающее на коммит D:

A--B--C--D    <-- master, develop

Теперь Git нужно знать , какое имя ветви для обновления, поэтому Git присоединяет имя HEAD к одному (и только одному) имени ветви:

A--B--C--D    <-- master, develop (HEAD)

Теперь, когда мы делаем новый коммит E, имя ветви, которое обновляет Git, больше не master, а скорее develop:

A--B--C--D    <-- master
          \
           E   <-- develop (HEAD)

фиксирует E сейчас на develop. Коммиты A и D также находятся в разработке и находятся на master. Давайте git checkout master сейчас и сделаем новый коммит F:

A--B--C--D--F    <-- master (HEAD)
          \
           E   <-- develop

Commit F теперь only on master, в то время как E is only on develop, а A-B-C-D оба.

Если мы сделаем еще несколько коммитов при разработке, мы получим:

A--B--C--D--F    <-- master
          \
           E--G--H   <-- develop

(я пропустил HEAD здесь, так как мы все равно собираемся его переместить.)

Теперь давайте запустим git checkout master && git merge develop. Это объединит все наши master изменения с момента общего коммита D со всеми нашими develop изменениями с момента общего коммита D. То есть Git будет работать:

git diff --find-renames <hash-of-D> <hash-of-E>   # what happened on master
git diff --find-renmaes <hash-of-D> <hash-of-H>   # what happened on develop

Git объединит два набора изменений, применяет их к снимку в D и сделает новый коммит I, имеющий двух родителей:

A--B--C--D--F------I    <-- master (HEAD)
          \       /
           E--G--H   <-- develop

Commit I возвращается к и H и F, а H возвращается к G, затем к E, а затем сделанная нами вилка возвращается в обратном направлении на D, что приводит к C и B и, наконец, A. Итак, все эти коммиты теперь включены master. Коммиты I и F являются только на master, в то время как A-B-C-D-E-G-H находятся на обеих ветвях.

Это особенность коммита слияния. Теперь, когда у нас есть коммит слияния, мы можем удалить имя develop:

A--B--C--D--F------I    <-- master (HEAD)
          \       /
           E--G--H

Все коммиты остаются на master. Имя develop больше не существует. Это было когда-нибудь, или это был мираж? Git в любом случае не волнует.

git rev-list и git log

И git rev-list, и git log связаны с этим графом коммитов. Эти две команды довольно близки друг к другу - на самом деле они построены из одного и того же исходного файла, с двумя разными точками входа в зависимости от того, выполняли ли вы git log или git rev-list. Оба будут проходить по графику, начиная с заданных вами концов и работая в обратном направлении. В то время как git log по умолчанию показывает каждый пройденный коммит, git rev-list по умолчанию просто печатает их хэш-идентификаторы.

Другое ключевое отличие - это HEAD понятие. Git хочет отслеживать, какую ветку вы извлекли, прикрепив имя HEAD к одному имени ветви. Команда git log по умолчанию ищет HEAD и использует ее в качестве своей (единственной) начальной точки; git rev-list требует, чтобы вы дали ему некоторую отправную точку (и).

Они оба делают это "начни с конца и работай задом наперед". Если master указывает на коммит I, как в нашем примере выше, и I является коммитом слияния, который указывает на коммиты F и H, оба сначала покажут, что вы зафиксировали I (или его хеш ID), затем выберите один из F и H, чтобы отобразить следующий. Затем они покажут G и EF, если они еще этого не сделали, где-то в этом миксе), а D и C and B and A , and since A` не имеют родителя , остановись там.

В конце оба будут показывать или перечислять каждый коммит, поскольку каждый коммит достижим с заданной начальной точки, коммит I. Если вы дадите им другую отправную точку, такую ​​как commit D, они покажут вам D, C, B, A.

.

Шаблоны (с --branches=*) могут быть хитрыми, потому что интерпретаторы командной строки ("оболочки") имеют тенденцию делать свое собственное расширение *.Если вы запустите echo *, вы увидите список всех имен ваших файлов.Если вы запустите echo foo*, вы увидите только имена файлов, начинающиеся с foo.--branches=* может попытаться развернуть файлы, имена которых начинаются с --branches=, особенно если у вас есть файлы с этими именами.

Если вы опустите * в конце, Git предоставит его внутренне, чтоизбегает снарядов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...