git に関する投稿を表示しています

git で用済みのブランチをまるっと消す

普段の開発はもちろん、確認用!作業用!などとやったり放っておくと増えていくブランチを一掃する。

基本的には master をチェックアウトしてからやったほうがいい。
別のブランチから見たときに、あるブランチがマージされていないように見えることがあるので、基本的には master ブランチをチェックアウトしてからやったほうがよさそう。
それも組み込めないかなあ、、シェルスクリプトだなー

ローカル用

git branch --merged | grep -v '*' | grep -v 'master' | xargs git branch -D

リモート用

git branch -a --merged | grep remotes | grep -v HEAD | grep -v master | sed -e "s/remotes\/origin\///" | xargs git push --delete origin

git の差分で何かをした(今回は phpunit をしたい)

テストをもりもり追加したけど、それだけいい感じに動かしたいなあってやつ。

キモはこのコマンド。

git diff --name-only 比較元ブランチ 比較対象ブランチ

--name-only をつけると変更のあったファイル名だけが出力される。

ちなみにブランチと書いたものの、コミットが示せればいい。
HEAD とか HEAD~2 とか。
あるいは abc123 みたいにハッシュとか。

そしたらこいつをパイプしながらゴニョゴニョすればおけ。

 

ここ最近、手元で使ってみてるのはこれ。

$ git diff --name-only HEAD $(git branch -a | peco | tr -d '*' | tr -d ' ') | grep -i "test.php" | xargs -I '{}' composer test '{}' | grep ')'
> phpunit --testsuite unit 'tests/Hoge/HogeTest.php'
.                                                                   1 / 1 (100%)
OK (1 test, 7 assertions)
> phpunit --testsuite unit 'tests/Hoge/HugaTest.php'
.......S.S...S.                                                   15 / 15 (100%)
> phpunit --testsuite unit 'tests/Hoge/FooTest.php'
.                                                                   1 / 1 (100%)
OK (1 test, 1 assertion)
> phpunit --testsuite unit 'tests/Hoge/BarTest.php'
........                                                            8 / 8 (100%)
OK (8 tests, 15 assertions)

peco が必要。
peco/peco: Simplistic interactive filtering tool

実行すると、ブランチを指定できて(リモート可)、ワーキングディレクトリとの差分があったテストだけ実行できる。 master ばかりではなくて、たまには違うブランチもやりたいよね。


と、思ったけど、ソースだけ変えた場合って出てこないよね…。
うまいこと差分に対してテストできるような仕組みできないかなー

ast 作れるし、いい感じに解析して、影響のありそうなテストだけいい感じに動かしてくれる君みたいなの。

大きくなってしまった git リポジトリへの対抗策

この記事は公開されてから1年以上経過しており、情報が古い可能性があります。

ギョームで使ってるあるシステムのリポジトリが大きすぎて、クローンめっちゃつらい、というかできないんだけど!となったので調べた。

git リポジトリの肥大化は、長年積み重なったログ、うっかりコミットされた大きなファイル、バイナリファイルの積み重ねが主な原因っぽい。
(バイナリファイルは差分でログにしていくのではなく、フルでログになる)

なので、大きく 2 つの方向があると思う。
1 つめはリポジトリ自体のログをねじ曲げる方向。ログが原因なので、そのログから原因要素を取り除けばいいじゃん!という理解。
もう 1 つはクローンするときに工夫する方向。ログが大きすぎるのでログを最小限にしていく。

方法 1 : ログを書き換える

方法 1-a : リポジトリを作り直す

リポジトリのログをねじ曲げるにあたって、手っ取り早い改善策はリポジトリを作り直すこと。最新の状態を手元に持っておき、リポジトリを捨て、最新の状態でファーストコミットをする。もろもろの再設定の手間をかけられる、ログや Github などのリポジトリに紐づく情報はポイしてもよい、そんな決断ができるなら一番簡単で効果も高い方法です。

ただ、これからクローンするのはを楽にしたいんだけど!という用途にはちょっと違うかも。

方法1-b : filter-branch サブコマンドを使って不要なファイルを歴史から隠蔽する

filter-branchを使うと、ログを遡ってコマンドを適用していけます。これを使ってログに潜む悪のファイルを精査します。ただあんまりつかったことないのでわからんね、かなしみ。

Git - 歴史の書き換え

全コミットからのファイルの削除

