Git初心者が最初から学ぶGitの入門(リモートリポジトリ)

前回の記事でGitの基本的な操作方法についてご紹介しました。ローカル上にGitのリポジトリを作成し、コミット、ブランチ、マージといったGitのコアとなる部分について学びました。今回は、GitHubを使用して主にリモートリポジトリの扱いに焦点を当ててご紹介します。あらかじめ、GitHubのアカウントを作成しておいてください。(今回はGitHubの機能を使用するわけではないため、厳密にはGitHubのアカウントについては必須ではありません)

まずは、GitHubにサインインして、新規リポジトリを作成しましょう。その際に、すでにコミットされているローカルリポジトリの情報をマージするため、「Initialize this repository with a README」のチェックは外しておきましょう。今回は、trainingという名前で空のリポジトリを作成しました。

リモートリポジトリ

前回の記事で作成した、ローカルのリポジトリを引き続き使用します。まだ、ローカルリポジトリを作成していない方は、作業ディレクトリを作成しgit initコマンドでローカルディレクトリにGitリポジトリを作成しておいてください。

Gitは、分散型バージョン管理システムです。それに対して、Subversionは集中型バージョン管理システムと呼ばれます。

Subversionは、1つのリポジトリに対して複数人が作業を実施します。ユーザーは、同一のファイルに対してあらかじめ編集の競合が起きないように、「チェックアウト」(ロック)を行います。また、マスターとなるリポジトリが決まっているため、そのリポジトリにアクセスできないユーザーは作業を実施できません。競合しないことが前提となっているためシンプルである分、複数人による同時作業には向きません。

それに対して、Gitはユーザー毎にリポジトリを持ちます。ユーザーは、各人のリポジトリで作業を実施し、最終的な結果をマスターとなるリポジトリにマージします。Gitは、リポジトリが複数存在するため、同一ファイルに対する編集の競合が発生するかもしれません。編集の競合(コンフリクト)については、前回の記事でご紹介しました。

リモートリポジトリの追加

GitHubは、Gitリポジトリを提供するマネージド型サービスです。複数人のユーザーが、GitHub上のリポジトリをクローン(clone)、またはフォーク(fork)して使用します。フォーク(fork)やプルリクエスト(pull request)など、GitHub固有の機能については、次回以降取り上げます。ユーザーは、ローカルホストのリポジトリ上で作業を実施し、実施した結果を最終的にGitHub上のリポジトリにGitの仕組みを使用してマージします。

まず、ローカルで作業していたリポジトリに対して、GitHub上のリポジトリをリモートリポジトリとして設定する方法を見ていきます。すでに作成済みのローカルリポジトリに対して、リモートリポジトリの設定を追加する場合、git remoteコマンドで行います。

git remote add origin https://github.com/ottanxyz/training

リモートリポジトリには、ローカルと区別するために名前を付与する必要があります。慣例的にoriginという名前を付けることが多いですが、名称は何でも構いません。なお、git cloneでリポジトリをコピーした場合、自動的にoriginという名称でリモートリポジトリが設定されます。なお、すでにoriginという名前のリポジトリが存在する場合は、git remoteで上書きすることもできます。

git remote set-url origin https://github.com/ottanxyz/training

なお、リモートリポジトリは複数設定できます。主にリポジトリをフォークした際に、フォーク元のリポジトリの変更を追跡するために使用しますが、詳細は次回以降に取り上げます。

リモートリポジトリの詳細

先ほど作成したリモートリポジトリの詳細を確認してみましょう。git remoteコマンドに-vオプションを付与します。

git remote -v

originという名前のリモートリポジトリが設定されていることがわかります。

origin https://github.com/ottanxyz/training (fetch)
origin https://github.com/ottanxyz/training (push)

リモートリポジトリにあるブランチの情報も確認してみましょう。git branchコマンドに-aオプションを付与すると、リモートリポジトリのブランチも合わせて表示されます。

git branch -a

remotes/origin/masterは、リモート(remote)リポジトリとして設定したoriginという名称のmasterブランチを指しています。では、origin/masterとはなんでしょうか。(今回の場合)remotes/origin/masterorigin/masterは同義です。即ち、リモートリポジトリとして設定したoriginHEADは、リモートリポジトリのmasterブランチの最新のコミットと一致していることもわかります。

* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/master

少し奥歯に物が挟まったような表現をしたのは、Gitではややこしいことにorigin/masterというブランチを作成することもできるからです!このような混乱を招きそうなブランチを作成したくはありませんが、試しに作成してみてgit branchコマンドを実行すると、以下のように表示されます。

