862
325

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

お前らのコミットは汚い

Last updated at Posted at 2020-02-23

お前らのXXXXは<ネガティブな形容詞>シリーズ で失礼します。

日頃gitをお使いの皆様におかれましては、キレイなコミットを心がけていらっしゃいますでしょうか。

私も心がけてはいますが、なかなか難しいものがあります。
参考までにこちら、最近業務で書いたプルリクエストのコミットログです。

スクリーンショット_2020-02-22_18_57_29.png

控えめに言って汚いと思われたかと思います。
ではキレイなコミットの例を。

スクリーンショット_2020-02-22_19_01_38.png

プルリクエストというのは、やはり先達の方に見ていただいてご指摘いただこうというものですから、
当然コミットハッシュもゾロ目等でキレイにするというのがマナーです。

では今回はこのキレイなコミットをどうやって作るのか、という話を書きます

(ショート)コミットハッシュ

コミットハッシュとは、gitのコミットごとに生成される、40桁の[0-9a-f]からなる文字列です。

お手元のリポジトリ上で git log --format=%H を叩くとコミットハッシュの一覧が出力されるかとおもいます。
これの頭7桁をとったものがショートコミットハッシュで、先程の画像の右側に表示されていたものです。
git log --format=%h で(ほぼ)ショートコミットハッシュ取得することができます(し、単にコミットハッシュの8文字以降を見なかったことにしても同一のものが得られます)。

ではコミットハッシュはどのように決定されるのかというと、
詳しくは

あたりをご参照いただいて、

簡単に説明すると、
「ディレクトリ構造」と「親コミット(≒直前のコミット)」と「Author」と「Committer」と「コミットメッセージ」によって決定されます。

これらのうちどれか1つでも変更があれば、コミットハッシュは再計算されます。

キレイなコミットハッシュを作る

gitはコミット後であっても過去のコミットについて情報を変更することができるのはご周知の通りです(git commit --amendgit filter-branch 等で歴史を改ざんした経験があるかと思います)。

ということは、コミットした後、ハッシュがキレイでなければ、情報を変更し、またハッシュを再確認し、キレイでなければ.....と続けることで最終的にはキレイなコミットハッシュを持ったコミットを生成することが可能なはずです。

狙ったコミットハッシュを生成するには、このループをおよそ 16 ^ 40 回繰り返せば達成されるはずですが、ちょっとだけ現実的ではないのと、
そもそもそういう衝突が起きないように設計されているのがコミットハッシュですからこれは諦める必要があります。

参考:
How hard is it to get a git hash collision?

天下のGoogle様でも3年前にようやくハッシュが衝突する2つのファイルを生成できるようになった程度です。
狙ったハッシュを生成するのはさらに困難でしょう。

ショートコミットハッシュならどうでしょうか。16^7(≒ 2.7億) 程度であれば総当たりでなんとかなりそうな気がします。

そこで、なんとかしてくれるツールを作りました。

commit_artist

Rustで書いたCLIっぽいものです。

使い方

# cargo を持ってない人はこちらから https://www.rust-lang.org/tools/install
$ cargo install commit_artist

$ cd <直近のコミットを改ざんしたいリポジトリのディレクトリ>

$ git log -1 --format=%H
86637c3f206d228df1dc1dafa49d31b159b8a358

$ commit_artist -p 1234567
173015040 hashes calculated...
Yay! Now your new hash of the latest commit is 12345672abd92a159f3886e08951f29ee7ce0041.

$ git log -1 --format=%H
12345672abd92a159f3886e08951f29ee7ce0041

yay!

ちなみに、 git cat-file (コミットオブジェクトを覗き見るコマンド) をするとこんなかんじです

$ git cat-file -p 12345672abd92a159f3886e08951f29ee7ce0041
tree 839ac38ae8ee7604922680774468473c33162b5c
parent eeeeeeea55ebeec4794897154720d08812304a1c
author rnitta <attinyes@gmail.com> 1582426518 +0900
committer d6dc479254c0faa95cd8ee438a6b6611ce62b1c8 <attinyes@gmail.com> 1582426518 +0900

update comments and did something

commiterの名前が怪しい文字列になっています。

仕組み

仕組みとしては、
外部コマンド git を叩いて最近のcommitに関する種々の情報を取得し、
コミッターの名前を変更して新たにコミットハッシュを再計算し、
これをキレイなハッシュが見つかるまで繰り返す、というものです。

コミッターの名前を変更している理由は、

「ディレクトリ構造」と「親コミット(≒直前のコミット)」と「Author」と「Committer」と「コミットメッセージ」によって決定されます。

この中で改ざんしやすいものはAuthorとCommiterとコミットメッセージで、
Authorを改ざんするのはよろしくないし、
コミットメッセージは人の目に触れやすいので改ざんはよろしくないし、
Committerの名前かメールアドレスなら改ざんしてもいいだろうと。

architecture.png

アーキテクチャを説明した風の図がこちらです。

マルチスレッドにしているのが工夫ポイントかなと思います。
こういった終わらなければ無限にタスクを積み続けるようなプログラムだと、ワークスティーリングなランタイムを使ったasync/awaitが相性良いかなとは思ったんですが、async/awaitなーんもわからん状態に陥ったので任意のサイズのイテレーションを回す感じのマルチスレッドで実装してみました。

Issue, PR, Star! お待ちしております。
https://github.com/rnitta/commit_artist

CLI

書いたプログラムをCLIにするにあたって、
(--path とか --jobs みたいなオプションを受け取るために)
Rust製のCLIフレームワークである、 Seahorse を使っています。
ロゴがかっちょいいでお馴染みのSeahorseです。まあ僕がロゴ(アイコン)作ったんですけど。

d3e77500-51a0-11ea-845e-3cc87714278b.png

ミニマルですし、外部クレートに依存していないし、コード量も多くないのでサクッと読めます。
https://github.com/ksk001100/seahorse
こちらもぜひ

免責

Committer nameを書き換えますし、コミットハッシュを意図的に頭数文字固定してしまおうというevilみがあるプログラムなので、業務で使って怒られてもしりません。
あと最悪リポジトリ壊れてもしりません。

862
325
12

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
  3. You can use dark theme
What you can do with signing up
862
325

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?