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

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

このコミットは、Goプロジェクトのコードレビューシステムで使用されるcodereview.pyスクリプトに対する変更です。具体的には、Mercurial (Hg) リポジトリの同期処理(hg sync)における挙動を改善し、ユーザーが常に最新の状態に更新されるように強制するものです。

コミット

commit 75aab1374196f454c9fa579863eaadbae2ac17c3
Author: Russ Cox <rsc@golang.org>
Date:   Tue Jun 25 17:23:21 2013 -0400

    codereview: force hg update after hg pull -u during hg sync
    
    If you hg update your client to an earlier CL, then
    hg sync will move you back to tip if it pulls anything in,
    but it will leave you where you are if it doesn't pull anything in.
    That's confusing: make hg sync always update to tip.
    
    R=golang-dev, bradfitz, r, dsymonds
    CC=golang-dev
    https://golang.org/cl/10456044

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

https://github.com/golang/go/commit/75aab1374196f454c9fa579863eaadbae2ac17c3

元コミット内容

codereview: force hg update after hg pull -u during hg sync

If you hg update your client to an earlier CL, then
hg sync will move you back to tip if it pulls anything in,
but it will leave you where you are if it doesn't pull anything in.
That's confusing: make hg sync always update to tip.

R=golang-dev, bradfitz, r, dsymonds
CC=golang-dev
https://golang.org/cl/10456044

変更の背景

この変更の背景には、Mercurial (Hg) を利用したGoプロジェクトのコードレビューワークフローにおける、hg syncコマンドの挙動に関する混乱がありました。

従来のhg syncコマンドは、リモートリポジトリから新しい変更セット(コミット)をプルした場合(hg pull -u)、自動的にローカルリポジトリの作業コピーを最新の状態("tip")に更新していました。しかし、もしリモートに新しい変更がなかった場合、hg syncは何もプルせず、結果としてローカルの作業コピーはユーザーが以前にhg updateでチェックアウトしていた古い変更セットのままになっていました。

この挙動は、特に開発者が意図的に古い変更セットにhg updateで戻っていた場合に問題となりました。開発者はhg syncを実行することで常に最新の状態に同期されることを期待するにもかかわらず、新しい変更がない場合は古い状態に留まってしまうため、混乱を招いていました。

このコミットは、このような混乱を解消し、hg syncコマンドが常にローカルの作業コピーをリポジトリの最新の状態("tip")に更新するように強制することを目的としています。これにより、開発者はhg syncを実行すれば、常に最新のコードベースで作業できるという一貫した期待を持つことができるようになります。

前提知識の解説

このコミットを理解するためには、以下の概念について理解しておく必要があります。

Mercurial (Hg)

Mercurialは、Gitと同様の分散型バージョン管理システム(DVCS)です。Goプロジェクトは初期にMercurialを使用していましたが、後にGitに移行しました。しかし、このコミットが作成された2013年時点では、Mercurialが主要なバージョン管理システムとして利用されていました。

Mercurialの基本的なコマンドと概念は以下の通りです。

  • リポジトリ (Repository): プロジェクトのすべてのファイルと変更履歴が保存されている場所。
  • 変更セット (Changeset): Gitにおけるコミットに相当し、一連の変更をまとめた単位。
  • ヘッド (Head): 特定のブランチにおける最新の変更セット。
  • tip: リポジトリ全体で最も新しい変更セット。
  • 作業コピー (Working Copy): リポジトリからチェックアウトされ、実際にファイルを編集する場所。

Mercurialの主要コマンド

  • hg pull: リモートリポジトリから新しい変更セットをローカルリポジトリにダウンロードします。このコマンドだけでは作業コピーは更新されません。
  • hg update: ローカルリポジトリ内の特定の変更セットに作業コピーを更新します。引数なしで実行すると、通常はローカルリポジトリのtipに更新されます。
  • hg pull -u: hg pullを実行した後、自動的に作業コピーをプルした最新の変更セットに更新します。
  • hg incoming: リモートリポジトリに、ローカルリポジトリにはまだ存在しない変更セットがあるかどうかを確認します。
  • hg sync: Goプロジェクトのコードレビューツールで使用されるカスタムコマンドで、hg pullhg updateを組み合わせたような動作をします。このコミットの変更対象です。

Goのコードレビューシステム

