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

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

このコミットは、Go言語のコマンドラインツール cmd/go における、Gitリポジトリに対する go get -u コマンドの挙動修正に関するものです。具体的には、go get -u がGitリポジトリを更新する際に、git fetch ではなく git pull --ff-only を使用するように変更することで、ユーザーのローカル変更を誤って上書きすることなく、より安全かつ確実に最新のコードを取得できるようにします。

コミット

commit 8b16a8bbc10c9cfc07123311e1cc05263c7beec9
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Sun Jul 7 23:06:30 2013 +0800

    cmd/go: fix "go get -u" for git repositories.
    CL 10869046 changed cmd/go to checkout master branch, so
    for "go get -u" to work, it must "git pull" instead of
    "git fetch". Added "--ff-only" so that it won't accidentally
    overwrite user changes.
    
    R=dsymonds
    CC=golang-dev
    https://golang.org/cl/10907043

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

https://github.com/golang/go/commit/8b16a8bbc10c9cfc07123311e1cc05263c7beec9

元コミット内容

cmd/go: fix "go get -u" for git repositories.
CL 10869046 changed cmd/go to checkout master branch, so
for "go get -u" to work, it must "git pull" instead of
"git fetch". Added "--ff-only" so that it won't accidentally
overwrite user changes.

R=dsymonds
CC=golang-dev
https://golang.org/cl/10907043

変更の背景

この変更の背景には、以前のコミット CL 10869046 があります。CL 10869046 は、cmd/go がGitリポジトリを扱う際に、デフォルトで master ブランチをチェックアウトするように変更しました。この変更以前は、go get -u は単にリモートの変更をフェッチ(git fetch)するだけで、ローカルブランチにマージする操作は行っていませんでした。

master ブランチをチェックアウトするようになったことで、go get -u が期待通りに最新のコードを反映するためには、フェッチした変更をローカルの master ブランチに統合する必要が生じました。しかし、単に git fetch を実行するだけでは、リモートの変更がローカルの作業コピーに反映されません。そのため、git pull のようなマージ操作が必要となりました。

また、go get -u はユーザーがローカルで行った変更を上書きすべきではありません。もしユーザーがローカルで作業中のリポジトリに対して go get -u を実行した場合、その変更が失われることは望ましくありません。この問題を解決するために、git pull--ff-only オプションを追加することが決定されました。

前提知識の解説

このコミットを理解するためには、以下のGitコマンドとGoの go get コマンドに関する基本的な知識が必要です。

  • git fetch: リモートリポジトリから最新の変更(コミット、ブランチ、タグなど)をローカルリポジトリにダウンロードしますが、ローカルのブランチにはマージしません。リモートの変更をローカルで確認したいが、現在の作業を中断したくない場合などに使用します。ダウンロードされた変更は、通常 origin/master のようなリモート追跡ブランチに保存されます。

  • git pull: git fetchgit merge の2つの操作を組み合わせたものです。リモートリポジトリから最新の変更をダウンロードし(fetch)、その後、現在のローカルブランチにその変更をマージします。これにより、ローカルの作業コピーがリモートの最新状態に更新されます。

  • git pull --ff-only: git pull--ff-only オプションを付けたものです。このオプションは、マージが「fast-forward」(早送り)マージである場合にのみ実行を許可します。fast-forwardマージとは、ローカルブランチがリモートブランチの直接の祖先である場合に発生するマージで、単にローカルブランチのポインタをリモートブランチの最新コミットに移動させるだけで完了します。 もしfast-forwardマージが不可能な場合(つまり、ローカルブランチにリモートにはないコミットがある場合、またはマージコンフリクトが発生する可能性がある場合)、--ff-only オプションが指定されていると git pull は失敗し、マージは実行されません。これにより、ユーザーのローカル変更が意図せず上書きされたり、複雑なマージコンフリクトが自動的に解決されたりするのを防ぎます。

  • go get: Go言語のパッケージ管理コマンドの一つです。指定されたパッケージとその依存関係をダウンロードし、インストールします。

    • go get -u: -u オプションは「update」を意味し、指定されたパッケージとその依存関係を最新バージョンに更新します。既にローカルに存在するパッケージの場合、リモートリポジトリから最新の変更を取得し、ローカルのコピーを更新しようとします。

