Git のコミットメッセージ

基本的なコマンドと意味

Branch

Git におけるブランチとは、

どういうときに使うかというと、例えば新しい機能をつけるときにオリジナルに手を加えて作業してしまうとプッシュしたときにどんどんmasterブランチが更新されてしまい、機能が完全に実装できていない中途半端なものがmasterとして公開されてしまう問題が発生します。

masterブランチは常にビルドが通り、本番環境として利用可能であるべきです。

なので開発中は例えばdevelopブランチのような開発用のブランチを更新するようにし、実装ができたタイミングでmasterに反映させるのが良いです。

これについてはより良い GitHub のブランチの運用手法がGit-flow って何? (opens new window)で解説されているのでこれを読むと良い気がします。

自分も参考にしようと思いました。

ブランチの作成

git branch <Branch Name>で新たにブランチを作成することができます。

ブランチの切り替え方

作業しているブランチを切り替えるには、git checkout <Branch Name>のコマンドを入力します。

ただし、このコマンドでは存在しないブランチに切り替えることができません。

どんなブランチが存在するかはgit branchでチェックすることができます。

* develop
  master
  production
1
2
3

例えば次のように表示された場合、ローカルにはdevelop, master, productionの三つのレポジトリがあり、現在developで作業していることがわかります。

ブランチを新たに作成して、そのブランチに切り替える場合にはgit checkout -b <Branch Name>を実行すればよいです。

Merge

git mergeは別ブランチの内容を別のブランチに反映させるためのコマンドです。

developブランチで開発していた機能が完成し、それをmasterブランチに反映させたい場合などに使われます。

今回は図のようにdevelopブランチの G という変更を master に反映させたい場合を考えます。

git checkout master
git merge develop
1
2

マージ先に移動してから、マージ元に対してmergeする必要があります。マージの際にはマージコミット(F)と呼ばれる特別なコミットが作成されます。

このときFCの変更もD, Eの変更も含まれています。

Fetch

git fetchはリモートの最新の情報をローカルに反映させるためのコマンドです。

ただし、masterブランチに反映されるのではなくorigin/masterに反映されます。masterorigin/masterが何が違うのかという問題なのですが、大雑把にいえば以下のような構造になっています。

つまり、git fetchをした段階ではまだmasterには反映されていないということになります。

エラーが発生したとき

git fetch後にビルドが通らなくなったなどのエラーが発生した場合には、まだローカルのmasterブランチは更新されていないので、git reset --hard HEADで最後にコミットしたところまでファイルを巻き戻してなかったことにします。

Pull

git pullgit fetchgit mergeを組み合わせたコマンドです。

これを実行するとremote masterの内容が即座にmasterブランチに反映されます。

--rebase オプション

git pullは本来はgit fetch + git mergeなのですがgit mergeは同じファイルを編集していた場合にコンフリクトが発生するという問題があります。

よって、単にmergeするのではなくてmasterブランチでの更新点をローカルレポジトリにくっつけてから反映させたいわけです。こうすればブランチが綺麗なまま残りますし、何よりコンフリクトが発生しにくいです。

なので、同一のブランチで作業している場合はgit pushする前にgit pull --rebaseをしたら作業が減って楽になりやすいということです。

エラーが発生したとき

git pullでエラーが発生するのはコンフリクトが発生した場合だと思うのですが、その場合はまずgit mergeの部分を取り消したいので、

git merge --abort
git reset --hard HEAD
1
2

というように二つのコマンドで対応します。

Rebase

git rebaseは作業が完了したブランチを分岐元のブランチに延長するときに使う機能です。

この図でいうと G の地点でgit rebase masterのコマンドを入力すると、その時点でのmasterブランチの先頭に対して現在のブランチの最も古いブランチがくっつきます。

git checkout develop    # developブランチに移動
git rebase master       # DにCをくっつける
1
2

これでdevelopブランチは図のように一直線(fast-forward)になったので、その変更をmasterブランチに反映させます。

git checkout master
git merge develop
1
2

このときECの変更も含まれています。つまり、マージした場合と内容は変わらないので最終的な統合結果には差がありませんが、リベースのほうがよりスッキリとした歴史になります。

