git入門 ~仕組みを理解する~ 第7回 git環境を2つ作成しrepositoryのcommitを共有する
第1回でgitは分散型バージョン管理システムであることをお伝えしました。
また、GitHubなどのPublicなリモートサーバを利用しなくても、複数人でgitを利用できることについてもお伝えしました。
今回はリモートサーバを利用せずに2人(Aさん、Bさん)でgitを利用して開発を進めることを想定した内容で説明します。
commitの特徴を思い出す
「第2回 リポジトリのcommitツリーをイメージする」でcommitの特徴について学習しましたね。
その特徴の中に「他のgit環境とcommitの情報を交換できる」ということをお伝えしました。
内容を振り返ると、こうでしたね。
git環境(リポジトリ)は他のgit環境(リポジトリ)から指定したcommitやリポジトリ全体の最新のcommitを取得するなどできます。
また、逆に他のgit環境(リポジトリ)にcommitを渡すこともできます。
今回は2つのgit環境での操作を学習しましょう。
2つのgitリポジトリの構成図
今回の用意する環境は次の図の通り、1台のPCに2つのrepository(repo-Aとrepo-B)を作成します。
また、ユーザもAさん(user-A)とBさん(user-B)を作成します。
Aさんのリポジトリ(repo-A)を作成する
まず、Aさんのリポジトリを作成していきましょう。
以下の内容を行います。
- repo-Aフォルダにrepositoryを作成
- ユーザ名とメールアドレスを設定
- file-Aを作成しcommitをrepositoryへ登録する
/c/Git
$ mkdir repo-A
/c/Git
$ cd repo-A/
/c/Git/repo-A
$ git init
Initialized empty Git repository in C:/Git/repo-A/.git/
/c/Git/repo-A (master)
$ git config --local user.name user-A
/c/Git/repo-A (master)
$ git config --local user.email user-A@abc.com
/c/Git/repo-A (master)
$ echo 'aaa' > file-A
/c/Git/repo-A (master)
$ git add .
/c/Git/repo-A (master)
$ git commit -m 'aaa add'
[master (root-commit) 0b0a05d] aaa add
1 file changed, 1 insertion(+)
create mode 100644 file-A
/c/Git/repo-A (master)
$ git log
commit 0b0a05db423a3c7e71193a9b47cec9e564293431 (HEAD -> master)
Author: user-A <user-A@abc.com>
Date: Fri Mar 20 18:31:50 2020 +0900
aaa add
ユーザ名とメールアドレスは2つのリポジトリで別のユーザを指定したいため「–local」を指定しています。
それ以外は、特に新しいコマンドなどはありませんね。
AさんのリポジトリをBさんの環境にコピーする
次にBさん側のリポジトリを作成するのですが、Aさんのリポジトリをコピーして作成します。
以下の内容を行います。
- Aさんのrepositoryを丸ごとBさんのrepositoryとしてコピーする
- ユーザ名とメールアドレスを設定
まず、repositoryをコピーするgitコマンドは「git clone」です。
/c/Git
$ git clone /c/Git/repo-A /c/Git/repo-B
Cloning into 'C:/Git/repo-B'...
done.
今回は同じPC内で実行していますが、別PCのrepositoryでもgit cloneコマンドでコピーできます。
git clone [コピー元のrepository] [コピー先のrepository]
git cloneコマンドを実行をするとrepositoryのコピーと同時にstaging-areaとworking-directoryにHEADが参照するcommitの内容が展開されます。
それぞれ確認してみると
/c/Git/repo-B (master)
$ git log
commit 0b0a05db423a3c7e71193a9b47cec9e564293431 (HEAD -> master, origin/master, origin/HEAD)
Author: user-A <user-A@abc.com>
Date: Fri Mar 20 18:31:50 2020 +0900
aaa add
/c/Git/repo-B (master)
$ git ls-files
file-A
/c/Git/repo-B (master)
$ ls
file-A
/c/Git/repo-B (master)
$ git config --local user.name user-B
/c/Git/repo-B (master)
$ git config --local user.email user-B@abc.com
Authorが「user-A」となっているcommitが取得できました。
そして、staging-areaとworking-directoryにfile-Aが登録された状態になっていますね。
厳密には違いますが、.gitフォルダがコピーされているとイメージしても概ね大丈夫です。
リモート追跡ブランチ(remote-tracking-branch)
先ほどのgit logで表示されたcommit-hashの横に、これまで1つのrepositoryで学習していた際には表示されていない「origin/master」「origin/HEAD」がありますね。
/c/Git/repo-B (master)
$ git log
commit 0b0a05db423a3c7e71193a9b47cec9e564293431 (HEAD -> master, origin/master, origin/HEAD)
Author: user-A <user-A@abc.com>
Date: Fri Mar 20 18:31:50 2020 +0900
aaa add
これは、remote-tracking-branch(リモート追跡ブランチ)と呼ばれるもので、branchやHEADと同様にcommit-hashへのポインタです。
リモート追跡ブランチの説明の前に、まずoriginについて説明します。
「origin」はリモートリポジトリ(この場合はrepo-A)の別名です。
originがrepo-Aの別名であることを確認するgitコマンドも存在します。
「git remote 」コマンドです。
/c/Git/repo-B (master)
$ git remote -v
origin C:/Git/repo-A (fetch)
origin C:/Git/repo-A (push)
origin と repo-Aが関連付けられていることがわかります。
この関連付けはgit cloneコマンドでリポジトリをコピーすると自動で設定されています。
リモート追跡ブランチの説明に戻ります。
リモート追跡ブランチという名前から勘違いしそうになりますが「origin/master」はrepo-Aのmasterが参照するcommitを常にリアルタイムで追跡しているわけではありません。
repo-B側で「git clone」コマンドで取得した時点でrepo-Aのmasterがこのcommitを参照していただけです。
ですので、この後でrepo-A側でcommitを追加された場合でも、repo-B側で何もしなければ「origin/master」は同じcommitを参照しているように見えます。
この後、実際に確認してみましょう。
そして、repo-A側で追加されたcommitを取得する方法についても学習しましょう。
また、git用語ですが、repo-Bから見た場合、
repo-Bをlocal-repository(ローカルリポジトリ)
repo-Aをremote-repository(リモートリポジトリ)
と呼びます。
Aさんのリポジトリにcommitを追加
repo-Aにcommitを追加しましょう。
/c/Git/repo-A (master)
$ echo 'bbb' > file-B
/c/Git/repo-A (master)
$ git add .
/c/Git/repo-A (master)
$ git commit -m 'file-B add'
[master f896d6b] file-B add
1 file changed, 1 insertion(+)
create mode 100644 file-B
/c/Git/repo-A (master)
$ git log --all
commit f896d6b21d82f58d8cfc43d416beef84e0908b4c (HEAD -> master)
Author: user-A <user-A@abc.com>
Date: Fri Mar 20 21:24:30 2020 +0900
file-B add
commit 0b0a05db423a3c7e71193a9b47cec9e564293431
Author: user-A <user-A@abc.com>
Date: Fri Mar 20 18:31:50 2020 +0900
aaa add
repo-Aにcommit(f896d6b21d82f58d8cfc43d416beef84e0908b4c)が追加されましたね。
では、repo-B側で追加されたcommitの状態を確認して取得してみましょう。
Aさんが追加したcommitをBさんが取得
repo-B側でまず、git logコマンドでcommitの状態を確認してみます。
/c/Git/repo-B (master)
$ git log --all
commit 0b0a05db423a3c7e71193a9b47cec9e564293431 (HEAD -> master, origin/master, origin/HEAD)
Author: user-A <user-A@abc.com>
Date: Fri Mar 20 18:31:50 2020 +0900
aaa add
repo-A側で追加されたcommitはrepo-B側では表示されていませんね。
このように、先ほどリモート追跡ブランチの際に説明しましたが、repo-B側で何もアクションを起こさなければrepo-Bのリポジトリに自動でrepo-Aのcommitが反映されるようなことはありません。
では、repo-Aで追加されたcommitをrepo-B側に取り込むにはどうすればよいでしょうか。
それは、「git fetch」コマンドを実行します。
git fetchコマンドはリモートリポジトリ(この場合はrepo-A)のcommitを取得します。
実際にコマンドを実行してみましょう。
/c/Git/repo-B (master)
$ git fetch origin master
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), 238 bytes | 18.00 KiB/s, done.
From C:/Git/repo-A
* branch HEAD -> FETCH_HEAD
「git fetch origin master」のように「origin」としましたが、「/c/Git/repo-A」と指定しても結果は同じになります。
繰り返しになりますが、理由はoriginがrepo-Aの別名だからです。
git fetch [リモートリポジトリ] [ブランチ名]
repo-Aからcommitが取得されたか確認してみましょう。
/c/Git/repo-B (master)
$ git log --all
commit f896d6b21d82f58d8cfc43d416beef84e0908b4c (origin/master, origin/HEAD)
Author: user-A <user-A@abc.com>
Date: Fri Mar 20 21:24:30 2020 +0900
file-B add
commit 0b0a05db423a3c7e71193a9b47cec9e564293431 (HEAD -> master)
Author: user-A <user-A@abc.com>
Date: Fri Mar 20 18:31:50 2020 +0900
aaa add
repo-Bのcommitが2つになりましたね。
repo-BのHEADは1つ目のcommitを指していますが、「origin/master」と「origin/HEAD」は2つ目のcommitを指していますので、repo-A側の状態も取得できました。
ですが、repo-Bのmasterブランチはfile-Bが追加された状態のcommitにはなっていません。
次にrepo-Aで追加されたcommitの内容をrepo-Bのmasterブランチに反映しましょう。
リポジトリに追加されたcommitを取り込む
repo-Aで追加されたcommitの内容をrepo-Bのmasterブランチに反映しましょう。
別のcommitの内容を現在のcommitに反映する方法は「git merge」コマンドを実行します。
/c/Git/repo-B (master)
$ git log --all
commit f896d6b21d82f58d8cfc43d416beef84e0908b4c (origin/master, origin/HEAD)
Author: user-A <user-A@abc.com>
Date: Fri Mar 20 21:24:30 2020 +0900
file-B add
commit 0b0a05db423a3c7e71193a9b47cec9e564293431 (HEAD -> master)
Author: user-A <user-A@abc.com>
Date: Fri Mar 20 18:31:50 2020 +0900
aaa add
/c/Git/repo-B (master)
$ git merge origin/master master
Updating 0b0a05d..f896d6b
Fast-forward
file-B | 1 +
1 file changed, 1 insertion(+)
create mode 100644 file-B
/c/Git/repo-B (master)
$ ls
file-A file-B
初めに、git log –allコマンドで取り込みたいcommitのbranch名(この場合はorigin/master)を確認します。
そして、git mergeコマンドを実行します。
git merge [取り込みたいcommit] [取り込み先commit]
今回の場合は「origin/master」のcommitを「master」に取り込みたいので、
「git merge origin/master master」となります。
staging-areaとworking-directoryにもfile-Bは追加されました。
git log で確認してみましょう。
/c/Git/repo-B (master)
$ git log --all
commit f896d6b21d82f58d8cfc43d416beef84e0908b4c (HEAD -> master, origin/master, origin/HEAD)
Author: user-A <user-A@abc.com>
Date: Fri Mar 20 21:24:30 2020 +0900
file-B add
commit 0b0a05db423a3c7e71193a9b47cec9e564293431
Author: user-A <user-A@abc.com>
Date: Fri Mar 20 18:31:50 2020 +0900
aaa add
HEADとmasterが最新のcommitを指すようになりましたね。
次はrepo-Bでcommitを作成しrepo-Aにcommitを反映していましょう。
Bさんがcommitを追加しAさんのリポジトリに反映
repo-B側でbranchを作成しcommitを追加した後にrepo-Aに反映してみましょう。
/c/Git/repo-B (master)
$ git branch branch-B
/c/Git/repo-B (master)
$ git checkout branch-B
Switched to branch 'branch-B'
/c/Git/repo-B (branch-B)
$ echo 'ccc' > file-C
/c/Git/repo-B (branch-B)
$ git add .
/c/Git/repo-B (branch-B)
$ git commit -m 'file-C add'
[branch-B d4bef39] file-C add
1 file changed, 1 insertion(+)
create mode 100644 file-C
/c/Git/repo-B (branch-B)
$ git push origin branch-B:branch-B
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 296 bytes | 296.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To C:/Git/repo-A
* [new branch] branch-B -> branch-B
少し長いですが、新しいgitコマンドは「git push」コマンドだけですね。
git pushコマンドは「リモートリポジトリ」「反映元ブランチ名」「リモートリポジトリのブランチ名」を指定します。
repo-B側でgit logコマンドを実行し状況を確認してみましょう。
/c/Git/repo-B (branch-B)
$ git log --all --graph
* commit d4bef39f85beec2262cc69c2f4533f3fbfa45538 (HEAD -> branch-B, origin/branch-B)
| Author: snow <snow@abc.com>
| Date: Fri Mar 20 22:28:44 2020 +0900
|
| file-C add
|
* commit f896d6b21d82f58d8cfc43d416beef84e0908b4c (origin/master, origin/HEAD, master)
| Author: user-A <user-A@abc.com>
| Date: Fri Mar 20 21:24:30 2020 +0900
|
| file-B add
|
* commit 0b0a05db423a3c7e71193a9b47cec9e564293431
Author: user-A <user-A@abc.com>
Date: Fri Mar 20 18:31:50 2020 +0900
aaa add
最後に追加したcommitに「origin/branh-B」が設定されていますね。
git push [リモートリポジトリ] [ローカルリポジトリ側のブランチ名]:[リモートブランチ名]
最後に、repo-A側の状態を確認してみましょう。
Aさんがリポジトリを確認
git logコマンドで確認してみましょう。
/c/Git/repo-A (master)
$ git log --all --graph
* commit d4bef39f85beec2262cc69c2f4533f3fbfa45538 (branch-B)
| Author: snow <snow@abc.com>
| Date: Fri Mar 20 22:28:44 2020 +0900
|
| file-C add
|
* commit f896d6b21d82f58d8cfc43d416beef84e0908b4c (HEAD -> master)
| Author: user-A <user-A@abc.com>
| Date: Fri Mar 20 21:24:30 2020 +0900
|
| file-B add
|
* commit 0b0a05db423a3c7e71193a9b47cec9e564293431
Author: user-A <user-A@abc.com>
Date: Fri Mar 20 18:31:50 2020 +0900
aaa add
repo-Bからpushされたcommitが反映されていることが確認できましたね。
まとめ
2つのリポジトリを用いてcommitのやり取りを行ってみました。
いくつか新しいコマンドもありましたが、commitツリーがイメージできていればお互いのcommitツリーを反映するだけですので、何度か実際に操作すれば理解できる内容だったと思います。
実際には2人以上でgitを利用していると同じファイルを修正した場合に、conflict(コンフリクト)が発生することもあるのですが、今回はgitの基本的な仕組みに絞って学習を進めていますので、conflict(コンフリクト)の解決方法は別の記事で紹介したいと思います。
また、最低限のgitコマンドで説明を進めていますので、実際には省略できるオプションや他にも便利なコマンドもありますが、そちらについても別の記事で紹介していければと考えています。
次回はGitHubをリモートリポジトリとして利用する方法について学んで行きましょう。