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

[インデックス 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ファイルに以下の主要な変更を加えています。

  1. vcsCmd構造体の拡張:

    • scheme []string: そのVCSがサポートするURLスキームのリストを保持します(例: Gitはgit, https, httpをサポート)。
    • pingCmd string: 指定されたスキームとリポジトリに対して、VCSがリポジトリに到達可能かどうかをテストするためのコマンド文字列を保持します。
  2. vcsCmdメソッドの変更:

    • run1関数のシグネチャが変更され、verboseという新しいブール引数が追加されました。これにより、コマンドの実行結果を標準エラー出力に表示するかどうかを制御できるようになります。
    • runrunOutputrun1を呼び出す際にverbosetrueに設定します。
    • runVerboseOnlyという新しいメソッドが追加され、これはrun1を呼び出す際にverbosefalseに設定します。これは、pingCmdの実行結果が通常はユーザーに表示される必要がないため、冗長な出力を避けるために使用されます。
    • pingという新しいメソッドがvcsCmdに追加されました。このメソッドは、指定されたスキームとリポジトリを使用してpingCmdを実行し、リポジトリへの到達可能性をテストします。
  3. vcsPath構造体の拡張:

    • ping bool: このフィールドは、そのvcsPathエントリがスキーム検出のためにpingを試みるべきかどうかを示します。
  4. vcsForImportPath関数のロジック変更:

    • この関数は、インポートパスに基づいて適切なVCSとリポジトリ情報を特定します。
    • srv.pingtrueの場合、vcs.schemeリスト内の各スキームを順番に試行し、vcs.pingメソッドを使用してリポジトリへの到達可能性をテストします。
    • 最初に成功したスキームがリポジトリURLに適用され、そのスキームが使用されます。これにより、go getは自動的に最適なスキームを選択できるようになります。
  5. vcsPathsの更新:

    • 一般的なサーバーの正規表現を定義するvcsPathエントリにping: trueが追加されました。これにより、一般的なリポジトリURLに対してもスキーム検出が有効になります。

これらの変更により、go getはリポジトリのURLにスキームが明示されていない場合でも、httpshttp、またはVCS固有のスキーム(git, svn, bzr)を自動的に試行し、接続可能なスキームを特定して使用するようになります。

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

このコミットのコアとなるコードの変更箇所は、主にsrc/cmd/go/vcs.goファイル内の以下の部分です。

  1. vcsCmd構造体へのschemepingCmdの追加:

    --- 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定義へのschemepingCmdの初期化:

    --- 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メソッドの追加:

    --- 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フィールドの追加:

    --- 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関数でのスキーム検出ロジックの追加:

    --- 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の追加:

    --- 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構造体へのschemepingCmdの追加:

    • schemeフィールドは、各VCS(Git, Mercurialなど)がサポートするプロトコル(https, http, gitなど)の優先順位付きリストを定義します。例えば、Mercurialはhttpshttpを、Gitはgit, https, httpをサポートします。
    • pingCmdフィールドは、そのVCSが特定のリポジトリとスキームの組み合わせで到達可能かどうかをテストするためのシェルコマンドのテンプレートを定義します。例えば、Gitの場合はls-remote {scheme}://{repo}、Mercurialの場合はidentify {scheme}://{repo}が使用されます。これらのコマンドは、実際にリポジトリをクローンする前に、接続が確立できるかを確認するために使われます。
  2. runVerboseOnlypingメソッド:

    • runVerboseOnlyは、pingCmdのような、通常はユーザーに詳細な出力を表示する必要がないコマンドを実行するために導入されました。これにより、go getの出力がクリーンに保たれます。
    • pingメソッドは、vcsCmdpingCmdを使用して、指定されたスキームとリポジトリに対して実際に接続テストを行います。エラーが返されなければ、そのスキームでリポジトリに到達可能であると判断されます。
  3. vcsPath構造体へのpingフィールドの追加:

    • vcsPathは、インポートパスの正規表現と、それに対応するVCSの種類、リポジトリのルートなどを定義する構造体です。
    • ping: trueが設定されたvcsPathエントリは、その正規表現にマッチするインポートパスに対して、スキーム検出ロジックを適用することを示します。これにより、特定のパターンにマッチするリポジトリに対してのみスキーム検出を有効にすることができます。
  4. vcsForImportPath関数でのスキーム検出ロジック:

    • この関数は、ユーザーが指定したインポートパスを解析し、どのVCSを使用すべきか、リポジトリのURLは何かなどを決定します。
    • もしsrv.pingtrue(つまり、そのインポートパスに対してスキーム検出が有効)であれば、vcs.schemeに定義されたスキームのリストを順番にループします。
    • 各スキームに対してvcs.pingメソッドを呼び出し、リポジトリへの接続を試みます。
    • 最初に成功したスキームが見つかると、そのスキームがリポジトリURLのプレフィックスとして使用され、ループを抜けます。これにより、go getは自動的に最適なスキームを選択し、リポジトリのフェッチに進むことができます。

この一連の変更により、go getはよりインテリジェントになり、ユーザーが明示的にスキームを指定しなくても、様々なネットワーク環境やリポジトリ構成に対応できるようになりました。これは、特にhttpsが推奨される現代のWeb環境において、go getの信頼性と使いやすさを大幅に向上させるものです。

関連リンク

参考にした情報源リンク

  • 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の詳細)