XcodeでGitを使おう 【ローカルリポジトリ編】
Gitはプログラムのソースコードの変更履歴を追跡して管理することのできるシステムです。Gitを使用することで、多くのユーザーとの共同開発や、バージョン管理などを簡単に行うことができます。今回は、XcodeでGitを使用する方法について詳しく解説したいと思います。
ローカルGitリポジトリを作ろう
Gitリポジトリとは、Gitでバージョン管理を行うためのデータ構造のことです。これを作成することで、バージョン管理が行えるようになります。
リポジトリには、ローカルとリモートの二種類存在します。ローカルは自分のコンピュータに存在して、コードの編集などの記録を保存します。リモートは、インターネット上のサーバー(GitHubやGitLabなど)に存在し、ローカルで記録した変更をアップロード(Push)して、他の開発者とコードを共有・共同開発したり、手元のPCが壊れたときのバックアップとして保存したりするための保管場所です。
まずは、XcodeでローカルGitリポジトリを作ってみましょう。プロジェクトを新規作成するときに一緒に作成するか、既存のプロジェクトにリポジトリを追加することができます。
プロジェクトを新規作成時にリポジトリを作成
まずは、プロジェクトを新規作成するときに一緒にリポジトリを作成してみましょう。
まず、Xcodeの初期画面から、Create New Project…をクリックして、使用するテンプレートを選択してください。今回はmacOS→Appを選択しました。

必要な部分を入力し、Nextをクリックすると、プロジェクトファイルを保存する場所を選択できます。

その画面で、下の方にSource Control:というチェックマークが出てきます。これにチェックを入れてから、Createボタンをクリックしましょう。そうすることで、Gitリポジトリを自動的に作成してくれます。

既存のプロジェクトにリポジトリを追加
次に、すでに作っているプロジェクトで、新規作成時にGitリポジトリを作成しなかった場合に追加する方法についてみていきます。
まず、自分のプロジェクトにリポジトリがあるかどうかを確認しましょう。左側のナビゲーターウィンドウで右から二番目のタブを開いて、No Git Repositoriesと表示されている場合は、リポジトリは存在しません。

その場合、次の手順でリポジトリを追加することができます。
まず、ツールバーからIntegrateを選択します。

そうしたら、Integrateの中のNew Git Repository… をクリックしましょう。

すると、次のような画面が表示されます。

Gitでバージョン管理するプロジェクトを選択して、Createをクリックします。
すると、先ほどNo Git Repositoriesだった場所が、No Changesに変化します。

これで、Xcodeプロジェクトでリポジトリを作成することができました。
コミットしてみよう
コミットとは、リポジトリに変更記録を保存することです。ここでは、Xcodeでのコミットの仕方について詳しく説明してきます。
まず、リポジトリを作成した状態で、ソースコードに変更を加えてみると、左側に青い線が表示されるようになります。

これは、前回のコミットから変更された箇所を示します。ここをクリックすると、Stage Change、Discard Changeという二つのボタンが現れます。

ステージとは、この変更を次のコミットの対象とすることです。例えば、バグ修正と新機能の追加を同時に行ったとしても、先にバグ修正だけをステージしてコミットすることで、変更記録がごちゃ混ぜにならず、見やすく記録をすることができます。
Discard Changeは、変更箇所を最後にコミットされた状態に戻します。今回は、Textを追加したので、Discard Changeをクリックするとその部分が削除されます。
Stage Changeについては、後から一括で行うこともできます。
ファイルに対して編集を行うと、ナビゲーターのファイルの右側にMという文字が現れます。

これは、Modifiedの頭文字で修正されたという意味になります。ファイルの中身が前回のコミットから変更されると、このマークが表示されます。
また、ファイルを新しく追加したときにはAddedのAが表示されます。

これらはコミットを行うと消えます。
それでは実際にコミットをしてみましょう。
まず、ナビゲーターウィンドウから、左から二番目のSource Control navigatorを選択します。すると、今回の変更が一覧になって表示されています。
ここにも、M,A,Dという頭文字がついています。これは先ほどと同様、MはModified、AはAdded、DはDeletedを示します。ちなみに、ファイル名を変更すると、RenamedのRが表示されます。

このUncommitted Changesというところをクリックすると、変更された箇所が一覧になって表示されます。

各編集箇所の右上をクリックしてステージする場合はStage Changesをクリックしましょう。

