Edited at

新入社員による新入社員のためのGit

More than 3 years have passed since last update.

今年の4月インターンから晴れて株式会社ユニキャストに入社して、Gitを研修でやったので、勉強がてらまとめてみました。

以前こんな記事を書きましたがもうちょっと詳しく書いてます。


1. Gitの基本

Gitとは分散型のバージョン管理システムです。Gitを使うことで、ファイルの状態を好きな時に更新履歴として保存しておくことができます。そのため、一度編集したファイルを過去の状態に戻したり、編集箇所の差分を表示することができます。


1-1. バージョン管理システム

バージョン管理システムとは、ファイルに対して「いつ」「誰が」「何を変更したか」といった情報を記録することで、過去のある時点の状態を復元したり変更内容の差分を表示できるようにするシステムのことです。バージョン管理システムは大きく「集中型バージョン管理システム」と「分散型バージョン管理システム」に分けることが出来ます。


1-2. 集中型バージョン管理システムと分散型バージョン管理システム


集中型バージョン管理システム

集中型(クライアント・サーバ型)のバージョン管理システムでは1つのリポジトリを使用します。リポジトリとはファイルそのものや変更履歴などを保存しておく場所です。そのため、複数メンバで開発する場合には、1つのリポジトリを複数人で共有して使用します。ソフトウェア開発に参加するメンバーは、中央リポジトリ(プロジェクトメンバー間で共有するリポジトリ)からソースコードを持ってきて編集し、編集が終わったら中央リポジトリに直接反映します。

e4abaa74-bc5a-9ecf-df15-1f51f71dd446.png


分散型バージョン管理システム(Git)

分散型バージョン管理システムではリポジトリを複数持つことが出来、開発の形態や規模に合わせてバージョン管理ができます。リポジトリを複数用意できるので「分散型」と呼ばれています。Gitで管理する場合、リモートリポジトリをサーバ上に置き、開発者それぞれがローカル環境にローカルリポジトリを持つという構成で使われています。この場合、普段はローカルリポジトリで作業を進めていき、ある程度作業をした時点でリモートリポジトリに反映するという流れとなります。そのためチームで開発をする場合、1つのリポジトリを使いまわす集中型よりも分散型バージョン管理システムは非常に効果的なツールとなります。

2c1fd172-caa7-6341-892a-6c50eae9201c.png


1-3. リポジトリ

上で取り上げたようにGitではローカルリポジトリとリモートリポジトリを使用します。

リポジトリ種類
概要

ローカルリポジトリ
ローカル環境(自分のマシン)に配置し、自分の作業履歴をバージョン管理するリポジトリです。

リモートリポジトリ
専用のサーバに配置し、複数人で共有するリポジトリです。

各々は、普段ローカルリポジトリでバージョン管理を行い、自分のローカルリポジトリで作業した内容を公開したい時は、リモートリポジトリにアップロードして公開します。また、リモートリポジトリを通してほかの人の作業内容を取得することもできます。


1-4. コミット

ファイルの追加や変更をリポジトリに追加するにはコミットと呼ばれる操作をする必要があります。この操作を行うと、前回のコミットから現在の状態の差分を記録したコミットと呼ばれるものが作成されます。

このコミットは、時系列順につながった状態でリポジトリに格納されています。

コミットをする際に必ずコミットメッセージが求められます。これは、変更内容の要約等を記入するものです。Gitでは標準的に以下のような形式でメッセージを書きます。

1行目 : コミットでの変更内容の要約

2行目 : 空行
3行目以降 : 変更した理由

各コミットにはユーザ名や日時、一意のIDが付与されます。


1-5. ワークツリーとインデックス

ワークツリーとは実際に自分のマシンで作業しているディレクトリのことを指し、インデックスとは、リポジトリとワークツリーの間に存在するものです。

Gitでは、コミットを実行した時にワークツリーから直接リポジトリ内に状態を記録するのでなく、その間に設けられているインデックスの設定された状態を記録するようになっています。

