Android Studio最速入門~効率的にコーディングするための使い方

第34回バージョン管理 ─プロジェクト管理ファイルについて[中編]

はじめに

今回と次回の2回にかけて、イマイチよくわかってないAndroid Studioのプロジェクト管理ファイルについて解説します。途中で飽きちゃう人のために、最低限共有する必要のある(バージョン管理する必要のある)管理ファイルを紹介します。

ここでの対象は.ideaディレクトリだけなので、説明のしやすさも兼ねて <PROJECT_HOME>/.idea/.gitignoreを作成し.ideaディレクトリに対するホワイトリスト」を提示していきます。

図1 新しく<PROJECT_HOME>/.idea/.gitignoreを作成する
図1
リスト1 最低限必要な<PROJECT_HOME>/.idea/.gitignoreの例
*.xml
!/gradle.xml
リスト2 最低限必要な<PROJECT_HOME>/.idea/.gitignoreの例.gitignoreの内容
/.gradle/
/local.properties
.DS_Store
*.iml

前回紹介した.gitignorebuild.gradle以外を除外するため、Android Studioにとっては「Gradleのプロジェクト」と認識します。⁠Android Studioのプロジェクト」として認識させるには<PROJECE_HOME>.ideaディレクトリがあることで、その中身を必要最低限まで削るとgradle.xmlだけが残ります。

.idea/gradle.xmlだけあればAndroid Studioは、そのディレクトリを自身のプロジェクトとして認識します。あとは、必要に応じて他に共有したい設定を追記していくことになります。どんなときに/どの設定ファイルを共有したら良いかが中編・後編の内容になります。

Android Studioの管理ファイルについて

Android Studioの管理ファイルは大きく分けて次の二種類があります。

モジュール定義ファイル*.iml
Android StudioやIntelliJでは、プロジェクトは「場」であって、その中にモジュールという「実体」があります。Eclipseにたとえると、プロジェクトが(Eclipseの)ワークスペースで、モジュールが(Eclipseの)プロジェクトです。
モジュール定義ファイル*.imlはそのモジュールごとの定義情報を保存しています。もう少し具体的に言うと「Project Structure」の設定内容がここに保存されます。ちなみに、このファイルの中身はXMLです。
.ideaディレクトリ配下の設定ファイル
<PROJECT_HOME>直下にある.ideaディレクトリです。ここには主にプロジェクト固有の設定情報が格納されます。もっと具体的に言うと「Preferences」「Proejct Settings」部分のほとんどと、⁠本連載ではまだ説明していませんが)⁠Run/Debug Configuration」一部が保存されます。
このディレクトリに保存される設定ファイルもすべてXMLファイルです。厳密なルールはありませんが「Preferences」の設定項目ごとに設定ファイルが独立しています。
Android Studioは、この.ideaディレクトリを含むディレクトリを「プロジェクト」と認識します。ディレクトリ名が.ideaなのは、Android StudioのベースとなったのがIntelliJ IDEAだからです。実を言えば、IntelliJベースの他のIDEもすべてこの形式なので、複数のJetBrains系IDEを併用している場合はちょっとした混乱の元になります。

これらの設定ファイルも基本的にはバージョン管理下に置き共有するのが望ましいと言われています。一応、公式な見解があり、それに従うと次のようになります。

これ、間違ってはいないのですが、正解でもありません。共有しないほうが良いファイルはもう少しあり、一部の設定ファイルは条件に応じて共有するかどうかを決めた方がよいものがあります。そして何より情報が古いです。

また、github / gitignoreにあるJetBrains.gitignoreもだいぶ極端な設定になっています。

モジュール定義ファイル(*.iml)

モジュール定義ファイルには、いつの頃からか隠してしまった「Project Structure / Modules」に関する情報が保存してあります。v0.3.6から「Project Structure」が復活しましたが、build.gradleの編集用に特化してしまって、モジュール定義とかけ離れています。

図2 v0.3.6以降の「Project Structure」設定画面
図2