$ git filter-branch --tree-filter 'rm -f passwords.txt' HEAD
Rewrite 6b9b3cf04e7c5686a9cb838c3f36a8cb6a0fc2bd (21/21)
Ref 'refs/heads/master' was rewritten

こういう使い方で、まあまあ、簡単に捨てられそうですね。

試しに適当なリポジトリで実行してみたら、キレイサッパリとファイルが消え去りました。ちなみにコミットハッシュも変わるようです(そりゃそうだ)

$ git log --oneline --graph
* 94a3acd (HEAD -> master) ...
* c5615d4 ...
* 1b58a6d ...
* 958f4b0 ...
* 6aeccd4 ...
* 2d39eca ...
* b22e882 ...
* 554d930 ...
* d620f96 ...
* d0a5a86 ...

$ git filter-branch -f --tree-filter "rm -f package.json"
Rewrite 94a3acd99880461b586931f4d4a283494d3f9fcc (8/10) (3 seconds passed, remaining 0 predicted)
Ref 'refs/heads/master' was rewritten

$ git log --oneline --graph
* ccd7b67 (HEAD -> master) ...
* 5159356 ...
* 5551da3 ...
* 862c676 ...
* c1f6cb5 ...
* 3f1eb70 ...
* 36bc53c ...
* d7c2fa6 ...
* d620f96 ...
* d0a5a86 ...

ということで、方法1はどちらもログをぶっ壊していくので、コミットハッシュが変わります。自分も含め、既にクローンなどによってリポジトリを追跡してた人は、整合性が取れなくなるので、再度ローカルのリポジトリ状況を刷新する必要がありそうです。 fetch しよう!とかそういう話じゃなくて、過去のログを持ち込まないとか、そういうところ。

方法 2 : クローンするときに工夫する

方法 2-a : Shallow Clone

Shallow Clone の方法の1つとして depth というプロパティが設定でき、ログをどこまで遡ってクローンするかを決められます。


$ git clone --depth ...

これをするとログの遡りが少なくなるので、その分早くクローンをすることができます。かなり大きくなったリポジトリで試したらこれくらいの違いでした。 およそ 1 分かかっていたものが 30 秒程度にまとまって非常にすばらしい。

$ time git clone ...
Initialized empty Git repository in ...
remote: Counting objects: 137514, done.
remote: Compressing objects: 100% (31116/31116), done.
remote: Total 137514 (delta 108680), reused 129419 (delta 102311)
Receiving objects: 100% (137514/137514), 215.85 MiB | 8.55 MiB/s, done.
Resolving deltas: 100% (108680/108680), done.

real    1m1.198s
user    0m19.858s
sys     0m6.843s


$ time git clone --depth 1 ...
Initialized empty Git repository in ...
remote: Counting objects: 10862, done.
remote: Compressing objects: 100% (6934/6934), done.
remote: Total 10862 (delta 4852), reused 8236 (delta 3409)
Receiving objects: 100% (10862/10862), 109.83 MiB | 7.15 MiB/s, done.
Resolving deltas: 100% (4852/4852), done.

real    0m27.406s
user    0m6.650s
sys     0m2.897s

足りないログについては git fetch --depth で値を増やすと補完できるっぽい。

$ git log --oneline | wc -l
1

$ git fetch --depth 10000
remote: Counting objects: 126652, done.
remote: Compressing objects: 100% (22233/22233), done.
remote: Total 126652 (delta 103609), reused 123617 (delta 100851)
Receiving objects: 100% (126652/126652), 106.69 MiB | 5.92 MiB/s, done.
Resolving deltas: 100% (103609/103609), completed with 1886 local objects.

$ git log --oneline | wc -l
13101

Shallow Clone したリポジトリは、そういうリポジトリになるようで、深さを再設定しないと永遠とログが出てこないそうです。ちょっと昔までは push と pull もままならなかったようですが、今は改善されたようです。

いつのまにかGitのshallow cloneが”Push”も"Pull"もできるように超進化していたよ! すごーい! - ブログなんだよもん


とりあえず手元でこのブランチの様子を検証したいんやけど!みたいなものなんかは --depth 1 を積極的に使っていくとよさげたん CI ツールの類もクローンするときは全部これでええやんけ。


そのほか勉強になった記事

[Git] git repository size を削減する | deadwood

巨大なリポジトリ を Git で上手く扱う方法 | Atlassian Blogs