git mergeがブランチの合流であるのに対して、git rebasemasterブランチに現在のブランチを直列につなげる効果を持ちます。別ブランチの作業をmasterに反映させるという点はmergeと同じですが、mergeはコミットメッセージが失われてしまうのに対して、rebaseの場合は個別のコミットメッセージが保存されるという違いがあります。

`git rebase`の恐怖

git rebaseは作業ブランチで実行するためのコマンドです。masterブランチでこれを実行すると他のブランチが全てmasterブランチにくっついてしまいます。

Log

過去の変更を確認します。

$ git log
commit 8bdd9cd163fed7442330d1535f5b4afff29665b1 (HEAD -> master, origin/master, origin/HEAD)
Author: tkgstrator <nasawake.am@gmail.com>
Date:   Tue Jul 13 10:55:13 2021 +0900
    - 記事の追加
commit 2361ec0ed7233a293c49ff1e5c6570ef61129fc3
Merge: 987f0db 02f84c2
Author: tkgstrator <nasawake.am@gmail.com>
Date:   Fri Jul 9 13:27:30 2021 +0900
    Merge pull request #30 from skmtie/master
    - 記事の追加と修正
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

commitの隣に表示されているのがハッシュ値で、これは次に紹介するResetをする際に必要になります。

この画面を閉じるのはEscではなくqですので覚えておくと良いでしょう。

$ git log --oneline
8bdd9cd (HEAD -> master, origin/master, origin/HEAD) - 記事の追加
2361ec0 Merge pull request #30 from skmtie/master
02f84c2 - 記事の修正
a906c40 - 記事の追加
987f0db - 記事の追加
65c7e97 Merge pull request #29 from skmtie/master
1
2
3
4
5
6
7

ログの一覧を見たい場合は--onelineのオプションが利用できます。短いですがこちらのハッシュ値も使えます。

より詳細のログ

git logは現在の状態よりも過去のログしか見ることができません。

$ git reflog
8bdd9cd (HEAD -> master, origin/master, origin/HEAD) HEAD@{0}: commit: - 記事の追加
2361ec0 HEAD@{1}: checkout: moving from assets to master
f1e8a5e (origin/assets, assets) HEAD@{2}: commit: - 透過PNG用のブランチ
2361ec0 HEAD@{3}: checkout: moving from master to assets
2361ec0 HEAD@{4}: pull: Fast-forward
6ed0dd4 HEAD@{5}: commit: - コメントの修正
621b01f HEAD@{6}: commit: - 記事のタグが誤っていた問題を修正
6ad9a72 HEAD@{7}: rebase finished: returning to refs/heads/master
1
2
3
4
5
6
7
8
9

より詳しい情報を見たい場合はgit reflogを利用します。

このコマンドはgit resetを誤って実行した場合に必要になってきます。

Reset

なにかやらかしてしまってそれを取り消したい場合に使います。

8bdd9cd - D # 誤ったコミット
02f84c2 - C
a906c40 - B
987f0db - A
1
2
3
4

誤ったコミットがまだプッシュされていなければローカルでこっそり修正すれば済みます。

ここで、Dのコミットの変更内容が必要かどうかで対応が変わってきます。

  1. Dのコミット内容が必要、コミットをなかったことにしたい
  2. Dのコミット内容は不要、Cの状態に戻したい

Soft

1 の場合がコレに該当します。コミットだけ巻き戻し、ファイルは変更しません。

git reset --soft 02f84c2 # Cのコミットのハッシュ値を入力
1

また、一つ戻すだけであればハッシュ値を使わずに以下のコマンドも利用できます。

git reset --soft HEAD^
1

HEADと入力するのがめんどくさければ代わりに@も使えます。

git reset --soft @^
1

Hard

巻き戻した位置のファイルの状態に変更します。

使い方はSoftの場合と同じですがオプションとして--hardを指定します。

戻しすぎた場合

SoftでもHardの場合でも戻しすぎた場合には未来のコミットは見えないという制約からgit logでは戻したいコミットを確認することができません。

$ git reflog # 戻したいコミットを確認
git reset --soft # ハッシュ値を指定
1
2

その場合はgit reflogで全てのログを確認することで対応できます。

名前・メールアドレス変更

Git にプッシュするときの Committer の名前やメールアドレスを変更しておきましょう。

グローバル