モジュール定義ファイル本来の設定情報については、第5回を参照してください。

最近のAndroid Studioの「Project Structure」からはIntelliJっぽさが無くなってきていますが、ユーザインタフェースを隠しているだけで設定ファイルの内容そのものは依然健在です。ただ、Android Studioにとって「Project Structure」の元ネタ(原本)はあくまでbuild.gradleであって、モジュール定義ファイル*.imlはその写像でしかないのです。

別の見方をすると、次のような考え方が成り立ちます。

モジュール定義ファイル*.imlbuild.gradleから生成可能な二次情報なので管理する必要がない。

実際、故意にモジュール定義ファイルを削除してから、そのプロジェクトをAndroid Studioで開き直すと消したはずのモジュール定義ファイルが再構築されます。

ちなみに「このプロジェクトを構成しているモジュール群」.idea/modules.xmlに記録されるので、*.imlファイルを意図的に削除するときは、このファイルもあわせて削除してください。

結論

モジュール定義ファイルの情報はbuild.gradleから再生成することができるので、あえてバージョン管理対象にする必要はありません。チェックアウトしたディレクトリ名に左右されること無くプロジェクトを開く事ができるため余計なトラブルに遭遇しないで済みます。

Android StudioのプロジェクトはGradleベースであるため、この理屈が成り立つのであって、そうではないプロジェクトの場合はモジュール定義ファイル*.imlがプロジェクト構造を把握している唯一の設定ファイルになります。何が言いたいのかわかりますよね。

モジュール定義ファイルをバージョン管理対象にすることはやぶさかではありませんが、この設定を共有することの制約を理解したうえで実施してください。

モジュール定義ファイルに登録されている情報のうち、共有する際に気を遣うのは「モジュールが使用するAndroid SDKの名称」ですリスト5⁠。

リスト5 モジュール定義ファイルのコンパイラ設定の例
<orderEntry type="jdk" jdkName="Android API 19 Platform" jdkType="Android SDK" />

プロジェクトが使用するコンパイラのバージョンは、build.gradlecompileSdkVersionに指定しますが、その具体名は「各自が使っているAndroid Studioの/該当するバージョンの/Android SDKの登録名」になります。

きっとチームの誰かが最初にAndroid Studioでプロジェクトを作ると思いますが、モジュール定義ファイルの該当項目には、その人が使ったAndroid StudioのAndroid SDKの登録名が設定されるというわけです。この「Android SDKの登録名」は環境によって微妙に異なる可能性があると前回紹介しましたが、その話がここにつながります。

よって、モジュール定義ファイルを共有する場合は、各自が使うAndroid StudioのAndroid SDKの登録名を揃えておくことが条件になります。

プロジェクト管理ファイル(.ideaの中身)

先ほど説明したとおり、.ideaディレクトリには「Preferences」「Project Settings」の内容と、ごく僅かですが「Project Structure」「Run/Debug Configuration」の内容も保存されています。

「Preferences」関連は次回紹介しますので、今回はそれ以外の設定ファイルについて説明しておきます。

プロジェクト設定、モジュールの関連設定ファイル(Project Structure)

.idea/misc.xml, .idea/modules.xml

簡単なほうから説明します。.idea/modules.xmlは名前から想像がつくように「モジュールの関連」を定義した設定ファイルです。プロジェクトに登録されているモジュールがどこにあるかが登録されています。要するに「Project Structure / Modules」の内容そのものです。

リスト6 .idea/modules.xmlの例
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ProjectModuleManager">
    <modules>
      <module fileurl="file://$PROJECT_DIR$/MyApplication.iml" filepath="$PROJECT_DIR$/MyApplication.iml" />
      <module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
    </modules>
  </component>
</project>

Android Studioの元になったIntelliJでは、モジュールはどこに作っても構わなかったのですが、Android StudioはGradleとの都合もあってプロジェクト<PROJECT_HOME>の中にしかモジュールを作ることができません。

