現場で使うGitのテクニック

  • 755
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

お疲れさまです、trebyです。
もうだいぶ日付が変わりそうな勢いですが、Git Advent Calendar 2014の23日目を担当させていただきます。

Gitを業務で使い始めて早2年、だいぶ慣れてきた感じがありますが、それをアウトプットする機会があるかといえばなかなかありません。せいぜいたまに同僚に聞かれるくらいでなんかもったいない感じがあります。

そこで今日は私個人がgitを使って仕事をする上でどういうフローしているかなーということを改めて文字にアウトプットしてみたいと思います。ご参考にしていただくなり、ツッコミしていただくなりしていただけますと幸いです。

なお、本投稿において想定するツールはGit、ホスティングサービスはGitHubですが、多分その他のサービスでもいけるのではないかと思います。

開発準備

「新しくチームに配属された!」等のシチュエーションを想定しています。

開発リソースの入手

あるディレクトリ以下をGitの管理下に置くためのコマンドとしては、git initというものがありますが(今更)、既に開発が始まっているプロジェクトにジョインするような場面でこれを打つことはまずないですよね。

というわけでまずやるべきはgit cloneだと思います。

$ git clone git@github.com:reddit/reddit.git

こうすることで、GitHubのreddit/redditのコードを手元に持ってくることができます。
あ、もちろん各種ホスティングサービスへの公開鍵の登録みたいな作業は完了させておいてくださいね。

リモートリポジトリの整理

開発フローがgit-flowであれ、github-flowであれホスティングサービス上に自分用のリポジトリを持っておくことになるでしょう。開発に参加するならば、GitHub上でリポジトリをforkしますよね。

そこで開発マシン上で自分用のリモートリポジトリに名前をつけてあげます。

$ git remote add self git@github.com:treby/reddit.git

とすれば、先ほどのreddit/redditをforkした(という想定)treby/redditにselfという名前をつけたことになります。

最新のmasterブランチのコードを入手するならmasterブランチ上で、git pull origin masterすれば良いですし、featureブランチを切って、新機能を開発した際はgit push self feature/foobarとして、ホスティングサービス上などからPull Requestを作成することになります。

各リポジトリのイメージ図

なお、余談ですがリモートの名前一覧はgit remote -vで見ることができます。

$ git remote -v
origin  git@github.com:reddit/reddit.git (fetch)
origin  git@github.com:reddit/reddit.git (push)
self    git@github.com:treby/reddit.git (fetch)
self    git@github.com:treby/reddit.git (push)

履歴に残す自分の情報の設定

要するにgit configの事なのですが設定していないとコミットする際にデフォルトで、

  • 名前: マシンに設定された名前
  • メールアドレス: 【ユーザ名】@【ホスト名】

という情報が使われます。(コミット自体ができないかと思いきや、できるのはできるんですね)

$ git commit -m 'Test Commit' 
[master 0acfdcb] Test Commit
 Committer: Hiroaki Ninomiya <treby@yourhost>
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

そこで、コミットのAuthor情報などに使われる自分の情報を設定してあげます。

$ git config --global user.name "treby"
$ git config --global user.email "treby@example.net"

とまあ通常であれば、大抵のケースではこれ一回行えば良いのですが、プロジェクトによってAuthorを変えたいという場合が生じることがあります。一つのマシンで仕事のコードと趣味のコード、両方を書くようなケースですね。

gitがコミットの際に付与する情報はデフォルトでは上記で設定した情報ですが、もちろんリポジトリごとに設定することもできます。やり方は簡単で設定したいプロジェクトのコードがあるディレクトリで先ほどのコマンドから--globalを外せば良いです。

$ git config user.name "Hiroaki Ninomiya"
$ git config user.email "hiroaki.ninomiya@example.com"

ここで設定した情報はプロジェクトルート以下の.git/configに記述されています。

$ tail .git/config 
:
<略>
:
[user]
    name = Hiroaki Ninomiya
    email = hiroaki.ninomiya@example.com

