こんにちは、バックエンドコースのShunsukeです。Advent Calendar 9日目ということでGit初心者に向けてGitで開発体験を向上させるためのTipsを、様々なユースケースごとに紹介したいと思います。
対象読者
本記事の対象読者はGitをコマンドラインで使ったことがあり、 add
, commit
, push
, pull
などの基本的な操作ができる人を想定しています。
Case 1. 別ブランチで開発をしている間に main
ブランチが進んでしまったが、ログをきれいに残したい。
これは普段の開発でもよく出てくるシチュエーションだと思います。新しく切ったブランチ(これを仮に feature-A
とします)で作業をしている間に main
に変更が取り込まれてしまった場合、それをただ git merge main
などとしてはマージによってログが一部ごちゃごちゃになってしまうことがあります。
こんな時は''もし作業ブランチをリモートに push
する前であれば''Gitの rebase
の機能を利用することができます。これにより実際にどのようなことが起こるのかを下の図に示します。
ここでただ単に merge
してしまうと、
こうなります。しかしここで git rebase main
とすると 、
こうなります。上と見比べると rebase
したときの方がログがきれいに直列に並んでいることが分かると思います。
ただしこの機能の利用には注意点があります。それは上で「''もし作業ブランチを リモートに push
する前であれば'' 」と注釈をつけた通り、すでにリモートに push
したブランチに対して行ってはいけないということです。ここで rebase
したときの図を見るとD, EのコミットがそれぞれD', E' と少し変わっています。これは rebase
を行うとコミットのIDとでも呼ぶべきもの(ハッシュ)が変化してしまうことを表しています。そのため、すでにリモートに push
したものに対して rebase
をしてしまうとリモートとコミットIDが一致せず、衝突が起きてしまいます。よって、既にリモートに push
してしまったブランチに関してはおとなしく merge
を行った方がいいでしょう。
今回は簡単のために rebase
コマンドを紹介しましたが、実際に main
ブランチの変更を取り込むときには pull
コマンドに --rebase
のオプションがあり同様の動作をするため、こちらを使用するといいでしょう。 ( git pull --rebase origin main
など)
またコンフリクトが起こることもありますが、その場合にはコンフリクトを解消した後に git rebase --continue
とすれば大丈夫です。もしコンフリクトによって rebase
が止まり、その rebase
を中止したいときには git rebase --abort
で中止することができます。
Case 2. すでにコミットしてしまったが、ソースコードの小さなミスやコミットメッセージをなおしたい。
Gitで作業していると、すでにコミットはしてしまったがソースコードに小さなミスやタイポを見つけてしまったり、コミットメッセージを間違えてしまったりしていることに気づくときがあると思います。ただ、新しくコミットをつくるには小さすぎて無駄なコミットを増やしてしまいます。
このようなときに使えるのは git commit
の --amend
オプションです。これは直前のコミットを修正することができます。コミットメッセージのみ修正したい場合にはそのまま、ソースコードを変更する場合には通常のコミットを作成するようにソースコードを add
でインデックスに追加してから、 git commit --amend
を実行します。すると直前のコミットメッセージの編集画面が現れ、コミットメッセージを修正ののち保存してエディタを閉じると、直前のコミットメッセージを修正することができます。コミットメッセージを修正する必要が無い場合にはそのまま保存してエディタを閉じれば大丈夫です。
ただしこの場合にもCase 1の時のように、すでに push
したコミットに対してこれを行ってしまうとリモートと競合状態になってしまうため、注意が必要です。
Case 3. 途中まで変更したファイルを残したままブランチを移って作業したい。
これも普段の開発でよく出てくるシチュエーションだと思います。Gitで作業していると、途中でブランチを移って作業したいのだけれども変更があるためにブランチを移動することができない、ただ変更は残しておきたいしコミットするには中途半端すぎるという場面が出てくるでしょう。このような時には stash
という機能を利用することができます。この stash
はワーキングディレクトリとインデックスの内容を記録し、変更前のクリーンな状態に戻してくれます。
では実際に使用するコマンドの例を説明します。今 feature-A
ブランチでコミット前の作業の状態があり、 feature-B
ブランチに移って作業したいとします。こんな時には、
$ git status
On branch feature-A
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: README.md
no changes added to commit (use "git add" and/or "git commit -a")
$ git stash
Saved working directory and index state WIP on feature-A: 3cfe4a2 initial commit
# ワーキングディレクトリとindexがクリーンな状態に戻る
$ git checkout feature-B
# 何かしらの作業
$ git checkout feature-A
$ git stash pop
On branch feature-A
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: README.md
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (31ea34970820eb8f9c4ace4c896c6cba0400c571)
# 元の状態に戻る
とすると別ブランチで作業後、元の状態を復旧することができます。ちなみに git stash list
とすることで stash
したものの一覧を見ることができます。また stash
したものを分かりやすくするためには git stash save -m "<message>"
としてメッセージを付与するといいでしょう。
Case 4. 別ブランチからあるコミットの変更だけ取り込みたい。
これは、例えばDjangoで開発をしていてオートマイグレーションの機能を使用しているときなどに、別ブランチでモデルの変更とマイグレーションファイルのみ取り込みたいという場合などが考えられます。このような時にはGitの cherry-pick
という機能を利用することができます。使い方は至って簡単で、
-
git status --oneline
などとして取り込みたいコミットのハッシュ値を得る。 -
git cherry-pick <ハッシュ値>
として変更を取り込む。
以上です。
ちなみに cherry-pick
というのは「いいとこ取り」を意味するようです。
まとめ
今回はGitの便利だけどあまり使われていなさそうな機能である rebase
, --amend
, stash
, cherry-pick
についてユースケースに基づいて簡単に紹介しました。ただ利用には様々な注意点もあったりするので、詳しくはリファレンスを参照してください。またGitにはこの他にも便利な機能がたくさんあるので、興味を持った方は下の参考リンクを参照してみてください。
Gitを使いこなして快適な開発体験を実現しましょう。
参考リンク
- Git SCM Book : https://git-scm.com/book/ja/v2
- Git SCM Reference : https://git-scm.com/docs