search
LoginSignup
1
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

Git Advent Calendar 2020 Day 14

posted at

updated at

Gitで軽率に歴史改変するためのエイリアス powered by fzf

歴史は一方通行ではない

今更言うまでもありませんが、Gitは便利ですよね。
Gitの基本的な使い方は歴史を重ね・分岐し・まとめていくことですが、歴史を逆行し・書き換え・削除したくなる場合もあります。
そんなときに使えるエイリアス(というかサブコマンド)をいくつか作ったので解説します。
みなさんにも軽率に歴史改変していただけると幸いです。

なお、実際に使用している.gitconfig等は以下のリポジトリで管理しています。

kawarimidoll/dotfiles

dependencies

本記事で紹介するエイリアスでは、git以外に下記のコマンドを使用します。

  • xargs
  • grep
  • head
  • fzf

fzfは改行区切りのテキストからfuzzy findできるCUIアプリです。本当に便利なので知らない方は使ってみてください。

fzf is a general-purpose command-line fuzzy finder.
junegunn/fzf

その他のコマンドは普通の環境には入っていると思いますが、xargsのバージョンが古い場合、以下で使用しているオプションが対応していない可能性があります。

直前のコミットの修正

歴史をひとつ戻る git uncommit

.gitconfig
[alias]
  uncommit = reset --soft HEAD^

直前のコミットをなかったことにし、変更をステージングエリアへ戻します。
ウンコミットではない

歴史を修正する git amend

.gitconfig
[alias]
  amend = commit --amend

現在ステージングされている変更を直前のコミットにまとめる形で再度コミットします。
コミットメッセージの修正もできます。

2つ以上前のコミットの修正

歴史を思い出す git remember

.gitconfig
[alias]
  remember = __remember
git-__remember
#!/bin/bash

git log --graph --color=always --format="%C(auto)%h%d %C(black bold)%cr %C(auto)%s" "$@" | \
  fzf --ansi --exit-0 --no-sort --no-multi --tiebreak=index --height=100% \
  --preview="grep -o '[a-f0-9]\{7\}' <<< {} | head -1 | xargs --no-run-if-empty git show --color=always" \
  --header="Ctrl-y to toggle preview, Ctrl-u to preview down, Ctrl-i to preview up" \
  --bind="ctrl-y:toggle-preview,ctrl-u:preview-down,ctrl-i:preview-up" \
  --preview-window=down:60% | grep -oE '[a-f0-9]{7}' | head -1

git-remember.gif

コミットグラフからfzfでコミットハッシュを取り出すコマンドです1。変更内容を見つつコミットを選択できます。
fzf公式サンプルのfshowを改変しました。
.gitconfig内に書くこともできますが、長い上にエスケープが面倒なのでファイルを分けています。
これ自体は歴史改変操作を行いませんが、引数にコミットハッシュを取るコマンドを簡単に利用するために必要なので、歴史改変の要です。

Gitはgit-xxxという名前の実行可能ファイルをPATHの通ったディレクトリに置くことでサブコマンドを追加することができます。今回のもので言うと、git-rememberという実行可能ファイルを上記の内容で作成し、PATHの通ったディレクトリに配置すれば、自動的にgit rememberというコマンドが使えるようになります。
ただ、私は自分の追加したコマンド群を.gitconfig内で一覧したいため、git-__rememberというファイルを追加し、さらに.gitconfigにてエイリアスを定義して呼び出すという形をとっています。

本質的には以下のワンライナーです。調整していくうちに、プレビューや色付けなどのオプションが増えて長くなりました。

git log --oneline | fzf --no-multi | grep -oE '[a-f0-9]{7}' | head -1

歴史を巻き戻す git rewind

.gitconfig
[alias]
  rewind = !git remember | xargs --no-run-if-empty git reset --soft

最初に示したgit uncommitの増強版です。2つ以上前の段階へ戻りたいとき、git uncommitを何度も打つよりはこちらを使ったほうが楽です。
前出のgit rememberでコミットを選択し、そこまで一気に戻り、以降の作業をなかったこと2にします。

歴史を書き換える git allohistory

.gitconfig
[alias]
  allohistory = !git remember | xargs --no-run-if-empty --open-tty git rebase -i

git rebase -iをカジュアルに使うためのコマンドです。
前出のgit rewindと同様に過去のコミットまで戻りますが、こちらは戻ったコミット以降の歴史を壊さないため、過去の1点のみを修正するということが出来ます。
ここではrebaseの使い方の説明は省略しますが、修正の他にも、歴史の削除や整理が可能です。

未来へ帰る git btf

.gitconfig
[alias]
  btf = rebase --continue

前出のgit allohistoryで過去に戻った後、1.21ジゴワット使って 未来へ帰るためのコマンドです。
git deloreanでも良かったかもしれません。
なお、未来へ帰ってくる途中の歴史の流れによってはコンフリクトが発生します。

歴史を整理する git fixup

.gitconfig
[alias]
  fixup = !git remember | xargs --no-run-if-empty -I_ git commit --fixup _ && git rebase -i --autosquash

git allohistoryなら柔軟に歴史を編集できますが、過去のコミットひとつに変更を加えたいときにはこちらのほうが便利です。
選択したコミットへ現在ステージングされている変更をねじ込みます。
こちらも変更内容によりコンフリクトが発生し得ます。

さあ歴史を変えよう

簡単に歴史を移動できるようになると、自分がどの段階にいるのかわからなくなるので、簡単に確認できるツールがあるといいと思います。私はstarshipを使っています。

starship/starship

歴史改変コマンドは用法・用量を守って正しくお使いください。


  1. タグが正規表現にマッチする場合があるため、head -1を入れています 

  2. --softを指定しているため、選択したコミット以降の変更はステージングエリアに残りますが、--hardにすれば本当にすべて消し去ることができます 

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
1
Help us understand the problem. What are the problem?