Как может я ускоренная перемотка вперед единственная фиксация мерзавца, программно?

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

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

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

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

  2. Как я могу сказать, сколько фиксаций "позади" моего ответвления?

  3. Как может я ускоренная перемотка вперед всего одной фиксацией, так, чтобы, например, мое локальное ответвление пошло бы от "позади 3 фиксациями" к "позади 2 фиксациями"?

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

17
задан Norman Ramsey 23 May 2010 в 04:18
поделиться

3 ответа

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

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

# 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 log был вызван без параметра --all (который перечислял бы все ветки), поэтому невозможно, чтобы текущая фиксация находилась на «боковой ветви» и все еще печатается на выходе.

Количество коммитов перед текущим фиксацией равно количеству строк в $ log перед $ current_commit.

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

ОБНОВЛЕНИЕ : вы можете использовать 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, если вы сохранить результат первого вызова в файл.

10
ответ дан 30 November 2019 в 12:43
поделиться

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

$ 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.
8
ответ дан 30 November 2019 в 12:43
поделиться

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

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

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

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

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

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

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

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

Проверка способности к перемотке

Ветвь на коммите 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 мог перематываться вперёд до upstream (считается только то, насколько HEAD отстаёт от upstream, а не то, насколько upstream отстаёт от HEAD).

Продвижение вперед на один коммит

В общем случае, история с возможностью быстрой перемотки вперед может быть нелинейной. В приведенной ниже DAG истории, master может переместиться вперед на upstream, но и A, и B находятся "на один коммит вперед" от master на пути к upstream.

---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 в качестве безымянной ветки. Пока ваше рабочее дерево чистое и вы не возражаете против перемещения ветки (или отсоединённого HEAD), git reset --hard ничего не испортит. Если вы все еще беспокоитесь, вам стоит серьезно рассмотреть возможность использования git fast-export, где вам вообще не придется трогать рабочее дерево.

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

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

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

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

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

.
11
ответ дан 30 November 2019 в 12:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: