4  バージョン管理

データ分析作業に再現性をもたせる次のステップは、バージョン管理です。 バージョン管理は、データ分析のプロジェクトにおいて、コードやデータの変更履歴を記録し、管理する方法です。 この章では、バージョン管理の基本的な考え方と、代表的なバージョン管理システムであるGitの使い方について説明します。

4.1 バージョン管理の必要性

データ分析で用いるソースコードやデータは日々更新(バージョンアップ)されていきます。 ある日の作業では、データ読み込みのプログラムを書いて、次の日にはデータの前処理と可視化のプログラムを書くという流れです。 またある日には、これまで使っていたデータを新しいデータに差し替えることもあるでしょう。 そんな中で、ある日突然、以前実行可能だったコードがエラーを吐いたり、以前正常に動作していたコードが動かなくなったりすることがあります。

バグとなるプログラムを書いてしまうことは誰にでもあることですが、そのような場合に、以前のバージョンに戻ることができると便利です。 別の場面として、複数人で共同作業を行う場合、いつだれがどのような変更を加えたのかを把握することも重要です。 分析に用いる解析手法の変更に気がつかず、誤ったコードを使って分析を行ってしまうことは避けるべきでしょう。

バージョン管理システムを導入しないプロジェクトでは、ファイルが煩雑になりがちです。 どのファイルが最新のものなのか、どのデータを使うのかがわからなくなることもあります。

とあるデータ分析プロジェクトでのフォルダ構成。あるファイルから派生したことが覗えるファイルが複数存在し、最終的にどのファイルを利用するのが適切なのかわからない状態。

バージョン管理は、このような問題を解決するための選択肢となります。 つまり、ファイルに対して、複数のバージョンを保存しておくこととと、共同作業を行う際に、誰がどのような変更を加えたのかを記録するのがバージョン管理の役割です。

本書では、データ分析のためにバージョン管理システムを導入しますが、バージョン管理本来の目的は、プログラム開発においても同じです。 プログラム開発では、データ分析と同じく、長期にわたるプロジェクトや多くの開発者による共同作業です。 ソースコードを管理する点は変わりません。 データ分析の場合、ソースコードの他にデータやドキュメントも管理する必要がありますが、バージョン管理システムはそれらを一元管理することができます。

4.2 分散型バージョン管理システム: Git

複数の開発者が共同でコードを管理・開発する際に便利な機能を提供するバージョン管理システムです。 バージョン管理システムには大別して2つの種類があります。 集中管理型は、ファイルの変更履歴を一つのリポジトリ(中央リポジトリ)に保存する方式です。 中央リポジトリはサーバー上に置かれるため、編集作業を行う際にはサーバーに接続する必要があります。

もう一つが分散管理型です。 分散管理型は、ファイルの変更履歴を各ユーザーの端末にリポジトリ(リモートリポジトリ)のコピーをローカルリポジトリとして保存する方式です。 各自の端末で行われる編集作業が行われる点は集中管理型と変わりませんが、リモートリポジトリのコピーをローカルリポジトリとして持つことで、オフラインでも作業が可能です。 リモートリポジトリに変更を反映する際には、リモートリポジトリに変更をプッシュすることで、他のユーザーとの共有が可能です。

分散管理型のバージョン管理システム

分散管理型の代表として、Gitがあります。 Gitは、Linuxカーネルの開発者であるLinus Torvalds氏によって開発されました。

Gitが開発された背景には、Linuxカーネルの開発と、従来利用されていたバージョン管理システムであるBitKeeperの利用停止があります。 バージョン管理システムを模索する中、完全な分散型やブランチ機能を強化した新たなバージョン管理システムが必要とされました。 Gitの最初のリリースは2005年で、バージョン管理システムの中では比較的新しいものですが、現在では最も広く使われているバージョン管理システムの一つとなっています。

ここではGitのインストール方法を含めた詳細なGitの説明は行いません。横田紋奈 と 宇賀神みずき (2022)渡辺宙志 (2024) を参考にしてください。オンラインで読めるPro GitやRユーザー向けのHappy Git and GitHub for the useRもあります。

4.2.1 リポジトリ

Gitではファイルやディレクトリの状態を記録し、変更履歴を管理する場所をリポジトリと呼びます。 リポジトリには、プロジェクトに関連するコードやファイルが含まれます。

リポジトリは、性質の違いによりローカルリポジトリとリモートリポジトリに分けられます。 ローカルリポジトリは、自分のコンピュータ上に保存されているリポジトリです。 個々の開発者が自身の作業を管理し、変更履歴を保存するために使用されます。

リモートリポジトリは、ネットワーク上のサーバーに保存されているリポジトリです。 ネットワークを介して、複数の開発者がアクセス可能であり、共同作業(共有と同期)を行うために使用されます。

Gitリポジトリを使った開発の流れは、開発者個人がそれぞれのローカルリポジトリで作業を行い、リモートリポジトリに変更を反映するというものです。 そのため、リモートリポジトリはバックアップとしての役割も果たします。

4.3 バージョン管理の流れ

最低限必要なコマンドを紹介しますので、まずはバージョン管理の雰囲気をつかんでください。 Gitバージョン管理は次の3つのステップで行います。

  1. リポジトリの作成
  2. ファイルの追加
  3. ファイルのコミット

Gitには100を超えるコマンドがありますが、覚えるべきコマンドは限定的です。 上記のバージョン管理の流れでは、それぞれ数個のコマンドを使います。 つまり、一連のバージョン管理で用いるコマンドは指で数えられる範囲に収まります。

最初の作業は一度行えば、その後は実行する必要がなくなるので、実際は2つのステップとなります。

4.3.1 リポジトリの作成

これからプロジェクトを立ち上げる、あるいは既存のプロジェクトをGitで管理する場合、まずリポジトリを作成します。 git initコマンドを実行すると、現在のディレクトリに.gitフォルダが作成されます。 このフォルダはGitリポジトリの中心的な役割を果たします。隠しフォルダとなっており、ユーザーが直接操作することはありません。

4.3.2 ファイルの追加: ステージングエリアへの登録

リポジトリにバージョン管理の対象とするファイルを追加するには、git addコマンドを使います。 リポジトリにファイルがない場合、何かファイルを作成してみてください。 その後、git statusというコマンドを実行すると Untracked files という箇所に作成したファイル名が表示されます。 この状態のファイルをリポジトリに追加するには、git addコマンドを使います。

`git add <ファイル名>`

git addしたファイルは、ステージングエリアという仮想的な場所に追加されます。 ステージングエリアに追加されたファイルは、次にコミットする対象となります。

Untracked状態のファイルが複数ある場合、git add .というコマンドを使うと、全てのファイルを一括で追加できます。

4.3.3 ファイルのコミット

作成・変更したファイルの状態を記録する作業をGitでは「コミット」と呼びます。 コミットを重ねることで、ファイルの変更履歴を残すことができ、必要に応じて過去の状態に遡ることができます。

コミットの対象となるのは、ステージングエリアに追加されたファイルです。 そのためgit addを行う前にgit commitを行うと、コミット対象のファイルがないというエラーが表示されるので注意してください。

git commitコマンドを使って、ファイルの変更をコミットします。 コミットには、コミットメッセージと呼ばれる説明文を付けることができます。 作業後、コミットの履歴を見た際に、どのような変更を行ったかを把握するために重要な情報となります。 なぜその変更を行ったのか、どのような変更を行ったのかを記述します。

最低限のGit設定

コミットを行う際には、氏名とメールアドレスの設定が必要となります。 2つの設定は、Gitのコミットログで誰がコミットしたかを示すために必要な情報です。

ターミナルを開いて以下のコマンドを実行します。 二重引用符で示した箇所は適宜変更してください。

git config --global user.name "<あなたの名前>"
git config --global user.email "<あなたのメールアドレス>"

設定が完了したかどうかは、以下のコマンドで確認できます。

git config --list

この出力でuser.nameuser.emailが正しく表示されていれば、設定が完了しています。

上記の処理はRパッケージであるusethisを使ってRからも行えます。

library(usethis)
use_git_config(user.name = "<あなたの名前>", 
               user.email = "<あなたのメールアドレス>")

コミットメッセージは、以下のような形式で記述します。

  • 1行目: 変更内容の要約
  • 2行目: 空行
  • 3行目以降: 変更内容の詳細

Gitに不慣れな方は、コミットメッセージを書くことに障壁を感じるかもしれません。 しかし、難しく考える必要はありません。 ここで大事なことはコミットを残すことです。コミットメッセージは後から修正することもできますし、正解があるわけではありません。 簡単な情報であっても、コミットメッセージを残すことが大切です。

4.4 アプリケーションを利用したバージョン管理

Gitはコマンドラインツールですが、GUIアプリケーションを使うことで、より簡単にバージョン管理を行うことができます。 Rの統合開発環境であるRStudioやVS Codeでも、Gitと連携した操作が可能です。 分析作業から離れずにバージョン管理を行うことができるため、非常に便利です。 すべてのコマンドを実行可能なわけではありませんが、基本的な操作はGUIを通して行えます。

4.4.1 RStudio

ターミナルあるいはGitタブを使ってバージョン管理を行います。 ここではGUIで操作可能な方法を紹介します。 なおRStudioのGUIでは、GitとSVNによるバージョン管理をサポートしています。

.gitフォルダが存在するプロジェクトを開くと、RStudioはGitタブを表示します。 この画面がRStudioでのGUIによるバージョン管理処理の中心となります。 もしもGitタブが見つからない場合、メニューのTools、Global Options…から、Git/SVNにてEnable version control interface for RStudio projectsにチェックを入れてください。

RStudioにおけるGitタブ

Gitタブには、ファイルの差分やコミット履歴を確認するボタンが用意されています。 メインとなるのはStagedStatusPathの3列で構成される箇所です。 それぞれの列には次の意味があります。

  • Staged: ステージングエリアに追加されたファイル
  • Status: ファイルの状態
  • Path: ファイルのパス

Stagedはチェックボックスになっており、ステージングエリアに追加するファイルを選択できるようになっています。 これにより、git addコマンドを使わずにステージングエリアへの登録が行えます。 Shiftキーを押しながらの選択で、複数ファイルの選択も可能です。

次のStatusには、いくつかのアイコンが表示されます。 各アイコンの意味は下記の通りです。

アイコン 状態
? unknown.png untracked バージョン管理の対象になっていない
Modified modified リポジトリの状態から変更された
Delete deleted リポジトリにあったファイルが削除された
Added added 新たにバージョン管理状態になった
Unmerged unmerged リポジトリ間で差分が衝突している
Renamed renamed ファイル名が変更された

4.4.2 VSCode

  • リポジトリを初期化する

4.5 GitHub

ここまでで、Gitを使ったバージョン管理の基本的な操作を学びました。 次に、リモートリポジトリを利用して、複数人での共同作業を行う方法を紹介しましょう。 複数人と書きましたが、個人であってもリモートリポジトリを利用することで、バックアップ目的や異なるデバイスでの作業を行うために利用することができます。

GitHub は、Gitリポジトリをホスティングするウェブサービス(リモートリポジトリ)です。 世界中のさまざまなプロジェクトで採用されるリモートリポジトリであり、オープンソースプロジェクトの共有やコラボレーションに広く利用されています。 ウェブサイトの閲覧は誰でもできますが、主要な機能を利用する際にはアカウントの作成が必要です。

RパッケージのソースコードをGitHubで管理する開発者も多く、CRANで配布されるものだけでなく、CRANポリシーに適合しないなどの理由でCRANでは配布されないパッケージも豊富に存在します。 remotesやpakパッケージを使えば、これらのGitHub上で配布されるRパッケージのインストールも可能です。

GitHub上のリポジトリには、誰でもアクセス可能なパブリックリポジトリと、特定のユーザーのみがアクセス可能なプライベートリポジトリの2種類があります。 プライベートリポジトリは、リポジトリの所有者と共同作業者(コラボレーター)のみがアクセスできるため、機密情報を扱うプロジェクトにも利用できます。

GitHubにはここで取り上げる内容以外にも多くの機能があります。GitHub公式が提供するGitHub Skillsでは、実際のGitHubリポジトリを操作してGitHubの使い方を学ぶことができます。

4.5.1 ローカルリポジトリの紐付け

プロジェクトの章で作成したプロジェクト(rrr-demo)をGitHubと紐付けます。 まずはGitHub上で新しいリポジトリを作成します。 ここでリポジトリ名を指定しますが、混乱を避けるためにローカルリポジトリのプロジェクト名と同じ名前にしておくことを薦めます。

GitHubでの新規リポジトリの作成

作成したリポジトリのURLは https://github.com/<アカウント名>/<リポジトリ名> となります。 ページにアクセスすると、次の図のような画面が表示されます。 この状態ではリポジトリにファイルは存在していませんが、 作成したリモートリポジトリをローカルリポジトリに紐付けるための手順が書かれています。 2つの方法が示されていますが、一つは新しいローカルリポジトリを作成する方法、もう一つは既存のリポジトリをリモートリポジトリとして指定する方法です。 今回は後者の方法に従い、既存のローカルリポジトリをリモートリポジトリとして指定します。

作成されたリモートリポジトリ

書かれた内容をコピーし、ローカルリポジトリのディレクトリで実行します。 このコマンドでは、リモートリポジトリを origin という名前で登録しています。 origin はリモートリポジトリのデフォルトの名前として一般的に使われています。

次に、ブランチ名の変更です。 GitHubでは、デフォルトのブランチ名として main が使用されています。 これに合わせるために、ローカルリポジトリのブランチ名を main に変更します。 そして、ローカルリポジトリで行った作業内容をリモートリポジトリに反映させるためにプッシュします。

git remote add origin https://github.com/uribo/rrr-demo.git
git branch -M main
git push -u origin main

GitHubとの

4.5.2 リモートリポジトリのクローン

新しいコンピュータなど、ローカルリポジトリを持っていない場合には、リモートリポジトリをクローンします。

