Подтвердить что ты не робот

Как я могу перепрограммировать один программный код git, программно?

Я периодически получаю сообщение от git, которое выглядит так:

Your branch is behind the tracked remote branch 'local-master/master' 
by 3 commits, and can be fast-forwarded.

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

  • Как я могу узнать, может ли моя текущая ветка быстро пересылаться из удаленной ветки, которую она отслеживает?

  • Как я могу узнать, сколько коммитов находится за моей ветвью?

  • Как я могу выполнить перемотку вперед одним сообщением, так что, например, моя локальная ветка переместилась бы с "позади 3 коммитов" на "позади 2 коммиты"?

(Для тех, кто заинтересован, я пытаюсь собрать качественное зеркало git/darcs.)

4b9b3361

Ответ 1

Удаленная ветка может быть быстро перенаправлена ​​в локальную ветвь, если текущая фиксация является предком удаленной ветки ветки. Другими словами, если "история с одним ветки" удаленной ветки содержит текущую фиксацию (потому что, если это так, то уверены, что новые коммиты были зафиксированы "на" текущей фиксации)

Таким образом, безопасный способ определить, можно ли переадресовать удаленную ветку:

# Convert reference names to commit IDs
current_commit=$(git rev-parse HEAD)
remote_commit=$(git rev-parse remote_name/remote_branch_name)

# Call git log so that it prints only commit IDs
log=$(git log --topo-order --format='%H' $remote_commit | grep $current_commit)

# Check the existence of the current commit in the log
if [ ! -z "$log" ]
  then echo 'Remote branch can be fast-forwarded!'
fi

Обратите внимание, что журнал git был вызван без параметра -all (который будет перечислять все ветки), поэтому невозможно, чтобы текущий фиксатор находился в "боковой ветке" и все еще печатается на выходе.

Число коммитов, предшествующих текущему фиксации, равно количеству строк в $log до $current_commit.

Если вы хотите перемотать только одну фиксацию, вы берете строку, предшествующую текущей фиксации (например, grep -B 1), и reset локальную ветвь для этой фиксации.

UPDATE: вы можете использовать git log commit1..commit2 для определения количества быстрых переадресаций:

if [ ! -z "$log" ]
then
  # print the number of commits ahead of the current commit
  ff_commits=$(git log --topo-order --format='%H' \
    $current_commit..$remote_commit | wc -l)
  echo "Number of fast-forwarding commits: $ff_commits"

  # fast-forward only one commit
  if [ $ff_commits -gt 1 ]
  then
    next_commit=$(git log --topo-order --format='%H' \
      $current_commit..$remote_commit | tail -1)
    git reset --hard $next_commit
  fi
fi

Конечно, вы можете сделать это с помощью одного вызова git, если вы сохраните результат первого вызова в файл.

Ответ 2

Альтернативные подходы

Вы упомянули, что работаете над каким-то зеркалом для Git и Darcs. Вместо перетаскивания рабочего дерева через историю вы можете вместо этого взглянуть на git fast-import и git fast-export, чтобы узнать, предлагают ли они лучший способ управления данными, которые необходимо извлечь/предоставить.

Как сообщить, может ли филиал быстро переадресовываться в свою ветвящуюся ветвь

Есть две части. Во-первых, вам нужно либо знать, либо определять, какая ветка является текущими ветвями "вверх по течению". Затем, как только вы знаете, как обращаться к восходящему потоку, вы проверяете возможность ускоренной перемотки вперед.

Поиск выходного потока для ветки

git 1.7.0 имеет удобный способ запроса к ветке ветки (ее ветвь "вверх по течению" ). Синтаксис спецификации объекта @{upstream} может использоваться как спецификатор ветвления. В качестве голого имени это относится к ветке восходящего потока для ветки, которая в настоящее время проверена. В качестве суффикса его можно использовать для поиска ветки восходящего потока для ветвей, которые в настоящее время не проверяются.

Для Gits до 1.7.0 вам придется самостоятельно анализировать параметры конфигурации ветки (branch.name.remote и branch.name.merge). В качестве альтернативы, если у вас есть стандартное соглашение об именах, вы можете просто использовать это, чтобы определить имя для ветки вверх по течению.

В этом ответе я напишу upstream, чтобы ссылаться на фиксацию на кончике ветки, которая находится выше по течению от текущей ветки.

Проверка возможности быстрой перемотки вперед

Разветвление в commit A может быть быстро перенаправлено для фиксации B тогда и только тогда, когда A является предком B.

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 истории мастер мог бы быстро переходить в восходящий поток, но оба A и B являются "одним движением вперед" от ведущего по пути вверх по течению.

---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, где вам вообще не нужно касаться рабочего дерева.

Следующим за другим родителем будет сложнее. Вам, вероятно, придется написать свой собственный хореограф истории, чтобы вы могли дать ему совет относительно "того направления", которое вы хотели использовать для каждого слияния.

Когда вы перешли к точке, близкой к слиянию, DAG будет выглядеть следующим образом (топология такая же, как и раньше, это только перемещение ведущей метки):

---o---o--A--o--o--o--o--o    master
       |                  \
       |                   o  upstream
        \                 /
         B---o---o---o---o

В этот момент, если вы "переместитесь на одно коммит", вы перейдете к слиянию. Это также будет "приносить" (сделать доступным от мастера) все фиксации от B до фиксации слияния. Если вы предполагаете, что "перемещение вперед одного коммита" добавит только одну фиксацию в DAG истории, то этот шаг нарушит это предположение.

Вероятно, вы захотите внимательно рассмотреть, что вы действительно хотите сделать в этом случае. Это нормально, просто перетащить дополнительные коммиты, подобные этому, или должен быть какой-то механизм "возврата" к родительскому элементу B и перемещение вперед по этой ветке до того, как вы обработаете фиксацию слияния?

Ответ 3

Это, вероятно, не самый элегантный, но он работает:

$ git fetch
$ git status | sed -n 2p
# Your branch is behind 'origin/master' by 23 commits, and can be fast-forwarded.
$ git reset origin/master~22 > /dev/null
$ git status | sed -n 2p
# Your branch is behind 'origin/master' by 22 commits, and can be fast-forwarded.