技術的詳細

このコミットの技術的な核心は、cmd/go がGitリポジトリを更新する際の内部的な挙動を変更した点にあります。

以前の go get -u は、Gitリポジトリに対して git fetch コマンドを実行していました。git fetch はリモートの変更をローカルに取得するものの、ローカルの作業ブランチ(例: master)には自動的にマージしません。これは、ユーザーが手動でマージを行うことを想定しているか、あるいは go get が単にリモートの最新状態をローカルにキャッシュするだけで十分だと考えられていたためかもしれません。

しかし、CL 10869046 によって cmd/gomaster ブランチをチェックアウトするようになったため、go get -u が実際にローカルの master ブランチを最新の状態に「更新」するためには、フェッチした変更をマージする必要が生じました。

そこで、git pull コマンドが導入されました。git pullgit fetchgit merge を組み合わせたものであり、リモートの変更を取得し、それを現在のブランチにマージします。これにより、go get -u が実行された際に、ローカルのGitリポジトリがリモートの最新状態に同期されるようになります。

さらに重要なのは、--ff-only オプションの追加です。このオプションは、git pull がfast-forwardマージのみを実行することを強制します。

  • fast-forwardマージが可能な場合: ローカルブランチにリモートにはないコミットがなく、単にリモートの最新コミットまでポインタを進めるだけで済む場合、git pull --ff-only は成功し、ローカルリポジトリはスムーズに更新されます。これは、ユーザーがローカルで変更を加えていない、または変更をコミットしていない場合に典型的です。
  • fast-forwardマージが不可能な場合: ローカルブランチにリモートにはないコミットがある場合(つまり、ユーザーがローカルで作業を進めている場合)、git pull --ff-only は失敗します。これにより、go get -u はユーザーの未コミットの変更や、リモートと競合する可能性のあるローカルコミットを自動的に上書きしたり、複雑なマージを試みたりすることなく、安全に処理を停止します。ユーザーはその後、手動でマージを行うか、変更をコミット/スタッシュするなどの対応を取ることができます。

この変更により、go get -u はGitリポジトリに対してより堅牢で安全な更新メカニズムを提供するようになりました。

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

変更は src/cmd/go/vcs.go ファイルの vcsGit 構造体内の downloadCmd フィールドにあります。

--- a/src/cmd/go/vcs.go
+++ b/src/cmd/go/vcs.go
@@ -91,7 +91,7 @@ var vcsGit = &vcsCmd{
 	cmd:  "git",
 
 	createCmd:   "clone {repo} {dir}",
-	downloadCmd: "fetch",
+	downloadCmd: "pull --ff-only",
 
 	tagCmd: []tagCmd{
 		// tags/xxx matches a git tag named xxx

コアとなるコードの解説

src/cmd/go/vcs.go ファイルは、Goの cmd/go ツールが様々なバージョン管理システム(VCS)とどのように連携するかを定義しています。このファイルには、Git、Mercurial (hg)、Subversion (svn)、Bazaar (bzr) などのVCSごとのコマンド定義が含まれています。

vcsGit 構造体は、Gitリポジトリを操作するためのコマンドを定義しています。

  • cmd: "git": Gitコマンドの実行ファイル名を指定します。
  • createCmd: "clone {repo} {dir}": 新しいリポジトリを作成(クローン)する際に実行されるコマンドです。{repo} はリモートリポジトリのURL、{dir} はローカルのディレクトリパスに置き換えられます。
  • downloadCmd: "fetch" から downloadCmd: "pull --ff-only" への変更がこのコミットの核心です。
    • 変更前は、downloadCmdfetch でした。これは、go get -u がリポジトリを更新する際に、単に git fetch を実行していたことを意味します。
    • 変更後は、downloadCmdpull --ff-only になりました。これにより、go get -ugit pull --ff-only を実行するようになり、リモートの変更をフェッチし、fast-forwardマージが可能な場合にのみローカルブランチにマージするようになりました。

この変更により、go get -u はGitリポジトリの更新において、より安全で意図した通りの挙動を示すようになりました。

関連リンク

参考にした情報源リンク