git clone

git clone https://github.com/uribo/rrr-demo.git ~/Documents/projects2024/rrr-demo

変更の同期 git pull, git fetch

クローンしたリポジトリはローカルリポジトリとして扱うことができます。

4.5.3 GitHub Issues

プロジェクトに関する問題やメモのために利用できます。

4.6 バージョン管理の流れ再び

コミット後のステップとして、以下が追加されます。

  1. リモートリポジトリの指定
  2. ファイルのプッシュ

git remote add

4.6.1 ファイルのプッシュ

コミットを行ったら、変更を確定させるためのプッシュを行います。 プッシュを行うことで、リモートリポジトリに変更内容が反映されます。

git push

4.7 .gitignore

Gitバージョン管理を行うリポジトリでは、バージョン管理に含めるべきではないファイルが存在します。 例えば、セキュリティ上重要な情報を含むファイルやキャッシュファイルなどです。 これらがバージョン管理に含まれると、セキュリティリスクが高まったり、リポジトリのサイズが大きくなるなどの問題が発生します。 実際に、GitHubなどのリモートリポジトリにデータベースの接続情報やAPIキーなどの機密情報が含まれていると、悪意のある第三者によって悪用される可能性があります。

Gitでは.gitignoreにバージョン管理の対象外とするファイルを記録することで、上記の問題の解決を図っています。 バージョン管理を行わないファイルの指定方法は、gitignoreにそのファイル名や名称のパターンを記述することで行います。 .gitignoreファイルはリポジトリ内の任意の場所に配置することができますが、通常はリポジトリのルートディレクトリ、.git/フォルダと同じ階層に配置します。

.gitignoreに記録するファイルはプロジェクトによって異なりますが、一般的にバージョン管理から除外される種類のファイルが存在します。 特にファイルやフォルダ名の先頭に . が付いているものは、バージョン管理から除外することが一般的です。 こうしたファイルやフォルダは隠しファイル、隠しフォルダと呼ばれ、システムによって生成され、ユーザーが直接操作することは限定的です。 加えて、サイズが大きかったり差分管理が困難なバイナリファイルはバージョン管理の対象外となることが多いです。

.gitignoreの記載例を示します。.gitignoreでは特定の正規表現を使って複数のファイルを指定したり、否定の表現(つまり管理に含める)を使った柔軟な指定が可能です。

Thumbs.db
.DS_Store
.Rproj.user
.Rhistory
.RData
.Ruserdata
*.pdf
*.log
README.html

Thumbs.db.DS_Storeは目にしたことがない方もいるかもしれません。 これらはWindowsやmacOSで生成されるファイルで、フォルダ内のファイルのサムネイルや表示方法を記録するファイルです。 プロジェクト内のコードには影響を与えないため、バージョン管理は不要です。

続いての.Rproj.userから.RuserdataはRに関連するものです。 特に.Rproj.userはRStudioのプロジェクト設定ファイルを含むフォルダでバージョン管理を行いません。 .Rhistory.RDataはRの履歴やデータを保存するファイルですが、ユーザーやセッションの度に更新されるものであるため、これらもバージョン管理を行いません。

*.pdf*.logはPDFおよびlog形式のファイル一式を除外する指示です。ファイル名に関わらず、拡張子によって除外される複数のファイルを想定しています。 アスタリスク(*)は任意の文字列を表し、*.pdfは拡張子が.pdfであるファイルをすべて指定します。

拡張子による指定という点で、その次の行 README.html も同様ですが、*.pdf*.logと異なるのはプロジェクト内のすべてのHTMLファイルを除外するわけではないという点です。 README.htmlは特定のファイル名を指定しています。つまり対象外となるのは1つのファイルだけです。

.Rprofileはバージョン管理に含めるか悩むところですが、 Rの設定や挙動を制御する内容が書かれているものであればバージョン管理するべきです。 プロジェクトに参加するすべてのメンバーが同じ条件でRを実行するために必要となる可能性があります。 一方で、.Rprofileに個人の機密情報や認証情報が含まれるのであれば.gitignoreに追加しましょう。

クレデンシャルな情報を扱う必要があるのであれば、.Renviron.Renviron.siteを使って環境変数として設定することを検討してください。 これらの情報は手間がかかるかもしれませんが、セキュリティ上のリスクを軽減するためには必要な対応です。 第6章では、configパッケージを使った環境変数の管理について紹介します。

最後に、作成した覚えのないファイルやフォルダがGitバージョン管理の対象となっているときは、注意してそのファイルをバージョン管理に含めるべきかを検討してください。 著者の経験では、身に覚えのないファイルは一時的に生成されたものや成果物であることが多く、バージョン管理の対象外とすることが多いです。