Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 11430] ファイルの概要

このコミットは、Go言語のコマンドラインツール cmd/go 内のバージョン管理システム (VCS) 関連のコード、具体的には src/cmd/go/vcs.go ファイルに影響を与えます。このファイルは、go get コマンドが様々なバージョン管理システムからソースコードを取得する際のロジックを定義しています。

変更されたファイル:

  • src/cmd/go/vcs.go: 28行の変更 (24行追加, 4行削除)

コミット

commit cdbed823bde062cf72f62437261ee9c84007269c
Author: Gustavo Niemeyer <gustavo@niemeyer.net>
Date:   Fri Jan 27 00:58:24 2012 -0200

    cmd/go: solve ambiguity of get lp.net/project/foo
    
    This solves the ambiguity for "lp.net/project/foo". In these URLs,
    "foo" could be a series name registered in Launchpad with its own
    branch, and it could also be the name of a directory within the
    main project branch one level up.
    
    Solve it by testing if the series branch exists in Launchpad
    and if it doesn't moving the root one level up.
    
    R=rsc
    CC=golang-dev
    https://golang.org/cl/5577058

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/cdbed823bde062cf72f62437261ee9c84007269c

元コミット内容

cmd/go: lp.net/project/foogo get における曖昧さを解決する。

このコミットは、"lp.net/project/foo" のようなURLにおける曖昧さを解決します。これらのURLでは、"foo" は Launchpad に登録された独自のブランチを持つシリーズ名である可能性と、メインプロジェクトブランチの1つ上の階層にあるディレクトリ名である可能性がありました。

この問題を解決するために、Launchpad にシリーズブランチが存在するかどうかをテストし、存在しない場合はルートを1つ上の階層に移動させます。

変更の背景

Go言語の go get コマンドは、指定されたインポートパスに基づいて、対応するソースコードリポジトリを自動的に検出し、ダウンロードする機能を提供します。しかし、Launchpad (Canonicalが運営するソフトウェアプロジェクトホスティングプラットフォーム) の特定のURL構造において、この自動検出に曖昧さが存在していました。

具体的には、launchpad.net/project/foo のようなパスが与えられた場合、foo の部分が以下の2つの異なる意味を持つ可能性がありました。

  1. Launchpad の「シリーズ」名: Launchpad では、プロジェクト内に複数の「シリーズ」(実質的にはブランチのようなもの)を作成できます。この場合、fooproject の特定のシリーズ(ブランチ)を指し、それ自体が独立したリポジトリとして扱われます。
  2. プロジェクト内のサブディレクトリ名: fooproject のメインブランチ内の単なるサブディレクトリを指す場合です。

この曖昧さにより、go get はどちらのリソースを取得すべきかを正確に判断できず、誤ったリポジトリをダウンロードしたり、エラーになったりする可能性がありました。このコミットは、この曖昧さを解消し、go get が常に正しいソースコードを取得できるようにするためのものです。

前提知識の解説

go get コマンド

go get は、Go言語のパッケージ管理ツールの一部であり、リモートリポジトリからGoパッケージとその依存関係をダウンロードし、ローカルの GOPATH に配置するために使用されます。これにより、開発者は簡単に外部ライブラリを利用できるようになります。go get は、インポートパスのプレフィックスに基づいて、Git, Mercurial, Bazaar, Subversion などの様々なバージョン管理システムを自動的に識別し、適切なコマンドを実行します。

Launchpad

Launchpad は、Canonical 社が開発・運営するソフトウェアプロジェクトホスティングプラットフォームです。特に Ubuntu などのオープンソースプロジェクトで広く利用されています。バグトラッキング、コードホスティング、翻訳、メーリングリスト、ファイルリリースなどの機能を提供します。コードホスティングには主に Bazaar (bzr) バージョン管理システムが使用されます。

Launchpad の特徴的な概念の一つに「シリーズ (Series)」があります。これは、プロジェクトの異なる開発ラインやリリースバージョンを管理するためのもので、実質的には独立したブランチとして機能します。例えば、launchpad.net/ubuntu/precise のように、precise が Ubuntu プロジェクトの特定のリリースシリーズを指すことがあります。

Bazaar (bzr)

Bazaar (bzr) は、分散型バージョン管理システム (DVCS) の一つです。Git や Mercurial と同様に、中央リポジトリに依存せず、各開発者が完全なリポジトリのコピーを持つことができます。Launchpad は、そのコードホスティングのバックエンドとして Bazaar を利用しています。

Go のインポートパスと VCS の解決

Go のインポートパスは、通常、リポジトリのルートパスとそれに続くディレクトリパスで構成されます。例えば、github.com/user/repo/pkg の場合、github.com/user/repo がリポジトリのルートであり、pkg がその中のパッケージディレクトリです。