もう一方の「プロジェクト設定」ですが、これは「Project Structure / General Settings for Project」の事で、この内容は.idea/misc.xmlに保存されます。

図3 ⁠Project Structure / General Settings for Project」設定画面
図3

IntelliJでは普通に見かけるこの画面(⁠⁠Project Structure / Project⁠⁠)ですが、Android Studioでこの画面に辿り着くのは至難の業です。というかAndroid Studioとしては見せたくない設定画面で、たまたまうっかり出てしまった、が正しい理解なのでしょう。

結論

.idea/modules.xml
モジュールの定義ファイル*.imlと同じく、このファイルもbuild.gradleから復元できます。よって、この設定ファイルもバージョン管理対象にする必要はありません。要するに*.imlの管理方法に合わせます。
.idea/misc.xml
「Project Structureの設定」という点では共有は不要です。ただし「その他(misc⁠⁠」の意味合いでは他の情報も保持しているため、共有すべきかどうかは次回まで保留にします。

ライブラリの定義(Project Structure)

.idea/libraries/<ライブラリ名>.xml

プロジェクトに登録しているライブラリの情報がライブラリごとに保存されています。ファイル名は <ライブラリ名>.xmlです。ここの内容は「Project Structure / Modules」設定画面の「Dependencies」タブの内容に基づきます。要するに build.gradledependencies から派生した情報です。

build.gradle「Project Structre」に定義するライブラリは大きく次の2種類に大別できます。

プロジェクト内にライブラリの実体があるもの
<PROJECT_HOME>内のどこかにライブラリの実体*.jarがあり、build.gradledependenciesfile として参照してあるものです(大抵は<PROJECT_HOME>/<MODULE_HOME>/libsに置きます⁠⁠。
リスト7 プロジェクト内のライブラリを参照するbuild.gradleの例
dependencies {
  compile files('libs/android-support-v4.jar')
}

※初期の頃のAndroid Studioが生成する build.gradle がこのパターンでした。

プロジェクト内にライブラリの実体が無いもの
Android SDKやMavenリポジトリを参照するライブラリです。実体*.jar<PROJECT_HOME> の外にあります。build.gradledependenciesリスト8のように参照してあるものです。
リスト8 外部のライブラリを参照するbuild.gradleの例
dependencies {
  compile 'com.android.support:appcompat-v7:+'
}

※最近のAndroid Studioが生成するbuild.gradleはこのパターンです。

ここでクセ者なのが後者の「プロジェクト内にライブラリの実体が無いもの」です。ライブラリ定義ファイルの内容は、参照しているライブラリのパス情報そのものが記録されます。プロジェクト内にその実体がある場合は、<PROJECT_HOME>からの相対パスとして記録してありますが、プロジェクト外の場合、その絶対パスが記録されます。

リスト9 .idea/libraries/support_v4_18_0_0.xmlの例
<component name="libraryTable">
  <library name="support-v4-18.0.0">
    <CLASSES>
      <root url="jar://$USER_HOME$/AndroidSDK/extras/android/m2repository/com/android/support/support-v4/18.0.0/support-v4-18.0.0.jar!/" />
    </CLASSES>
    <JAVADOC />
    <SOURCES />
  </library>
</component>

Android SDKやGradleのホームディレクトリは環境によって異なるため、このファイルを共有するのは非常に困ることになります。

結論

では、どうしたらよいか?という話になりますが、答えは簡単で共有しません

幸いなことにAndroid StudioのプロジェクトはGradleベースであるため、参照ライブラリの情報は build.gradleに記述してあります。.idea/librariesが存在しない場合、builg.gradleを元にライブラリ定義を再構築するので、このディレクトリが無くても何の問題もないのでした。

実行構成の設定

.idea/runConfigurations/<構成名>.xml

この連載ではまだきちんと説明していない「実行構成(Run/Debug Configuration⁠⁠」の内容が保存されます。名前から内容は想像できると思いますが、プロジェクトを実行してみるときの構成(エミュレータなのかテストランナーなのか、など)を指定するものです。

実行構成は複数の設定を持つ事ができ、さらにその設定ごとに共有(Share)するかどうかを指定できます。

図4 ⁠Run/Debug Configurations」設定画面(クリックすると動きがわかります)

「Run/Debug Configurations」ダイアログで「Share」をONした場合のみ、このディレクトリに <構成名>.xml として保存されます。

結論

わざわざ共有指定をするくらいなので、当然その設定ファイルは共有対象にすべきでしょう。それ以前に実行構成を共有したい用途がどれほどあるか?という疑問というか興味が残ります。

なお、共有設定をしていない実行構成や「現在選択中の実行構成の名前」.idea/workspace.xml に記録されます。

中編のまとめ

「Project Structre」まわりは build.gradle から再生できるのと、ヘンに共有すると環境の調整が面倒なので共有対象にはしません。それ以外では、実行構成(Run/Debug Configurations)「共有する(Shared⁠⁠」と明示するので、共有対象にしないわけにはいきませんよね。

というわけで、冒頭に示した.idea/.gitignoreリスト10のようになります。

リスト10 中編のまとめによる<PROJECT_HOME>/.idea/.gitignore の例
*.xml

!/gradle.xml
!/runConfigurations/*.xml

残りの設定ファイルについては次回に続きます。

番外編:チェンジリスト(Changelist)ってなに?

突然ですが、第23回第26回でもったいぶった言い回しをしていた「チェンジリスト(Changelist⁠⁠」について解説します。

Android Studioでバージョン管理操作を行う場合、エディタから/⁠Projectツールウィンドウ」から/⁠Changesツールウィンドウ」からなどさまざまなやり方があることを紹介しました。その中でも筆者がオススメするのは、だんぜん「Changesツールウィンドウ」から操作する方法です。

昔話になりますが、Android Studioの元になったIntelliJではバージョン管理の操作は「Changesツールウィンドウ」でしかできませんでした。その後のバージョンアップに伴い、他のIDEみたく「Projectツールウィンドウ」でもできるようになったわけなのです。そんな経緯もあってか、バージョン管理に関する諸々の操作は「Changesツールウィンドウ」のほうが便利に使えるようにできているのです。

その端的な機能が「チェンジリスト」です。⁠Changesツールウィンドウ / Localタブ」にある「Default」は、そのチェンジリストのひとつの例です。

図6 ⁠Changesツールウィンドウ / Localタブ」にある「Default」チェンジリスト
図6

このチェンジリスト、何が便利かと言うと、チェンジリストごとに「変更のカタマリ(コミットする単位⁠⁠」を複数管理することができます。たとえば、とある変更作業を行っていたとき、偶然にも関係のない修正箇所を見つけた場合を考えてみましょう。

そんなときに取れる対応はだいたい次の2通りになると思います。

  1. まずは今の作業を終わらせて(コミットして)から、そっちを修正する。
  2. とりあえず修正して、コミットするとき複数回に分けて修正する。

いずれにしろ、作業者の負担(メモっておかないといけない、うっかりミスの元になる、など)が少なくありません。そんなときにチェンジリストを使うと便利です。

あるファイル群foo.txt, bar.txtを修正中に、どうしても他のファイル群boo.txt, hoge.txtを修正してしまった場合、⁠Changesツールウィンドウ / Localタブ」のDefaultチェンジリストから一方のファイル群を選び、コンテキストメニューから「Move to Another Changelist」を実行し、新しいチェンジリストに移動させます。

図7 新しいチェンジリストの作成(クリックすると動きがわかります)

これでboo.txt, hoge.txtは別のチェンジリストに属したことになります。

チェンジリストは必ずどれかひとつが Active状態 になります(Active、非Activeの切り替えはツールバーやコンテキストメニューでできます⁠⁠。ファイルを編集したり、その変更結果をコミットする場合、Activeなチェンジリストがその対象となります。

図8 チェンジリストの切り替え(クリックすると動きがわかります)
※Activeなチェンジリストが一番上に表示されます(とても見づらいですが、太字になっています⁠⁠。

チェンジリストを活用することで、ローカルで編集した内容を一律にコミットするのではなく目的別に分けてコミットすることができます。ここまで聞いてピンとくる人にとってはとても便利な機能です。あまり良い例ではありませんが、一番よく使う用途は、プログラムの修正と、その間にPreferencesやProject Structreなどを弄って変更が発生してしまったAndroid Studioの設定ファイルを別に管理する事です。

図9 チェンジリストを使って設定ファイルの変更を別に管理する
図9

なお、Activeではないチェンジリストに属するファイルを編集してしまった場合は「そのファイルを今のチェンジリストに移すか?(Move changes⁠⁠Activeなチェンジリストを切り替えるか?(Switch changelist⁠⁠なにもしないか?(Ignore⁠⁠」と聞いてきます。

図10 Activeでないチェンジリストのファイルを修正した場合
図10

ただし、さすがに同じファイルを複数のチェンジリストで共有することはできません。また、チェンジリストの概念そのものはAndroid Studio独自のものです。そのため、使っているバージョン管理システムのコマンドや他のクライアントでコミット操作を行うと、チェンジリストに関係なく全ての変更がコミットされます。

チェンジリストの管理方法もさまざまです。前述したとおり、既存のチェンジリストの一部から新しいチェンジリストを作成するする方法もあれば、ツールバーの「+」アイコン("New Changelist")からまっさらなチェンジリストを作ることもできます。また、ドラッグ&ドロップでチェンジリスト間のファイルを移動できます。

図11 チェンジリスト間のファイルの移動(クリックすると動きがわかります)

チェンジリストはコミット単位そのものであるため、この概念を利用してバグ管理システム(BTS)や課題管理システム(ITS)と連携することが可能です。この件については、次回の番外編で説明します。

さらに"Shelve Changes..."という変更の待避機能にも使われます。先ほどの例で作成したチェンジリストを、とある理由でいったん破棄したい場面に遭遇したとしましょう。でも、もしかしたら、それも取りやめて破棄前に戻す可能性もある……そんな場面です。GitやMercurialを使っているのであればgit stashhg shelveを使えばいいや」と思いますが、Subversionならどうでしょうか?

特定のバージョン管理システムに依存せず、修正の待避を行うのが、この"Shelve Changes..."です。使い方は簡単で、待避したいチェンジリストを選択してコンテキストメニューから"Shelve Changes..."を実行するだけです。

図12 "Shelve Changes..."の実行例(クリックすると動きがわかります)

コマンドを実行すると「Commit Changesダイアログ」によく似た「Shelve Changesダイアログ」が表示されます。コミット欄に任意の文言を入力して「Shelve Changes」ボタンを押して下さい(通常、コミット欄にはチェンジリスト名がプリセットしてあると思います⁠⁠。すると「Changesツールウィンドウ / Shelveタブ」にその内容が待避されます。待避した登録名が先ほど入力したコミットメッセージになるので、メッセージ欄とは言え、ほどほどの文言にしておかないと、この画面がとても見づらくなります。

この待避機能の正体はパッチ(patch)です。待避した変更内容をまた元に戻したい場合は、⁠Changesツールウィンドウ / Shelveタブ」で対象を選び、コンテキストメニューから "Unshelve Changes" を実行します。

図13 待避したチェンジリストを戻す
図13

復帰するコマンドは "Unshelve Changes" の他に "Unshelve..." の2つがあります。前者は復帰すると待避していた変更内容はShelveタブから消えますが、後者は復帰しても「Shelveタブ」に残ります。

GitやMercurialといったDVCSの普及によって、さほど珍しい機能ではなくなりましたが、その前は大変特徴的な機能だったんですよ。なお、チェンジリストの定義情報は .idea/workspace.xml に待避した変更内容の実体は <AS_CONFIG>/shelf/<待避名>.patch にそれぞれ保存されます。どちらもバージョン管理システムに登録する対象ではありません。

おすすめ記事

記事・ニュース一覧