ソースコード管理をする(ユーザー編)

複数人で開発する時に面倒なのが、ソースコードのマージ。


特にリリース直前になると、ミスコミュニケーションで古いソース使ってて、
それが原因でメンバー間にわだかまりが・・・なんてこと結構無いですか?


もしくは、ちょっとした変更をいくつかしてビルドしたら動かなくなって、
一体何が原因だったかわからなくなったなんて経験ないですか?


ってことで、プロジェクトにマストなソースコード管理ツールとして、
今ウェブ系ではおそらく一番よく使われているであろうGithubについてです。



今回はプロジェクトのユーザー編、次回はオーナー編とします。
アカウントの作成は必須ですが、その辺は割愛します。
またGithubのポイントであるコミュニティ機能(フォロー等)も割愛します。
今回はあくまで、誰かが作ったソースコードを編集する点のみです^ ^


説明用として、今回の登場者は以下の3人とします。

[プロジェクト・オーナー]
たくと

[ユーザー]
てつを
しんちゃん


さて、初期状態では以下のようなイメージです。

上記マスターが、常に正式なレポジトリです。
レポジトリは、まぁプロジェクトとでも置き換えて下さい。
1つのサービスなりプロジェクトなりにつき、1レポジトリって感じです。


さて、このレポジトリへの編集を行いましょう。
マスターを直接変更は出来ないので、まずはforkします。
同じGithub上に、レポジトリのコピーを作るイメージです。

forkはGithubのサイトで、ボタン押すだけ。簡単〜♫
forkしたいリポジトリのアドレスは、プロジェクト・オーナーに聞きましょう^ ^


forkしたレポジトリは編集可能なので、まずは自分のローカル環境にcloneしましょう。

cloneのためのアドレスは、以下の通りGithubに書かれています。

さて、上記をみるとわかる通り、cloneでの通信ではsshを使います。
sshってのは、簡単に言うと暗号化による安全な通信方式。
暗号化のために公開鍵という方法を使います。


まぁあまり深く考えず、パスワードの代わりに鍵が必要と考えて下さい。
自分でロック用の鍵(公開鍵)と、アンロック用の鍵(秘密鍵)を作ります。
今回はAWS EC2をローカル環境とするので、そこで鍵を作成します。

[ec2-user@ip-10-117-93-66 ssh-key]$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/ec2-user/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/ec2-user/.ssh/id_rsa.
Your public key has been saved in /home/ec2-user/.ssh/id_rsa.pub.
The key fingerprint is:
a2:56:3f:ac:c1:41:f5:d2:70:6c:94:34:8d:7f:4b:e6 ec2-user@ip-10-117-93-66
The key's randomart image is:
+--[ RSA 2048]----+
| o+=+ |
| . ==.. |
| . ..o. |
| . . . + |
| + S = . |
| + = E |
| o o + |
| . o . |
| . |
+-----------------+

id_rsa.pubがロック用の鍵、id_rsa.pubがアンロック用の鍵。
で、アンロック用の鍵をGithubに登録します。





公開鍵の登録終わったら、clone!

[ec2-user@ip-10-117-93-66 ~]$ git clone git@github.com:takuto1981/mocha.git
Cloning into mocha...
The authenticity of host 'github.com (207.97.227.239)' can't be established.
RSA key fingerprint is XXXXX.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'github.com,YYYYY' (RSA) to the list of known hosts.
remote: Counting objects: 2675, done.
remote: Compressing objects: 100% (903/903), done.
remote: Total 2675 (delta 1844), reused 2569 (delta 1738)
Receiving objects: 100% (2675/2675), 1.04 MiB, done.
Resolving deltas: 100% (1844/1844), done.

さて、ここでブランチの説明。こんがらがるので、意図的に今まで描いていませんでしたが、
実は下の図のように、リポジトリの中にはさらにブランチという概念があります。

デフォルト上記のように、masterのみがあります。
これをコピーして、新たにブランチを作れます。


