[インデックス 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コマンドの一般的な使用法