ここまでで一通りの開発準備ができました。

よく使うテクニックあれこれ

次は実際に開発作業を行う上でよく使うコマンドをTips風に紹介します。

ブランチの作成と切り替え

ブランチを切るコマンドとしてはgit branch <ブランチ名>ですが、git checkout-bオプションを使用することが多いです(ブランチを新しく作成して、かつそのブランチに切り替えてくれる)

$ git checkout master
$ git pull origin master           # 開発環境上のコードを最新にする
$ git checkout -b feature/sugoi_kinou

git branch自体はブランチを一覧するための用途に使用することが多いです。

$ git branch
* feature/foobar
  master

git branch--mergedオプションで現在のブランチ(というかHEADというか)にマージされている(含まれている)ブランチの一覧、-dオプションでブランチの削除ができます。
これらのオプションを利用して、開発が進んでブランチが多くなった時などmasterブランチ(最新にしておく)上などで以下のようにして、マージされたブランチを一括で削除したりします。

$ git branch --merged | grep -v '*' | xargs git branch -d
Deleted branch ...
Deleted branch ...

コミット前の準備

コードの修正がひと段落したらコードをステージングエリアにあげましょう。
ステージングエリアという考え方が独特でとっつきにくく感じていらっしゃる方も多いようですが、コミットの前の準備状態くらいの認識で良いのではないかと思います(15日目の記事では「手紙を封筒に入れる」というユニークな表現をされています)。

基本はプロジェクトディレクトリのルートで

$ git add .

と入力すれば、そこまで行った全てのコード修正がステージングエリアにあがります(封筒に詰められます)。

と、全てのコード修正をステージングエリアにあげて、コミットしたい場合はこれで良いのですが、コード修正を行ったけれど一部は別にしたいなぁとか、今はまだいいかなというケースではどうすれば良いのでしょうか。

例えば、私は移り気な性格をしていますので、機能を実装している途中に実装と全く関係のない部分のタイポ修正だとか、インデントの崩れの修正だとかを行ってしまいます。

そこで私は、

$ git add <パス>

で明示的にステージングエリアにあげるファイルを指定したり、

$ git add -p

で、どの部分の変更をステージングエリアにあげるのか指定することも割とあります。(-pオプションは6日目の記事でも紹介されていますね!)

細かな修正は本当に軽微ならば一緒にコミットしちゃうこともありますが、また別の機会にある程度まとまった単位で修正Pull Requestしたほうが行儀が良い気がします。

ちなみにコード修正・追加ではなく、名前の変更やファイル削除をしたい場合はそれぞれ、git mvgit rmを使うと、これらの変更をステージングエリアにあげることができます。

コミットの作り方

ステージングエリアの準備ができたら、次はいよいよコミットです。行った修正を歴史に刻みましょう。

まず、git commitを打つ前に、git diff --stagedgit statusを使ってコミットしようとしている内容を本当にコミットして良いか確認しましょう。.DS_StoreThumb.dbが含まれていると恥ずかしいですよ:)

実際のところ、普段は-mオプションつけて一行でコメントを書くことが多いです。

$ git commit -m 'Redirect to root if user has something bad.'

チケット対応など、修正に対応するリソースがある場合は、あえてオプションをつけずエディタを起動させます。そしてエディタ上で2行目などにチケットへのURLを添付します。

$ git commit

〜エディタ起動〜

Fix for TICKET-XXXXXX
http://internal.example.com/TICKET-XXXXXX

本当に稀ですが、プロジェクトのルート上で、-aオプション(git add .)つけてコミットすることもあります。

$ git commit -am 'Fix typo.'

ちなみにコメントは現在形で書くのが良いらしいです。まあ、日本語/英語、現在形/過去形、自動詞/他動詞などのお作法はそれぞれのプロジェクトの文化によると思いますので、そちらに合わせてください。

コミットの作り直し

ちゃんと正しいかを確認してコミットしたつもりでも、やっぱりミスっていたり、そうでなくても後で関連する場所のtypoを直したくなったりすることはよくあります。そのようなケースでのコミットの作り直し(歴史の刻み直し)方を紹介します。