[ec2-user@ip-10-117-93-66 mocha]$ pwd
/home/ec2-user/mocha
[ec2-user@ip-10-117-93-66 mocha]$ git branch
* master
[ec2-user@ip-10-117-93-66 mocha]$ git checkout -b myFeatureSpike
Switched to a new branch 'myFeatureSpike'
[ec2-user@ip-10-117-93-66 mocha]$ git branch
master
* myFeatureSpike

これもあまり深く考えず、単にコピーを作っているだけと考えて下さい。
上記の図では、新たなブランチとしてmyFeatureSpikeを作っています。


ブランチを作ってすぐのタイミングでは、当然ですが全く同じものです。
上記の例では、masterとmyFeatureSpikeは全く同じものです。
masterは基本的に変更せず、myFeatureSpikeを変更します。
なんでmasterを直接編集せず、myFeatureSpikeを編集するかは、後述します。


なんらかの変更を行ったら、こまめにcommitします。
commitってのは、まぁsaveみたいなもんですかね。
なんらかの変更して動くことをローカルで確認したらcommitします。
commiは全て変更履歴として管理されるので、後で変更前に戻すことが出来ます^ ^
と言っても、どこまで戻せばいいか分からなくなってしまいますよね。
なので、commit時には何を変更したかも記述出来ます。
自分が後で困らないためにも、このコメントはしっかり書きましょう!


ちなみにcommitはローカルでしか行われません。
下の図の例えいえば、てつを ローカル Spaceのみで行われます。
他のユーザに変更が影響することはないので、安心して何度でもcommit出来ます。
commitしたらバグがあって、他のユーザに大影響・・・なんてことはならないです^ ^;

[ec2-user@ip-10-117-93-66 mocha]$ git commit -a
[myFeatureSpike d36a3d8] index.jsにコメント追加。
Committer: EC2 Default User
Your name and email address were configured automatically based
on your username and hostname. Please check that they are accurate.
You can suppress this message by setting them explicitly:

git config --global user.name "Your Name"
git config --global user.email you@example.com

After doing this, you may fix the identity used for this commit with:

git commit --amend --reset-author

1 files changed, 2 insertions(+), 2 deletions(-)


上記のように"git commit -a"を実施すると、更新されたファイルを自動で判別しcommitします。
この際、以下のようにエディタが開いて、変更点の記述を求められます。

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Committer: EC2 Default User
#
# On branch myFeatureSpike
# Changes to be committed:
# (use "git reset HEAD ..." to unstage)
#
# modified: index.js
#
index.jsにコメント追加。


何度かのcommitを経て、予定していた変更が完了しました。
すぐにでもマスターである、たくと Github Spaceのレポジトリにマージしたいところですが、
こっからちょっと複雑な手順を踏みます。


まず、ユーザーローカルに作成したmyFeatureSpikeをGithubにpushします。
これで、ローカルのmyFeatureSpikeが誤って壊れたりしても安心です。


以下のように、まずリモート(Github)として登録されているリポジトリ名がoriginであることを確認したら、
originにmyFeatureSpikeをpushします。

[ec2-user@ip-10-117-93-66 mocha]$ git remote
origin
[ec2-user@ip-10-117-93-66 mocha]$ git push origin myFeatureSpike
Counting objects: 5, done.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 368 bytes, done.
Total 3 (delta 1), reused 0 (delta 0)
To git@github.com:takuto1981/mocha.git
* [new branch] myFeatureSpike -> myFeatureSpike


ちゃんとpush出来たかはGithubを自分のアカウントページを見ればわかります^ ^


次に、Github上のマスターであるリポジトリから最新のものをpullします。


なぜわざわざpullするかというと、以下のようにローカルレポジトリを編集中に、
別のユーザの変更がマスターのリポジトリに追加されているかもしれないからです。
なんで最新のマスターリポジトリをローカルに持つ必要があるかは後述します。
# ちなみに、以下の図はちょっと正確ではないです。説明用にわざと簡単に書いています。



マスターのリポジトリは、以下のようにアドレス取得可能です。
自分のリポジトリの場合は"git@〜"なのが、マスターのリポジトリは"git://〜"なのに注意。
前者は読み書き可能、後者は読み込みのみ可能です。


マスターのリポジトリにupstreamという名前をつけて、リモートからpullします。

