1. ホーム
  2. git

[解決済み] Gitブランチの最も近い親を見つける方法

2022-03-18 06:06:37

質問

例えば、以下のようなローカルリポジトリがあり、コミットツリーが以下のような状態だとします。

master --> a
            \
             \
      develop c --> d
               \
                \
         feature f --> g --> h

master は私の これは最新の安定版リリースコードです , develop は私の これは「次の」リリースコードです。 であり、かつ feature のために準備されている新しい機能です。 develop .

へのプッシュを拒否できるようにしたい。 feature をリモートリポジトリにコミットしない限り、私のリモートリポジトリに f の直接の子孫である。 develop HEADです。つまり、コミットツリーはこのようになります。これは、フィーチャーが git rebase オン d .

master --> a
            \
             \
      develop c --> d
                     \
                      \
               feature f --> g --> h

ということは、可能なのでしょうか。

  • の親ブランチを特定する。 feature ?
  • 親ブランチのコミットで f の子孫なのか?

そこから、親ブランチのHEADが何であるかをチェックし、次のことを確認します。 f の前任者が親ブランチの HEAD に一致し、その機能がリベースされる必要があるかどうかを判断します。

解決方法は?

リモートリポジトリに デベロッパー ブランチ (最初の説明ではローカルリポジトリにあると書かれていましたが、リモートにもあるようです) を使用すれば、私が思うようなことを実現できるはずですが、その方法はあなたが思い描いているものとは少し異なります。

Gitの歴史は DAG のコミットです。ブランチ(および一般的な「参照」)は、継続的に増加するコミット DAG の特定のコミットを指す一時的なラベルにすぎません。そのため、ブランチ間の関係は時間の経過とともに変化しますが、コミット間の関係は変化しません。

    ---o---1                foo
            \
             2---3---o      bar
                  \
                   4
                    \
                     5---6  baz

次のように表示されます。 baz は、(古いバージョンの) bar ? しかし、もし bar ?

    ---o---1                foo
            \
             2---3
                  \
                   4
                    \
                     5---6  baz

これで、次のようになります。 baz がベースになっています。 foo . しかし、その祖先は baz は変更されませんでした。私たちはラベルを削除しただけです(その結果、ぶら下がったコミットも削除されました)。で新しいラベルを追加したらどうでしょう。 4 ?

    ---o---1                foo
            \
             2---3
                  \
                   4        quux
                    \
                     5---6  baz

これで、次のようになります。 baz がベースになっています。 quux . それでも、祖先は変わらず、ラベルだけが変わりました。

しかし、もし「コミットが 6 の子孫である。 3 ?" (仮に 36 が完全なSHA-1コミット名である場合)、答えは「イエス」でしょう。 barquux ラベルが存在するかどうか。

そこで、「押されたコミットは、現在の先端の デベロップ ブランチ? "といった質問をすることはできますが、"プッシュされたコミットの親ブランチは何か? "といった質問を確実にすることはできません。

あなたが望むものに近いと思われる、ほとんど信頼できる質問は、次のとおりです。

プッシュされたコミットのすべての先祖(現在の先端である 開発 とその祖先)で、現在の先端が デベロップ を親とする。

  • そのようなコミットが少なくとも1つ存在するか?
  • そのようなコミットはすべて単一の親コミットですか?

というように実装することができる。

pushedrev=...
basename=develop
if ! baserev="$(git rev-parse --verify refs/heads/"$basename" 2>/dev/null)"; then
    echo "'$basename' is missing, call for help!"
    exit 1
fi
parents_of_children_of_base="$(
  git rev-list --pretty=tformat:%P "$pushedrev" --not "$baserev" |
  grep -F "$baserev"
)"
case ",$parents_of_children_of_base" in
    ,)     echo "must descend from tip of '$basename'"
           exit 1 ;;
    ,*\ *) echo "must not merge tip of '$basename' (rebase instead)"
           exit 1 ;;
    ,*)    exit 0 ;;
esac

これは、制限されたいことの一部をカバーしますが、すべてではないかもしれません。

参考までに、拡張履歴の例を示します。

    A                                   master
     \
      \                    o-----J
       \                  /       \
        \                | o---K---L
         \               |/
          C--------------D              develop
           \             |\
            F---G---H    | F'--G'--H'
                    |    |\
                    |    | o---o---o---N
                     \   \      \       \
                      \   \      o---o---P
                       \   \
                        R---S

上記のコードで HS を受け入れながら H' , J , K または N を受け付けるが LP (の先端をマージすることはありません)。 開発 ).

も拒否すること LP というように、質問を変更することができます。

<ブロッククオート

プッシュされたコミットのすべての先祖 (現在の先端である 開発 とその祖先)。

  • 親が2つあるコミットはありますか?
  • そうでない場合、そのようなコミットの少なくとも 1 つは、現在の tip が 開発する その(唯一の)親は?
pushedrev=...
basename=develop
if ! baserev="$(git rev-parse --verify refs/heads/"$basename" 2>/dev/null)"; then
    echo "'$basename' is missing, call for help!"
    exit 1
fi
parents_of_commits_beyond_base="$(
  git rev-list --pretty=tformat:%P "$pushedrev" --not "$baserev" |
  grep -v '^commit '
)"
case "$parents_of_commits_beyond_base" in
    *\ *)          echo "must not push merge commits (rebase instead)"
                   exit 1 ;;
    *"$baserev"*)  exit 0 ;;
    *)             echo "must descend from tip of '$basename'"
                   exit 1 ;;
esac