そのため、一度インデックス(ステージングエリア)にファイルを登録してからリポジトリに登録します。逆にリポジトリに上げたくないファイルはインデックスに登録しなければリポジトリにはアップロードされません。なので、必要なものだけを選んでバージョン管理を行うことができます。

656f14b0-8072-19c7-2baf-398b8d22c783.png

※ Git初心者の人がよく分からなくなる1つがワークツリーやインデックス、ローカルリポジトリやリモートリポジトリの違いだと思いますので、しっかり理解してから進むことをおすすめします。


2. バージョン管理の流れ(基本)


2-1. リポジトリの作成

バージョン管理を行うには、まずリポジトリを作成する必要があります。git_trainingフォルダをGitの管理下にするには、そのディレクトリに移動してinitコマンドを使用します。

cd git_training

git init


2-2. ローカルリポジトリにコミット

ファイルの追加や変更を行ったらaddコマンドを使用してバージョン管理を行う、ファイル群をステージングエリア(インデックス)に登録します。

ワークツリー => ステージング


追加方法

git add <filename>

git add . #サブディレクトリを含めた全てのファイルを登録
git add -f . #.gitignoreに登録されているファイルを含めて登録
git add -u #リポジトリに登録されていて変更されたファイルのみ登録
git add -A #ワークツリーの変更と新しく作成されたファイルをリポジトリに登録


追跡を解除

git reset HEAD -- <file> # addしたファイルを解除する

git rm --cached <filename> # addしたファイルを解除する


確認方法

git add -n . # 実際には追加せずに追加されるファイルを調べる

git status # 変更されたファイルの一覧を調べる
git diff HEAD # 現在のワークツリーが最後のコミットからどのように編集されたかを調べる


2-3. リモートリポジトリにプッシュ

ローカルリポジトリにcommitをしたら次に、これまでの変更履歴をローカルリポジトリからリモートリポジトリにpushします。pushするとローカルリポジトリの内容がアップロードされリモートリポジトリがローカルリポジトリと同じ状態になります。

pushに入る前にリモートリポジトリに関する操作を少し解説します。


リモートリポジトリの操作


リモートリポジトリをクローンする

既に稼働しているプロジェクトに途中参戦するときは通常git cloneすることからはじまります。urlにはとってきたいgithubやgitlabのプロジェクトのURLが入ります。プロジェクトをとってきたい場所に移動して以下のコマンドを使用してください。

git clone <url>


リモートリポジトリを追加する

ローカルリポジトリに対してリモートリポジトリを追加します。にはoriginを入れてください。

git remote add <name> <url>


リモートリポジトリの内容を変更する

git remote set-url <name> <newurl> #<name>で登録済みのリモートリポジトリのURLを変更する

git remote rename <old> <new> #<olt>で登録済みのリモートリポジトリのURLを変更する


リモートリポジトリにpushする

git push <repository> <refspec>

=> 例:git push origin master

repositoryにはリモートリポジトリのURLや名前が入ります。refspecにはブランチ名等が指定できます。

git push --delete <repository> <branchname> #リモートリポジトリのブランチを削除する


2-4. リモートリポジトリからpullする

複数人で開発している時、他の人がpushしたリモートリポジトリの内容を自分のローカルリポジトリに取り込む場合にはpullコマンドを使用します。pullを実行すると、リモートリポジトリから最新の変更履歴をダウンロードしてきて、自分のローカルリポジトリにその内容を取り込みます。

リモートリポジトリの変更内容を確認する

git fetch <repository> <refspec>

リモートリポジトリの変更内容を取り込む

git pull <repository> <refspec>


2-5. ブランチを切り替える


ブランチとは

ブランチは並行して行われる複数の機能追加やバージョン管理を支援するためのものであり、履歴の流れを分岐して記録していくものです。分岐したブランチは他のブランチの影響を受けないため、同リポジトリ内で複数の変更を同時に進めていくことができます。

プロジェクトから枝分かれさせることをよく「ブランチを切る」といいます。