Goプロジェクトは、独自のコードレビューシステムを使用していました。これは、Gerritのようなツールに似ていますが、Mercurialと密接に統合されていました。codereview.pyスクリプトは、このシステムの一部であり、Mercurialの操作をラップして、コードレビューワークフローを円滑に進めるためのユーティリティ機能を提供していました。

codereview.py

このPythonスクリプトは、GoプロジェクトのMercurialベースのコードレビューシステムの中核をなすものでした。Mercurialコマンドのラッパーや、コードレビューに関連する様々なヘルパー機能を提供していました。このコミットでは、特にsync関数が変更されています。

技術的詳細

このコミットの技術的な核心は、lib/codereview/codereview.pyファイルのsync関数における条件分岐の変更です。

変更前は、sync関数内でリモートからの変更をプルする際に、hg_pull(ui, repo, update=True)が直接呼び出されていました。このupdate=Trueは、hg pull -uに相当し、プルが成功すれば作業コピーも更新されることを意味します。

しかし、前述の背景で説明したように、hg pullで新しい変更が何もプルされなかった場合、hg pull -uは実質的に何もせず、作業コピーは古い変更セットのままになっていました。

このコミットでは、この挙動を修正するために、sync関数内に以下のロジックが追加されました。

  1. hg_incoming(ui, repo)の呼び出し: まず、hg_incoming関数を呼び出して、リモートリポジトリにローカルにはまだない新しい変更セットがあるかどうかを確認します。
  2. 条件分岐:
    • もし新しい変更セットがある場合(hg_incomingがTrueを返す場合)、これまで通りhg_pull(ui, repo, update=True)を実行します。これにより、新しい変更がプルされ、作業コピーも最新に更新されます。
    • もし新しい変更セットがない場合(hg_incomingがFalseを返す場合)、hg_update(ui, repo)を呼び出します。このhg_updateは、新しく追加されたヘルパー関数で、引数なしのhg updateに相当し、ローカルリポジトリのtipに作業コピーを強制的に更新します。

この変更により、hg syncは常に以下のいずれかの方法で作業コピーを最新の状態に保つことが保証されます。

  • 新しい変更をプルして更新する。
  • 新しい変更がない場合でも、明示的にhg updateを実行して最新の状態に更新する。

コミットメッセージにある「It is important not to do both hg pull -u and hg update in the same command, because the hg update will end up marking resolve conflicts from the hg pull -u as resolved, causing files with <<< >>> markers to not show up in hg resolve -l. Yay Mercurial.」というコメントは、Mercurialの挙動に関する注意点を示しています。hg pull -uhg updateを連続して実行すると、hg pull -uによって発生したマージコンフリクトが、その後のhg updateによって誤って解決済みとマークされてしまう可能性があるため、これを避けるために条件分岐でどちらか一方のみを実行するようにしている、という非常に具体的なMercurialの癖への対応が示されています。これは、Mercurialの内部動作を深く理解している開発者ならではの配慮と言えます。

また、このコミットでは、hg_updateという新しいヘルパー関数がcodereview.pyに追加されています。この関数は、hg_commands.updateをラップし、hg_pullと同様に、Mercurialの出力からノイズを除去し、ユーザーにとって分かりやすい形式で表示する役割を担っています。

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

lib/codereview/codereview.pyファイルのsync関数と、新しく追加されたhg_update関数が主な変更箇所です。

--- a/lib/codereview/codereview.py
+++ b/lib/codereview/codereview.py
@@ -1168,6 +1168,25 @@ def hg_pull(ui, repo, **opts):\
 		ui.write(line + '\n')
 	return err
 
+def hg_update(ui, repo, **opts):\
+	w = uiwrap(ui)
+	ui.quiet = False
+	ui.verbose = True  # for file list
+	err = hg_commands.update(ui, repo, **opts)
+	for line in w.output().split('\n'):
+		if isNoise(line):
+			continue
+		if line.startswith('moving '):
+			line = 'mv ' + line[len('moving '):]
+		if line.startswith('getting ') and line.find(' to ') >= 0:
+			line = 'mv ' + line[len('getting '):]
+		if line.startswith('getting '):
+			line = '+ ' + line[len('getting '):]
+		if line.startswith('removing '):
+			line = '- ' + line[len('removing '):]
+		ui.write(line + '\n')
+	return err
+
 def hg_push(ui, repo, **opts):\
 	w = uiwrap(ui)
 	ui.quiet = False
@@ -2019,7 +2038,19 @@ def sync(ui, repo, **opts):\
 		raise hg_util.Abort(codereview_disabled)\
 
 	if not opts["local"]:\