* master
  origin/master
  remotes/origin/HEAD -> remotes/origin/master
  remotes/origin/master

Gitが気を利かせて、origin/masterではなくremotes/origin/masterとして表示してくれました。

git branch -D origin/master

誤って作成してしまった、ややこしい名前のブランチはさっさと消しておきましょう。以降、origin/masterと言えば、remotes/origin/masterのことを指します。

プッシュ

続いて、ローカルリポジトリでコミットした内容を、リモートリポジトリに反映してみましょう。ローカルリポジトリの内容をリモートリポジトリへ反映させることを、プッシュ(push)と呼びます。逆の操作を、フェッチ(fetch)と呼びますが、こちらは後述します。

ローカルリポジトリのmasterブランチを、リモートリポジトリであるoriginにプッシュするためには、以下のコマンドを実行します。

git push -u origin master

GitHub上にローカルリポジトリでコミットした内容が反映されているはずです。

上流ブランチ

ところで、git pushに付与した-uオプションは何を意味するのでしょうか。-uオプションを付与しない場合と比較して何が異なるのでしょうか。-uオプションの謎を解き明かすため、ここでもう一度整理しておきましょう。Gitは、用語が多いので時に混乱を招きます。

以下のコマンドで新たなブランチを作成します。

git checkout -b sample

この状態でgit statusを確認すると、以下のように表示されるはずです。ここまでは前回と同様です。

On branch sample
nothing to commit, working tree clean

ここで以下のコマンドを実行します。

git branch sample --set-upstream-to master

--set-upstream-toオプションはよく使用されるため、-uという短縮形が用意されています。現在のブランチに対してmasterブランチを上流ブランチとして設定しなさいという意味になります。先ほどgit pushの際に使用したオプションも同様です。なお、sampleを省略すると、現在のブランチに対して同じ操作を実行します。Gitのコマンドの多くは、現在のブランチを省略できます。

この状態でステータス(何かあったときのための、git status!)を確認してみます。新たに情報が追加されていることがわかりますね。「masterブランチは、sampleブランチの上流ブランチである」ことを示しています。

On branch sample
Your branch is up to date with 'master'.

nothing to commit, working tree clean

では、具体的に「上流ブランチ」には何の役目があるのでしょうか。具体的な例を示すために、masterブランチで新たにファイルの変更をコミットします。

git checkout master
touch upstream.txt
git add upstream.txt
git commit -m "add upstream.txt"

この状態で、再びgit checkoutコマンドでsampleブランチに切り替えてみます。

Switched to branch 'sample'
Your branch is behind 'master' by 1 commit, and can be fast-forwarded.
  (use "git pull" to update your local branch)

master ブランチを上流ブランチとして設定したことにより、sampleブランチに切り替えた際、masterブランチに変更がある旨を表示してくれました。また、Fast-forward(早送り)によるマージが可能であることもわかります。実際に、git mergeを実行するとmasterブランチの変更が、sampleブランチにマージされます。なお、再三繰り返していますが、この情報はgit statusコマンドでも確認できます。

さて、再びmasterブランチに戻り、Gitリポジトリの状態を確認してみます。

git checkout master
git status

origin/master、即ちリモートリポジトリであるoriginmasterブランチは、ローカルリポジトリのmasterブランチの上流ブランチであることがわかりました。

On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean

リモート追跡ブランチ

このorigin/masterブランチについて、もう少し詳しくみていきます。

origin/masterブランチは、originというリポジトリにあるmasterブランチを指し示していることは前述の通りです。さらに、origin/masterブランチは、ローカルリポジトリにあるmasterブランチの上流ブランチです。ということは、GitHub上のmasterブランチにファイルを追加すると、先ほどのようにローカルリポジトリのmasterブランチに通知されるはずです!

GitHubのmaster ブランチで適当にファイルを作成してみてください。そのあと、git statusコマンドにより、ローカルリポジトリのmasterブランチの様子を確認してみます。

On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean

origin/masterブランチを更新したはずなのに、何も変更が表示されません。ただ、origin/mastermasterの上流ブランチであることは、上記の表示から理解できます。

origin/masterは、originというリモートリポジトリにあるmasterブランチを指していることを冒頭から散々述べてきました。実は、このorigin/masterはリモートリポジトリにあるmasterブランチの参照であり、実態ではないのです。よく考えれば、リポジトリが異なるので当たり前です。

