1. ホーム
  2. git

[解決済み] git rev-listを理解する

2022-03-09 09:34:01

質問

git hookのサンプルを探しているときに、以下の投稿に出会いました。 https://github.com/Movidone/git-hooks/blob/master/pre-receive で、以下のコマンドを理解したいと思いました。

git rev-list $new_list --not --all 

new_listの取得先。

NULL_SHA1="0000000000000000000000000000000000000000" # 40 0's
new_list=
any_deleted=false
while read oldsha newsha refname; do
    case $oldsha,$newsha in
        *,$NULL_SHA1) # it's a delete
            any_deleted=true;;
        $NULL_SHA1,*) # it's a create
            new_list="$new_list $newsha";;
        *,*) # it's an update
            new_list="$new_list $newsha";;
    esac
done

rev-listはコミットを時系列で逆順に表示するのだと思いました。

しかし、誰かもっと詳しく -not-all のオプションは何のためにあるのでしょうか?

ドキュメントの通りです。

--not
Reverses the meaning of the ^ prefix (or lack thereof) for all following revision specifiers, up to the next --not.
--all
Pretend as if all the refs in refs/ are listed on the command line as <commit>. 

これらのオプションを完全に理解することはできません。

[更新情報] いくつかのテストコミットを行った後、もし私が --not--all のオプションは、その後 git rev-list は、ブランチ上のすべてのコミットをリストアップし、プッシュする予定のコミットはリストアップしません。

しかし、なぜ、以下のような場合に、ターミナルに sha の値が表示されないのかを理解したいと思います。 --all オプションが渡されるのですか?

解決方法は?

その git rev-list コマンドは 非常に 複雑な 超中心 コマンドは、Git で何をするかというと グラフを歩く . この単語は グラフ は、コミットグラフそのものと、場合によってはその次のレベル (Gitオブジェクトの から到達可能な コミット)。

rev-listはコミットを時系列で逆順に表示するのだと思いました。

正確ではありませんが、近いです。

  • 順番は変更可能です。 その デフォルト は逆時系列です。
  • デフォルトでは、いくつかのコミットをウォークしますが、その際に rev-list を使えば、ツリーやブロブオブジェクト、さらにはタグオブジェクトも含めて、より深く掘り下げることができます。 これは、以下のようなプログラムのためのものです。 git fetchgit push (を呼び出す)。 git pack-objects ) と git pack-objects . この可能性はここでは完全に無視するつもりですが、少なくとも触れておくべきだと思います。????

そこで デフォルト は、いくつかのコミットを時系列で逆順にリストアップします。 を正確に指定することは重要ですが、少し厄介です。 どの部分 グラフの git rev-list を歩く。 一部 いくつかのコミット .

しかし、誰かもっと詳しく教えてください --not--all のオプションは何のためにあるのでしょうか?

として VonCノート は、その 効果 では、受信側のリポジトリに新しく追加されたコミットをリストアップします。 これは、この git rev-list コマンドは プリレシーブフック . 一般に、この特定のフックの外では、有用なことは何も行いません。 このように、Gitにおけるフックの実行時環境は、少なくとも少しは特別であることが多いのです。 (これはpre-receiveフック以外にも言えることです。それぞれのフックの起動時のコンテキストについて考えなければなりません)。

についてもっと詳しく --not --all

--all オプションは、あなたがドキュメントから引用したとおりの働きをします。

にあるすべての参照は refs/ がコマンドラインにリストアップされています.

ということは、これは同等のことをするわけです。 git for-each-ref refs : 各参照をループします。 これにはブランチ名 ( master または main , develop , feature/tall などがありますが、これらはすべて本当は refs/heads/ )、タグ名( v1.2 というのは、本当は refs/tags/v1.2 )、リモートトラッキングの名前( origin/develop というのは、本当は refs/remotes/origin/develop ) 、置換参照 ( refs/replace/ )、隠し場所( refs/stash )、bisection refs、Gerritを使っている場合はGerrit refs、など。 なお、この はしません。 は reflog エントリをループします。

--not プレフィクスは単純なブーリアン演算です。 gitrevisions の構文では、次のようになります。 gitrevisionsのドキュメント -のような書き方ができます。 develop という意味です。 から始めるように言っています。 develop と逆算し インクルード これらのコミット のようなものだけでなく ^develop という意味です。 から始めるように言っています。 develop と逆算し 除く これらのコミット . だから、もし私が書いたら

git rev-list feature1 feature2 ^main

Gitにコミットをウォークするよう依頼しています から到達可能な という名前で識別されるコミット feature1feature2 に、しかし 除く で識別されるコミットから到達可能なコミット。 main . の一般的な考え方については、(かなり) 詳しく説明します。 到達可能性 とグラフウォーキングを参照してください。 Gitのように考える .

--not 演算子は事実上 ^ をそれぞれの参照に追加します。

git rev-list --not feature1 feature2 ^main

は、いわば略語です。

git rev-list ^feature1 ^feature2 main

から到達可能なコミットのリストをウォークします。 main のどちらかから到達可能なものは除外されます。 feature1 または feature2 .

通常 すべて でコミットを見つけることができます。 --all

Gitを普通に日常的に使っている場合。 は今のところ "detached HEAD" を持っていません-detached HEAD モードは正確には 異常 が、通常の作業方法とは異なります。 --all オプションで git rev-list を含めるように指示します。 すべて というのは、すべてのコミット すべてのリファレンスから到達可能です。 1 そこで --not --all 効果的に は除外されます。 のすべてのコミットに対してです。 そのため --not --all に、任意の git rev-list を使用すると、コミットの一覧が表示されなくなることがあります。 出力は空っぽです。なぜ私たちは悩んだのでしょうか?

