Upgrade to Pro — share decks privately, control downloads, hide ads and more …

開発効率をあげるgitテクニック / Useful git

3f6af00e6779114f20bda021a28313c9?s=47 buty4649
August 27, 2018

開発効率をあげるgitテクニック / Useful git

2018/08/27にペパボ社内で行われたシェル大活用講座の発表資料です

3f6af00e6779114f20bda021a28313c9?s=128

buty4649

August 27, 2018
Tweet

Transcript

  1. 開発効率をあげるgitテクニック 〜シェル芸もあるよ〜

  2. 高谷雄貴 @buty4649 シニアエンジニア 技術部 プラットフォームグループ

  3. git

  4. • 普段からgitを使っている ◦ GitHub / GH:E ◦ GitHub Workflow •

    gitコマンドを便利にして生産性↑↑ 今日のテーマ
  5. • gitってなに???って話はしません ◦ ググるか誰かに聞いてください • bashを使うことを前提で書いている部分があります。 ◦ zsh/fish/bsh/ksh/csh/tcsh/ash/dash な人は適宜読み替えてください おことわり

  6. 目次 1. 減らす 2. 便利にする

  7. PRを作る場合のオペレーション例 普段どんなオペレーションをしますか? $ git clone git@github.com/foo/bar $ cd bar $

    git checkout -b new-branch <ファイルを変更する> $ git status $ git add foobar $ git commit $ git push origin new-branch <ブラウザを開いてPRを作る> 1.減らす
  8. PRを作る場合のオペレーション例 普段どんなオペレーションをしますか? $ git clone git@github.com/foo/bar $ cd bar $

    git checkout -b new-branch <ファイルを変更する> $ git status $ git add foobar $ git commit $ git push origin new-branch <ブラウザを開いてPRを作る> 打鍵数が多い! 84文字! (下線部のみ) 1.減らす
  9. 減らす

  10. alias g=git

  11. 1.減らす • シェルのエイリアス機能 ◦ コマンドに別名を割り当てる • g で gitコマンドが実行される alias

    g=git
  12. 1. 減らす • 引数はそのまま処理される ◦ g status は git status

    として実行される • 引数付きのコマンドもエイリアスにできる ◦ alias gs=’git status’ とするとgsでgit statusが実行される • エイリアスを消したい場合は unalias コマンドを使う ◦ unalias g ◦ 一時的に無効にしたい場合はコマンド名の頭に \ をつける • エイリアスはカレントシェルのみに反映される ◦ 永続化したい場合は ~/.bash_profile へ エイリアス機能の補足
  13. 84文字 → 74文字!! エイリアスによって削減 $ g clone git@github.com/foo/bar $ cd

    bar $ g checkout -b new-branch <ファイルを変更する> $ g status $ g add foobar $ g commit $ g push origin new-branch <ブラウザを開いてPRを作る> 1.減らす
  14. まだ減らす

  15. git config --global alias.st status

  16. 1.減らす • “git” のエイリアス機能 ◦ サブコマンドをにエイリアスをつけられる • よく使うサブコマンドを短い文字にエリアスする ◦ st

    → status, a → add など • 設定された内容は ~/.gitconfig に保存される ◦ ~ は $HOME と同じ意味 ◦ ファイルに設定を書いても良い(即時反映される) git config --global alias.st status $ cat ~/.gitconfig [alias] st = status
  17. 1.減らす gitサブコマンドのエイリアス例 $ cat ~/.gitconfig [alias] a = add cl

    = clone cm = commit co = checkout cob = checkout -b p = push st = status
  18. 84文字 → 50文字!! エイリアスによって削減 $ g cl git@github.com/foo/bar $ cd

    bar $ g cob new-branch <ファイルを変更する> $ g st $ g a foobar $ g cm $ g p origin new-branch <ブラウザを開いてPRを作る> 1.減らす
  19. 1.減らす • よく使うコマンドを短くすると効果的 • 引数を含めよく使うコマンドもエイリアスにする ◦ サブコマンド+オプションにするとよいと思う ◦ 例: git

    checkout -b → git cob • シェルのエイリアスとの使い分け ◦ シェルのエイリアスにした方がサブコマンドのスペースが減らせる ◦ 例: alias gcob=’git checkout -b’ ◦ シェルの補完が効かないしコマンド名がかぶる可能性 ◦ 好みの問題かも エイリアス作成のコツ(私のやり方)
  20. 1. 減らす 私の設定例 [alias] br = branch cm = commit

    -v cma = commit -v --amend cman = commit -v --amend --no-edit co = checkout cob = checkout -b d = diff dc = diff --cached g = grep 詳しくは https://github.com/buty4649/dotfiles/blob/master/cookbooks/configfiles/files/.gitconfig
  21. もっと減らす

  22. もっと減らす $ g cl git@github.com/foo/bar $ cd bar $ g

    cob new-branch <ファイルを変更する> $ g st $ g a foobar $ g cm $ g p origin new-branch <ブラウザを開いてPRを作る> 1.減らす めんどくさい!
  23. 1.減らす • いちいちファイル名を書くのが手間 ◦ git add . でもいいけど、一部だけaddしたい場合不便 • 極力ファイル名を打ちたくない!

    ◦ 浅い階層ならいいが深い階層だとその分タイプ数が増える ファイル名を書くのがめんどくさい! どうしたらファイル名を打たずにgit addできるか?
  24. 1.減らす • git addのあとにaddしたいファイル名を羅列する ◦ git add <file1> <file2> …

    • このコマンドを生成できればよい • 変更がかかったファイルはgit statusで取れる • つまり↑をいい感じにしてgit addするスクリプトを書けばよい! どうしたらファイル名を打たずにgit addできるか?
  25. 1.減らす • 生のgit statusの結果はパースしづらいのでオプションをつける スクリプトを作る $ git status --short MM

    file1 ワークツリーの状態 インデックスの状態 M: 変更された A:追加された D:削除された U:更新されたがマージされていない(コンフリクトなど) ※ 詳しくはgit status --helpを参照
  26. 1.減らす • 出力結果をフィルタする必要がある ◦ Dなファイルをgit addに渡すとエラーになる ◦ addしたいファイルのみを選択したい ▪ コマンドラインセレクターを使うと便利!

    ▪ 例えば peco (詳しくはこのあとのudzuraさんの発表で) • スクリプトをどこに書くか? ◦ gitのエイリアスはスクリプトが書ける ◦ !を先頭に書くとスクリプトとして解釈される ◦ 例: v = !vim → git v でvimが起動する スクリプトを作る
  27. 1.減らす 完成したコマンド [alias] a = !"git status --short | awk

    '!/^[ADRM] /' | peco | awk '{print $NF}' | xargs -r git add" git status --short | awk '!/^[ADRM] /' | peco | awk '{print $NF}' | xargs -r git add 先頭がADRMを除外 ファイルを選択 ファイル名のみ切り出し git addを組み立て
  28. 1.減らす 解説(awk) $ git status --short D file1 M file2

    $ git status --short | awk ‘!/[ADRM] /’ M file2 $ git status --short | awk ‘!/[ADRM] /’ | > awk ‘{print $NF}’ file2
  29. 1.減らす 解説(xargs) • 標準入力をコマンドの引数に追加し実行する ◦ 例: echo b | xargs

    echo a → echo a b が実行される • forやwhileでも同じことができる ◦ 例: date | xargs echo → date | while read LINE;do echo $LINE;done ◦ サブシェルを作らない分xargsのほうが早い • -r オプションをつけると入力が空の場合実行しない ◦ macOSのxargsにはないかも… • 通常は末尾に引数を追加するが-Iで任意の場所に追加できる ◦ -Iに続いて置換する文字列を書く ◦ 2文字以上でも使える ◦ 例: date | xargs -I{} echo {} hogehoge • これ以外にもすごく便利なのだけどそれを書くには十分な余白がry
  30. 1.減らす これでpushはgit gpushでいけるようになる git push origin new-branchも短くする [alias] gpush =

    !"git rev-parse --abbrev-ref HEAD | xargs git push origin" $ git rev-parse --abbrev-ref HEAD new-branch
  31. 1.減らす これでpushはgit gpushでいけるようになる git push origin new-branchも短くする [alias] gpush =

    !"git rev-parse --abbrev-ref HEAD | xargs git push origin" $ git rev-parse --abbrev-ref HEAD new-branch
  32. 1.減らす • git push origin HEAD でいける ◦ この資料を作っているときに知った。。 •

    つまり、↑をエイリアスすればよい git push origin new-branchも短くする [alias] gpush = push orogin HEAD
  33. 1.減らす 私の設定例 # ブランチを選択してcheckout c = !"git branch | awk

    '!/^\\*/' | peco | xargs -r git checkout" # リモートブランチを選択してcheckout cobr = !"git branch -r | grep -vE '/(HEAD|master$)' | sed -e 's,origin/,,g' | peco | xargs -r -I{} git checkout -b {} origin/{}" # 変更されたファイルを選択してcheckout cof = !"git status --short | peco | awk '{print $2}' | xargs -r git checkout --" ※ \ はエスケープする必要がある
  34. 84文字 → 29文字!! 65%効率アップ!!!! 最終結果 $ g cl git@github.com/foo/bar $

    cd bar $ g cob new-branch <ファイルを変更する> $ g st $ g a $ g cm $ g gpush <ブラウザを開いてPRを作る> 1.減らす
  35. 目次 1. 減らす 2. 便利にする

  36. • 減らすではタイプ数を減らすことに注力した • 次はタイプ数削減以外の方法で効率化する • 便利なツールや機能を使ってより作業効率をあげる 2.便利にする

  37. 2.便利にする git-completion.bash & git-prompt.sh • gitをインストールするとついてくる便利スクリプト • bashの補完機能を提供 ◦ (brew)

    /usr/local/etc/bash_completion.d/git-completion.bash ◦ sourceで読み込む • プロンプトにカレントブランチを表示 ◦ (brew) /usr/local/etc/bash_completion.d/git-prompt.sh ◦ PS1環境変数に __git_ps1関数を追加する $ git branch 20180827-pepabo-college ~/path/to/hoge ( 20180827-pepabo-college ) $ echo $PS1 \w$(__git_ps1 " (\[\e[0;31m\]%s\[\e[00m\])")\n\$
  38. 2.便利にする ghq • ローカルリポジトリの管理ツール ◦ https://github.com/motemen/ghq • 指定のディレクトリ配下にgit cloneする ◦

    デフォルトは ~/.ghq ◦ GOPATHと合わせておくと便利 ▪ git config --global ghq.root ~/src • 使い方 ◦ リモートリポジトリのクローン ▪ ghq get <リモートブランチのURL> ◦ ローカルリポジトリのフルパスを一覧で表示 ▪ ghq list -p
  39. 2.便利にする ghq • ghq list と pecoを組み合わせて移動できる ◦ cd $(ghq

    list -p | peco) • これをコマンドにする ◦ 外部コマンドではcdできないので関数で定義する ◦ 例: gcd() { cd $(ghq list -p | peco); }
  40. 2.便利にする tig • TUIなgitクライアント ◦ https://github.com/jonas/tig • 詳しくはこのあとのjune29の発表で!!! • 私の使い方

    ◦ git ll にエイリアスしている ◦ fixup / squash できるようにしている $ cat ~/.gitconfig [tig "bind"] diff = F ?!git commit --fixup %(commit) diff = S ?!git commit --squash %(commit) main = F ?!git commit --fixup %(commit) main = S ?!git commit --squash %(commit)
  41. 2.便利にする hub • Github社が作ったgitコマンドラッパー ◦ https://github.com/github/hub • gitにエイリアスして使う ◦ eval

    $(hub alias -s) • gitに機能を追加する ◦ issueやPRの作成ができる ◦ 詳しくは https://hub.github.com/hub.1.html • ローカルリポジトリでhub browseすると便利 ◦ GH:Eなリポジトリを見るときは設定が必要 ◦ git config --global hub.host <GHEのFQDN>
  42. 2.便利にする git hookを使う • 特定のアクションの実行前/後にスクリプトを実行する ◦ 詳しくは https://git-scm.com/docs/githooks • hookスクリプトは

    .git/hooks 配下にある ◦ 例えばcommit前にhookするなら ▪ .git/hooks/pre-commit ▪ chmod +x するのを忘れない(重要)
  43. 2.便利にする hook利用例(1): プッシュ前にlintを行う • プッシュした後にCIのlintチェックで落ちると悲しい… • git pushする前にlintすれば回避できる! $ cat

    .git/hooks/pre-push #!/bin/bash rake lint • しかし、プッシュに時間がかかるようになるというデメリット
  44. 2.便利にする hook利用例(2): master pushを防ぐ • master pushやりがち • Github側で設定変更するという手段もある ◦

    すべてのリポジトリに設定するのは手間 • hookを使って防ぐ
  45. 2.便利にする hook利用例(2): master pushを防ぐ https://github.com/buty4649/dotfiles/blob/master/cookbooks/configfiles/files/.git_template/hooks/pre-push $ cat .git/hooks/pre-push #!/bin/bash CURRENT_BRANCH="$(git

    rev-parse --abbrev-ref HEAD)" if [ "$CURRENT_BRANCH" = "master" ];then if [ -z "$ALLOW_GIT_MASTER_PUSH" -a \ -z "$(git config --get git.allow-master-push)" ];then echo "WARN: It's the master branch !!" echo 'If you want git push, please set either.' echo '* $ALLOW_GIT_MASTER_PUSH=1' echo '* git config --local git.allow-master-push 1' exit 1 fi fi
  46. 2.便利にする hook利用例(2): master pushを防ぐ $ git branch master $ git

    push WARN: It's the master branch !! If you want git push, please set either. * $ALLOW_GIT_MASTER_PUSH=1 * git config --local git.allow-master-push 1 error: failed to push some refs to 'foobar'
  47. 2.便利にする hook利用例(2): master pushを防ぐ • master pushしなくなって悲しみが減った • ただ、master pushでもいいときに回避するのが手間

    ◦ プロンプトをだしてyを入力したらpushでもいいかも • hookにスクリプトを直接書くと変更するときに面倒 ◦ hookからはコマンドを呼び出すだけにしたほうが良いかも • git init / git cloneの度にhookを配置するのが手間 ◦ git-templateを使うと便利 ◦ https://git-template.readthedocs.io/en/latest/ • すでにあるリポジトリに設置したい ◦ ghq list -p | xargs -L1 -I{} cp -pv pre-push "{}/.git/hooks" ◦ (自信なし)
  48. まとめ

  49. まとめ • gitコマンドのタイプ数を減らすと効率があがる • シェルスクリプトを駆使してサブコマンドを作ると便利だし楽しい • 便利なツールを使うとより生産性があがる