また分岐したブランチは他のブランチを統合することができ、複数人でブランチを切って開発した内容を1つにまとめることが出来ます。


ブランチの切り替え

ブランチの操作

git branch <branchname> #ブランチを作成する

git branch -m <oldbranch> <newbranch> #ブランチ名を変更する
git branch -d <branchname> #ブランチを削除する

ブランチ一覧と自分の今いるブランチを確認する

git branch

ブランチを切り替える

git checkout <branchname>

通常の流れは以下の通りです。

git branch # 今いるブランチを確認

git branch <branchname> # ブランチを作成
git checkout <branchname> # ブランチを切る


ブランチの運用

ブランチは自由に作成出来ますが、複数人で開発する場合、Gitを効果的に運用するためにあらかじめ運用のルールを定めておく必要があります。ここでは統合ブランチとトピックブランチという二種類のブランチを使った運用方法について紹介します。

ブランチ
概要

統合ブランチ
リリース版が何時でも作成可能なようしておくためのブランチ、安定版を統合するブランチです。

トピックブランチ
機能追加やバグ修正といったある課題に関する作業を行うために作成するブランチです。

そして詳しい運用方法はこちらを読みましょう!


2-6. ブランチをマージする

作業が終わったブランチは最終的には統合ブランチに統合する流れとなります。ブランチを統合するのはrebaseによる方法とmergeによる方法があります。


mergeによるマージ

今自分のいるブランチがbugfixブランチだったとし、bugfixブランチをmasterブランチにマージするとします。

masterブランチの履歴がbugfixブランチを分岐した時より進んでいた場合、両方の変更を取り込んだmergeコミットが作成されます。

masterブランチがC, bugfixブランチがYのときにmasterブランチからbugfixブランチマージしようと、両方の変更を取り込んだDコミットが作成されます。Dにはmasterとbugfix両方の変更が取り込まれています。

git checkout master

git merge bugfix

bdacb4f8-6139-d763-0589-410cb09adde0.png


rebaseによるマージ

rebaseによってマージを行うとマージするブランチの後にマージされるブランチのコミット履歴が付け替えられブランチが一本化されます。

b5ce083b-4491-232f-9cfe-51995ad93759.png

git rebase bugfix


2-7. 衝突を解決する

mergeした時やpullしたときに競合が発生する場合があります。その場合、競合のあった箇所には、Gitが差分を挿入しています。その場合、手動でワークツリー上でその部分を修正して改めてコミットします。

git add .

git commit


Gitを使いこなす


3-1. タグ


タグとは

Gitにおいてタグとはコミットを参照しやすくするために、わかりやすい名前を付けるものです。タグには軽量タグと注釈タグの2種類が存在します。一度付けたタグは移動ができないため、タグの移動が必要な場合は、そのタグを削除してから改めて作り直します。

タグ
概要

軽量タグ
名前のみを付けられる。

注釈タグ
名前、コメント、署名を付けられる。

タグの一般的な使い方は、リリースタグには注釈付きタグを使ってコメントや署名を追加します。 軽量タグはローカルで一時的に使用する使い捨てなどに使用します。


コマンド


軽量タグ

現在のHEADが指しているコミットに軽量タグをつける

git tag <tabname> #HEADに軽量タグをつける


注釈タグ

現在のHEADが指しているコミットに注釈タグをつける

git tag -a <tagname> #HEADに注釈タグをつける

上のコマンドを実行するとエディタが起動するのでそこにコメントを記述します。

エディタを起動せずにコメントを書くには以下のようにします。

git tag -am "コメントです" <tagname> #HEADに注釈タグをつける


タグを削除

git tag -d <tagname> #タグを削除する。


タグを調べる

git tag #タグ一覧を調べる

git log --decorate #タグ情報を含めて履歴を調べる
git show <tagname> #タグのついた情報を調べる


3-2. 過去のバージョンの戻る

