17

次のようなメッセージを git から定期的に受け取ります。

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

次のことができるシェルスクリプトでコマンドを記述できるようにしたいと思います。

  1. 現在のブランチが追跡しているリモート ブランチから早送りできるかどうかを確認するにはどうすればよいですか?

  2. ブランチの「背後」にあるコミットの数を確認するにはどうすればよいですか?

  3. たとえば、私のローカル ブランチが「3 コミット後」から「2 コミット後」になるように、コミットを1 つだけ早送りするにはどうすればよいですか?

(興味のある方のために、高品質の git/darcs ミラーをまとめようとしています。)

4

3 に答える 3

11

別のアプローチ

あなたは、Git と Darcs のある種のミラーに取り組んでいると述べています。作業ツリーを履歴からドラッグする代わりに、git fast-importおよびgit fast-exportコマンドを調べて、抽出/提供する必要があるデータを管理するためのより良い方法が提供されているかどうかを確認できます。

ブランチがその上流ブランチに早送りできるかどうかを確認する方法

これには 2 つの部分があります。まず、どのブランチが現在のブランチの「上流」であるかを知るか、決定する必要があります。次に、アップストリームを参照する方法を理解したら、早送りできるかどうかを確認します。

ブランチのアップストリームを見つける

Git 1.7.0 には、ブランチが追跡するブランチ (その「上流」ブランチ) を照会する便利な方法があります。@{upstream}オブジェクト指定構文は分岐指定子として使用できます。素の名前として、現在チェックアウトされているブランチの上流ブランチを指します。サフィックスとして、現在チェックアウトされていないブランチの上流ブランチを見つけるために使用できます。

1.7.0 より前の Git の場合、ブランチ構成オプションを自分で解析する必要があります (branch.name.remoteおよびbranch.name.merge)。または、標準の命名規則がある場合は、それを使用して上流ブランチの名前を決定できます。

この回答upstreamでは、現在のブランチの上流にあるブランチの先端にあるコミットを参照するように記述します。

早送り機能の確認

A が B の祖先である場合にのみ、コミット A のブランチをコミット B に早送りできます。

gyim は、この状態をチェックする 1 つの方法を示しています (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

「コミット遅れ」の数を見つける</h2>
git rev-list ^HEAD upstream | wc -l

これは、HEAD が上流に早送りできることを必要としません(上流が HEAD からどれだけ遅れているかではなく、HEAD が上流にどれだけ遅れているかだけをカウントします)。

コミットを 1 つ進める

一般に、早送り可能な履歴は線形ではない場合があります。以下のヒストリー DAG では、マスターはアップストリームに早送りできますが、A と B は両方とも、マスターからアップストリームに向かう途中で「1 つのコミット フォワード」です。

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

直線的な履歴であるかのように一方をたどることができますが、マージ コミットの直前の祖先までしかたどることができません。

リビジョン ウォーキング コマンドには、--first-parentマージ コミットの最初の親につながるコミットのみを簡単に追跡できるオプションがあります。これをgit resetと組み合わせると、ブランチを「一度に 1 つのコミットで前方に」効果的にドラッグできます。

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 は次のようになります (トポロジは以前と同じで、移動したのはマスターラベルのみです)。

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

この時点で「コミットを 1 つ進める」とマージに移行します。これにより、B からマージ コミットまでのすべてのコミットが「取り込まれます」( masterから到達可能になります)。「コミットを 1 つ進める」ことで履歴 DAG にコミットが 1 つだけ追加されると仮定すると、このステップはその仮定に違反します。

この場合、本当に何をしたいのかを慎重に検討する必要があります。このように追加のコミットをドラッグするだけで問題ありませんか、それとも、マージ コミットを処理する前に、B の親に「戻って」そのブランチを進めるメカニズムが必要ですか?

于 2010-05-29T06:22:30.340 に答える
10

現在のコミットがリモート ブランチ ヘッドの先祖である場合、リモート ブランチをローカル ブランチに早送りできます。つまり、リモート ブランチの「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 log は --all パラメーター (すべてのブランチを一覧表示する) なしで呼び出されたため、現在のコミットが「サイド ブランチ」にあり、出力に出力されることはありません。

現在のコミットより前のコミットの数は、$current_commit の前の $log の行数と同じです。

1 つのコミットのみを早送りする場合は、現在のコミットの前の行を取得し (たとえば、grep -B 1 を使用)、ローカル ブランチをこのコミットにリセットします。

UPDATEgit 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

もちろん、最初の呼び出しの結果をファイルに保存すれば、1 回の git log 呼び出しでこれを実行できます。

于 2010-05-28T22:53:48.097 に答える
8

これはおそらく最もエレガントではありませんが、機能します。

$ git フェッチ
$ git ステータス | sed -n 2p
# あなたのブランチは 'origin/master' より 23 コミット遅れており、早送りできます。
$ git reset origin/master~22 > /dev/null
$ git ステータス | sed -n 2p
# あなたのブランチは 'origin/master' より 22 コミット遅れており、早送りできます。
于 2010-05-23T07:31:03.030 に答える