このリモートリポジトリにあるブランチを指す、ローカルリポジトリのブランチ(今回の場合は、origin/master)のことをリモート追跡ブランチと呼びます。リモート追跡ブランチは、文字通りリモートリポジトリにあるブランチを追跡するために使用されるブランチです。

まとめると、origin/masterは、ローカルリポジトリのmasterブランチの上流ブランチであり、リモートリポジトリのmasterブランチのリモート追跡ブランチです。

(local)                        |     (remote)
master ---> origin/master -----|----> master

では、リモートリポジトリの変更をローカルリポジトリへ取り込むためにどうすればよいでしょうか。リモートリポジトリの情報をローカルリポジトリに取り込むためには、git fetchコマンドを使用します。もっと言えば、git fetchコマンドによりローカルリポジトリのmasterブランチの上流ブランチであるorigin/masterが更新されます。

git push -uの意味

先に-uオプションの謎を解き明かしておきましょう。-uオプションは、現在のブランチ、もしくは指定したブランチの上流ブランチを設定するオプションでした。では、以下のコマンドはどのような意味になるでしょうか。

git push -u origin master

これは、ローカルリポジトリのmasterブランチを、リモートリポジトリであるoriginにプッシュするとともに、origin/mastermasterの上流ブランチとして設定しています。逆に言えば、-uオプションを使用しないと、上流ブランチは自動的に設定されません。

なお、別のGitリポジトリをクローン(clone)すると、自動的にローカルリポジトリへmasterブランチが作成され、origin/mastermasterブランチの上流ブランチとして設定されます。

プッシュ(2回目以降)

さて、上流ブランチについて学んだところで、もう一度リモートリポジトリに対してプッシュしてみましょう。今度は、リモートリポジトリ名、ブランチを指定しなくても、以下のコマンドだけでプッシュできるはずです。

git push

これは、masterブランチの上流ブランチとしてorigin/masterが設定されているからです。もし、上流ブランチとして設定されていない場合、以下のようなエラーが表示されます。

fatal: The current branch master has no upstream branch.
To push the current branch and set the remote as upstream, use

    git push --set-upstream origin master

フェッチ

では、GitHub(リモートリポジトリ)の変更を取り込んでみましょう。リモートリポジトリの変更を取り込むためには、git fetchコマンドによりorigin/masterを更新する必要があることは述べました。実際に、git fetchコマンドを実行してみましょう。

git fetch

これで、origin/masterに変更が取り込まれました。では、ローカルリポジトリとリモートリポジトリの差分を確認してみましょう。リモートリポジトリとの差分を確認するためには、git diffコマンドにリモートリポジトリのブランチを指定します。git diffは、リポジトリ上で作業をしていると、さまざまな差分を取得してくれます。

git diff origin/master

以下のようなログが確認できたでしょうか。

diff --git a/github-4.txt b/github-4.txt
deleted file mode 100644
index 8b13789..0000000
--- a/github-4.txt
+++ /dev/null
@@ -1 +0,0 @@

内容が問題ないと確認できた場合は、git mergeコマンドによりリモートリポジトリの変更をマージします。

git merge

前回、マージする際にブランチ名を指定していたことを思い出してください。今回は、masterブランチの上流ブランチとしてorigin/masterブランチを指定していたため、Gitが気を利かせて上流ブランチの変更を自動的にマージしてくれたわけです。

プル

プル(pull)とは、リモートリポジトリの変更を取り込み(fetch)、マージ(merge)まで一気に行う方法です。GitHubにおける「プル」リクエスト(通称、プルリク)とは、別のリポジトリやブランチからの変更を取り込み、マージしてほしい意思を伝えるための手段です。プルリクエストについては、今回は取り上げません。

git pull

上流ブランチを指定している場合、ブランチ名を指定する必要はありません。上記のコマンドは、以下と同義です。

git fetch && git merge

英語の対義語としては、プッシュ(push)の反対はプル(pull)を思い浮かべるかもしれません。しかし、Gitにおけるプル(pull)とは、上記の通りリモートリポジトリの変更をローカルに取り込んだ上で、さらにマージする行為を指します。プッシュ(push)、即ちローカルリポジトリの変更をリモートリポジトリに取り込む行為の反対は、フェッチ(fetch)が正しい認識です。

まとめ

Gitにおける用語が多数登場しました。また、前回と今回でご紹介していないコマンドやオプションも多数あります。次回以降のタイミングで、GitHubのコラボレーション機能(フォーク、プルリクエスト)について取り上げたいと思います。

comments powered by Disqus