Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
84
Help us understand the problem. What is going on with this article?
@cushy79

あまり知られていない便利なGitサブコマンド4選

More than 5 years have passed since last update.

この記事について

Livesense(その2) Advent Calendar7日目の記事です。
6日目はI/OスケジューラがMySQLにどの程度影響するのか確認してみると、Simplenote でブックマークレットで GitHub Flavored Markdown (GFM)でした。

はじめに

Git便利ですよね。

触り始めた当初は、git statusgit addgit commitgit logくらいしか使ってませんでした。
いろんなサブコマンドに興味を持ち始めたのは、git rebaseを知ってからだと思います。
CVSやSubversionからGitに移った私としては、git rebase -iにより気軽にコミットの順番を入れ替えたり、メッセージを変更できることに衝撃を受けた記憶があります。

今回は、あまり知られてないけど(個人調べ)、知っておくと何かと役立つサブコマンド(+オプション)をご紹介します。

便利なGitサブコマンド4選

  • git reflog
  • git log -S/-G
  • git alias
  • git name-rev

git reflog

Gitのいわゆる操作履歴を時系列に表示してくれるコマンドです。
操作とはcommitはもちろん、他ブランチのcheckout、rebase、mergeなどを含みます。

とても便利ですが、rebaseやblame、stashなどに比べてあまり広く知られていない気がします。
通常作業時に使うことはほとんどないですが、ミスをしてしまったときに役立ちます。

たとえば、developブランチで開発していて、いくつかコミットしているとします。
極端な例ですが、git reset --hard orgin/developを間違えて叩いてしまい、青ざめてしまったり。
git logで確認しても、無情にもコミットが消えています。

またイチから作るのも面倒臭い、なんとか復旧はできないものでしょうか。
こんなときにgit reflogです。

さっそく叩いてみましょう。

$ git reflog --date=local -n 10
b51fc6a HEAD@{Mon Dec 7 08:29:10 2015}: reset: moving to origin/develop
787dedf HEAD@{Mon Dec 7 08:28:52 2015}: commit: 機能2
b51fc6a HEAD@{Mon Dec 7 08:27:45 2015}: commit: 機能1
5da05c3 HEAD@{Mon Dec 7 08:20:58 2015}: checkout: moving from master to develop

おお、見覚えのあるコミットログが。
戻したい時点のハッシュ値を確認し、resetしてみましょう。

$ git reset --hard 787dedf

これで元どおりです。
つまり履歴上表示されなくてもオブジェクト自体は残っているということですね。
.gitディクトリを覗くと、実際のオブジェクトを確認できます。

# .git/objects/(ハッシュ値の先頭2桁)/(ハッシュ値残り)
$ ls .git/objects/78/7dedf4532fddf57acdde17a615c9c395ed63ed

blob、tree、objectsなどGitの内部的な仕組みはGitの.git/objectsの中身を追ってみるがとてもわかりやすいです。

このコマンドを知ってから、rebaseやmergeなどのGit操作が大胆に行えるようになった気がします。
なにかあれば、任意の時点まで戻せるという安心感からでしょうか。

便利なgit reflogですが、デフォルトの保持期間は90日間のようです。git gcによって削除されます。
この挙動を変えたい場合は、git configでgc.reflogexpireをneverなどに設定することができるようです(未検証)

The optional configuration variable gc.reflogExpire can be set to indicate how long historical entries within each branch’s reflog should remain available in this repository. The setting is expressed as a length of time, for example 90 days or 3 months. It defaults to 90 days.

補足ですが、コミット自体ではなく、一部のファイルだけ戻したいケースもあると思います。
そのときはgit show --stat 787dedfでファイルを調べて、
git checkout 787dedf -- hoge1.rb hoge2.rbとするだけです。

git log

現在のコード(ワークツリー)を対象に検索する場合、git grepが使えます。
では過去のコミットの差分に対して検索する場合はどうすればいいでしょうか。

例えば、hogehogeというメソッドがあるコミットで消されており、それによりメソッドが見つからずエラーが出てしまったとします。