グローバルの設定は各レポジトリの gitconfig に対して何も設定していなかった場合のデフォルト値です。

git config --global -eで設定ファイルがひらくので、

[user]
        name = tkgstrator
        email = nasawake.am@gmail.com
[core]
        ...
1
2
3
4
5

[user]の項目を編集しましょう。またvi ~/.gitconfigでも同様の効果が得られます。

ローカル

各レポジトリに対するユーザ名とメールアドレスを変更したい場合はこちらを利用します。

例えば A というレポジトリには X という名前でプッシュしたいけれど、B というレポジトリにはは Y という名前でプッシュしたい場合にレポジトリごとに設定をわけたいというような場合があるからです。

コマンド自体は簡単で、git conig -eとすればローカルの設定ファイルがひらきます。

例えば、この HP のソースコードを管理しているレポジトリの設定はこんな感じです。

[user]
        name = tkgstrator
        email = nasawake.am@gmail.com
[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
        ignorecase = true
        precomposeunicode = true
[remote "origin"]
        url = git@github.com:tkgstrator/vuepress-blog.git
        fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
        remote = origin
        merge = refs/heads/master
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

コミットの内容を変更

プッシュしていないコミットの変更

コミットはしてしまったが、プッシュはしていない場合にはgit config -eで設定を変更した後に以下のコマンドで変更を取り込むことができます。

git commit --amend
1

これだと Commiter しか変更できないので、Author もついでに変更する場合は以下のコマンドを入力します。

git commit --amend --author="tkgstrator <nasawake.am@gmail.com>"
git rebase --continue
1
2

過去のコミット全てを一括変更

既にプッシュしたコミット全ての Commiter と Author を変更するコマンドは以下の通り。

git filter-branch -f --env-filter "GIT_AUTHOR_NAME='tkgstrator'; GIT_AUTHOR_EMAIL='nasawake.am@gmail.com'; GIT_COMMITTER_NAME='tkgstrator'; GIT_COMMITTER_EMAIL='nasawake.am@gmail.com';" HEAD
1

既にプッシュしたコミットを変更するので、強制上書きできるように、

git push -f
1

--forceオプションを付けるようにしましょう。

参考文献

Git の Commit Author と Commiter を変更する (opens new window)

フォーク元から最新のデータを取得

GitHub でレポジトリをフォークし、それを Clone した場合にはgit pullをしても自身のレポジトリから最も新しいコミットを取得してきてしまいます。

フォーク元がガンガン開発を進めている場合、フォーク元の最新コミットを取得したいというケースがあります。

git remote add upstream <GitHub Repository>
1

その場合はレポジトリに対してフォーク元のレポジトリをupstreamとして設定してあげればよいです。

こうすれば普段の Push は自分のフォークしたレポジトリに対して行われますが、最新の内容をフォーク元から取得する場合には、

git fetch upstream
git merge upsttream/master
1
2

で行なうことができるようになります。もちろんmasterブランチ以外を反映させたい場合は適時コマンドの内容を変更してください。

参考文献

GitHub でフォーク元の差分を取り込む (opens new window)

役立つコマンド集

最新の内容を反映

作業ブランチでいろいろ作業していたものの、その間にmasterブランチも進んでいたのでその差分を取り込みたい場合があります。

git checkout master
git pull origin master
1
2

まずはこのコマンドでリモートのmasterブランチの内容を取得し、その内容をローカルのmasterブランチに反映させます。

これで、ローカルのmasterはリモートのmasterと全く同一のものになりました。

git checkout develop
git rebase master
1
2

次にmasterブランチの後ろにdevelopがくるように変更します。こうすることで最後のmasterの変更までを取り込んだ上でこれまでのdevelopの作業がくっつくことになります。

結果として、現在の作業ブランチに最新のmasterの内容が反映されたことになります。

指定したコミットの内容を反映

別ブランチの指定したコミットの内容を反映させます。

今回はdevelopAの内容をdevelopBに反映させたいと思います。

git checkout developA   # ブランチを移動
git log                 # コミットのハッシュをチェック
1
2

まず、取り込みたい内容のハッシュをチェックします。

git checkout developB   # ブランチを移動
git cherry-pick <HASH>  # ハッシュを指定して取り込み
1
2