NHN Cloud NHN Cloud Meetup!

GitHub環境での実戦Gitレシピ


Gitでソースを管理し、GitHubでコードレビューを行っている方も多いでしょう。
しかし、Gitを使って開発してみると、何かをミスしたり、予想に反する動作をすることが多いようです。

この記事では、実際に使用されているGit環境をサンプルに、よく発生する状況で使用できるGitコマンドを紹介したいと思います。
レシピのため、深く説明したり、Gitの各オプションについては記載していません。

サンプルに用いるリポジトリリンクは任意に作成したものです。
そのまま使用できないので、参考程度にして自分の環境に合わせて使い換えてください。

ローカル環境の構築


[図1] GitHubのリポジトリ相互関係
[著作] GitHub
[画像ソース] https://github.com/blog/2042-git-2-5-including-multiple-worktrees-and-triangular-workflows

[図1]は、GitHubのユーザーがリポジトリを使用するシナリオを簡単に表した関係図です。
upstreamはメインリポジトリで、originはメインリポジトリを自分のアカウントにforkしたリポジトリです。
forkはGitHubのforkボタンを使って簡単に実行できます。forkされたリポジトリをローカルにcloneしてみよう。
以下のコマンドで使用したリンクのようなリポジトリリンクは、個人のGitHubページからコピーして取得できます。

$ git clone https://github.com/junsik-shim/some-project.git

some-projectという名前のフォルダが作成され、プロジェクトが取得できましたね。
そのフォルダに入ってgit remoteを実行すると、自分のfork repositoryがoriginにすでに設定されていることが分かります。メインリポジトリはupstreamという名前で設定する必要があります。
メインリポジトリURLもGitHubプロジェクトページからコピーできます。

$ git remote add upstream https://github.com/project-space/some-project.git

もう一度git remoteを実行すると、下記のように設定されます。

$ git remote
origin
upstream

これで環境構築は完了です。

メインルーチン

問題が発生したとき、これを処理するためpullで受け取り、ソースを修正、コミット、pushして、pr(pull request)を飛ばすところまで、必要なプロセスを説明します。

upstreamから最新ソースを受け取る

マージの競合を避けるためにも、ローカルのソースは常に最新の状態にしておくべきでしょう。
ここでは最新の開発内容をブランチ戦略に基づいてdevelopブランチに置いていると仮定しましょう。
よって次のようにdevelopブランチにアクセスして、最新の開発内容を取得します。
コミットの履歴管理のため、通常はgit pullよりもgit pull –rebaseを使用することをお勧めします。

$ git pull --rebase upstream develop

新しいブランチを作成する

Git Flow戦略により、既存のdevelopブランチから新しい開発課題に対応するfeatureブランチを作って作業している場合、これにより、従来よく作動する開発コードと新たに変更される開発コードを分離して、それぞれ保存ができます。
featureブランチはGit Flow戦略でいう単位開発ブランチを普遍的に指す用語で、ブランチに名前を付ける際、featureという名前を必ずしも使う必要はありません。

新規開発のため、新しいfeatureブランチを作成します。
課題番号が「qa/123」の場合、以下のように実行します。

$ git checkout -b qa/123
Switched to a new branch 'qa/123'

変更したファイルを追加し、コミットする

シェルからコマンドを使って変更したファイルを追加してコミットするのは煩わしいことでしょう。このような作業は、IDEやGit Clientで行うことをお勧めします。

コミットをfork repositoryに上げる

$ git push origin qa/123
To https://github.com/junsik-shim/some-project.git
 * [new branch]       qa/123 -> qa/123

メインリポジトリにpull requestを飛ばす

本人のfork repositoryに行き、compare&pull requestボタンを押します。
こうすると、メインリポジトリにコードレビューのprを飛ばすことができます。

起こり得るさまざまな状況

さまざまな状況に対する解決方法を紹介します。(もちろん唯一の解決法ではありません)

ソースを変更していたら、新しいブランチではなく、developで作業していた!

まだコミットしていない状態なので、そのまま新しいブランチを作成すると、修正している部分も一緒に移動します。

$ git checkout -b new-branch

ソースを変更してコミットもしたが、新しいブランチではなく、developで作業をしていた!

現在の状態で新しいブランチを作成し、developをコミットする前の状態に戻します。

$ git checkout -b new-branch
$ git checkout develop
$ git reset --hard HEAD~1

コミットが複数あれば、HEADブランチの後に1ではなく、コミット数の数字だけ入れるとよいでしょう。

新しいブランチを作成するとき、developにコードレビューが終わっていないコミットがあった!

これは既存のジョブを別途ブランチにせず、作業内容をdevelopにコミットしてprを飛ばしたとき、発生することがあります。既存のジョブのコードレビューが終わっていない状態で、新たな課題を処理する新しいブランチをdevelopに作成すると、コードレビューを受けていない既存のジョブのコミットがついてきます。

このような場合は、当該コミットが生成される前の状態で新しいブランチを作成します。

$ git checkout -b new-branch HEAD~1

このコマンドでも同様に、HEADブランチの後、1の代わりに戻るべきコミット数を入れます。

何かを実行したら、おかしくなってしまった!

