[インデックス 17547] ファイルの概要
このコミットは、Go言語のコマンドラインツール cmd/go
におけるバージョン管理システム (VCS) のエラーメッセージの改善に関するものです。具体的には、go get
コマンドなどが「既知のバージョン管理システムを使用していません」というエラーを報告する際に、誤ったディレクトリ名を表示していた問題を修正します。
コミット
このコミットは、go get
コマンドがバージョン管理システムを検出できない場合に表示するエラーメッセージにおいて、ユーザーが意図したディレクトリではなく、GOPATH のルートディレクトリを表示してしまう不具合を修正します。これにより、ユーザーは問題の発生源を正確に特定できるようになります。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b99fdb2a11ae58834a2c0c646f54d0a587dd269c
元コミット内容
commit b99fdb2a11ae58834a2c0c646f54d0a587dd269c
Author: Russ Cox <rsc@golang.org>
Date: Tue Sep 10 15:28:29 2013 -0400
cmd/go: report correct directory for 'no version control'
The scan starts at the directory we care about and works
backward to the GOPATH root. The error should say the
original directory name, not the name of the GOPATH root.
Fixes #6175.
R=golang-dev, minux.ma
CC=golang-dev
https://golang.org/cl/13366050
変更の背景
この変更は、Go の issue #6175 に対応するものです。元の問題は、go get
コマンドが特定のディレクトリでバージョン管理システムを検出できなかった場合に、エラーメッセージがユーザーが実際に操作しようとしていたディレクトリではなく、GOPATH のルートディレクトリを指してしまうというものでした。
例えば、$GOPATH/src/foo
というディレクトリで go get -u foo
を実行し、foo
がバージョン管理下にない場合、エラーメッセージは「directory "$GOPATH" is not using a known version control system」のように表示されていました。しかし、ユーザーが実際に問題に直面しているのは $GOPATH/src/foo
であり、GOPATH ルートではありません。この誤解を招くメッセージは、ユーザーが問題の原因を特定し、解決するのを困難にしていました。
このコミットは、エラーメッセージが常にユーザーがコマンドを実行した元のディレクトリを正確に報告するようにすることで、このユーザーエクスペリエンスの問題を解決することを目的としています。
前提知識の解説
- Go Modules と GOPATH: Go 1.11 以降、Go Modules が導入され、プロジェクトの依存関係管理とビルドの仕組みが大きく変わりました。しかし、このコミットが作成された2013年時点では、Go Modules は存在せず、Go のプロジェクトは
GOPATH
という環境変数によって管理されていました。GOPATH
は、Go のソースコード、パッケージ、バイナリが配置されるワークスペースのルートディレクトリを指します。通常、$GOPATH/src
にソースコードが、$GOPATH/pkg
にコンパイル済みパッケージが、$GOPATH/bin
に実行可能バイナリが格納されます。 go get
コマンド:go get
は、Go のパッケージとその依存関係をダウンロードし、インストールするためのコマンドです。このコマンドは、指定されたパッケージのソースコードを、そのパッケージが利用しているバージョン管理システム (Git, Mercurial, Subversion など) を介して取得します。- バージョン管理システム (VCS): ソフトウェアのソースコードの変更履歴を管理するためのシステムです。Git、Mercurial (hg)、Subversion (svn) などが代表的です。
go get
コマンドは、これらのVCSツールを内部的に利用してリモートリポジトリからコードを取得します。 src/cmd/go/vcs.go
: Go のソースコードツリー内にあるこのファイルは、go
コマンドが様々なバージョン管理システムとどのように連携するかを定義しています。具体的には、リポジトリのルートを特定したり、VCS固有のコマンドを実行したりするロジックが含まれています。fmt.Errorf
: Go 言語の標準ライブラリfmt
パッケージに含まれる関数で、フォーマットされたエラー文字列を生成するために使用されます。
技術的詳細
cmd/go
の内部では、vcsForDir
関数が、与えられたディレクトリがどのバージョン管理システムを使用しているかを判断するために、そのディレクトリから GOPATH ルートに向かって親ディレクトリを遡ってスキャンします。このスキャンは、.git
や .hg
といったVCS固有のメタデータディレクトリを探すことで行われます。
元の実装では、vcsForDir
関数がVCSを検出できなかった場合、エラーメッセージを生成する際に、スキャンが最終的に到達したディレクトリ(多くの場合、GOPATH のルートディレクトリ)をエラーメッセージに含めていました。
このコミットの修正は、この挙動を変更します。具体的には、vcsForDir
関数が最初に受け取った、ユーザーがコマンドを実行した「元のディレクトリ」を origDir
という変数に保存します。そして、VCSが見つからなかった場合にエラーメッセージを生成する際に、スキャン中に到達したディレクトリではなく、この origDir
を使用するように変更します。
これにより、エラーメッセージは常にユーザーが意図した操作の対象となったディレクトリを正確に指し示すようになり、ユーザーはより迅速に問題の原因を特定できるようになります。
コアとなるコードの変更箇所
変更は主に src/cmd/go/vcs.go
ファイルの vcsForDir
関数にあります。
--- a/src/cmd/go/vcs.go
+++ b/src/cmd/go/vcs.go
@@ -321,6 +321,7 @@ func vcsForDir(p *Package) (vcs *vcsCmd, root string, err error) {
return nil, "", fmt.Errorf("directory %q is outside source root %q", dir, srcRoot)
}
+ origDir := dir
for len(dir) > len(srcRoot) {
for _, vcs := range vcsList {
if fi, err := os.Stat(filepath.Join(dir, "."+vcs.cmd)); err == nil && fi.IsDir() {
@@ -337,7 +338,7 @@ func vcsForDir(p *Package) (vcs *vcsCmd, root string, err error) {
dir = ndir
}
- return nil, "", fmt.Errorf("directory %q is not using a known version control system", dir)
+ return nil, "", fmt.Errorf("directory %q is not using a known version control system", origDir)
}
// repoRoot represents a version control system, a repo, and a root of
また、この修正を検証するためのテストケースが src/cmd/go/test.bash
に追加されています。
--- a/src/cmd/go/test.bash
+++ b/src/cmd/go/test.bash
@@ -139,6 +139,18 @@ if ! ./testgo test ./testdata/testimport/*.go; then
ok=false
fi
+TEST version control error message includes correct directory
+export GOPATH=$(pwd)/testdata/shadow/root1
+if ./testgo get -u foo 2>testdata/err; then
+ echo "go get -u foo succeeded unexpectedly"
+ ok=false
+elif ! grep testdata/shadow/root1/src/foo testdata/err >/dev/null; then
+ echo "go get -u error does not mention shadow/root1/src/foo:"
+ cat testdata/err
+ ok=false
+fi
+unset GOPATH
+
# Test that without $GOBIN set, binaries get installed
# into the GOPATH bin directory.
TEST install into GOPATH
コアとなるコードの解説
src/cmd/go/vcs.go
の変更点:
-
origDir := dir
の追加:vcsForDir
関数の冒頭で、引数として渡されたdir
(ユーザーがコマンドを実行した元のディレクトリ) の値をorigDir
という新しい変数に保存しています。このorigDir
は、関数内でディレクトリを遡るスキャンが行われる間も、元のディレクトリのパスを保持し続けます。 -
エラーメッセージの変更:
return nil, "", fmt.Errorf("directory %q is not using a known version control system", dir)
がreturn nil, "", fmt.Errorf("directory %q is not using a known version control system", origDir)
に変更されています。 これにより、VCS が見つからなかった場合に生成されるエラーメッセージは、スキャンによって変更されたdir
変数の値ではなく、常にユーザーが最初に指定したorigDir
の値を参照するようになります。
src/cmd/go/test.bash
の変更点:
追加されたテストケースは、以下の手順でこの修正が正しく機能するかを検証しています。
GOPATH
を一時的に$(pwd)/testdata/shadow/root1
に設定します。./testgo get -u foo
を実行し、エラー出力をtestdata/err
にリダイレクトします。foo
はバージョン管理下にないことを想定しています。go get -u foo
が予期せず成功した場合、テストは失敗します。testdata/err
の内容にtestdata/shadow/root1/src/foo
(元のディレクトリ) が含まれているかをgrep
で確認します。含まれていない場合、エラーメッセージが正しくないためテストは失敗します。- 最後に
GOPATH
をunset
して、環境をクリーンアップします。
このテストは、エラーメッセージが修正された通り、元のディレクトリ名を正確に報告していることを保証します。
関連リンク
- Go issue #6175: https://github.com/golang/go/issues/6175
- Go CL 13366050: https://golang.org/cl/13366050
参考にした情報源リンク
- Go issue #6175 の内容
- Go の
cmd/go
ソースコード (src/cmd/go/vcs.go
およびsrc/cmd/go/test.bash
) - Go の
GOPATH
に関する公式ドキュメント (当時の情報に基づく) - Go の
go get
コマンドに関する公式ドキュメント (当時の情報に基づく) - Go のバージョン管理システム連携に関する一般的な知識
fmt.Errorf
の Go 言語ドキュメントos.Stat
およびfilepath.Join
の Go 言語ドキュメントgrep
コマンドの一般的な使用法