go get は、インポートパスの最初の部分(ドメイン名など)を見て、どのVCSプロバイダ(GitHub, Bitbucket, Launchpadなど)に対応するかを判断します。その後、そのプロバイダ固有のルールに基づいて、リポジトリのURLを構築し、VCSコマンド(git clone, bzr branch など)を実行してソースコードを取得します。

技術的詳細

このコミットの核心は、go get が Launchpad のインポートパスを解析する際のロジックを改善することにあります。

従来の vcs.go では、launchpad.net/ プレフィックスを持つパスに対して、正規表現を用いてリポジトリのルートを特定していました。しかし、この正規表現は lp.net/project/foo のようなパスが、projectfoo シリーズなのか、それとも project の中の foo ディレクトリなのかを区別できませんでした。

このコミットでは、vcsPath 構造体に check フィールドが追加されました。この check フィールドは、VCSパスがマッチした後に実行されるカスタム検証関数を指します。Launchpad のエントリには、新しく導入された launchpadVCS 関数がこの check フィールドに割り当てられています。

launchpadVCS 関数のロジックは以下の通りです。

  1. 正規表現によって抽出された projectseries の両方の部分が存在するかどうかを確認します。もし series が空であれば、曖昧さは存在しないため、何もしません。
  2. projectseries が両方存在する場合、launchpadVCShttps://code.launchpad.net/{project}{series}/.bzr/branch-format というURLに対して HTTP GET リクエストを試みます。
    • Launchpad の Bazaar リポジトリは、.bzr/branch-format というファイルを持っており、これはリポジトリのルートに存在します。
    • もし https://code.launchpad.net/{project}{series}/.bzr/branch-format へのリクエストが成功すれば、それは foo が実際に project のシリーズ(ブランチ)であることを意味します。この場合、go get はこのシリーズをリポジトリのルートとして扱います。
    • もしリクエストが失敗した場合(例: 404 Not Found)、それは foo という名前のシリーズが存在しないことを意味します。この場合、launchpadVCSmatch マップ内の rootrepo の値を更新し、リポジトリのルートを launchpad.net/{project} に変更します。これにより、fooproject リポジトリ内のサブディレクトリとして扱われるようになります。

この動的なチェックにより、go getlaunchpad.net/project/foo のような曖昧なパスが与えられた場合でも、Launchpad の実際の構造に基づいて正しいリポジトリを特定し、ダウンロードできるようになります。

コアとなるコードの変更箇所

src/cmd/go/vcs.go

  1. vcsBzr 構造体の downloadCmd コメントの更新: bzr pull --overwrite コマンドに関するコメントが追加され、タグのプルに関する挙動と、将来的な http://pad.lv/681792 での改善が言及されています。これは直接的な曖昧さの解決とは関係ありませんが、Bazaar 関連の改善の一部です。

  2. vcsPath 構造体コメントの修正: コメントのタイポ (is describes -> describes) が修正されています。

  3. vcsPaths 配列内の Launchpad エントリの正規表現 (re) の変更:

    -		re:     `^(?P<root>launchpad\\.net/([A-Za-z0-9_.\\-]+(/[A-Za-z0-9_.\\-]+)?|~[A-Za-z0-9_.\\-]+/(\\+junk|[A-Za-z0-9_.\\-]+)/[A-Za-z0-9_.\\-]+))(/[A-Za-z0-9_.\\-]+)*$`,\n
    +		re:     `^(?P<root>launchpad\\.net/((?P<project>[A-Za-z0-9_.\\-]+)(?P<series>/[A-Za-z0-9_.\\-]+)?|~[A-Za-z0-9_.\\-]+/(\\+junk|[A-Za-z0-9_.\\-]+)/[A-Za-z0-9_.\\-]+))(/[A-Za-z0-9_.\\-]+)*$`,\n
    

    新しい正規表現は、projectseries という名前付きキャプチャグループを導入しています。これにより、launchpad.net/project/series のようなパスから projectseries の各部分を個別に抽出できるようになります。

  4. vcsPaths 配列内の Launchpad エントリに check フィールドの追加:

    +		check:  launchpadVCS,\n
    

    vcsPath 構造体に check フィールドが追加され、Launchpad のエントリに対して launchpadVCS 関数が割り当てられています。この関数は、正規表現マッチング後に呼び出され、パスの曖昧さを解決するための追加のロジックを実行します。

  5. 新しい関数 launchpadVCS の追加:

    // launchpadVCS solves the ambiguity for "lp.net/project/foo". In this case,
    // "foo" could be a series name registered in Launchpad with its own branch,
    // and it could also be the name of a directory within the main project
    // branch one level up.
    func launchpadVCS(match map[string]string) error {
    	if match["project"] == "" || match["series"] == "" {
    		return nil
    	}
    	_, err := httpGET(expand(match, "https://code.launchpad.net/{project}{series}/.bzr/branch-format"))
    	if err != nil {
    		match["root"] = expand(match, "launchpad.net/{project}")
    		match["repo"] = expand(match, "https://{root}")
    	}
    	return nil
    }
    

    この関数が、前述の技術的詳細で説明した曖昧さ解決のロジックを実装しています。