まずは不具合を引き起こしてしまったコミットを特定したいですよね。
そんなときはgit log -S --pickaxe-regexgit log -Gが使えます。

-S --pickaxe-regex

-S単体では文字列検索、--pickaxe-regexをつけると正規表現による検索になります。
さっそく使ってみましょう。

$ git log -S hogehoge -p
commit 93de1b1021c974b9dd5ceee7166698af902cf34c
Author: hoge <hoge@gmail.com>
Date:   Sun Dec 6 14:06:07 2015 +0100
--- a/hoge.rb
+++ b/hoge.rb
@@ -37,9 +37,7 @@ EOT
-      def hogehoge

差分の中から検索できてますね。93de1b1021c97で不具合が紛れ込んでしまったようです。
ノイズが多い場合は-diff-filterや、--sinceと--after指定で期間を絞ってもいいかもしれません。

-G

-Gは-Sとほぼ同様にコミットの差分を検索してくれます。
違いはgit log --helpに記載されています。

+    return !regexec(regexp, two->ptr, 1, &regmatch, 0);
...
-    hit = !regexec(regexp, mf2.ptr, 1, &regmatch, 0);

While git log -G"regexec(regexp" will show this commit, git log -S"regexec(regexp" --pickaxe-regex will not (because the number of occurrences of that string did not change)

-Gは指定した正規表現が差分の中に含まれていたら対象として表示しますが、-Sはそれだけではなく、出現回数の差分も見て判断しています。上記の場合はregexec(regexpという文字列が1つのコミットの中に+と-があり、差し引きゼロのため表示されません。

具体的な例としては、リファクタリングでメソッドを移動したときなどは-Sだと表示されません。
hogehogeというメソッドが消えたコミットを特定したいという今回の目的に対しては、ノイズが減るため-Sの方が適していそうです。

git alias

すいません、現状このようなコマンドはありません。
ただ、Gitにはエイリアスを定義する機能があり、下記のようにgitconfigファイルに設定すると他のサブコマンドと同様に実行することができます。

~/.gitconfig
[alias]
  alias = confg --get-regexp \"alias.*\"
  br = branch
  bra = branch -a -vvv
  co = checkout
  ls = ls-files
  cm commit -m
  tree log -n 15 --graph --pretty='format:%C(yellow)%h%Creset %C(green)%cd%Creset %s %C(cyan)(%an)%Creset %Cred%d%Creset'
  log1 log -n 1
  log3 log -n 3
  ss stash save
  sp stash pop
  sl stash list
  ref = reflog --date=local -n 10

これで、git aliasと叩いた結果はこのようになります。たくさん定義していて、忘れてしまってもこれで一目瞭然ですね。

$ git alias
alias.st status
alias.br branch
‥
$ git co HEAD
$ git ref -n 30 #このようにエイリアスで指定したオプションを上書きもできる

git name-rev

これも影の薄いコマンドですが、役立つ場面が結構あります。
ハッシュ値を渡すとhuman-readableな形式で教えてくれます。

$ git name-rev 1ee87e4
1ee87e4 master~4^2

1ee87e4はmasterブランチから数えて、4つ前のコミットの2つ目の親を表しています。
そのコミットが現状どのような状態か、さっと確認するときに便利ですね。

補足ですが、あるコミットがどのブランチに属しているかはbranchコマンドでわかります。

$ git branch --contains 1ee87e4
* master

さいごに

以上、他のエンジニアと話してて、あまり知られてないと感じたコマンドを紹介しました。

Gitもバージョンがあがるにつれ、ますます便利になってきています。
今回の記事を書くときに知りましたが、git rebase --autostashにより、git pullする際にいちいちgit stashする必要がなくなりました。
2.5では同一Gitリポジトリ内に作業ツリーを追加して作成することのできるgit worktreeが追加され、2.7も絶賛開発中のようです。

これからも、Gitの動向に目が離せそうにありませんね!

84
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
cushy79

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
84
Help us understand the problem. What is going on with this article?