ステージが完了すると、先ほどは青文字に背景が透明だったMの文字が、塗りつぶされて白文字に変わりました。AddedとDeletedについては、その場でステージが完了するようです。

次に、Commit messageを記入します。

Commit messageには、今回行った変更について、わかりやすく記載しましょう。
次にコミットを行うのですが、この画面では灰色になっています。その理由は、Authorの設定がされていないためです。No Authorの右側のGit Author Settings…をクリックするか、ツールバーのXcode→SettingsからSource Control→Git settingsをクリックしましょう。

ここで、自分の名前とemailを登録しておきましょう。

ここで記入したAuthor nameは、コミットしたユーザーとして記録されます。GitHubやGitLabなどのアカウントを持っていて、将来リモートリポジトリも利用する場合は、同じユーザー名とメールアドレスを使うのが無難でしょう。

設定が完了すると、先ほどのCommitボタンが押せるようになっています。

ボタンを押すとコミットが完了します。

ブランチを作ろう
Gitでは、ブランチというものを作ることができます。ブランチは、コミットの履歴を分岐して保存する機能です。自分のコード変更の影響は自分の使っているブランチにしか反映されないため、新機能の追加やバグ修正をブランチを分けてから適用することで、共同開発などの場合に役立ちます。また、新機能の追加がうまくいかず、実装を取りやめる際も、ブランチごと捨てて仕舞えばいいので、バージョン管理の面でも便利です。
まず、ナビゲーターウィンドウのChangesの部分をRepositoriesに変えます。

Branchesには、現在存在しているブランチが一覧で表示されます。初期設定では、mainブランチというブランチが作られています。また、このmainブランチをクリックすると、右側にこのブランチのコミット履歴を確認することができます。

それでは、新しいブランチを作ってみましょう。今回は、mainブランチから新しいadd-buttonブランチを作ってみます。まず、ナビゲーターウィンドウで、mainブランチを右クリックします。

そうしたら、New Branch from “main”…をクリックします。ブランチの名前を指定できるので、自由に名前を設定します。今回はadd-buttonを入力します。

Createを押すと、新しいブランチが作成されて、自動的に作ったブランチにスイッチされます。

自分が今いるブランチを確認する方法はいくつかありますが、一つ目はなビゲーターウィンドウでBranchesに(current)とついているブランチを見る方法です。

もう一つは、左上にあるプロジェクト名の下を確認する方法です。ここに現在のブランチが表示されます。

この状態で、ファイルに変更を加えてコミットすると、add-buttonというブランチにのみ変更が加えられ、mainは何も影響を受けません。
現在のブランチを変更する
ブランチを切り分けて作業中に、別のブランチに移りたくなることがあります。そうした場合は、ブランチをswitchすることでブランチを変更することができます。
ブランチを変更するには、ナビゲーターウィンドウで変更したブランチを右クリックします。今回はmainブランチに変更したいため、mainを右クリックします。

クリックすると、確認画面が表示されます。Switchをクリックしてスイッチします。

このとき、元のブランチにコミットされていない変更があると、Stash Changesに追加するように求められます。

stashとは、コミットしていない変更を一時的に保存しておく機能です。変更した内容をコメントとして書いておきましょう。その後Stash and Switchを押すとブランチのスイッチが完了します。
Xcodeの謎の仕様?かバグなのかはわからないのですが、ブランチ変更後でも一度画面を更新しないとブランチの(current)が変更されません。そのため、一度ナビゲーターウィンドウでChangesをクリックしてから再度Repositoriesを開くと、正しく現在のブランチが(current)で表示されます。
また、元のブランチ(add-button)にもう一度スイッチすると、先ほどまで加えていたコミットしていない変更が反映されません。そのため、先ほどstashに保存しておいた変更内容をブランチに適用します。
Stashed Changesを開き、先ほどのstashを右クリックし、Apply Stashed Changes…をクリックしましょう。

stashを適用した後にstashを残すかどうかを選択した後、Apply Stashをクリックすると、先ほどまでのコミットしていなかった内容が復活します。