Gitの大きなメリットの1つが過去のバージョンに戻って作業することができることです。この機能のお陰で私達開発者は何も恐れることなくリファクタリングできたり、機能の書き換えをすることができます。

過去のバージョンを戻るには reset コマンドを使用します。

git reset --hard <コミットID>

git reset --hard HEAD #1つ前のコミットをかき消す
git reset --hard HEAD^ #2つ前のコミットをかき消す

このようにすることで以前のコミットに戻ることができます。

またGitでは前回取り消された内容がORIG_HEADというものに1つだけ格納されています。間違えてresetしたなどの場合は、ORIG_HEADにresetするとreset前の状態に戻すことができます。

git reset --hard ORIG_HEAD #前の状態に戻す


resetコマンド

resetコマンドは便利ですがしっかりと使い方を覚えて使わないといけなそうなので、ここで一度resetコマンドについて詳しく見ていきます。

まずresetコマンドは結局何をしているのかですが、resetはHEADの位置を変更するコマンドであり、オプションによってはステージングやワークツリーも変更することができます。HEADの位置を変更できるということはresetを使うとコミットを取り消すことができるということです。

reset にはいくつかオプションがあり、それによって影響範囲を指定することができます。

オプション
概要

--hard
コミットを取り消した上でステージング、ワークツリーの内容も書き換える。

--mixed
コミットを取り消した上でステージングの内容も書き換える。

--soft
ワークディレクトリの内容はそのままでコミットだけを取り消す。

※HEAD: 今いるブランチの最新のコミット


3-3. 作業の一時退避

作業途中で別の修正や機能追加をする必要がでてきて、別のブランチに切り替えて作業をする必要があるとき、現在の変更を一時的に退避しておくことのできる機能がGitには備わっています。それを実現するのが stash コマンドです。

早速使い方です。

まずは、まだcommitしていない状態の変更ファイル(addしてる or add していない)が存在する状況で、次のコマンドを実行すると変更ファイルを退避することができます。

git stash save #変更を退避する

変更ファイルが退避できたかどうかは git status などで確認できます。

stashで退避できるのは1つとは限らず、複数退避できます。

退避した作業の一覧を確認

git stash list #退避作業一覧を調べる

git stash list -p #変更内容も含めて調べる

退避した履歴を取り出す

git stash apply stash@{0} #退避履歴を取り出す

stash apply で変更を復活した場合は、stashリストのなかに復活済みの変更が残されているこれを削除する

git stash drop stash@{0} #退避作業を削除する

上記の取り出しと削除を一気に行う

git stash pop stash@{0} #退避作業を取り出し、削除する

退避した作業をすべて削除する

git stash clear #退避作業をすべて削除する

使用する流れとしては、今いるブランチで作業を一時退避(stash save)してbranchを切り替えます(checkout)。そこで作業をして作業が終わったらコミットして(commit)、また最初のブランチに戻ってきて(checkout)退避していた作業を戻します。(stash pop)


3-4. コミットを書き換える


直前のコミットを修正する

1つ前のコミットを修正するには、commitに--amendオプションを追加します。

git commit --amend #1つ前のコミットを修正する

そうすると、直前のコミットのコミットメッセージがエディタで表示されます。「addとcommitの説明を追加」に変更して保存・終了してください。


過去のコミットを打ち消す

revert コマンドを使うと過去のコミットを打ち消すことができます。revertは正確には、commit を無かったことにするのではなく、対象の commit の変更を相殺するような差分commitを自動で生成するというものです。resetはコミット自体なかったことにするのに対してrevertはなかったことにはならずに相殺するコミットを作成します。

git revert HEAD #1つ前のコミットを打ち消す


過去のコミットをなかったことにする

これは先ほど書いたresetを使用しておこないます。


コミットを抜き取る

cherry-pick コマンドを使うと他のブランチのある特定のコミットのみを抜き取って、取り込むことができます。

git cherry-pick <commit ID> #コミットを取り込む


コミットをまとめる

複数のコミットを1つにまとめるには rebase -i を使用します。