ちなみにエチケット上、公開されている環境下(チームの共用リポジトリなど)のコミットを書き換えることは厳禁です。事故の元になるので控えましょう。(まあ、そこの判断がまた難しいんですけどね……自分のリモートリポジトリなら良い気がしますが、Pull Request投げている状態でそれされるとーとか、いや、まだコメント付いてないならセーフだろうとかとか)

まずは、素直にゴミファイル削除のコミットや、Fix typoのコミットを作成してみて、それに慣れた段階で参考されてみてください。

git commit --amend

単純に直前のコミットに修正やファイル追加やファイル削除などを行いたい場合は、--amendオプションを使います。

$ git add .
$ git commit --amend

git rebase

機能開発途中のコミットでtypoをしてしまった場合など、対話的rebaseを使います(-iオプション)。
例えば3つ前のコミットで軽微なミスをした場合は以下のように入力します。

$ git rebase -i HEAD~3

するとエディタが起動して、下記のような画面になります。

pick c7b8f45 commit 1                 # 3つ前のコミット
pick e218500 commit 2                 # 2つ前のコミット
pick f408755 commit 3                 # 1つ前のコミット

:
<略>
:

ここで、3つ前のコミットの「pick」の部分を「edit」に変更して保存、エディタを終了するとGitは3つ前のコミット(commit 1)を行った状態で一時停止してくれます。

この状態でミスを修正して、前述したgit commit --amendでコミットをまとめます。すると3つ前のコミット(commit 1)にまとめられる形になります。

修正が完了したら、

$ git rebase --continue

でrebase作業を続行します。

なお、途中で訳がわからなくなった場合などは

$ git rebase --abort

でrebase処理そのものを中断することもできます。

git rebase -iについて、ここではコミット間に変更を加えるeditを紹介しましたが、その他にもコミットを一つにまとめられるsquashなどを利用することができます。

さらに、pickの記述をなくすことでコミットそのものをなかったことにしたり、前後を入れ替えることでコミットの順番を変更することもできます。

また、git rebase自体は作成してからちょっと時間が経っちゃったなというブランチに対して行うことが多いです。

$ git checkout master
$ git pull origin master           # 開発環境上のmasterブランチを最新にする
$ git checkout feature/foobar      # 開発開始から時間が経ってしまったブランチ
$ git rebase master

git reset

実際上、ブランチのつじつま合わせに使っています。

例えば、本来であれば別途featureブランチを作成して行うべきコミットを誤ってmasterブランチに行ってしまった場合などです。以下に例を示します。

$ git checkout master

# 本当であればこの間で git checkout -b feature/foobar とすべきだった

$ edit
$ git add .
$ git commit -m 'foobar.'
$ git branch feature/foobar       # とりあえず今の位置に本来のブランチ名をつける
$ git reset --hard HEAD^          # masterブランチのHEADを一個前に戻す

$ git checkout feature/foobar     # 改めて開発を再開できる

また、マージしようとしたらコンフリクトして失敗した!というような場合はひとまずgit reset --hardします(一旦戻そう、的な)。

$ git reset --hard

まとめ

gitはあくまでツールの一つなので最低限使えればそれで十分だと思います。が、自由自在に使えるとドヤリングできますし、頼られますし、微かな全能感があって楽しいのも確かです。

使い始めはなんだかよくわからなくておっかなびっくりになってしまいがちですが(私の場合、開発環境上でコミットすら怖かったです)、コミットしていてリポジトリ(プロジェクトの.gitディレクトリ)が無事ならばなんとかなります。masterブランチ消してもなんとかなります。

Gitを使って、どんどんHappy Hacking!!していきましょう。

明日はshigemk2さんによる「GitHubEnterpriseを導入してみるにあたって気をつけたいことをいくつか」(既に記事がある……!)です。よろしくお願いします!

この投稿は Git Advent Calendar 201423日目の記事です。