コアとなるコードの解説

vcsPaths の正規表現の変更

変更された正規表現 ^(?P<root>launchpad\\.net/((?P<project>[A-Za-z0-9_.\\-]+)(?P<series>/[A-Za-z0-9_.\\-]+)?|~[A-Za-z0-9_.\\-]+/(\\+junk|[A-Za-z0-9_.\\-]+)/[A-Za-z0-9_.\\-]+))(/[A-Za-z0-9_.\\-]+)*$ は、launchpad.net/ 以下のパスをより詳細に解析します。

  • (?P<project>[A-Za-z0-9_.\\-]+): project という名前のキャプチャグループで、プロジェクト名を抽出します。
  • (?P<series>/[A-Za-z0-9_.\\-]+)?: series という名前のキャプチャグループで、オプションのシリーズ名(スラッシュで始まる)を抽出します。

これにより、launchpad.net/myproject/myseries のようなパスが与えられた場合、myprojectproject に、myseriesseries にそれぞれマッチするようになります。

launchpadVCS 関数の詳細

launchpadVCS 関数は、vcsPathcheck フィールドを通じて呼び出されます。引数 match は、正規表現によって抽出された名前付きキャプチャグループの値を保持するマップです。

  1. if match["project"] == "" || match["series"] == "": この条件は、正規表現が project または series のいずれかを抽出できなかった場合に nil を返します。これは、パスが launchpad.net/project のようにシリーズ部分を含まない場合や、ユーザーディレクトリ (~user/project) のような別の Launchpad パス形式である場合に該当します。これらのケースでは曖昧さがないため、追加の処理は不要です。

  2. _, err := httpGET(expand(match, "https://code.launchpad.net/{project}{series}/.bzr/branch-format")):

    • expand(match, "...") は、match マップの値をプレースホルダー ({project}, {series}) に展開してURL文字列を生成します。
    • 生成されるURLは https://code.launchpad.net/myproject/myseries/.bzr/branch-format のようになります。
    • httpGET は、指定されたURLに対してHTTP GETリクエストを実行します。
    • Launchpad の Bazaar リポジトリは、リポジトリのルートに .bzr/branch-format というファイルを持っています。このファイルが存在するかどうかをチェックすることで、{project}{series} が有効な Bazaar シリーズ(ブランチ)のルートであるかどうかを判断します。
  3. if err != nil:

    • httpGET がエラーを返した場合(例: HTTP 404 Not Found)、それは https://code.launchpad.net/{project}{series}/.bzr/branch-format が存在しないことを意味します。つまり、{series} は独立したシリーズではなく、{project} リポジトリ内のサブディレクトリである可能性が高いと判断されます。
    • この場合、以下の2行が実行されます。
      • match["root"] = expand(match, "launchpad.net/{project}"): root の値を launchpad.net/myproject に変更します。これにより、go getmyproject をリポジトリのルートとして認識します。
      • match["repo"] = expand(match, "https://{root}"): repo の値を https://launchpad.net/myproject に変更します。これは、go get が実際にクローンするリポジトリのURLです。

このロジックにより、go get は Launchpad のパスがシリーズを指すのか、それともサブディレクトリを指すのかを動的に判断し、適切なリポジトリをダウンロードできるようになります。

関連リンク

参考にした情報源リンク

  • Launchpad (software): https://en.wikipedia.org/wiki/Launchpad_(software)
  • Bazaar (software): https://en.wikipedia.org/wiki/Bazaar_(software)
  • Go Modules Reference: https://go.dev/ref/mod (一般的な go get の動作について)
  • Novation Launchpad Series: https://novationmusic.com/en/launchpad (Launchpad の一般的な検索結果で出てきた音楽関連の製品ですが、Launchpad の概念を理解する上で、ソフトウェアプラットフォームとしての Launchpad との区別を明確にするために参照しました。)
  • bzr branch-format (Bazaar documentation): Bazaar のリポジトリ構造に関する情報源として、.bzr/branch-format ファイルの存在を確認しました。
  • Go get Launchpad ambiguity: このコミットの背景にある問題について、一般的な情報収集のために検索しました。