-		err = hg_pull(ui, repo, update=True)\
+		# If there are incoming CLs, pull -u will do the update.
+		# If there are no incoming CLs, do hg update to make sure
+		# that an update always happens regardless. This is less
+		# surprising than update depending on incoming CLs.
+		# It is important not to do both hg pull -u and hg update
+		# in the same command, because the hg update will end
+		# up marking resolve conflicts from the hg pull -u as resolved,
+		# causing files with <<< >>> markers to not show up in 
+		# hg resolve -l. Yay Mercurial.
+		if hg_incoming(ui, repo):
+			err = hg_pull(ui, repo, update=True)
+		else:
+			err = hg_update(ui, repo)
 		if err:
 			return err
 	sync_changes(ui, repo)

コアとなるコードの解説

hg_update関数の追加

このコミットで新しく追加されたhg_update関数は、Mercurialのupdateコマンドをラップしています。

  • w = uiwrap(ui): uiwrapは、MercurialのUIオブジェクトをラップし、コマンドの出力をキャプチャするためのユーティリティです。
  • ui.quiet = Falseui.verbose = True: Mercurialコマンドの出力を詳細にする設定です。
  • err = hg_commands.update(ui, repo, **opts): 実際のMercurialのupdateコマンドを実行します。
  • for line in w.output().split('\n'):: updateコマンドの出力を1行ずつ処理します。
  • if isNoise(line): continue: 不要な出力(ノイズ)をスキップします。
  • if line.startswith('moving '): line = 'mv ' + line[len('moving '):]: movingで始まる行をmvに変換し、より一般的なファイル移動の表示に合わせます。
  • if line.startswith('getting ') and line.find(' to ') >= 0: line = 'mv ' + line[len('getting '):]: getting ... to ...のような行もmvに変換します。
  • if line.startswith('getting '): line = '+ ' + line[len('getting '):]: gettingで始まる行を+に変換し、ファイル追加の表示に合わせます。
  • if line.startswith('removing '): line = '- ' + line[len('removing '):]: removingで始まる行を-に変換し、ファイル削除の表示に合わせます。
  • ui.write(line + '\n'): 処理された行をUIに出力します。

このhg_update関数は、hg_pull関数と同様に、Mercurialの生出力をよりユーザーフレンドリーな形式に整形する役割を担っています。

sync関数の変更

sync関数内の変更は、以下の条件分岐が追加された部分です。

		if hg_incoming(ui, repo):
			err = hg_pull(ui, repo, update=True)
		else:
			err = hg_update(ui, repo)
  • if hg_incoming(ui, repo):: リモートリポジトリに新しい変更セットがあるかどうかをhg_incoming関数で確認します。
    • hg_incomingは、Mercurialのincomingコマンドをラップしたもので、新しい変更がある場合にTrueを返します。
  • err = hg_pull(ui, repo, update=True): 新しい変更がある場合、これまで通りhg pull -uに相当するhg_pullを呼び出します。これにより、新しい変更がプルされ、作業コピーも最新に更新されます。
  • else:: 新しい変更がない場合。
  • err = hg_update(ui, repo): 新しい変更がない場合でも、新しく追加されたhg_update関数を呼び出し、作業コピーをローカルリポジトリのtipに強制的に更新します。

このロジックにより、hg syncは常に作業コピーを最新の状態に保つことが保証され、ユーザーの混乱が解消されます。また、Mercurialの特定の挙動(hg pull -uhg updateの連続実行によるコンフリクト解決の誤認識)を回避するためのコメントも非常に重要で、当時のMercurialの利用における深い知見を示しています。

関連リンク

  • Goプロジェクトのコードレビューシステムに関する情報(当時のもの):https://golang.org/doc/contribute.html (現在はGitベースのGerritに移行しているため、当時のMercurialベースのシステムに関する直接的なドキュメントは少ないかもしれません。)
  • Mercurial公式サイト: https://www.mercurial-scm.org/

参考にした情報源リンク

  • Mercurial公式ドキュメント
  • GoプロジェクトのGitHubリポジトリのコミット履歴
  • Goプロジェクトのメーリングリストや開発者フォーラム(当時の議論を追う場合)
  • https://golang.org/cl/10456044 (Goのコードレビューシステムにおける変更リストのURL)
  • Mercurialのpull, update, incomingコマンドに関する一般的な情報