pullを間違えたかリバートを間違えたか…それとも原因が分からない場合は、resetしてみよう。
まずHEADブランチの変更履歴を確認するためreflogコマンドを使用します。

$ git reflog
544ebb9 HEAD@{0}: pull upstream monkey3: Fast-forward
e94fc27 HEAD@{1}: checkout: moving from monkey3 to test
e94fc27 HEAD@{2}: checkout: moving from analyticsqa/246 to monkey3
f19873 HEAD@{3}: commit: チャートローディング追加 (analyticsqa/246)
e94fc27 HEAD@{4}: checkout: moving from monkey3 to analyticsqa/246

HEADブランチの履歴から作業履歴と、その間にどれが間違っているか確認できます。
最初のpullをキャンセルしたい場合は、その下にあるHEAD@{1}に移動します。

$ git reset --hard HEAD@{1}

これも同様に、1の代わりに戻るべき目的地を入れます。

メインリポジトリにpushしてから、何かおかしくなったことに気づいた!

すでにpublicで行ったコミットはresetをしてはいけません。
このような場合は、コミットをリバートして、もう一度pushする必要があります。

まずgit logを行い、リバートしたいコミットのハッシュ(例: 028c6298eb025ff8e5ccfbba399501a7e8e50af8)をコピーした後、次のように実行します。

$ git revert 028c6298eb025ff8e5ccfbba399501a7e8e50af8

その後、コミットメッセージを変更できるウィンドウが開き、終了するとリバートが行われます。

マージコミットをリバートしたい!

マージコミットをリバートするにはコマンドの-mフラグを追加すればよいでしょう。
-mの後ろに親番号を入れる必要があり、通常は親の開始番号である1を入れます。
この親番号はブランチのマージを実行するときに、マージを実行したベースブランチがどちらだったかにより異なります。

$ git revert -m 1 028c6298eb025ff8e5ccfbba399501a7e8e50af8

ねじれたローカルブランチを整理して、originにある同じブランチを被せたい!

強制的に被せたい場合は、-fを貼るとよいでしょう。
メインリポジトリには絶対にしてはいけません。通常はすでにできなくなっており、詰まっているでしょう…

$ git push -f origin some-branch

ローカルにだけ必要なファイルがpushされてしまった!

IDEの設定ファイルのように、ローカルにのみ存在すべきファイルがメインリポジトリまで誤って入ってしまうことがあります。この場合、Gitは消去し、ローカルのファイルは消去してはいけません。

$ git rm --cached some-filename

コミットしたコミットメッセージを変更したい!

コミットメッセージを変更したいときは、commitコマンドに引数としてamendを使います。

$ git commit --amend

このコマンドは厳密には、既存のコミットを修正するのではなく、新しいコミットを作成します。
すでにそのコミットがpushされている状況であれば、他人の履歴を複雑にしてしまうので、なるべく避けた方がよいでしょう。

ブランチの名前を変えたい!

下記のようにします。

$ git branch -m old-name new-name

似たようなコミットをまとめたい!

同じテーマで作業したコミットが複数ある場合、整然とした履歴管理を行うためコミットをsquashするのが望ましいでしょう。

squashは実際に存在するコマンドではなく、git rebase機能のinteractive rebaseのときに使用するオプションです。

git logコマンドを使って、次のような作業内容を確認しました。

$ git log --oneline
971e62d a 修正
fc8812c a 修正
a1b2ae7 a 修正
16cfc94 Merge pull request #249 from changuk-lee/renewal_monkey3
3d19554 Merge remote-tracking branch 'origin/renewal_monkey3' into renewal_monkey3
...

上記3つの「a 修正」コミットを1つにまとめてみよう。これはinteractive rebaseと呼ばれます。

$ git rebase -i HEAD~3

テキストエディタが実行され、各コミットに対して必要な作業ができます。

pick 971e62d a 修正
pick fc8812c a 修正
pick a1b2ae7 a 修正
...

2番目と3番目のラインのpickをs(squash)に変えてみよう。

pick 971e62d a 修正
s fc8812c a 修正
s a1b2ae7 a 修正
...

保存して終了したらコミットメッセージを変更する画面が表示されます。

# This is a combination of 3 commits.
# The first commit's message is:
a 修正

# This is the 2nd commit message:

a 修正

# This is the 3rd commit message:

a 修正

希望するコミットメッセージを入れて保存/終了し、git logを確認すると、3つのコミットが1つに統合されたことがわかります。

Squashしたいのに順序がバラバラになっている!

コミット順序が次のようなとき、2つの「a 修正」コミットを統合したいならどうすればよいでしょうか?

$ git log --oneline
ae5ba65 a 修正
3f30dc1 b 修正
b341e53 a 修正
...

通常のsquashと同じですが、コミットの順番を入れ替える必要があります。

pick b341e53 a 修正
pick 3f30dc1 b 修正
pick ae5ba65 a 修正

から、

pick b341e53 a 修正
s ae5ba65 a 修正
pick 3f30dc1 b 修正

このように変えるとよいでしょう。

NHN Cloud Meetup 編集部

NHN Cloudの技術ナレッジやお得なイベント情報を発信していきます
pagetop