ブランチをマージしよう
次に、ブランチをマージしましょう。マージは、切り分けたブランチでコミットした変更を、別のブランチにも反映させることを意味します。具体的には、新機能を実装するためにブランチを分けて作業を行い、実装が完了したため、mainブランチに新機能を反映させる、といったように使うことができます。
マージするためには、まずマージ先のブランチにswitchします。例えば、上記の例のようにmainブランチに新機能(add-buttonブランチ)を反映させたい場合、まずmainブランチにスイッチします。
その後、マージしたいブランチを選択し、右クリックします。ここでは、add-buttonブランチを右クリックします。

すると、Merge “add-button” into “main”…というメニューが表示されるので、それをクリックします。確認画面が表示されるので、Mergeをクリックします。

すると、mainブランチにadd-buttonのコミットを反映することができるようになりました。

Cherry-Pickをしよう
Cherry-Pickは、先ほどのマージと少し似たような機能を提供します。マージは別のブランチを丸ごとブランチに反映させるのに対して、Cherry-Pickは別のブランチにあるあるコミットを、ブランチに反映させることができます。
例えばこのようなコードがあったとします。
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, world!")
Text("Hello, new branch!")
.font(.title)
.foregroundStyle(Color.white)
.frame(width: 200, height: 50)
.background(Color.blue)
Text("Add button").
Text("Add button in new branch") // commit 1
.font(.title)
Button("Press Me") { // commit 2
print("Button Pressed")
}
Button("Press Me Too") { // commit 3
print("Another Button Pressed")
}
}
.padding()
}
}
#Preview {
ContentView()
}
ここで、最初のコミットでText(“Add button in new branch”)を追加、次のコミットでボタンを追加、最後のコミットで最後のボタンを追加したとします。ここで、commit 2をメインにCherry-Pickしてみましょう。
まず、ブランチをメインに変更します。その後、add-buttonのブランチを開きましょう。

今回Cherry-Pickしたいのは「ボタンを追加した」というコメントがついているコミットです。これを右クリックします。

すると、Cherry-Pick …というボタンが表示されるため、クリックします。確認画面が表示されるため、Cherry-Pickをクリックします。

すると、次のような画面が表示されました。これは、コンフリクトが起きている状態です。

左右にコードが示されています。左側はmainブランチ、右側はadd-buttonブランチのコードです。
真ん中の1という部分をクリックすると、Choose Left, Choose Rightというボタンが表示されます。

Choose Leftを選択すると、元のmain側が優先され、Cherry-Pickの意味がなくなってしまうので、今回はChoose Rightをクリックして右下のCherry-Pickを押しました。実際は各コンフリクトに対して評価をしてから選択してください。

import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, world!")
Text("Hello, new branch!")
.font(.title)
.foregroundStyle(Color.white)
.frame(width: 200, height: 50)
.background(Color.blue)
Text("Add button")
Text("Add button in new branch")
.font(.title)
Button("Press Me") {
print("Button Pressed")
}
}
.padding()
}
}
#Preview {
ContentView()
}
すると、コミット1,2の結果が反映されました。
コミットにタグをつけよう
gitでは、コミットに対してタグを作成することができます。よく使われる使用法は、新しいバージョンのコミットが完了したときにそのコミットに対してv1.2.3といったようにバージョン名のタグをつけるといったことです。
現在のブランチの最新コミットに対してタグをつけたい場合は、ナビゲーターウィンドウのブランチから選択して右クリックするか、ブランチを選択して先頭のコミットを右クリックしてTag…をクリックします。
過去のコミットに対してタグをつけたい場合は、そのコミットを選択して右クリックし、同様にTag…をクリックしましょう。

その後、タグの名前を書いてCreateします。

すると、選択したコミットにタグの名前が表示されます。

過去のコミットにスイッチする
過去のコミットにスイッチする場合も、ブランチのスイッチと同様に行うことができます。遡りたいコミットを右クリックし、Switch to…をクリックすることで、その時点のコードに遡ることができます。

Switchボタンを押しましょう。

また、最新のコミットに戻りたいときは、同様の手順で最新のコミットにSwitchすることができます。
まとめ
今回は、XcodeでローカルGitリポジトリを使う方法について詳しく解説しました。
前述したように、Gitにはリモートリポジトリも存在します。その使い方については次回の記事で解説する予定です。お疲れ様でした。
Gitは、コミットを、全体のファイルとしてではなく、差分として保持します。そのため、マージやCherry-Pickするときに、持ってきた差分の変更箇所が被っていることや、変更箇所が存在しない場合にはコンフリクトとなり、ユーザーに決定を委ねます。