git rebase -i HEAD^ #HEADからHEAD^までのコミットをまとめる


ブランチ上のコミットを一つにまとめてマージする

git checkout develop

git merge --squash issue1 #ブランチのコミットを1つにまとめてマージする


4. git-flow

git-flowはGitの運用ガイドラインの1つです。

A successful Git branching modelといったモデルを採用することにより、Gitによるバージョン管理の見通しがよくなります。

弊社では、これをベースとして、git運用方針が定められています。http://www.slideshare.net/UnicastInc/git-v09


4-1. 基本ブランチ


masterブランチ

masterブランチは常に安定稼働するリリース可能なソフトウェアある必要があります。一定の機能がdevelopブランチにマージされたときに、developブランチの内容をmasterブランチにマージします。masterブランチのコミットにはタグ付けをしてバージョンを表します。


developブランチ

開発中主にメインとなるのがdevelopブランチです。各トピックブランチの起点となり、マージ先になります。開発者はdevelopブランチを起点にしてトピックブランチを作成し、開発が完了したらdevelopブランチにマージします。

6682da2a-d9d9-02d6-9f00-88b67f4f11aa.png


4-2. サポートブランチ


featureブランチ

実際に機能を開発するブランチで、developブランチより先に作成されます。機能の開発が完了したらdevelopブランチにマージし、featureブランチは削除します。

マージのときfast-forwardマージを行わないことが必要です。これにより、developブランチには実装が完了したトピックブランチのコミットのみが作成されることになり見通しがよくなります。

git merge --no-ff function1

30422b8b-dffc-5e04-2914-05613a63d820.png


releaseブランチ

releaseブランチは、新しくリリースするための準備をするブランチです。developブランチより作成し、releaseブランチで作成した内容はdevelop, masterブランチにマージします。


hotfixブランチ

hotfixブランチは、リリース後の不足の事態や、重度のバグ対策に用いられるブランチです。masterブランチより作成し、hotfixブランチで対応した内容はdevelp, masterブランチにマージします。

ed321c12-73c7-64d5-7d2d-79d5ad7fa4fb.png


.gitignore

.gitignore とは名前からも分かるように git で無視するファイルを指定するファイルです。

.gitignoreで設定できるパターン

パターン
概要

*~
ファイル名の最後に ~ がある全てのファイル 例) index.html~ 等

*.[ao]
拡張子が a 又は o のファイル 例) hello.a foo.o

t/
t ディレクトリは全て無視されます

v
ファイル名が v の場合無視されるが v がディレクトリ名の場合は無視されません

!*.t
拡張子 t を持つファイルは無視されません


5-1. WordPress

*.log

.htaccess
sitemap.xml
sitemap.xml.gz
wp-config.php
wp-content/advanced-cache.php
wp-content/backup-db/
wp-content/backups/
wp-content/blogs.dir/
wp-content/cache/
wp-content/upgrade/
wp-content/uploads/
wp-content/wp-cache-config.php


5-2. Ruby on Rails

*.rbc

