FreeDup で Qt のソースコードのディスク容量を節約してみました

# はじめに

仕事で同じプロジェクトのリポジトリを複数扱っていたりする場合、ファイルの重複は避けられません。
９割がた同じファイル構成のディレクトリが複数ある場合に、その分だけ容量を食われるのってイライラしますよね。

**同じファイルがあった場合には容量を割かないようにしたいなぁ** と思い、色々調べたところ、[FreeDup](http://freedup.org/) というアプリケーションを見つけたので試してみました。

同じファイルシステム上に同じファイルがあった場合にハードリンク化したり、複数のファイルシステム間の場合にはシンボリックリンクに差し替えることでディスク容量を削減くれる素敵なソフトウェアです。

## FreeDup のダウンロード

[FreeDup](http://freedup.org/) の「Download」より、以下のソースをダウンロードし展開しました。

```
$ wget http://freedup.org/freedup-1.6-3-src.tar.bz2

$ tar xf freedup-1.6-3-src.tar.bz2

$ cd freedup-1.6-3

$ ls
COPYING      Makefile.AIX            Makefile.Linux  README.html  config.c   demo       freedup.h       html   mp3.h  mpc.h      ogg.c    sha1.c    symharden    web.c
COPYING.SHA  Makefile.BSD            Makefile.tests  TODO         config.h   freedup    freedup.spec    jpg.c  mp4.c  my.c       ogg.h    sha1.h    symharden.1  web.h
ChangeLog    Makefile.CYGWIN_NT-5.1  README          auto.c       copyright  freedup.1  freedup.xinetd  jpg.h  mp4.h  my.h       pad.xml  socket.c  symharden.c
Makefile     Makefile.Darwin         README.SHA      auto.h       debian     freedup.c  helper.h        mp3.c  mpc.c  nethelp.h  rules    socket.h  verify

$ file freedup
freedup: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=600c144926945f4675f424dd682e65081d8f86e9, stripped

$ ./freedup -h

USAGE:	./freedup [options] [<tree> ...]

-a --freedups	   provide compatibility to freedups by William Stearns.[=-upg]
-b --basedir <path>     sets the current working directory to <path>.
-c --countsave	   count file space savings per linked file.
-d --sametime	   requires the modification time stamps to be equal.
-D --timediff <seconds> allow the modification time stamps to differ by maximum #.
-e --environment <env>  load or store command line as an environment. There exist:
		
-f --samename	   requires the path-stripped file names to be equal.
-g --samegroup	   requires groups of the files to be equal before linking.
-h --help	   shows this help. [other option are ignored]
-H --showhard	   do treat hardlink files as duplicates and show them on -in
-i --interact	   Decide in interactive mode individually, what to do with them.
-k --globalkey <key>    Decide linking direction by key. Valid are @,#,<,>,+ and -.
-l --hardlink	   only allow hardlinks. No symlinks are established.
-m --minbytes <bytes>   only consider larger files. (preferred: -o "-size +#")
-n --noaction	   do not really perform links [no action].
-o --findoptions <opts> pass an option string to the initially called find command.
-p --sameperm	   requires file permissions to be equal.
-P --permmask <mask>    sets the octal mask for file permissions comparison.
-q --quietrun	   produces no output during the run (toggles -c and -v to off).
-s --symlinks	   generate symlinks although some given paths are relative.
-t --hashmethod <type>  select hash method: sha512,sha384,sha256,sha224,sha1,md5,sum
-T --dirmtime	   restore the time stamp of the directory after linking.
-u --sameuser	   requires users of the files to be equal before linking.
-v --verbose	   display shell commands to perform linking [verbose mode].
-V --version	   display current version.
-w --weaklinks	   only weak symbolic links allthough hardlinks might be possible.
-x --extra <style> select comparison style:  auto, jpg, mp3, mp4, mpc, ogg
-# --hash	   disable fast comparison using hash strings [0=no,1=classic,2=fast].
-0 --nonzero	   disable linking of empty files i.e. files of size 0.
	<tree>	   any directory to scan for duplicate files recursively.

	Options are toggle switches. Their final state applies.
	Later <tree> entries are linked to the earlier ones.
	If no <tree> is given, filenames are expected from standard input.
	When standard input is used the option -o has no effect.
	freedup: Version 1.6-3d ?2007-2008 by AN@freedup.org.
	 sha1.c/h of sha-1.0.4 ?2001-2003 by Allan Saddi.

$ export PATH=$PWD:$PATH
```
ということで、特にコンパイルなどは必要なく動くものが手に入りました。

MacOS で試す場合は、

```
$ make
```
で Darwin 用のバイナリを生成しましょう。

*MSG_NOSIGNAL で怒られるので、適当に MSG_FREEDUP に置換しておくと通るようになりました。*

## Qtの１つのバージョンで動作確認

まず試したのは [qt-everywhere-src-5.12.3.tar.xz](https://download.qt.io/archive/qt/5.12/5.12.3/single/qt-everywhere-src-5.12.3.tar.xz) です。

```
$ wget https://download.qt.io/archive/qt/5.12/5.12.3/single/qt-everywhere-src-5.12.3.tar.xz

$ mkdir src

$ tar xf qt-everywhere-src-5.12.3.tar.xz -C src

$ find src/qt-everywhere-src-5.12.3 | wc -l
249853

$ du -s src/qt-everywhere-src-5.12.3
2814198	src/qt-everywhere-src-5.12.3
```

24万ファイル（ディレクトリを含む）、2.8GB の巨大な構成になっています。

ではこれに対して `freedup` を実行して重複がどのくらいあるのか確認してみましょう。

```
$ freedup -l -T -t sha512 src/qt-everywhere-src-5.12.3
Reading directory tree - 
228016 files to investigate


12468 files of 228016 replaced by links.
The total size of replaced files was 66755038 bytes.

$ du -s qt-everywhere-src-5.12.3
2712267	qt-everywhere-src-5.12.3
```
228016 個のファイルを操作し、12468の重複を検知し、ハードリンクに置き換えることで 667MB の容量の削減を実現できました。

MacOS の場合は `/usr/bin/sha512sum` コマンドが存在しないので、`-t sum` を指定するといいでしょう。

## Qt の2つのバージョンのソースコードで確認

次に、5.12.3 と 5.12.0 の２つに対して実行してみましょう。

```
$ rm -rf src/qt-everywhere-src-5.12.3

$ tar xf qt-everywhere-src-5.12.3.tar.xz -C src

$ wget https://download.qt.io/archive/qt/5.12/5.12.0/single/qt-everywhere-src-5.12.0.tar.xz

$ tar xf qt-everywhere-src-5.12.0.tar.xz -C src

$ du -s src/
11231880	src/

$ freedup -l -T -t sha512 src
Reading directory tree | 
456256 files to investigate

236160 files of 456256 replaced by links.
The total size of replaced files was 2218745857 bytes.
```

22GBほど削減できたようです。

```
$ du -sh *
2.6G	qt-everywhere-src-5.12.0
141M	qt-everywhere-src-5.12.3
```

ファイルの構成は中身は一切変わっていないですが、重複ファイルをハードリンク化したことにより、ディスク領域の大幅な節約となりました。

## TODO: git のリポジトリで試してみる

git 内部のデータファイルがほぼ重複しているのでこちらも大幅な削減が見込めるのではないかと思います。

## TODO: ビルドディレクトリでも試してみる

バージョンやコンパイルのオプションに依存しますが、コンパイル済みのバイナリも内容が重複しているものがあるような気がしています。

# おわりに

仕事や趣味で同じプロジェクトのソースコードがディスク上に複数存在している場合に容量を削減する方法が見つかりました。

ただ、ハードリンクを使っている性質上、 **ファイルを修正する可能性がある場合は注意が必要** です。

開発ブランチとリリースアーカイブ間でハードリンク化されている場合、開発ブランチのファイルを編集するとリリースアーカイブのファイルも更新されてしまうと思います。