git remote add upstream https://github.com/visionmedia/mocha.git
[ec2-user@ip-10-117-93-66 mocha]$ git remote
origin
upstream
[ec2-user@ip-10-117-93-66 mocha]$ git checkout master
Switched to branch 'master'
[ec2-user@ip-10-117-93-66 mocha]$ git branch
* master
myFeatureSpike
[ec2-user@ip-10-117-93-66 mocha]$ git pull upstream master
remote: Counting objects: 18, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 11 (delta 9), reused 9 (delta 7)
Unpacking objects: 100% (11/11), done.
From https://github.com/visionmedia/mocha
* branch master -> FETCH_HEAD
Updating 877cab0..89af463
Fast-forward
History.md | 7 +++++++
lib/mocha.js | 2 +-
lib/runner.js | 3 ++-
mocha.js | 7 ++++---
package.json | 2 +-
5 files changed, 15 insertions(+), 6 deletions(-)


リモートであるupstreamのmasterをpullしています。
この時ローカルではマスターを選択(checkout)するのを忘れずに。
選択したブランチに対してpullが行われます。


また、更新したローカルのmasterをGithubにpushしておきます。


ここでrebaseを行います。
以下の絵のように、マスターとの差分を自動的にマージします。
自分が更新した箇所が、他人の更新とコンフリクトした場合のみ、
エラーが出て自分でマージしないといけないようです。でも、滅多にないとか。


[ec2-user@ip-10-117-93-66 mocha]$ git checkout myFeatureSpike
Switched to branch 'myFeatureSpike'
[ec2-user@ip-10-117-93-66 mocha]$ git rebase master myFeatureSpike
First, rewinding head to replay your work on top of it...
Applying: index.jsにコメント追加。


rebaseしたmyFeatureSpikeをpushしておきます。

[ec2-user@ip-10-117-93-66 mocha]$ git push -f origin myFeatureSpike
Counting objects: 5, done.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 373 bytes, done.
Total 3 (delta 1), reused 0 (delta 0)
To git@github.com:takuto1981/mocha.git
+ d36a3d8...a4ba552 myFeatureSpike -> myFeatureSpike (forced update)


マスターのリポジトリとのマージ用リポジトリを作成します。



[ec2-user@ip-10-117-93-66 mocha]$ git checkout myFeatureSpike
Already on 'myFeatureSpike'
[ec2-user@ip-10-117-93-66 mocha]$ git checkout -b myFeature
Switched to a new branch 'myFeature'
[ec2-user@ip-10-117-93-66 mocha]$ git rebase -i master
Unknown command: ・コメントの追加
Please fix this in the file /home/ec2-user/mocha/.git/rebase-merge/git-rebase-todo.


ここでは、myFeatureをマージ用のリポジトリとします。
ちなみにSpikeは作業用という意味があるらしいです。
作業用リポジトリであるmyFeatureSpikeのブランチを作り、
マージ用リポジトリであるmyFeatureを作る感じですね。


さらに、上記の"git rebase -i master"で、ローカルで何度も行われたcommitを1つにまとめます。
そのままGithubでマージしてしまうと、ローカルで行われたたくさんのcommitが、
マスター・リポジトリの変更履歴にも全て反映されてしまい、煩雑となります。
それを避ける為に、わかりやすいようcommitを1つにまとめるわけですね。


commitした時同様、コメント追加用のエディタが開きます。

pick a4ba552 index.jsにコメント追加。

# Rebase 89af463..a4ba552 onto 89af463
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#


ここでコメントを書かず、代わりにpickをsquashに書き換えます。
するとcommitを1つにまとめられます。
1つにまとめたcommit用にコメントが要求されるので、適切なコメントを書きます。

squash a4ba552 index.jsにコメント追加。

# Rebase 89af463..a4ba552 onto 89af463
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#


では、出来たmyFeatureをGithubにpushします。

[ec2-user@ip-10-117-93-66 mocha]$ git push origin myFeature
Total 0 (delta 0), reused 0 (delta 0)
To git@github.com:takuto1981/mocha.git
* [new branch] myFeature -> myFeature


さて準備出来ました。最後にプロジェクト・オーナーにpull requestします!





出来た!