capybara-*.html
.rspec
/log
/tmp
/db/*.sqlite3
/db/*.sqlite3-journal
/public/system
/coverage/
/spec/tmp
**.orig
rerun.txt
pickle-email-*.html

# TODO Comment out these rules if you are OK with secrets being uploaded to the repo
config/initializers/secret_token.rb
config/secrets.yml

## Environment normalisation:
/.bundle
/vendor/bundle

# these should all be checked in to normalise the environment:
# Gemfile.lock, .ruby-version, .ruby-gemset

# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
.rvmrc

# if using bower-rails ignore default bower_components path bower.json files
/vendor/assets/bower_components
*.bowerrc
bower.json

# Ignore pow environment settings
.powenv


Git用語集

名前
概要

HEAD
現在チェックアウトしているブランチの最新コミットを指定する代名詞

HEAD^
1つ前のコミット

HEAD~{n}
n個前のコミット

FETCH_HEAD
最後に取得したリモートブランチの最新コミットを指定する代名詞

ORIG_HEAD
前のHEADの値を指定する代名詞

MERGE_HEAD
マージ中に生成され,ブランチにマージするコミットが記録されている代名詞


Gitコマンド集


init

リポジトリを作成する

git init

git init --bare #共有リポジトリの作成


add

ワークツリーからインデックスに追加する

git add <filename>

git add . #サブディレクトリを含めた全てのファイルを登録
git add -f . #.gitignoreに登録されているファイルを含めて登録
git add -u #リポジトリに登録されていて変更されたファイルのみ登録
git add -A #ワークツリーの変更と新しく作成されたファイルをリポジトリに登録


commit

git commit #コミットする

git commit --amend #1つ前のコミットを修正する


reset

git reset HEAD -- <file> # addしたファイルを解除する

git reset --hard HEAD #1つ前のコミットをかき消す(ワークツリーとステージングも)
git reset --mixed HEAD #1つ前のコミットをかき消す(ステージングも)
git reset --soft HEAD #1つ前のコミットをかき消す


rm

git rm --cached <filename> # addしたファイルを解除する


status

git status # 変更されたファイルの一覧を確認する


diff

git diff #変更されたファイルの一覧を確認する

# オプションなしの場合ワークツリーとステージングのdiffをとる
# HEAD やコミットを指定すると、ワークツリーと指定した HEAD との差分を表示する


log

コミットログを確認する

git log #コミット履歴を確認する

git log --oneline #コンパクトに確認する
git log -p #diffも見る
git log --stat #どのファイルがどれくらい変わったかを確認する
git log --decorate #タグ情報を含めて履歴を調べる


clean

追跡対象でないファイルを削除する

git clean -n #リポジトリで管理されていないファイルを調べる

git clean -n *.txt #拡張子が.txtのものを追跡から外す


remote

git remote # リモートリポジトリの一覧を表示する

git remote add <name> <url> # リモートリポジトリを追加する
git remote set-url <name> <newurl> #<name>で登録済みのリモートリポジトリのURLを変更する
git remote rename <old> <new> #<olt>で登録済みのリモートリポジトリのURLを変更する


push

git push <repository> <refspec> #リモートリポジトリのブランチにpushする

git push --delete <repository> <refspec> #リモートリポジトリのブランチを削除する


fetch

git fetch <repository> <refspec> # リモートリポジトリに変更内容を確認する


pull

git pull <repository> <refspec> # リモートリポジトリの変更内容を取り込む


branch

git branch <branchname> #ブランチを作成する

git branch -m <oldbranch> <newbranch> #ブランチ名を変更する
git branch -d <branchname> #ブランチを削除する
git branch # ブランチ一覧を確認する


checkout

git checkout <branchname> #ブランチを切り替える

git checkout -b <branchname> #ブランチを作成して切り替える
git checkout <commit> <file> # <file> が、指定した <commit> に含まれそのファイルの完全なコピーとなり、さらにそれをステージングエリアに追加
git checkout <commit> #指定したコミットと同一の状態に更新する


tag

git tag <tabname> #HEADに軽量タグをつける

git tag -a <tagname> #HEADに注釈タグをつける
git tag -d <tagname> #タグを削除する。
git tag #タグ一覧を調べる


stash

git stash save #変更を退避する

git stash list #退避作業一覧を調べる
git stash list -p #変更内容も含めて調べる
git stash apply stash@{0} #退避履歴を取り出す
git stash drop stash@{0} #退避作業を削除する
git stash pop stash@{0} #退避作業を取り出し、削除する
git stash clear #退避作業をすべて削除する


revert

git revert HEAD #1つ前のコミットを打ち消すコミットを自動生成する


cherry-pick

git cherry-pick <commit ID> #コミットを取り込む


merge

git merge <brachname> #ブランチをマージする

git merge --squash issue1 #ブランチのコミットを1つにまとめてマージする

素敵なGit Lifeを!