Альтернативные подходы
Вы упоминаете, что работаете над каким-то зеркалом для Git и Darcs. Вместо перетаскивания рабочего дерева по истории, вы можете вместо этого взглянуть на команды git fast-import и git fast-export для посмотрите, предлагают ли они лучший способ управления данными, которые вам нужно извлечь / предоставить.
Как определить, может ли ветвь быстро перейти к вышестоящей ветке
Есть две части этого. Во-первых, вы должны либо узнать, либо определить, какая ветвь является «восходящей» для текущей ветки. Затем, когда вы знаете, как обращаться к восходящему потоку, вы проверяете возможность быстрой перемотки вперед.
Поиск восходящего потока для ветви
В Git 1.7.0 есть удобный способ запрашивать, какая ветка отслеживает ветку (ее «восходящая» ветка). Синтаксис спецификации объекта @{upstream}
может использоваться в качестве спецификатора ветви. Как простое имя, оно относится к восходящей ветви для ветви, которая в настоящий момент извлечена. В качестве суффикса его можно использовать для поиска восходящей ветви для ветвей, которые в данный момент не извлечены.
Для Gits более ранних, чем 1.7.0, вам придется самостоятельно проанализировать параметры конфигурации ветки (branch.name.remote
и branch.name.merge
). В качестве альтернативы, если у вас есть стандартное соглашение об именах, вы можете просто использовать его для определения имени для восходящей ветви.
В этом ответе я напишу upstream
, чтобы сослаться на коммит в конце ветви, которая находится перед текущей ветвью.
Проверка возможности быстрой перемотки вперед
Ветвь при коммите А может быть быстро перенесена на коммит Б, если и только если А является предком Б.
gyim показывает один из способов проверки этого условия (перечислите все коммиты, доступные из B и проверьте A в списке). Возможно, более простой способ проверить это условие - проверить, что A является базой слияния A и B.
can_ff() {
a="$(git rev-parse "$1")" &&
test "$(git merge-base "$a" "$2")" = "$a"
}
if can_ff HEAD local-master/master; then
echo can ff to local-master/master
else
echo CAN NOT ff to local-master/master
fi
Нахождение количества «коммитов позади»
git rev-list ^HEAD upstream | wc -l
Для этого не требуется, чтобы HEAD мог выполнять быструю перемотку вперед до в восходящем направлении (он только подсчитывает, насколько далеко HEAD находится за восходящим потоком, а не как далеко вверх находится за HEAD).
Переместиться вперед на один коммит
В общем, история с быстрой перемоткой может быть не линейной. В приведенной ниже истории DAG master может быстро перейти к upstream , но и A, и B являются «одним коммитом вперед» от master на пути к * * перед тысячей пятьдесят-одина * 1 052 *.
---o---o master
|\
| A--o--o--o--o--o--o upstream
\ /
B---o---o---o---o
Вы можете следовать одной стороне, как если бы это была линейная история, но только до непосредственного предка коммита слияния.
Команды просмотра ревизии имеют опцию --first-parent
, которая позволяет легко следовать только коммитам, которые приводят к первому родителю коммитов слияния. Объедините это с git reset , и вы сможете эффективно перетаскивать ветку «вперед, один коммит за раз».
git reset --hard "$(git rev-list --first-parent --topo-order --reverse ^HEAD upstream | head -1)"
В комментарии к другому ответу вы выражаете из страха перед git reset . Если вы беспокоитесь о повреждении какой-либо ветви, то вы можете использовать временную ветку или отсоединить HEAD в качестве неназванной ветви. Пока ваше рабочее дерево чистое и вы не возражаете перемещать ветку (или отдельную ГОЛОВКУ), git reset --hard
ничего не будет выбрасывать. Если вы все еще волнуетесь, вам следует серьезно заняться использованием git fast-export , когда вам вообще не нужно прикасаться к рабочему дереву.
Следить за другим родителем будет сложнее. Возможно, вам придется написать свой собственный ходок по истории, чтобы вы могли дать ему совет относительно того, «в каком направлении» вы хотите идти при каждом слиянии.
Когда вы продвигаетесь к точке, которая находится чуть ниже слияния, группа обеспечения доступности баз данных будет выглядеть следующим образом (топология такая же, как и раньше, перемещается только метка master ): * * 1077
---o---o--A--o--o--o--o--o master
| \
| o upstream
\ /
B---o---o---o---o
На данный момент, если вы«Двигаться вперед на один коммит», вы перейдете к слиянию. Это также «принесет» (сделает доступным из master ) все коммиты от B до коммита слияния. Если вы предполагаете, что «продвижение вперед на один коммит» добавит только один коммит в историю DAG, тогда этот шаг нарушит это предположение.
Возможно, вы захотите тщательно обдумать, что вы действительно хотите сделать в этом случае. Можно просто перетаскивать дополнительные коммиты, как это, или должен быть какой-то механизм для «возврата» к родителю B и продвижения вперед по этой ветви, прежде чем вы обработаете коммит слияния?