デタッチド HEAD モードで、いくつかの新しいコミットを行った場合-たとえば、対話的なリベースや競合するリベースの最中などに起こります-は git rev-list HEAD --not --all は、以下のようなコミットをリストアップします。 から到達可能な HEAD しかし ない をどのブランチ名からも削除することができます。 たとえばこのリベースでは、これまでにコピーしたコミットだけが対象となります。

つまり、quot;detached HEAD"モードは、一度だけ、以下のような場所になります。 git rev-list --not --all は、コマンドラインから有用である可能性があります。 しかし、あなたが調べているような状況、つまり プリレシーブフック -コマンドラインではありません。

受信前フック

を使用する場合 git push を使用してコミットを送信します。 への あなた自身のGit、あなたのGitです。

  • は、新しいオブジェクト (新しいコミットやブロブなど) を保持するための隔離領域をセットアップします。 1
  • は、送信者が何を送信すべきかを決定するために、送信者と交渉します。
  • これらのオブジェクトを受信する。
  • のリストを受け取ります。 Refの更新要求 . これらの更新リクエストは、基本的には単に この名前にこのハッシュIDを持たせる . 2

実際に使用する前に している 要求されたアップデートのいずれかを、あなたのGit:

  1. リスト全体をpre-receiveフックに送ります。 このフックは "no"と言うことができ、その場合、プッシュ全体が拒否されます。
  2. OKと出たら、リストを1つずつupdateフックに送ります。 そのフックが "ok" と言ったら、更新を実行します。 フックが "no"と言ったら、Git はその更新を拒否し、他の更新を調べます。
  3. ステップ2ですべての更新が受理または拒否された後、受理されたリストを受信後フックに送ります。

ステップ2で何らかのrefに追加された、必要なオブジェクトは、隔離からGitのオブジェクト・データベースに移動されます。 拒否されたものは、そうではありません。

では、典型的な git push . 新しいコミット(複数可)とリクエストを取得します。 新しいブランチ名を作成します。 feature/short または、新しいコミットとリクエストを取得します。 既存のブランチ名を更新 develop 新しいコミットを古いコミットと一緒に含めるように .

上記のステップ1では、新しいハッシュIDは1つです。 すべての参照名とその現在のハッシュIDおよび提案された新しいハッシュIDを読み込むループを実行しましたが、ループは一度だけしか実行されませんでした。 名前 されていた git push -された。 そのハッシュIDは 新しい このコミットは、既存のブランチに追加されるか、あるいは新しいブランチ専用の tip やその他のコミットとなります。

次に 検査 これらのコミットは、既存のブランチから到達可能な既存のコミットとは関係ありません。 シンプルにするために $new_list では、新しいハッシュIDを1つだけと仮定してみましょう。 $new と、ブランチ名には旧ハッシュIDを使用します。 $old ブランチがすべて新しい場合は all-zeros となり、既存のブランチ名である場合は有効な既存のコミットとなります。

新しいコミットが完全に新しいブランチにある場合。

git rev-list $new ^master ^develop ^feature/short ^feature/tall

は、たとえば、既存のブランチがこの4つだけだとわかっている場合、それらをカバーします (そして、心配するようなタグなどがない場合)。 しかし、例えば、これらのブランチが追加される場合はどうでしょう。 develop ? というコミットは除外したい。 現在 にあります。 develop . 私たちは $old のハッシュIDで行います。

git rev-list $new ^master ^$old ^feature/short ^feature/tall

を実行している人の新しいコミットだけがリストアップされます。 git push origin develop に追加したい。 develop .

しかし、考えてみてください。 $old . これはハッシュIDです。 Gitはどこでそれを手に入れたのでしょうか? Git ゲット このハッシュIDは 名前 develop . これは プリレシーブフック 名前 develop はまだ更新されていません . そのため、名前 develop 古いハッシュIDに対応する名前 $old . ということです。

git rev-list $new ^master ^develop ^feature/short ^feature/tall

意志 また を実行します。

もし git rev-list $new の後に "and not all existing" が続くと、仕事ができるようになります。

git rev-list $new --not --branches

が仕事をすることになります。 ここにあるのはほとんどそれです

を使用した場合の不具合は --branches は、タグや他の参照先を取得しないことです。 そこで --not --branches --tags しかし --not --all の方が短く、他の参照もすべて取得できます。

ということで、ここは --not --all は、受信前フックという特殊なケースに依存する。 を実行している人が提案する、新しいハッシュIDをリストアップします。 git push は、Git が行のリストとして私たちに渡してきたものです。 私たちは git rev-list は、更新予定のコミットグラフを歩き、隔離領域にある新しいコミットを調べますが、すでにリポジトリにあるすべてのコミットは除外されます。 rev-list コマンドは一行ごとにハッシュ ID を生成し、それをシェルループで読み込んで以下のような処理を行います。 検査する のコミットです。


1 隔離領域はGit 2.11で新たに導入されたものです。それ以前は、新しいオブジェクトはプッシュが拒否されてもしばらくはリポジトリに残っていることがありました。 隔離領域は、ほとんどの人にとってはそれほど大きな問題ではありませんが、GitHub のような大きなサーバーでは ロット のディスクスペースが必要です。

2 リクエストは強制か非強制か、そして強制の場合は強制解放かそうでないかということもあり得ます。 この情報は、pre-receiveフックでは(updateフックでも)利用できません。これは、えーと、つまり、こうです。 いまいち が、これを追加するのは互換性の問題がある。 でも、ほとんど問題なく使えます。 フックは、それが 新規作成 または 既存の参照を削除する なぜなら、もしそうなら、2つのハッシュIDのうちoldかnewのどちらかが、all-zeroの "null hash" (これは予約されています。all-zeroになるハッシュIDは許可されていません) になってしまうからです。