【図解】SVNコミットの取り消し・リバートのやり方を分かりやすく解説:完全ガイド
はじめに
ソフトウェア開発の現場において、バージョン管理システム(VCS)は不可欠なツールです。中でもSubversion(SVN)は、シンプルながらも堅牢な履歴管理機能を提供し、長年にわたり多くのプロジェクトで利用されてきました。しかし、人間である以上、作業には常に間違いがつきまといます。意図しない変更をコミットしてしまったり、後からその変更がバグの原因であることが判明したり、あるいは機密情報や巨大なファイルを誤ってリポジトリに含めてしまったりすることもあるでしょう。
このような状況に直面したとき、「あのコミットを取り消したい」「以前の状態に戻したい」と強く思うはずです。SVNにおいて、コミットの「取り消し」や「リバート」は非常に重要な操作であり、適切に行うことでプロジェクトの健全性を保ち、問題から迅速に回復することができます。
しかし、SVNにおける「取り消し」は、Gitのような他のVCSとは少し異なる概念を持っています。SVNのコミット履歴は原則として「不変」であり、一度コミットされた内容は物理的に削除されることはありません。代わりに、「過去の変更を打ち消す新しいコミットを作成する」という形で「取り消し」を実現します。この仕組みを理解することが、SVNを安全に運用するための鍵となります。
この記事では、SVNにおけるコミットの取り消し・リバートのあらゆる側面を、初心者の方にも分かりやすく、そして経験豊富な開発者の方にも役立つように詳細に解説します。基本的な概念から、コマンドラインやGUIツール(TortoiseSVNなど)を使った具体的な手順、さらには起こりうる問題とその解決策、そして予防策としてのベストプラクティスに至るまで、約5000語にわたる徹底解説で、あなたのSVN運用を強力にサポートします。
さあ、SVNの「時を戻す」魔法を習得し、より安全で効率的な開発ワークフローを構築しましょう。
1. SVNの基本的なコミット・リビジョン管理の理解
SVNでコミットを取り消す操作を正しく理解するためには、まずSVNがどのように履歴を管理しているかを把握しておく必要があります。この基本的な知識が、なぜ特定の操作が必要なのか、そしてその操作が実際に何を行っているのかを理解する土台となります。
1.1. リビジョンとは何か
SVNにおける「リビジョン」(Revision)とは、リポジトリの状態をある特定の時点における「スナップショット」として捉えたものです。あなたが何らかの変更(ファイルの追加、削除、編集など)をコミットするたびに、リポジトリ全体に対して新しいリビジョン番号が割り当てられます。
例えば、最初にリポジトリを作成した時点がリビジョン0 (r0) とすると、最初のコミットでr1、その次のコミットでr2、というように、コミットが成功するたびにリビジョン番号は1ずつ増加していきます。このリビジョン番号はグローバルなものであり、リポジトリ全体で一意に採番されます。
図のイメージ:
リポジトリ
├── r0 (初期状態)
├── r1 (最初のコミット)
├── r2 (2番目のコミット)
├── r3 (3番目のコミット)
└── …
それぞれのリビジョンは、その時点でのプロジェクトの全てのファイルとディレクトリの状態を完全に再現できる「状態」を表しています。SVNは、この一連のリビジョンを連鎖的に記録することで、プロジェクトの全歴史を管理しているのです。
1.2. コミットとは何か
「コミット」(Commit)とは、あなたのローカル作業コピーで行った変更(新しいファイルの作成、既存ファイルの編集、ファイルの削除など)を、SVNリポジトリに永続的に記録する操作を指します。コミットが成功すると、前述のように新しいリビジョンが生成され、その変更がリポジトリの履歴の一部となります。
コミットは、以下の情報をリポジトリに記録します。
* 変更されたファイルやディレクトリのリストと内容: どのファイルがどう変わったか。
* コミットメッセージ: その変更が何であるかを説明するテキスト。
* 作者: コミットを行ったユーザー名。
* タイムスタンプ: コミットが行われた日時。
* 新しいリビジョン番号: このコミットによって生成された一意の番号。
コミットはプロジェクトの進捗を記録する重要なマイルストーンであり、共同開発においては他のメンバーと変更を共有するための主要な手段です。
1.3. SVNのリポジトリ構造(ツリー、履歴)
SVNリポジトリは、論理的にはファイルシステムのツリー構造を持っています。通常、プロジェクトは /trunk
、/branches
、/tags
といった標準的なディレクトリ構造で管理されます。
* /trunk
: メインの開発ライン。
* /branches
: トランクから分岐して開発された実験的な機能や特定バージョンの開発ライン。
* /tags
: 特定のリビジョンのスナップショット(リリースバージョンなど)を記録するための読み取り専用ブランチ。
SVNは、これらのパスに対する変更をリビジョンとして記録します。リビジョンは直線的な履歴として表現され、一つ前のリビジョンから現在のリビジョンへの差分が記録されています。これにより、いつでも過去の任意のリビジョンにさかのぼって、その時点のファイルやディレクトリの状態を確認したり、あるいはそのリビジョンの変更内容だけを取り出したりすることが可能になります。
図のイメージ:
r1 -> r2 -> r3 -> r4 -> r5 (直線的なリビジョン履歴)
1.4. 不変の履歴:SVNコミットの「取り消し」が実際には「取り消しをコミットする」ことである理由
ここがSVNの「取り消し」を理解する上で最も重要なポイントです。
SVNでは、一度リポジトリにコミットされたリビジョンは原則として変更することも、完全に削除することもできません。 これは、SVNが「履歴の不変性」を重視しているためです。全てのコミットは永続的な記録として残り、プロジェクトの真の歴史を保証します。
「じゃあ、間違ったコミットはどうするんだ?」という疑問が当然湧きます。
SVNにおけるコミットの「取り消し」や「リバート」とは、「特定の過去のリビジョンで加えられた変更を打ち消す、新しい変更をコミットする」ことを意味します。つまり、過去のリビジョンを抹消するのではなく、その過去のリビジョンの内容を「元に戻す」差分を作成し、それを新しいリビジョンとしてコミットするのです。
図のイメージ:
r1 (変更A) -> r2 (変更B) -> r3 (変更C)
もしr2の「変更B」を取り消したい場合、SVNは以下のような処理を行います。
1. r2で追加された内容を削除する。
2. r2で削除された内容を元に戻す(追加する)。
3. r2で編集された内容を元の状態に戻す。
これらの操作をローカル作業コピーに適用し、その結果をr4として新たにコミットします。
結果として、リポジトリの履歴は以下のようになります。
r1 (変更A) -> r2 (変更B) -> r3 (変更C) -> r4 (変更Bを元に戻す)
このように、r2自体はリポジトリから消えず、r2で行われた変更を打ち消す新しいリビジョンr4が追加されることで、「実質的に」r2の変更が取り消された状態になります。この性質を理解していれば、リバート後の履歴の状態や、コンフリクト発生時の挙動を予測しやすくなります。
2. コミットを取り消す・リバートする主なケースと考慮事項
SVNでコミットを取り消す、あるいはリバートする状況は様々です。それぞれの状況に応じた適切なアプローチを理解し、操作前に考慮すべき点を把握しておくことは、予期せぬトラブルを避ける上で非常に重要です。
2.1. 特定のリビジョンで導入された変更を「元に戻す」ケース (Revert)
これがSVNにおける最も一般的で推奨される「取り消し」のシナリオです。
* シナリオ: 特定のリビジョン(例: r100)でコミットされた機能やバグ修正が、実際には新たな問題を引き起こした、あるいは不要であることが判明した。そのr100の変更だけを無効にしたい。
* 目的: r100で行われた変更(ファイルの追加、削除、編集)を「なかったこと」にする。
* 操作の概念: r100の変更を打ち消すための「逆差分」を現在の作業コピーに適用し、その結果を新しいコミットとしてリポジトリに記録します。
* 特徴: この操作は、履歴からr100を消すわけではありません。r100はそのまま残り、その後のリビジョンも残ります。ただ、r100の変更内容が、その後の新しいリビジョンによって打ち消されるだけです。
図のイメージ:
r99 —(変更A)–> r100 (変更B) —(変更C)–> r101 —(変更D)–> r102(HEAD)
↓
リバート操作:r100の変更Bを逆適用
↓
r99 —(変更A)–> r100 (変更B) —(変更C)–> r101 —(変更D)–> r102 —(変更Bを打ち消す変更)–> r103(HEAD)
2.2. 特定のリビジョン以降の変更を「すべて元に戻す」ケース (Rollback)
これは、プロジェクト全体を過去の特定のリビジョンの状態に「巻き戻す」シナリオです。
* シナリオ: ある時点(例: r100)以降にコミットされた全ての変更が、プロジェクトの方向性と合わなくなった、あるいは重大なバグが複数混入し、まとめて無かったことにしたい。
* 目的: 現在のリビジョン(HEAD)から、指定した過去のリビジョン(例: r100)まで、間にあった全ての変更をまとめて打ち消す。
* 操作の概念: 現在のリビジョンから、目的のリビジョンの「直前」のリビジョンまでの全ての変更の集合を「逆差分」として現在の作業コピーに適用し、それを新しいコミットとして記録します。
* 特徴: この操作は、複数のリビジョンにわたる変更を一括して打ち消します。対象となる期間が長い場合、コンフリクトが発生する可能性が高まります。
図のイメージ:
r99 —(変更A)–> r100 (変更B) —(変更C)–> r101 —(変更D)–> r102(HEAD)
現在r102の状態からr100の状態に戻したい。
↓
リバート操作:r101の変更Cとr102の変更Dを逆適用
↓
r99 —(変更A)–> r100 (変更B) —(変更C)–> r101 —(変更D)–> r102 —(変更C+Dを打ち消す変更)–> r103(HEAD)
2.3. 不要なファイルの誤コミット(秘密情報、巨大バイナリなど)
これは少し特殊なケースで、通常の「リバート」だけでは解決しきれない問題を含みます。
* シナリオ: 誤ってパスワードなどの秘密情報、あるいはGB単位の巨大なバイナリファイル(例えば、開発中に生成された一時ファイルやメディアファイル)をコミットしてしまった。
* 目的: そのファイルをリポジトリから完全に消去したい(履歴を含めて)。
* 考慮事項: 通常のsvn delete
やsvn merge -c -R
によるリバートでは、そのファイルが「削除された」という新しい変更が記録されるだけで、過去のリビジョンにはファイルが残ってしまいます。 つまり、そのリビジョンにアクセスすれば、誤ってコミットされたファイルの内容をいつでも取り出すことができてしまいます。
* 解決策: この問題に対するSVNの「公式な」解決策は非常に限定的です。真にリポジトリから履歴を含めてファイルを消去するには、svndumpfilter
を使ったリポジトリのダンプ・フィルター処理・再ロードという、非常に手間とリスクを伴う破壊的な操作が必要になります。これは最終手段であり、通常は推奨されません。(詳細は後述の「リバートすべきでないケースと代替案」で解説します。)
2.4. ブランチ戦略とリバートの関係(トランク vs ブランチ)
プロジェクトがブランチを使用している場合、リバートはさらに複雑になることがあります。
* トランクでのリバート: メインの開発ラインで行うリバートは、通常通り上記のセクションで説明した方法で行います。
* ブランチでのリバート: 特定のブランチ内で何かをリバートする場合も、そのブランチの履歴内で上記の方法を適用できます。
* ブランチ間のリバート/マージの影響:
* あるブランチで行われた変更がトランクにマージされ、その後トランクでその変更がリバートされた場合、そのリバートの履歴はトランクにのみ存在します。元のブランチには影響しません。
* もし、リバートされた変更を含むトランクのコードが、別のブランチにマージされると、そのブランチにもリバートが適用されます。
* 最も注意が必要なのは、一度リバートされた変更を、後から再びマージしてしまうケースです。SVNのマージ追跡機能が十分に機能しない古いバージョンや、複雑なマージを繰り返す環境では、リバートされた変更が再度導入されてしまう「マージ地獄」に陥る可能性があります。
2.5. リバートの際に考慮すべきこと(コンフリクト、影響範囲、チームへの周知)
リバートは強力な操作であるため、実行前には以下の点を十分に考慮する必要があります。
* コンフリクトの可能性: リバート対象のリビジョンと、現在のリビジョンとの間に、同じファイルや行に後続の変更が加えられている場合、コンフリクト(競合)が発生する可能性が非常に高まります。コンフリクトが発生した場合は、手動での解決が必要になります。
* 影響範囲の特定: どのファイルやディレクトリが影響を受けるのか、慎重に確認する必要があります。特に、リバート対象のリビジョンが広範囲にわたる変更を含んでいた場合、その影響はプロジェクト全体に及びます。
* チームへの周知と調整: 共同開発環境でリバートを行う場合は、必ず事前にチームメンバーに連絡し、調整を行うべきです。
* 他のメンバーが既にリバート対象の変更をベースに作業を進めている場合、彼らがsvn update
を行った際に大量のコンフリクトが発生したり、彼らの作業内容が意図せず失われたりする可能性があります。
* 理想的には、リバート対象の変更をベースにしている他のメンバーがいない、あるいは彼らが一旦作業を退避できるタイミングを見計らって実施することが望ましいです。
* リバートをコミットした後も、その旨をチームに再度共有し、必要であれば各自の作業コピーをアップデートしてもらうよう促しましょう。
3. SVNコミットのリバート(元に戻す)の具体的な方法
SVNでコミットをリバートする方法は、主にコマンドラインとGUIツール(TortoiseSVNなど)の2つがあります。どちらの方法でも、基本的には同じ概念で操作が行われますが、GUIツールの方が視覚的に分かりやすく、操作ミスを減らせる場合があります。
最も重要なコマンドは svn merge
です。これには強力なマージアルゴリズムが含まれており、変更を適用するだけでなく、「変更を逆方向に適用する」ことにも使用できます。
3.1. コマンドライン(svn revert vs svn merge -c -r)
混同されやすい2つのコマンドについて、まず違いを明確にしておきます。
* svn revert
: これは、作業コピーで行った変更を破棄し、リポジトリの最新の状態に戻すためのコマンドです。例えば、ファイルを誤って編集してしまったが、まだコミットしていない場合に、その編集を取り消して元の状態に戻すために使います。コミットされた履歴には一切影響しません。 したがって、本記事の目的である「コミットされた変更の取り消し」には直接使用できません。
* svn merge
: これは、異なるブランチ間の変更を結合したり、あるリビジョンの変更を別の場所に適用したりするための非常に強力なコマンドです。そして、特定のコミットの変更を「逆方向に」適用することで、そのコミットを打ち消す用途にも使われます。本記事の目的はこちらです。
それでは、svn merge
を使った具体的なリバート方法を見ていきましょう。
3.1.1. 特定のリビジョンで加えられた変更を取り消す (最も一般的)
これは、例えば「r100でコミットされた変更だけを無効にしたい」という場合に使う方法です。
コマンドの形式:
bash
svn merge -c -<取り消したいリビジョン番号> <作業コピーのパス または リポジトリURL>
または
bash
svn merge --revision <取り消したいリビジョン番号>:<取り消したいリビジョン番号-1> <作業コピーのパス または リポジトリURL>
より簡潔な前者の形式が一般的です。
-c <リビジョン番号>
または--change <リビジョン番号>
: 特定のリビジョンで加えられた変更を対象とします。-
をr
の前に付ける (-r
) ことで、そのリビジョンの変更を「逆方向に」適用することを意味します。例えば-c -100
は「リビジョン100で加えられた変更を逆方向に適用する」という意味になります。<作業コピーのパス>
: 通常は現在いるディレクトリ(.
)を指定します。<リポジトリURL>
: リポジトリの特定のパス(例:^/trunk
やhttps://example.com/svn/project/trunk
)を指定することもできます。パスを指定しない場合、コマンドを実行している現在の作業コピーのパスが対象となります。
例1:現在のディレクトリにある作業コピーに対して、リビジョン100の変更を取り消す
bash
svn merge -c -100 .
(git revert
と同様に、このコマンドが最もよく使われます。)
例2:特定のファイル src/main.c
に対して、リビジョン100の変更を取り消す
bash
svn merge -c -100 src/main.c
(ファイル名やディレクトリ名を指定すれば、そのオブジェクトに対する変更のみが対象になります。)
実行後の流れ:
1. コマンドを実行すると、指定されたリビジョンで加えられた変更が、あなたの作業コピーに「逆方向に」適用されます。
2. これにより、作業コピーのファイルが、指定されたリビジョンがコミットされる前の状態に戻ったような変更が加えられます。
3. svn status
コマンドで確認すると、これらの変更がM(Modified)、D(Deleted)、A(Added)などのステータスで表示されます。これらの変更はまだコミットされていません。
4. 変更内容をよく確認し、意図した通りになっていることを確認したら、新しいコミットメッセージを付けてコミットします。
```bash
svn commit -m "Revert r100: Accidentally introduced a bug in feature X."
```
**重要:** コミットメッセージには、**どのリビジョンをリバートしたのか、なぜリバートしたのか**を明確に記述することが極めて重要です。これにより、後から履歴を見た人が状況を理解しやすくなります。
3.1.2. 特定のリビジョン「まで」の状態に戻す (ロールバック)
これは「現在の状態から、r100の状態にプロジェクト全体を巻き戻したい」という場合に使う方法です。つまり、現在のHEADリビジョンから、ターゲットとなるリビジョンまでの間にあった全ての変更をまとめて取り消します。
コマンドの形式:
bash
svn merge -r <現在のリビジョン番号>:<元に戻したいリビジョンの直前の番号> <作業コピーのパス または リポジトリURL>
または
bash
svn merge -r HEAD:<元に戻したいリビジョンの直前の番号> <作業コピーのパス または リポジトリURL>
* -r <開始リビジョン>:<終了リビジョン>
: 開始リビジョンから終了リビジョンまでの差分を適用します。
* 今回の「ロールバック」の目的では、現在の状態から過去へ向かって差分を適用したいので、現在のリビジョン
を開始リビジョン
、元に戻したいリビジョンの直前の番号
を終了リビジョン
として指定します。SVNは、この指定された範囲の変更を「逆方向に」適用します。
* HEAD
はリポジトリの最新リビジョンを指します。
例:現在の状態 (r105) から、リビジョン100の状態にプロジェクトを巻き戻す
(つまり、r101, r102, r103, r104, r105 の変更を全て取り消す)
現在のリビジョンがr105だと仮定します。r100の状態に戻したいので、r100の直前、つまりr99からr105までの変更を逆方向に適用することになります。
正確には、r105からr99への変更をマージします。
bash
svn merge -r 105:99 .
または
bash
svn merge -r HEAD:99 .
実行後の流れ:
1. コマンドを実行すると、現在のリビジョンから指定した過去のリビジョンまでの全ての変更が、作業コピーに逆方向に適用されます。
2. これにより、作業コピーのファイルが、指定された過去のリビジョンの状態に戻ったような変更が加えられます。
3. svn status
で変更を確認し、問題なければコミットします。
```bash
svn commit -m "Rollback to r100: Reverted all changes from r101 to HEAD due to critical bugs."
```
**重要:** こちらの場合も、コミットメッセージには巻き戻したリビジョンの範囲と理由を明確に記述してください。
この方法は、一度に複数のコミットを取り消す必要がある場合に便利ですが、対象範囲が広いためコンフリクトが発生しやすく、注意が必要です。
3.2. GUIツール (TortoiseSVN) を使用する方法
Windowsユーザーであれば、TortoiseSVNはSVN操作を非常に簡単にしてくれる強力なGUIクライアントです。リバート操作も直感的に行えます。
3.2.1. 特定のリビジョンで加えられた変更を取り消す (最も一般的)
手順:
1. リバートしたいファイルまたはフォルダを含む作業コピーのパスに移動します。(例: プロジェクトルートフォルダ)
2. そのフォルダを右クリックし、「TortoiseSVN」->「ログを表示」 を選択します。
* 図のイメージ: Windowsエクスプローラのコンテキストメニューに表示される「TortoiseSVN」サブメニューと、「ログを表示」という項目。
3. ログダイアログが表示されます。ここに、対象の作業コピーのコミット履歴が新しいものから順に表示されます。
* 図のイメージ: リビジョン番号、作者、日付、コミットメッセージが一覧表示されたログダイアログ。
4. 取り消したい特定のリビジョンを探し、それを右クリックします。
* 図のイメージ: 特定のリビジョンの行を右クリックすると表示されるコンテキストメニュー。
5. コンテキストメニューから「このリビジョンでの変更を取り消す」(Revert changes from this revision)を選択します。
* 図のイメージ: 「このリビジョンでの変更を取り消す」という選択肢がハイライトされている状態。
6. 確認ダイアログが表示されます。「はい」をクリックして続行します。
* TortoiseSVNは、選択したリビジョンで加えられた変更の「逆差分」を、あなたの作業コピーに適用します。
7. 操作が完了すると、作業コピーに変更が適用されたことを示すメッセージが表示されます。
8. 作業コピーの状態を確認します。(例: 変更されたファイルが赤い感嘆符アイコンで表示されるなど)
* 図のイメージ: 作業コピーのファイルアイコンが変更されたことを示すオーバーレイアイコン(通常は赤丸に感嘆符)。
9. 変更内容が意図した通りであることを確認したら、作業コピーを右クリックし、「SVN コミット…」 を選択します。
* 図のイメージ: 「SVN コミット…」ダイアログボックス。
10. コミットダイアログで、自動生成されたコミットメッセージ(通常は「Reverting revision X: [元のコミットメッセージ]」のような形式)が既に記入されています。必要に応じて、なぜこのリバートを行ったのか、詳細な理由を追記します。
* 図のイメージ: 「SVN コミット…」ダイアログボックスのメッセージ入力欄に、リバートを示す自動生成されたメッセージが入力されている状態。
11. 「OK」をクリックしてコミットを完了します。これにより、リバート操作が新しいリビジョンとしてリポジトリに記録されます。
3.2.2. 特定の範囲のリビジョンをロールバック
これは、「現在からr100の状態まで巻き戻したい」という場合に使います。
手順:
1. リバートしたいファイルまたはフォルダを含む作業コピーのパスに移動します。
2. そのフォルダを右クリックし、「TortoiseSVN」->「ログを表示」 を選択します。
3. ログダイアログが表示されたら、「2つのリビジョンの差分を表示」機能を利用します。
* 図のイメージ: ログダイアログの上部ツールバーまたは右クリックメニューに「2つのリビジョンの差分を表示」ボタンまたは項目。
4. 「比較するリビジョンを選択」ダイアログで、以下のリビジョンを指定します。
* 「開始リビジョン」: 現在のHEADリビジョン(または巻き戻したい範囲の最後のコミットのリビジョン)
* 「終了リビジョン」: 巻き戻したい状態のリビジョンの直前のリビジョン(例: r100まで戻したいならr99)
* 図のイメージ: 「比較するリビジョンを選択」ダイアログで、開始リビジョンと終了リビジョンを入力する欄。
5. 「OK」をクリックすると、指定したリビジョン範囲の差分が表示されます。この差分は、本来「フォワードマージ」されるべき差分です。
6. この差分を「逆方向に」適用したいので、差分表示画面で「マージを逆適用」のようなオプションを探すか、あるいはログダイアログに戻り、「リビジョン範囲の変更を元に戻す」(Revert a range of revisions)を選択します。
* これは通常、ログダイアログで何も選択していない状態で右クリックするか、ツールバーから「リビジョン範囲の変更を元に戻す」を選択することで表示される特別なダイアログで行います。
* 図のイメージ: 「リビジョン範囲の変更を元に戻す」ダイアログ。
7. ダイアログで、巻き戻したい範囲のリビジョンを指定します。
* 「開始リビジョン」: 巻き戻しを開始したいリビジョン(例: r101)
* 「終了リビジョン」: 巻き戻しを終了したいリビジョン(例: r105、つまりHEAD)
* この場合、r101からr105までの全ての変更が逆方向に適用されます。
* 注釈: ここでのリビジョン指定は、svn merge -r HEAD:99
のように、Old:New
で指定するのとは異なる感覚なので注意が必要です。TortoiseSVNの「リビジョン範囲の変更を元に戻す」は、指定された開始リビジョンから終了リビジョンまでの「フォワードマージ」を逆方向に実行するという意味合いになります。
8. 「OK」をクリックし、作業コピーに変更が適用されたことを確認します。
9. その後、前述の「SVN コミット…」の手順に従って、変更をコミットします。コミットメッセージには、巻き戻したリビジョンの範囲と理由を明確に記述しましょう。
3.2.3. コンフリクト発生時の対処法
リバート操作は、マージ操作の一種であるため、他の変更と競合する可能性があります。
* 発生する状況: リバート対象のリビジョンで変更された同じ行が、後続のリビジョンでさらに変更されている場合など。
* TortoiseSVNでの表示: コンフリクトが発生すると、ファイルアイコンが黄色の感嘆符(競合)で表示されます。また、コミットしようとすると、「競合しているファイルがあります」というエラーメッセージが表示されます。
* 解決方法:
1. コンフリクトしているファイルを右クリックし、「TortoiseSVN」->「競合を解決」 を選択します。
* 図のイメージ: コンテキストメニューの「TortoiseSVN」サブメニューに表示される「競合を解決」項目。
2. 通常は「編集コンフリクト」を選択し、TortoiseSVNの提供するマージツール(TortoiseMergeなど)を起動します。
* 図のイメージ: TortoiseMergeの画面。3ペインまたは2ペインで、Yours、Theirs、Base、そしてMergedの各バージョンが表示され、コンフリクト箇所が色分けされている。
3. マージツールで、コンフリクト箇所を手動で修正し、どちらの変更を残すか、あるいは両方を組み合わせるかを決定します。リバートの場合、基本的には「リバート側の変更」(つまり、元のリビジョンの内容に戻す変更)を採用することになるでしょう。
4. 修正が完了したら、マージツールを保存して閉じます。
5. 作業コピーのフォルダを右クリックし、「TortoiseSVN」->「競合を解決」 を再度選択し、解決済みであることをSVNに通知します。(この手順を忘れるとコミットできません)
* 図のイメージ: 「競合を解決」ダイアログで、解決したファイルをチェックし、「OK」をクリックする画面。
6. 全てのコンフリクトが解決したら、通常通りコミットします。
3.2.4. コミットメッセージの工夫
コマンドラインでもGUIでも共通ですが、リバートをコミットする際のメッセージは非常に重要です。
* 必須情報:
* どのリビジョン(またはリビジョン範囲)をリバートしたのかを明記する。
* なぜそのリビジョンをリバートしたのか(理由、背景)を簡潔に記述する。
* 推奨フォーマット:
Revert r<リビジョン番号>: <元のコミットメッセージ>
例: Revert r100: Fix: NullPointerException on startup
もし詳細な理由があるなら、その下に詳細を追記します。
Revert r100: Fix: NullPointerException on startup
原因不明の頻繁なクラッシュが発生したため、r100の変更を一時的に取り消します。
詳細はチケット #XXX を参照してください。
適切なコミットメッセージは、後から履歴を追う際の解読性を劇的に向上させます。
4. リバート時の注意点とトラブルシューティング
SVNのリバートは強力なツールですが、誤って使用すると混乱を招く可能性があります。ここでは、リバート操作を行う際に注意すべき点と、よくある問題とその解決策について詳しく見ていきましょう。
4.1. コンフリクトの発生と解消方法
リバート操作は、本質的に「マージ」の一種です。したがって、他のマージ操作と同様にコンフリクト(競合)が発生する可能性があります。
-
コンフリクトが発生する主な理由:
- 同じ箇所の変更: リバート対象のリビジョンで変更されたファイルやコードの同じ行が、そのリバート対象のリビジョンより「後で」コミットされた別のリビジョンでも変更されている場合。
- ファイルの移動・改名: ファイルが移動したり改名されたりした後に、そのファイルに対するリバートを行う場合。
- 追加と削除の競合: あるリビジョンでファイルが追加され、別のリビジョンで同じ名前の別のファイルが追加された後に、最初の追加をリバートする場合など。
-
コンフリクト発生時のSVNの挙動:
SVNは、自動的に解決できない変更箇所がある場合、そのファイルを「競合状態」(conflicted)としてマークします。そして、作業コピーのファイル内に特別なマーカー(<<<<<<<
,=======
,>>>>>>>
)を挿入して、競合箇所を示します。また、競合したファイルの元のバージョンや、コンフリクトを引き起こした各側のバージョンが、.mine
,.rOLD
,.rNEW
などの拡張子を持つ一時ファイルとして作業コピーに作成されます。 -
解消方法:
- コンフリクトの特定:
svn status
コマンドを実行し、C
(Conflicted) と表示されているファイルを確認します。
例:C some_file.txt
- 競合箇所の編集: 競合しているファイルを開き、SVNが挿入したマーカーを目印に、手動でコードを修正します。
- マーカーは以下のようになります:
<<<<<<< .mine
// あなたの現在の作業コピーの変更 (リバート操作後の状態)
=======
// リポジトリから適用された変更 (リバート対象の元の変更)
>>>>>>> .r<リビジョン番号>
リバート操作の場合、<<<<<<< .mine
から=======
までの間が、リバートの結果適用された変更(つまり、元の状態に戻ったもの)です。=======
から>>>>>>>
までの間が、リバートしようとしているリビジョンで加えられた元の変更です。
基本的には、リバートしたい内容(つまり<<<<<<< .mine
の側)を残し、=======
と>>>>>>>
の間の内容を削除し、さらにSVNが挿入したマーカー行自体も削除します。
例: r100で追加された行をリバートするが、その行の後に別の人が追記していた場合。
// r99の状態
Existing code.
// r100で追加された行
New feature code.
// r101で追加された行
Another new feature.
r100をリバートすると、New feature code.
が消える変更が適用される。
コンフリクトマーカーは例えばこうなる。
Existing code.
<<<<<<< .mine
// リバートによって`New feature code.`が消える(見かけ上、r99の状態)
=======
New feature code.
>>>>>>> .r100
Another new feature.
この場合、New feature code.
を含む行と、マーカー行を全て削除して、Existing code.
とAnother new feature.
だけを残すのが正しいリバートの解決方法です。
- マーカーは以下のようになります:
- マージツールの利用: 大量のコンフリクトや複雑なコンフリクトの場合、
svn diff
と組み合わせて、専用のマージツール(TortoiseMerge, Meld, KDiff3など)を使うと効率的です。- コマンドライン:
svn mergetool <file>
(設定が必要な場合があります) - TortoiseSVN: ファイルを右クリック -> TortoiseSVN -> 競合を解決 -> 編集コンフリクト
- コマンドライン:
- 解決済みの通知: 全ての競合箇所を手動で修正し、ファイルからSVNのマーカーを全て削除したら、SVNに解決済みであることを通知します。
bash
svn resolved <競合したファイルまたはディレクトリのパス>
TortoiseSVNの場合は、「競合を解決」ダイアログで解決済みのファイルにチェックを入れて「OK」をクリックします。 - コミット: 全てのファイルが解決されたことを確認したら、通常通り
svn commit
で新しいリビジョンとしてコミットします。
- コンフリクトの特定:
4.2. リバート後の再コミットの重要性
前述の通り、SVNのリバート操作は、作業コピーに変更を適用するだけで、リポジトリにはまだ何も記録されていません。 そのため、リバート操作の後には必ずsvn commit
を実行し、その変更をリポジトリに新しいリビジョンとして永続化する必要があります。
- 新しいリビジョンが生成されること: リバートをコミットすると、新しいリビジョン番号(例:
r106
)が生成され、そのリビジョンに「以前の変更を打ち消す」という事実が記録されます。元のリビジョン(例:r100
)は消えず、引き続き履歴に残ります。 - コミットメッセージに「リバートした」旨を明記することの重要性: これは繰り返しになりますが、非常に重要です。後から履歴を追う人が、なぜ特定の変更がなくなったのか、どのような経緯でそうなったのかを理解できるよう、コミットメッセージに明確に記述してください。これにより、将来的な混乱や誤った再マージを防ぐことができます。
4.3. 誤ってリバートをコミットしてしまった場合(さらにリバート?)
もし、間違ったリビジョンをリバートしてコミットしてしまった場合、どうすれば良いでしょうか?
答えはシンプルです。その「誤ったリバート」をさらにリバートします。
- シナリオ: r100をリバートすべきだったのに、誤ってr99をリバートしてしまい、それをr106としてコミットしてしまった。
- 対処法:
svn log
で、誤ってコミットしてしまった「リバート」のリビジョン番号を確認します。(この例ではr106)- その「リバート」のコミット(r106)を再度リバートします。
bash
svn merge -c -106 . - 作業コピーに
r106
のリバートを取り消す変更が適用されるので、svn commit
で新しいリビジョン(例:r107
)としてコミットします。
コミットメッセージは「Revert r106: Mistake. Reverted previous revert of r99.」のように、経緯を明確に記述しましょう。
- 結果: 履歴は「r99の変更 -> r100の変更 -> … -> r106(r99のリバート) -> r107(r106のリバート)」となり、r99の変更が再び有効な状態に戻ります。
このように、SVNの履歴は不変であるため、全ての操作は積み重ねられていきます。間違いを訂正する操作もまた、履歴として記録されるのです。
4.4. リバートが他のメンバーに与える影響
共同開発環境では、リバートは他のチームメンバーに影響を与える可能性があります。
- アップデート時のコンフリクト:
あなたがリバートをコミットした後、他のメンバーが自分の作業コピーをsvn update
すると、あなたのリバートによって生じた変更が彼らの作業コピーに適用されます。もし、彼らがリバートされた部分と関連する作業を進めていた場合、update
時にコンフリクトが発生する可能性が高まります。 - 作業の巻き戻し:
もし他のメンバーが、あなたがリバートした変更を前提とした作業を既にコミットしてしまっていた場合、彼らのコードは「リバートされた状態」と矛盾することになります。彼らがupdate
すると、彼らのコミットした変更の一部が意図せず打ち消されてしまう可能性があります。 - チームへの事前連絡・調整の重要性:
- 大規模なリバート(特にロールバック)を行う前には、必ずチーム全員に事前に連絡し、相談することをお勧めします。 可能であれば、他のメンバーに一時的に作業を中断してもらうか、自分の作業を別のブランチに退避してもらうなど、協力体制を築きましょう。
- リバートをコミットした後も、その旨を再度チームに周知し、各自の作業コピーを最新にアップデートするよう促します。必要であれば、コンフリクト解決の手助けも行いましょう。
4.5. ブランチ運用におけるリバートの考慮事項
ブランチを使用しているプロジェクトでは、リバートの判断と実施がより複雑になることがあります。
- トランクへのリバートとブランチへのマージ/リバート:
- ブランチで開発された機能がトランクにマージされ、その後トランクでその機能の一部が問題を引き起こし、トランクでリバートされたとします。このリバートの履歴はトランクにのみ存在します。元のブランチには影響しません。
- もし、その「リバートされた変更」を含むトランクを、別のブランチにマージする必要がある場合、そのブランチにもリバートが適用されます。
- リバートされた変更が再度マージされる可能性(マージ地獄):
SVNのバージョンが古い(マージ追跡機能が不完全な)場合や、非常に複雑なブランチ運用を行っている場合、一度リバートして打ち消された変更が、将来的に誤って再びマージされてしまう可能性があります。- これは、SVNが「どの変更がマージ済みか」を完全に追跡できない場合に起こりえます。例えば、あるリビジョンAの変更をリバートするリビジョンBをコミットした後、別のブランチからAと同じ内容の変更を再度マージしてしまい、結果的にAの変更が「復活」してしまう、といったシナリオです。
- 対策:
- SVNのバージョンを可能な限り最新に保つ(特に1.5以降のマージ追跡機能は重要)。
- マージを行う際は、
svn merge --reintegrate
やsvn merge --record-only
などの適切なオプションを使用し、マージ追跡機能を最大限に活用する。 - コミットメッセージにリバートの経緯を詳細に記述し、再マージの際に注意喚起する。
- 複雑なマージを避けるため、ブランチのライフサイクルを明確にする。
- 可能であれば、マージ前にコードレビューを行う。
5. リバートすべきでないケースと代替案
SVNのリバートは非常に強力ですが、全ての状況で最適な解決策とは限りません。特に、リポジトリから過去の履歴を完全に抹消したいという要望に対しては、通常の「リバート」操作では対応できません。
5.1. リポジトリから完全にファイルを削除したい場合(機密情報、巨大ファイル)
最も質問が多いのがこのケースです。「誤ってコミットしてしまったパスワードファイルや、巨大な一時ファイルを履歴から完全に消し去りたい」という要望です。
-
SVNの制限:履歴は消えない
前述の通り、SVNは履歴の不変性を原則としています。svn merge -c -R
によるリバートや、svn delete
をコミットする操作は、そのファイルが「削除された」という新しいリビジョンを生成するだけです。元のコミット(ファイルが追加されたリビジョン)は依然としてリポジトリに存在し、svn log -r <古いリビジョン>
やsvn cat -r <古いリビジョン> <ファイルパス>
のように指定すれば、いつでもそのファイルを履歴から取り出すことができてしまいます。
これは、特に機密情報が誤ってコミットされた場合に、セキュリティ上の深刻な問題となります。 -
svnadmin dump
/svnadmin load
とsvndumpfilter
を利用した履歴の完全抹消 (最終手段、慎重な検討とテストが必要)
SVNリポジトリから特定のファイルやディレクトリの履歴を完全に消去する唯一の方法は、リポジトリをダンプし、svndumpfilter
ツールを使って不要な履歴を除外し、その後新しいリポジトリにロードし直すことです。これは、非常に手間がかかり、リポジトリ全体に影響を与える破壊的な操作であり、通常は推奨されません。手順の概要(極めて慎重に!):
1. 既存リポジトリのバックアップ: 最も重要です。この操作は取り返しがつかないため、必ず完全なバックアップ(例:svnadmin hotcopy
またはファイルシステムのコピー)を取ってください。
2. リポジトリのロック (推奨): 操作中に誰もコミットできないように、リポジトリをロックします。
3. リポジトリをダンプ:
bash
svnadmin dump /path/to/your/repository > repo_dump.dump
これにより、リポジトリの全履歴がダンプファイルとして出力されます。
4.svndumpfilter
で履歴をフィルター: 削除したいファイルやディレクトリのパスを指定して、ダンプファイルをフィルタリングします。
* 特定のパスを完全に除外する場合:
bash
svndumpfilter exclude <path/to/remove> --drop-empty-revs < repo_dump.dump > filtered_repo.dump
例:svndumpfilter exclude secret_info.txt giant_temp_file.zip --drop-empty-revs < repo_dump.dump > filtered_repo.dump
*--drop-empty-revs
: フィルタリングの結果、何も変更が残らなくなったリビジョンを履歴から除外します。
* 特定のパスだけを残す場合:
bash
svndumpfilter include <path/to/keep> --drop-empty-revs < repo_dump.dump > filtered_repo.dump
5. 新しい空のリポジトリを作成:
bash
svnadmin create /path/to/new/repository
6. フィルター済みダンプを新しいリポジトリにロード:
bash
svnadmin load /path/to/new/repository < filtered_repo.dump
7. 新しいリポジトリへの切り替え: ユーザーに新しいリポジトリURL(またはパス)を知らせ、既存の作業コピーを新しいリポジトリにリロケート(svn switch --relocate
)してもらうか、または全員が新しいリポジトリから新規チェックアウトし直す必要があります。
bash
# 作業コピーを新しいリポジトリに切り替える場合
svn switch --relocate https://old.server/svn/repo https://new.server/svn/repo /path/to/working/copy
8. 古いリポジトリの削除: 新しいリポジトリが完全に機能し、全員が切り替えたことを確認した後、古いリポジトリを削除します(ただし、削除前に十分な期間バックアップとして保管しておくことを推奨します)。 -
なぜこの操作は推奨されないのか(手間、リスク、整合性):
- 複雑性と手間: 手順が多く、専門知識が必要です。大規模なリポジトリでは時間がかかります。
- データ損失のリスク: 一歩間違えると、リポジトリの履歴全体が破損したり失われたりする可能性があります。バックアップは必須です。
- チームへの影響: 全員がリポジトリを切り替えたり、作業コピーをリロケートしたりする必要があり、混乱を招きます。開発を一時的に停止する必要があるかもしれません。
- 整合性の問題: フィルタリングによってリビジョン番号が飛んだり、一部の履歴が失われたりすることで、監査証跡や将来のマージ作業に影響を与える可能性があります。
結論として、機密情報や巨大なファイルをコミットしてしまった場合、それがプロジェクトに致命的な影響を与えない限り、通常の「リバート」(svn merge -c -R
)で対応し、再発防止策を講じるのが現実的です。履歴からの完全抹消は、最後の手段として、細心の注意を払って行うべきです。
5.2. 間違ったリビジョンにタグを打ってしまった場合
タグは、通常、特定のリビジョンに名前を付けて、その時点の状態を「スナップショット」として保持するためのものです。一度打ったタグのリビジョンを「取り消す」ことはできません。
- 代替案:
- 間違ったタグを削除し、正しいリビジョンで新しいタグを打ち直す。
bash
svn delete ^/tags/間違ったタグ名 -m "Deleted incorrect tag."
svn copy ^/trunk@<正しいリビジョン番号> ^/tags/新しいタグ名 -m "Created tag for release X.X.X."
svn delete
でタグを削除する操作もまた、履歴に残ります。完全に削除されるわけではありませんが、実運用上はこれで十分です。 - もし、タグを打つべきリビジョンがまだ存在しない場合(例: コミット前の状態にタグを打ちたい)、それは不可能です。タグは常に「既にコミットされたリビジョン」に対してのみ打てます。
- 間違ったタグを削除し、正しいリビジョンで新しいタグを打ち直す。
5.3. 単なるコミットログの修正(不可能)
一度コミットされたリビジョンのコミットメッセージや作者、タイムスタンプを直接編集することは、SVNの標準機能ではできません。これは履歴の整合性を保つためです。
- 代替案:
- SVNサーバーの設定変更:
pre-revprop-change
フックを設定すれば、svn propset
コマンドでリビジョンのプロパティ(ログメッセージを含む)を変更できるようになります。しかし、これはサーバー管理者による設定が必要であり、推奨される操作ではありません(特に共有リポジトリでは)。履歴の改ざんと見なされ、後から監査する際に問題になる可能性があります。 - 新しいコミットで訂正: コミットメッセージの誤字脱字など、軽微な間違いであれば、新しいコミットでその旨を記述して訂正するのが一般的です。例えば、「Re: Previous commit message had a typo.」のように。
svndumpfilter
(最終手段): 前述のsvndumpfilter
を使えば、ダンプ時にログメッセージを編集することは技術的に可能ですが、これは通常の運用では行われません。
- SVNサーバーの設定変更:
6. ベストプラクティスと予防策
コミットのリバートは強力なリカバリ手段ですが、最も良いのは「リバートする必要がないようにする」ことです。ここでは、SVNの運用におけるベストプラクティスと、コミットの誤りを未然に防ぐための予防策を紹介します。
6.1. コミット前に「svn diff」「svn status」で確認する習慣
コミットボタンを押す前に、必ず自分の変更内容を再確認する習慣をつけましょう。
* svn status
: 作業コピーで変更されたファイルの一覧を確認します。意図しないファイル(IDEの設定ファイル、ビルド一時ファイルなど)が含まれていないかチェックします。
* svn diff
: 実際に変更した内容(行単位の差分)を確認します。これにより、不要なデバッグコード、コメントアウト忘れ、誤字脱字などが含まれていないか最終チェックできます。GUIツール(TortoiseSVNなど)を使えば、変更内容を視覚的に分かりやすく確認できます。
6.2. 小さくこまめにコミットする
大きな変更を一度にコミットするのではなく、論理的な単位で小さく、頻繁にコミットするよう心がけましょう。
* メリット:
* 問題の特定が容易: 問題が発生した場合、どのコミットが原因かを特定しやすくなります。
* リバートが容易: 特定の小さな変更をリバートする方が、巨大な変更をリバートするよりも、コンフリクトのリスクが低く、影響範囲も小さくなります。
* コードレビューの効率化: 小さなコミットはレビューしやすく、品質向上につながります。
* 例: 新機能開発でも、まずは基盤部分の追加、次にUIの追加、最後にビジネスロジックの実装といった具合に、段階的にコミットしていく。
6.3. コミットメッセージを分かりやすく書く
コミットメッセージは、そのコミットが何を行ったのか、なぜ行ったのかを後から(そして他の人が)理解するための最も重要な情報源です。
* 推奨事項:
* 一行目に簡潔な要約: 変更内容を短くまとめる(50文字以内が目安)。
* 空行を挟む: その後に詳細を記述する。
* 詳細な記述: 変更の目的、実装の背景、影響範囲、関連するチケット番号などを記述する。
* リバートの場合: どのリビジョンをリバートしたのか、なぜリバートしたのかを明確に記述する。
* 例:
“`
Fix: NullPointerException on user login page
- The previous implementation incorrectly assumed that the user
object would always be non-null after authentication.
- Added a null check before accessing user properties.
- Resolves #1234.
```
6.4. ブランチ戦略の活用
メインライン(トランク)を保護し、実験的な開発や大規模な変更を隔離するためにブランチを積極的に利用しましょう。
* メリット:
* 安定したトランク: ブランチで開発を行い、十分にテストされた後でトランクにマージすることで、トランクの安定性を保てます。
* 安全なリバート: ブランチ内でのリバートは、トランクに比べて他の開発者への影響が限定的です。
* 実験的な機能: 壊れる可能性のあるコードも、ブランチ内であれば比較的自由に試せます。
6.5. コードレビューの導入
コミット前に他の開発者にコードをレビューしてもらうことは、バグの早期発見、品質向上、知識共有に非常に有効です。
* メリット:
* 問題の未然防止: 誤った変更がリポジトリにコミットされる前に発見できます。
* 品質向上: より良い設計や実装方法について議論できます。
* 知識共有: チーム全体のコードベースへの理解が深まります。
6.6. バックアップの重要性
SVNリポジトリの定期的なバックアップは、いかなる事故(ハードウェア障害、人為的ミス、データ破損など)からもプロジェクトを保護する最後の砦です。
* svnadmin hotcopy
コマンドは、リポジトリのホットバックアップを安全に作成できるため、非常に推奨されます。
* バックアップの頻度、保存場所、復元手順を明確にしておきましょう。
6.7. チーム内でのSVN運用ルールの策定
チーム内でSVNの運用に関するルールを明確にし、全員がそれに従うことが重要です。
* コミットの粒度、コミットメッセージの書式、ブランチの作成・マージ・削除のルール、コンフリクト解決のガイドラインなど。
* リバートのような影響の大きい操作を行う際の承認プロセスや、チームへの連絡方法なども決めておくと良いでしょう。
7. よくある質問 (FAQ)
SVNのリバートに関して、よく寄せられる質問とその回答をまとめました。
Q1: svn revert
と svn merge -c -R
はどう違うのですか?
A1: 全く異なる目的を持つコマンドです。
* svn revert
: 作業コピー上の未コミットの変更を破棄し、作業コピーをリポジトリの最新状態に戻すコマンドです。リポジトリの履歴には一切影響を与えません。例えば、ファイルを編集し始めたが、やっぱりその変更をやめたい場合に「元の状態に戻す」ために使います。
* svn merge -c -R
: 特定の過去のコミットによって加えられた変更を「打ち消す」新しい変更を作成し、それを作業コピーに適用するコマンドです。その後、その変更をリポジトリにコミットすることで、過去のコミットを実質的に「無効化」します。リポジトリに新しいリビジョンが生成されます。
本記事で解説している「コミットの取り消し・リバート」は、後者の svn merge -c -R
を指します。
Q2: リバートしたコミットは完全に消えるのですか?
A2: いいえ、完全に消えることはありません。 SVNの履歴は原則として不変です。リバート操作は、過去のコミット自体を削除するのではなく、そのコミットによって加えられた変更を打ち消す「逆の変更」を新しいコミットとしてリポジトリに記録するものです。元のコミットは履歴の中にそのまま残り続けます。これにより、プロジェクトの全ての履歴が常に追跡可能であるというSVNの原則が守られます。
Q3: リバートが原因で他の人が困ることはありますか?
A3: はい、大いにあります。あなたがリバートをコミットした後、他の開発者が自分の作業コピーを最新にアップデートすると、リバートによって発生した変更(ファイルの削除、変更の打ち消しなど)が彼らの作業コピーに適用されます。もし彼らがリバートされた部分に関連する作業を進めていた場合、svn update
時に大量のコンフリクトが発生したり、彼らの作業が意図せず失われたりする可能性があります。
したがって、特に大規模なリバートを行う前には、必ずチームメンバーに事前連絡と調整を行い、可能であれば彼らの作業を一時中断してもらうか、退避してもらうことが非常に重要です。
Q4: 秘密情報をコミットしてしまいました。どうすれば完全に消せますか?
A4: SVNの標準的な機能である svn merge -c -R
によるリバートや svn delete
では、履歴からそのファイルを完全に抹消することはできません。ファイルがコミットされたリビジョンには、引き続きその秘密情報が含まれたファイルが存在します。
リポジトリの履歴から完全にファイルを抹消する唯一の方法は、svndumpfilter
を使ってリポジトリをダンプし、フィルタリングして、新しいリポジトリにロードし直すことです。これは非常に手間がかかり、リポジトリ全体に影響を与える破壊的な操作であり、専門知識と細心の注意が必要です。通常は推奨されず、最後の手段と考えるべきです。可能であれば、その秘密情報を変更し、今後のコミットでは絶対に含めないように再発防止策を講じる方が現実的です。
Q5: Gitのリバートとは同じですか?
A5: 概念的には似ていますが、内部的な仕組みには重要な違いがあります。
* 共通点: どちらも「過去の変更を打ち消す新しいコミットを作成する」という点で共通しています。元のコミットは履歴に残ります。
* 相違点:
* SVN: 主にsvn merge -c -R
コマンドを使用します。リビジョンは直線的な履歴を形成します。
* Git: git revert
コマンドを使用します。Gitは分散型バージョン管理システムであり、より複雑なブランチとマージの概念を持っています。Gitのコミットはハッシュ値で識別され、SVNのリビジョン番号のようにグローバルに連番ではありません。
また、Gitには git reset --hard
のように、履歴を書き換える(物理的にコミットを削除する)強力なコマンドも存在しますが、これは非共有のローカルリポジトリでのみ安全に使用でき、共有リポジトリでは通常推奨されません。SVNにはGitのreset --hard
に相当する、コミットを物理的に削除するコマンドは標準では提供されていません(svndumpfilter
のような例外的な手段を除く)。
まとめ
SVNにおけるコミットの取り消し・リバートは、開発プロセスにおいて非常に重要なリカバリ操作です。この記事を通じて、以下の主要な点を理解していただけたことと思います。
- SVNの履歴は不変である: 一度コミットされたリビジョンは、原則として削除・変更されることはありません。「取り消し」とは、その変更を打ち消す新しいコミットを生成することを意味します。
svn merge -c -R
が核心: 特定のリビジョン、あるいはリビジョン範囲の変更を取り消すには、svn merge
コマンドの逆マージ機能を使用します。TortoiseSVNなどのGUIツールも、この機能を内部で利用しています。- 再コミットが必須: リバート操作は、作業コピーに「打ち消す変更」を適用するだけです。その変更をリポジトリに永続化するには、必ず
svn commit
を行う必要があります。 - コンフリクトへの備え: リバートはマージ操作の一種であるため、他の変更との競合(コンフリクト)が発生する可能性があります。コンフリクト解決の知識は不可欠です。
- チームへの配慮: 共同開発環境では、リバートが他のメンバーに与える影響を理解し、事前に連絡・調整を行うことが非常に重要です。
- 完全な履歴抹消は困難かつ非推奨: 機密情報や巨大ファイルを履歴から完全に消し去りたい場合、通常の「リバート」では不十分です。
svndumpfilter
を用いたリポジトリの再構築は最終手段であり、極めて高いリスクと手間を伴います。
最も大切なのは、そもそもリバートの必要がないように、日々の開発でベストプラクティスを実践することです。コミット前の入念な確認、小さくこまめなコミット、分かりやすいコミットメッセージ、そしてコードレビューの導入は、間違いを未然に防ぎ、スムーズな開発ワークフローを維持するための鍵となります。
SVNのリバートは強力な機能であり、正しく理解し、慎重に操作することで、どんなトラブルからもプロジェクトを安全に回復させることが可能です。この記事が、あなたのSVN運用の一助となれば幸いです。