[インデックス 11883] ファイルの概要
このコミットは、Go言語のgo
コマンドにおけるgo get
サブコマンドの機能改善に関するものです。具体的には、バージョン管理システム(VCS)のリポジトリをフェッチする際に、使用すべきスキーム(例: https
, http
, git
など)を自動的に検出する機能が追加されました。これにより、go get
がより堅牢になり、様々な形式のリポジトリURLに対応できるようになります。
コミット
commit dcf5ca706b6faef942d49e7637aa926ba76139a3
Author: Daniel Krech <eikeon@eikeon.com>
Date: Mon Feb 13 23:46:31 2012 -0500
cmd/go: go get scheme detection
Fixes #2895.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5651055
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/dcf5ca706b6faef942d49e7637aa926ba76139a3
元コミット内容
このコミットの元の内容は以下の通りです。
cmd/go: go get scheme detection
Fixes #2895.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5651055
これは、go get
コマンドにおけるスキーム検出機能の追加を目的としたものであり、GoのIssue 2895を修正するものです。
変更の背景
この変更の背景には、go get
コマンドがリポジトリをフェッチする際に、どのプロトコル(スキーム)を使用すべきかを自動的に判断する必要があったという問題があります。GoのIssue 2895("cmd/go: go get should try https before http")によると、go get
はデフォルトでhttp
を使用しようとし、https
を試さないため、http
がリダイレクトされる場合や、https
のみが利用可能な場合に問題が発生していました。
例えば、GitHubのようなサービスでは、リポジトリのクローンにhttps
が推奨されており、http
でアクセスしようとするとリダイレクトされるか、認証の問題が発生する可能性があります。このコミットは、go get
がリポジトリにアクセスする際に、複数のスキーム(https
, http
, git
, svn
, bzr
など)を試行し、最初に成功したスキームを使用することで、この問題を解決しようとしています。これにより、go get
の信頼性と柔軟性が向上し、より多くのリポジトリ構成に対応できるようになります。
前提知識の解説
Go言語のgo get
コマンド
go get
は、Go言語の標準ツールチェーンに含まれるコマンドで、リモートリポジトリからGoパッケージとその依存関係をダウンロードし、ビルドしてインストールするために使用されます。開発者はこのコマンドを使って、外部のライブラリやツールを簡単にプロジェクトに組み込むことができます。
バージョン管理システム (VCS)
VCSは、ソフトウェア開発においてソースコードやその他のファイルの変更履歴を管理するためのシステムです。主要なVCSには以下のようなものがあります。
- Git: 分散型VCSの代表格。GitHubなどで広く利用されています。
- Mercurial (Hg): Gitと同様に分散型VCS。
- Bazaar (Bzr): 分散型VCS。
- Subversion (Svn): 集中型VCS。
go get
は、これらのVCSを内部的に利用してリポジトリのクローンや更新を行います。
URLスキーム
URLスキーム(またはプロトコル)は、インターネット上のリソースにアクセスするための方法を定義します。例えば、https://
はHTTPSプロトコル、http://
はHTTPプロトコル、git://
はGitプロトコル、svn://
はSubversionプロトコル、bzr://
はBazaarプロトコルを示します。go get
は、これらのスキームを適切に選択してリポジトリに接続する必要があります。
go
コマンドの内部構造 (src/cmd/go
)
src/cmd/go
ディレクトリは、Go言語の公式コマンドラインツールであるgo
コマンドのソースコードが格納されている場所です。このディレクトリ内のvcs.go
ファイルは、go get
コマンドが様々なバージョン管理システム(Git, Mercurial, Subversion, Bazaarなど)と連携するためのロジックを定義しています。このファイルには、各VCSのコマンド、リポジトリのパスの正規表現、タグの同期方法などが記述されています。
技術的詳細
このコミットは、src/cmd/go/vcs.go
ファイルに以下の主要な変更を加えています。
-
vcsCmd
構造体の拡張:scheme []string
: そのVCSがサポートするURLスキームのリストを保持します(例: Gitはgit
,https
,http
をサポート)。pingCmd string
: 指定されたスキームとリポジトリに対して、VCSがリポジトリに到達可能かどうかをテストするためのコマンド文字列を保持します。
-
vcsCmd
メソッドの変更:run1
関数のシグネチャが変更され、verbose
という新しいブール引数が追加されました。これにより、コマンドの実行結果を標準エラー出力に表示するかどうかを制御できるようになります。run
とrunOutput
はrun1
を呼び出す際にverbose
をtrue
に設定します。runVerboseOnly
という新しいメソッドが追加され、これはrun1
を呼び出す際にverbose
をfalse
に設定します。これは、pingCmd
の実行結果が通常はユーザーに表示される必要がないため、冗長な出力を避けるために使用されます。ping
という新しいメソッドがvcsCmd
に追加されました。このメソッドは、指定されたスキームとリポジトリを使用してpingCmd
を実行し、リポジトリへの到達可能性をテストします。
-
vcsPath
構造体の拡張:ping bool
: このフィールドは、そのvcsPath
エントリがスキーム検出のためにping
を試みるべきかどうかを示します。
-
vcsForImportPath
関数のロジック変更:- この関数は、インポートパスに基づいて適切なVCSとリポジトリ情報を特定します。
srv.ping
がtrue
の場合、vcs.scheme
リスト内の各スキームを順番に試行し、vcs.ping
メソッドを使用してリポジトリへの到達可能性をテストします。- 最初に成功したスキームがリポジトリURLに適用され、そのスキームが使用されます。これにより、
go get
は自動的に最適なスキームを選択できるようになります。
-
vcsPaths
の更新:- 一般的なサーバーの正規表現を定義する
vcsPath
エントリにping: true
が追加されました。これにより、一般的なリポジトリURLに対してもスキーム検出が有効になります。
- 一般的なサーバーの正規表現を定義する
これらの変更により、go get
はリポジトリのURLにスキームが明示されていない場合でも、https
、http
、またはVCS固有のスキーム(git
, svn
, bzr
)を自動的に試行し、接続可能なスキームを特定して使用するようになります。
コアとなるコードの変更箇所
このコミットのコアとなるコードの変更箇所は、主にsrc/cmd/go/vcs.go
ファイル内の以下の部分です。
-
vcsCmd
構造体へのscheme
とpingCmd
の追加:--- a/src/cmd/go/vcs.go +++ b/src/cmd/go/vcs.go @@ -27,6 +27,9 @@ type vcsCmd struct { tagLookupCmd []tagCmd // commands to lookup tags before running tagSyncCmd tagSyncCmd string // command to sync to specific tag tagSyncDefault string // command to sync to default tag + + scheme []string + pingCmd string }
-
各VCS定義への
scheme
とpingCmd
の初期化:--- a/src/cmd/go/vcs.go +++ b/src/cmd/go/vcs.go @@ -74,6 +77,9 @@ var vcsHg = &vcsCmd{ },\n \ttagSyncCmd: \"update -r {tag}\",\n \ttagSyncDefault: \"update default\", +\n+\tscheme: []string{\"https\", \"http\"},\n+\tpingCmd: \"identify {scheme}://{repo}\", } // vcsGit describes how to use Git. @@ -94,6 +100,9 @@ var vcsGit = &vcsCmd{ },\n \ttagSyncCmd: \"checkout {tag}\",\n \ttagSyncDefault: \"checkout origin/master\", +\n+\tscheme: []string{\"git\", \"https\", \"http\"},\n+\tpingCmd: \"ls-remote {scheme}://{repo}\", } // vcsBzr describes how to use Bazaar. @@ -110,6 +119,9 @@ var vcsBzr = &vcsCmd{ tagCmd: []tagCmd{{\"tags\", `^(\\S+)`}},\n \ttagSyncCmd: \"update -r {tag}\",\n \ttagSyncDefault: \"update -r revno:-1\", +\n+\tscheme: []string{\"https\", \"http\", \"bzr\"},\n+\tpingCmd: \"info {scheme}://{repo}\", } // vcsSvn describes how to use Subversion. @@ -122,6 +134,9 @@ var vcsSvn = &vcsCmd{ // There is no tag command in subversion. // The branch information is all in the path names. +\n+\tscheme: []string{\"https\", \"http\", \"svn\"},\n+\tpingCmd: \"info {scheme}://{repo}\", }
-
run1
関数の変更とrunVerboseOnly
,ping
メソッドの追加:--- a/src/cmd/go/vcs.go +++ b/src/cmd/go/vcs.go @@ -136,17 +151,23 @@ func (v *vcsCmd) String() string { // command's combined stdout+stderr to standard error. // Otherwise run discards the command's output. func (v *vcsCmd) run(dir string, cmd string, keyval ...string) error { - _, err := v.run1(dir, cmd, keyval) + _, err := v.run1(dir, cmd, keyval, true) + return err +} + +// runVerboseOnly is like run but only generates error output to standard error in verbose mode. +func (v *vcsCmd) runVerboseOnly(dir string, cmd string, keyval ...string) error { + _, err := v.run1(dir, cmd, keyval, false) return err } // runOutput is like run but returns the output of the command. func (v *vcsCmd) runOutput(dir string, cmd string, keyval ...string) ([]byte, error) { - return v.run1(dir, cmd, keyval) + return v.run1(dir, cmd, keyval, true) } // run1 is the generalized implementation of run and runOutput. -func (v *vcsCmd) run1(dir string, cmdline string, keyval []string) ([]byte, error) { +func (v *vcsCmd) run1(dir string, cmdline string, keyval []string, verbose bool) ([]byte, error) { m := make(map[string]string) for i := 0; i < len(keyval); i += 2 { m[keyval[i]] = keyval[i+1] @@ -168,13 +189,20 @@ func (v *vcsCmd) run1(dir string, cmdline string, keyval []string) ([]byte, erro err := cmd.Run() out := buf.Bytes() if err != nil { - fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.cmd, strings.Join(args, " ")) - os.Stderr.Write(out) + if verbose || buildV { + fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.cmd, strings.Join(args, " ")) + os.Stderr.Write(out) + } return nil, err } return out, nil } +// ping pings to determine scheme to use. +func (v *vcsCmd) ping(scheme, repo string) error { + return v.runVerboseOnly(".", v.pingCmd, "scheme", scheme, "repo", repo) +} + // create creates a new copy of repo in dir. // The parent of dir must exist; dir must not. func (v *vcsCmd) create(dir, repo string) error {
-
vcsPath
構造体へのping
フィールドの追加:--- a/src/cmd/go/vcs.go +++ b/src/cmd/go/vcs.go @@ -236,6 +264,7 @@ type vcsPath struct { repo string // repository to use (expand with match of re) vcs string // version control system to use (expand with match of re) check func(match map[string]string) error // additional checks + ping bool // ping for scheme to use to download repo regexp *regexp.Regexp // cached compiled form of re }
-
vcsForImportPath
関数でのスキーム検出ロジックの追加:--- a/src/cmd/go/vcs.go +++ b/src/cmd/go/vcs.go @@ -283,6 +312,14 @@ func vcsForImportPath(importPath string) (vcs *vcsCmd, repo, root string, err er if vcs == nil { return nil, "", "", fmt.Errorf("unknown version control system %q", match["vcs"]) } + if srv.ping { + for _, scheme := range vcs.scheme { + if vcs.ping(scheme, match["repo"]) == nil { + match["repo"] = scheme + "://" + match["repo"] + break + } + } + } return vcs, match["repo"], match["root"], nil } return nil, "", "", fmt.Errorf("unrecognized import path %q", importPath)
-
vcsPaths
の一般的なエントリへのping: true
の追加:--- a/src/cmd/go/vcs.go +++ b/src/cmd/go/vcs.go @@ -340,7 +377,8 @@ var vcsPaths = []*vcsPath{ // General syntax for any server. {\n-\t\tre: `^(?P<root>(?P<repo>([a-z0-9.\\-]+\\.)+[a-z0-9.\\-]+(:[0-9]+)?/[A-Za-z0-9_.\\-/]*?)\\.(?P<vcs>bzr|git|hg|svn))(/[A-Za-z0-9_.\\-]+)*$`,\n+\t\tre: `^(?P<root>(?P<repo>([a-z0-9.\\-]+\\.)+[a-z0-9.\\-]+(:[0-9]+)?/[A-Za-z0-9_.\\-/]*?)\\.(?P<vcs>bzr|git|hg|svn))(/[A-Za-z0-9_.\\-]+)*$`,\n+\t\tping: true,\n },\n }
コアとなるコードの解説
このコミットの核心は、go get
がリポジトリのURLスキームを動的に決定する能力を追加した点にあります。
-
vcsCmd
構造体へのscheme
とpingCmd
の追加:scheme
フィールドは、各VCS(Git, Mercurialなど)がサポートするプロトコル(https
,http
,git
など)の優先順位付きリストを定義します。例えば、Mercurialはhttps
とhttp
を、Gitはgit
,https
,http
をサポートします。pingCmd
フィールドは、そのVCSが特定のリポジトリとスキームの組み合わせで到達可能かどうかをテストするためのシェルコマンドのテンプレートを定義します。例えば、Gitの場合はls-remote {scheme}://{repo}
、Mercurialの場合はidentify {scheme}://{repo}
が使用されます。これらのコマンドは、実際にリポジトリをクローンする前に、接続が確立できるかを確認するために使われます。
-
runVerboseOnly
とping
メソッド:runVerboseOnly
は、pingCmd
のような、通常はユーザーに詳細な出力を表示する必要がないコマンドを実行するために導入されました。これにより、go get
の出力がクリーンに保たれます。ping
メソッドは、vcsCmd
のpingCmd
を使用して、指定されたスキームとリポジトリに対して実際に接続テストを行います。エラーが返されなければ、そのスキームでリポジトリに到達可能であると判断されます。
-
vcsPath
構造体へのping
フィールドの追加:vcsPath
は、インポートパスの正規表現と、それに対応するVCSの種類、リポジトリのルートなどを定義する構造体です。ping: true
が設定されたvcsPath
エントリは、その正規表現にマッチするインポートパスに対して、スキーム検出ロジックを適用することを示します。これにより、特定のパターンにマッチするリポジトリに対してのみスキーム検出を有効にすることができます。
-
vcsForImportPath
関数でのスキーム検出ロジック:- この関数は、ユーザーが指定したインポートパスを解析し、どのVCSを使用すべきか、リポジトリのURLは何かなどを決定します。
- もし
srv.ping
がtrue
(つまり、そのインポートパスに対してスキーム検出が有効)であれば、vcs.scheme
に定義されたスキームのリストを順番にループします。 - 各スキームに対して
vcs.ping
メソッドを呼び出し、リポジトリへの接続を試みます。 - 最初に成功したスキームが見つかると、そのスキームがリポジトリURLのプレフィックスとして使用され、ループを抜けます。これにより、
go get
は自動的に最適なスキームを選択し、リポジトリのフェッチに進むことができます。
この一連の変更により、go get
はよりインテリジェントになり、ユーザーが明示的にスキームを指定しなくても、様々なネットワーク環境やリポジトリ構成に対応できるようになりました。これは、特にhttps
が推奨される現代のWeb環境において、go get
の信頼性と使いやすさを大幅に向上させるものです。
関連リンク
- Go Issue 2895: https://github.com/golang/go/issues/2895
- Gerrit Change 5651055: https://golang.org/cl/5651055
参考にした情報源リンク
- Go言語の公式ドキュメント (
go get
コマンドについて) - Git, Mercurial, Subversion, Bazaarの公式ドキュメント (各VCSのコマンドとプロトコルについて)
- URLスキームに関する一般的な情報
- GitHubのヘルプドキュメント (HTTPSクローンについて)
- Go言語のソースコード (
src/cmd/go/vcs.go
の変更前後の比較) - Go言語のIssueトラッカー (Issue 2895の詳細)
- Go言語のGerritコードレビューシステム (Change 5651055の詳細)
# [インデックス 11883] ファイルの概要
このコミットは、Go言語の`go`コマンドにおける`go get`サブコマンドの機能改善に関するものです。具体的には、バージョン管理システム(VCS)のリポジトリをフェッチする際に、使用すべきスキーム(例: `https`, `http`, `git`など)を自動的に検出する機能が追加されました。これにより、`go get`がより堅牢になり、様々な形式のリポジトリURLに対応できるようになります。
## コミット
commit dcf5ca706b6faef942d49e7637aa926ba76139a3 Author: Daniel Krech eikeon@eikeon.com Date: Mon Feb 13 23:46:31 2012 -0500
cmd/go: go get scheme detection
Fixes #2895.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5651055
## GitHub上でのコミットページへのリンク
[https://github.com/golang/go/commit/dcf5ca706b6faef942d49e7637aa926ba76139a3](https://github.com/golang/go/commit/dcf5ca706b6faef942d49e7637aa926ba76139a3)
## 元コミット内容
このコミットの元の内容は以下の通りです。
cmd/go: go get scheme detection
Fixes #2895.
R=golang-dev, rsc CC=golang-dev https://golang.org/cl/5651055
これは、`go get`コマンドにおけるスキーム検出機能の追加を目的としたものであり、GoのIssue 2895を修正するものです。
## 変更の背景
この変更の背景には、`go get`コマンドがリポジトリをフェッチする際に、どのプロトコル(スキーム)を使用すべきかを自動的に判断する必要があったという問題があります。GoのIssue 2895("cmd/go: go get should try https before http")によると、`go get`はデフォルトで`http`を使用しようとし、`https`を試さないため、`http`がリダイレクトされる場合や、`https`のみが利用可能な場合に問題が発生していました。
例えば、GitHubのようなサービスでは、リポジトリのクローンに`https`が推奨されており、`http`でアクセスしようとするとリダイレクトされるか、認証の問題が発生する可能性があります。このコミットは、`go get`がリポジトリにアクセスする際に、複数のスキーム(`https`, `http`, `git`, `svn`, `bzr`など)を試行し、最初に成功したスキームを使用することで、この問題を解決しようとしています。これにより、`go get`の信頼性と柔軟性が向上し、より多くのリポジトリ構成に対応できるようになります。
## 前提知識の解説
### Go言語の`go get`コマンド
`go get`は、Go言語の標準ツールチェーンに含まれるコマンドで、リモートリポジトリからGoパッケージとその依存関係をダウンロードし、ビルドしてインストールするために使用されます。開発者はこのコマンドを使って、外部のライブラリやツールを簡単にプロジェクトに組み込むことができます。
### バージョン管理システム (VCS)
VCSは、ソフトウェア開発においてソースコードやその他のファイルの変更履歴を管理するためのシステムです。主要なVCSには以下のようなものがあります。
* **Git**: 分散型VCSの代表格。GitHubなどで広く利用されています。
* **Mercurial (Hg)**: Gitと同様に分散型VCS。
* **Bazaar (Bzr)**: 分散型VCS。
* **Subversion (Svn)**: 集中型VCS。
`go get`は、これらのVCSを内部的に利用してリポジトリのクローンや更新を行います。
### URLスキーム
URLスキーム(またはプロトコル)は、インターネット上のリソースにアクセスするための方法を定義します。例えば、`https://`はHTTPSプロトコル、`http://`はHTTPプロトコル、`git://`はGitプロトコル、`svn://`はSubversionプロトコル、`bzr://`はBazaarプロトコルを示します。`go get`は、これらのスキームを適切に選択してリポジトリに接続する必要があります。
### `go`コマンドの内部構造 (`src/cmd/go`)
`src/cmd/go`ディレクトリは、Go言語の公式コマンドラインツールである`go`コマンドのソースコードが格納されている場所です。このディレクトリ内の`vcs.go`ファイルは、`go get`コマンドが様々なバージョン管理システム(Git, Mercurial, Subversion, Bazaarなど)と連携するためのロジックを定義しています。このファイルには、各VCSのコマンド、リポジトリのパスの正規表現、タグの同期方法などが記述されています。
## 技術的詳細
このコミットは、`src/cmd/go/vcs.go`ファイルに以下の主要な変更を加えています。
1. **`vcsCmd`構造体の拡張**:
* `scheme []string`: そのVCSがサポートするURLスキームのリストを保持します(例: Gitは`git`, `https`, `http`をサポート)。
* `pingCmd string`: 指定されたスキームとリポジトリに対して、VCSがリポジトリに到達可能かどうかをテストするためのコマンド文字列を保持します。
2. **`vcsCmd`メソッドの変更**:
* `run1`関数のシグネチャが変更され、`verbose`という新しいブール引数が追加されました。これにより、コマンドの実行結果を標準エラー出力に表示するかどうかを制御できるようになります。
* `run`と`runOutput`は`run1`を呼び出す際に`verbose`を`true`に設定します。
* `runVerboseOnly`という新しいメソッドが追加され、これは`run1`を呼び出す際に`verbose`を`false`に設定します。これは、`pingCmd`の実行結果が通常はユーザーに表示される必要がないため、冗長な出力を避けるために使用されます。
* `ping`という新しいメソッドが`vcsCmd`に追加されました。このメソッドは、指定されたスキームとリポジトリを使用して`pingCmd`を実行し、リポジトリへの到達可能性をテストします。
3. **`vcsPath`構造体の拡張**:
* `ping bool`: このフィールドは、その`vcsPath`エントリがスキーム検出のために`ping`を試みるべきかどうかを示します。
4. **`vcsForImportPath`関数のロジック変更**:
* この関数は、インポートパスに基づいて適切なVCSとリポジトリ情報を特定します。
* `srv.ping`が`true`の場合、`vcs.scheme`リスト内の各スキームを順番に試行し、`vcs.ping`メソッドを使用してリポジリへの到達可能性をテストします。
* 最初に成功したスキームがリポジトリURLに適用され、そのスキームが使用されます。これにより、`go get`は自動的に最適なスキームを選択できるようになります。
5. **`vcsPaths`の更新**:
* 一般的なサーバーの正規表現を定義する`vcsPath`エントリに`ping: true`が追加されました。これにより、一般的なリポジトリURLに対してもスキーム検出が有効になります。
これらの変更により、`go get`はリポジトリのURLにスキームが明示されていない場合でも、`https`、`http`、またはVCS固有のスキーム(`git`, `svn`, `bzr`)を自動的に試行し、接続可能なスキームを特定して使用するようになります。
## コアとなるコードの変更箇所
このコミットのコアとなるコードの変更箇所は、主に`src/cmd/go/vcs.go`ファイル内の以下の部分です。
1. **`vcsCmd`構造体への`scheme`と`pingCmd`の追加**:
```diff
--- a/src/cmd/go/vcs.go
+++ b/src/cmd/go/vcs.go
@@ -27,6 +27,9 @@ type vcsCmd struct {
tagLookupCmd []tagCmd // commands to lookup tags before running tagSyncCmd
tagSyncCmd string // command to sync to specific tag
tagSyncDefault string // command to sync to default tag
+
+ scheme []string
+ pingCmd string
}
```
2. **各VCS定義への`scheme`と`pingCmd`の初期化**:
```diff
--- a/src/cmd/go/vcs.go
+++ b/src/cmd/go/vcs.go
@@ -74,6 +77,9 @@ var vcsHg = &vcsCmd{
},\n \ttagSyncCmd: \"update -r {tag}\",\n \ttagSyncDefault: \"update default\",
+\n+\tscheme: []string{\"https\", \"http\"},\n+\tpingCmd: \"identify {scheme}://{repo}\",
}
// vcsGit describes how to use Git.
@@ -94,6 +100,9 @@ var vcsGit = &vcsCmd{
},\n \ttagSyncCmd: \"checkout {tag}\",\n \ttagSyncDefault: \"checkout origin/master\",
+\n+\tscheme: []string{\"git\", \"https\", \"http\"},\n+\tpingCmd: \"ls-remote {scheme}://{repo}\",
}
// vcsBzr describes how to use Bazaar.
@@ -110,6 +119,9 @@ var vcsBzr = &vcsCmd{
tagCmd: []tagCmd{{\"tags\", `^(\\S+)`}},\n \ttagSyncCmd: \"update -r {tag}\",\n \ttagSyncDefault: \"update -r revno:-1\",
+\n+\tscheme: []string{\"https\", \"http\", \"bzr\"},\n+\tpingCmd: \"info {scheme}://{repo}\",
}
// vcsSvn describes how to use Subversion.
@@ -122,6 +134,9 @@ var vcsSvn = &vcsCmd{
// There is no tag command in subversion.
// The branch information is all in the path names.
+\n+\tscheme: []string{\"https\", \"http\", \"svn\"},\n+\tpingCmd: \"info {scheme}://{repo}\",
}
```
3. **`run1`関数の変更と`runVerboseOnly`, `ping`メソッドの追加**:
```diff
--- a/src/cmd/go/vcs.go
+++ b/src/cmd/go/vcs.go
@@ -136,17 +151,23 @@ func (v *vcsCmd) String() string {
// command's combined stdout+stderr to standard error.
// Otherwise run discards the command's output.
func (v *vcsCmd) run(dir string, cmd string, keyval ...string) error {
- _, err := v.run1(dir, cmd, keyval)
+ _, err := v.run1(dir, cmd, keyval, true)
+ return err
+}
+
+// runVerboseOnly is like run but only generates error output to standard error in verbose mode.
+func (v *vcsCmd) runVerboseOnly(dir string, cmd string, keyval ...string) error {
+ _, err := v.run1(dir, cmd, keyval, false)
return err
}
// runOutput is like run but returns the output of the command.
func (v *vcsCmd) runOutput(dir string, cmd string, keyval ...string) ([]byte, error) {
- return v.run1(dir, cmd, keyval)
+ return v.run1(dir, cmd, keyval, true)
}
// run1 is the generalized implementation of run and runOutput.
-func (v *vcsCmd) run1(dir string, cmdline string, keyval []string) ([]byte, error) {
+func (v *vcsCmd) run1(dir string, cmdline string, keyval []string, verbose bool) ([]byte, error) {
m := make(map[string]string)
for i := 0; i < len(keyval); i += 2 {
m[keyval[i]] = keyval[i+1]
@@ -168,13 +189,20 @@ func (v *vcsCmd) run1(dir string, cmdline string, keyval []string) ([]byte, erro
err := cmd.Run()
out := buf.Bytes()
if err != nil {
- fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.cmd, strings.Join(args, " "))
- os.Stderr.Write(out)
+ if verbose || buildV {
+ fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.cmd, strings.Join(args, " "))
+ os.Stderr.Write(out)
+ }
return nil, err
}
return out, nil
}
+// ping pings to determine scheme to use.
+func (v *vcsCmd) ping(scheme, repo string) error {
+ return v.runVerboseOnly(".", v.pingCmd, "scheme", scheme, "repo", repo)
+}
+
// create creates a new copy of repo in dir.
// The parent of dir must exist; dir must not.
func (v *vcsCmd) create(dir, repo string) error {
```
4. **`vcsPath`構造体への`ping`フィールドの追加**:
```diff
--- a/src/cmd/go/vcs.go
+++ b/src/cmd/go/vcs.go
@@ -236,6 +264,7 @@ type vcsPath struct {
repo string // repository to use (expand with match of re)
vcs string // version control system to use (expand with match of re)
check func(match map[string]string) error // additional checks
+ ping bool // ping for scheme to use to download repo
regexp *regexp.Regexp // cached compiled form of re
}
```
5. **`vcsForImportPath`関数でのスキーム検出ロジックの追加**:
```diff
--- a/src/cmd/go/vcs.go
+++ b/src/cmd/go/vcs.go
@@ -283,6 +312,14 @@ func vcsForImportPath(importPath string) (vcs *vcsCmd, repo, root string, err er
if vcs == nil {
return nil, "", "", fmt.Errorf("unknown version control system %q", match["vcs"])
}
+ if srv.ping {
+ for _, scheme := range vcs.scheme {
+ if vcs.ping(scheme, match["repo"]) == nil {
+ match["repo"] = scheme + "://" + match["repo"]
+ break
+ }
+ }
+ }
return vcs, match["repo"], match["root"], nil
}
return nil, "", "", fmt.Errorf("unrecognized import path %q", importPath)
```
6. **`vcsPaths`の一般的なエントリへの`ping: true`の追加**:
```diff
--- a/src/cmd/go/vcs.go
+++ b/src/cmd/go/vcs.go
@@ -340,7 +377,8 @@ var vcsPaths = []*vcsPath{
// General syntax for any server.
{\n-\t\tre: `^(?P<root>(?P<repo>([a-z0-9.\\-]+\\.)+[a-z0-9.\\-]+(:[0-9]+)?/[A-Za-z0-9_.\\-/]*?)\\.(?P<vcs>bzr|git|hg|svn))(/[A-Za-z0-9_.\\-]+)*$`,\n+\t\tre: `^(?P<root>(?P<repo>([a-z0-9.\\-]+\\.)+[a-z0-9.\\-]+(:[0-9]+)?/[A-Za-z0-9_.\\-/]*?)\\.(?P<vcs>bzr|git|hg|svn))(/[A-Za-z0-9_.\\-]+)*$`,\n+\t\tping: true,\n },\n }
```
## コアとなるコードの解説
このコミットの核心は、`go get`がリポジトリのURLスキームを動的に決定する能力を追加した点にあります。
1. **`vcsCmd`構造体への`scheme`と`pingCmd`の追加**:
* `scheme`フィールドは、各VCS(Git, Mercurialなど)がサポートするプロトコル(`https`, `http`, `git`など)の優先順位付きリストを定義します。例えば、Mercurialは`https`と`http`を、Gitは`git`, `https`, `http`をサポートします。
* `pingCmd`フィールドは、そのVCSが特定のリポジトリとスキームの組み合わせで到達可能かどうかをテストするためのシェルコマンドのテンプレートを定義します。例えば、Gitの場合は`ls-remote {scheme}://{repo}`、Mercurialの場合は`identify {scheme}://{repo}`が使用されます。これらのコマンドは、実際にリポジトリをクローンする前に、接続が確立できるかを確認するために使われます。
2. **`runVerboseOnly`と`ping`メソッド**:
* `runVerboseOnly`は、`pingCmd`のような、通常はユーザーに詳細な出力を表示する必要がないコマンドを実行するために導入されました。これにより、`go get`の出力がクリーンに保たれます。
* `ping`メソッドは、`vcsCmd`の`pingCmd`を使用して、指定されたスキームとリポジトリに対して実際に接続テストを行います。エラーが返されなければ、そのスキームでリポジトリに到達可能であると判断されます。
3. **`vcsPath`構造体への`ping`フィールドの追加**:
* `vcsPath`は、インポートパスの正規表現と、それに対応するVCSの種類、リポジトリのルートなどを定義する構造体です。
* `ping: true`が設定された`vcsPath`エントリは、その正規表現にマッチするインポートパスに対して、スキーム検出ロジックを適用することを示します。これにより、特定のパターンにマッチするリポジトリに対してのみスキーム検出を有効にすることができます。
4. **`vcsForImportPath`関数でのスキーム検出ロジック**:
* この関数は、ユーザーが指定したインポートパスを解析し、どのVCSを使用すべきか、リポジトリのURLは何かなどを決定します。
* もし`srv.ping`が`true`(つまり、そのインポートパスに対してスキーム検出が有効)であれば、`vcs.scheme`に定義されたスキームのリストを順番にループします。
* 各スキームに対して`vcs.ping`メソッドを呼び出し、リポジトリへの接続を試みます。
* 最初に成功したスキームが見つかると、そのスキームがリポジトリURLのプレフィックスとして使用され、ループを抜けます。これにより、`go get`は自動的に最適なスキームを選択し、リポジトリのフェッチに進むことができます。
この一連の変更により、`go get`はよりインテリジェントになり、ユーザーが明示的にスキームを指定しなくても、様々なネットワーク環境やリポジトリ構成に対応できるようになりました。これは、特に`https`が推奨される現代のWeb環境において、`go get`の信頼性と使いやすさを大幅に向上させるものです。
## 関連リンク
* **Go Issue 2895**: [https://github.com/golang/go/issues/2895](https://github.com/golang/go/issues/2895)
* **Gerrit Change 5651055**: [https://golang.org/cl/5651055](https://golang.org/cl/5651055)
## 参考にした情報源リンク
* Go言語の公式ドキュメント (`go get`コマンドについて)
* Git, Mercurial, Subversion, Bazaarの公式ドキュメント (各VCSのコマンドとプロトコルについて)
* URLスキームに関する一般的な情報
* GitHubのヘルプドキュメント (HTTPSクローンについて)
* Go言語のソースコード (`src/cmd/go/vcs.go`の変更前後の比較)
* Go言語のIssueトラッカー (Issue 2895の詳細)
* Go言語のGerritコードレビューシステム (Change 5651055の詳細)