tags 284274 patch thanks Attached is a patch to the program sources (through the use of a dpatch patch in the Debian package) that adds a new -L / --linkhard option to fdupes. This option will replace all duplicate files with hardlinks which is useful in order to reduce space.
It has been tested only slightly, but the code looks (to me) about right. Please consider this patch and include it in the Debian package. Regards Javier
diff -Nru fdupes-1.50-PR2/debian/changelog fdupes-1.50-PR2-2/debian/changelog --- fdupes-1.50-PR2/debian/changelog 2009-07-31 00:47:23.000000000 +0200 +++ fdupes-1.50-PR2-2/debian/changelog 2009-07-31 00:44:27.000000000 +0200 @@ -1,3 +1,11 @@ +fdupes (1.50-PR2-2.1) unstable; urgency=low + + * debian/patches/50_bts284274_hardlinkreplace.dpatch created. + - added -L / --linkhard to make fdupes replace files with hardlinks. Also + update the manual page; Closes: 284274 + + -- Javier Fernandez-Sanguino Pen~a <j...@debian.org> Fri, 31 Jul 2009 00:43:11 +0200 + fdupes (1.50-PR2-2) unstable; urgency=low * debian/control diff -Nru fdupes-1.50-PR2/debian/patches/00list fdupes-1.50-PR2-2/debian/patches/00list --- fdupes-1.50-PR2/debian/patches/00list 2009-07-31 00:47:23.000000000 +0200 +++ fdupes-1.50-PR2-2/debian/patches/00list 2009-07-31 00:43:02.000000000 +0200 @@ -5,3 +5,4 @@ 20_bts447601_lfs_support 30_bts481809_manpage_summarize 40_bts511702_nohidden_support +50_bts284274_hardlinkreplace diff -Nru fdupes-1.50-PR2/debian/patches/50_bts284274_hardlinkreplace.dpatch fdupes-1.50-PR2-2/debian/patches/50_bts284274_hardlinkreplace.dpatch --- fdupes-1.50-PR2/debian/patches/50_bts284274_hardlinkreplace.dpatch 1970-01-01 01:00:00.000000000 +0100 +++ fdupes-1.50-PR2-2/debian/patches/50_bts284274_hardlinkreplace.dpatch 2009-07-31 00:42:42.000000000 +0200 @@ -0,0 +1,228 @@ +#! /bin/sh /usr/share/dpatch/dpatch-run +## 50_bts284274_hardlinkreplace.dpatch by <j...@debian.org> +## +## All lines beginning with `## DP:' are a description of the patch. +## DP: No description. + +...@dpatch@ +diff -urNad fdupes-1.50-PR2~/fdupes.1 fdupes-1.50-PR2/fdupes.1 +--- fdupes-1.50-PR2~/fdupes.1 2009-07-31 00:38:28.000000000 +0200 ++++ fdupes-1.50-PR2/fdupes.1 2009-07-31 00:42:14.000000000 +0200 +@@ -58,10 +58,17 @@ + .B CAVEATS + below) + .TP ++.B -L --hardlink ++replace all duplicate files with hardlinks to the ++first file in each set of duplicates ++.TP + .B -N --noprompt + when used together with \-\-delete, preserve the first file in each + set of duplicates and delete the others without prompting the user + .TP ++.B -D --debug ++provide debugging information ++.TP + .B -v --version + display fdupes version + .TP +diff -urNad fdupes-1.50-PR2~/fdupes.c fdupes-1.50-PR2/fdupes.c +--- fdupes-1.50-PR2~/fdupes.c 2009-07-31 00:38:28.000000000 +0200 ++++ fdupes-1.50-PR2/fdupes.c 2009-07-31 00:41:08.000000000 +0200 +@@ -53,6 +53,8 @@ + #define F_NOPROMPT 0x0400 + #define F_SUMMARIZEMATCHES 0x0800 + #define F_EXCLUDEHIDDEN 0x1000 ++#define F_HARDLINKFILES 0x2000 ++#define F_DEBUGINFO 0x4000 + + char *program_name; + +@@ -881,6 +883,88 @@ + free(preservestr); + } + ++void hardlinkfiles(file_t *files, int debug) ++{ ++ int counter; ++ int groups = 0; ++ int curgroup = 0; ++ file_t *tmpfile; ++ file_t *curfile; ++ file_t **dupelist; ++ int max = 0; ++ int x = 0; ++ ++ curfile = files; ++ ++ while (curfile) { ++ if (curfile->hasdupes) { ++ counter = 1; ++ groups++; ++ ++ tmpfile = curfile->duplicates; ++ while (tmpfile) { ++ counter++; ++ tmpfile = tmpfile->duplicates; ++ } ++ ++ if (counter > max) max = counter; ++ } ++ ++ curfile = curfile->next; ++ } ++ ++ max++; ++ ++ dupelist = (file_t**) malloc(sizeof(file_t*) * max); ++ ++ if (!dupelist) { ++ errormsg("out of memory\n"); ++ exit(1); ++ } ++ ++ while (files) { ++ if (files->hasdupes) { ++ curgroup++; ++ counter = 1; ++ dupelist[counter] = files; ++ ++ if (debug) printf("[%d] %s\n", counter, files->d_name); ++ ++ tmpfile = files->duplicates; ++ ++ while (tmpfile) { ++ dupelist[++counter] = tmpfile; ++ if (debug) printf("[%d] %s\n", counter, tmpfile->d_name); ++ tmpfile = tmpfile->duplicates; ++ } ++ ++ if (debug) printf("\n"); ++ ++ /* preserve only the first file */ ++ ++ printf(" [+] %s\n", dupelist[1]->d_name); ++ for (x = 2; x <= counter; x++) { ++ if (unlink(dupelist[x]->d_name) == 0) { ++ if ( link(dupelist[1]->d_name, dupelist[x]->d_name) == 0 ) { ++ printf(" [h] %s\n", dupelist[x]->d_name); ++ } else { ++ printf("-- unable to create a hardlink for the file: %s\n", strerror(errno)); ++ printf(" [!] %s ", dupelist[x]->d_name); ++ } ++ } else { ++ printf(" [!] %s ", dupelist[x]->d_name); ++ printf("-- unable to delete the file!\n"); ++ } ++ } ++ printf("\n"); ++ } ++ ++ files = files->next; ++ } ++ ++ free(dupelist); ++} ++ + int sort_pairs_by_arrival(file_t *f1, file_t *f2) + { + if (f2->duplicates != 0) +@@ -971,10 +1055,14 @@ + printf(" \twith -s or --symlinks, or when specifying a\n"); + printf(" \tparticular directory more than once; refer to the\n"); + printf(" \tfdupes documentation for additional information\n"); +- //printf(" -l --relink \t(description)\n"); ++ /* printf(" -r --dlink \t(description)\n"); */ ++ printf(" -L --linkhard \thardlink duplicate files to the first file in\n"); ++ printf(" \teach set of duplicates without prompting the user\n"); + printf(" -N --noprompt \ttogether with --delete, preserve the first file in\n"); + printf(" \teach set of duplicates and delete the rest without\n"); + printf(" \twithout prompting the user\n"); ++ printf(" -D --debug \tenable debugging information\n"); ++ printf(" \teach set of duplicates without prompting the user\n"); + printf(" -v --version \tdisplay fdupes version\n"); + printf(" -h --help \tdisplay this help message\n\n"); + #ifdef OMIT_GETOPT_LONG +@@ -1010,12 +1098,14 @@ + { "symlinks", 0, 0, 's' }, + { "hardlinks", 0, 0, 'H' }, + { "relink", 0, 0, 'l' }, ++ { "linkhard", 0, 0, 'L' }, + { "noempty", 0, 0, 'n' }, + { "nohidden", 0, 0, 'A' }, + { "delete", 0, 0, 'd' }, + { "version", 0, 0, 'v' }, + { "help", 0, 0, 'h' }, + { "noprompt", 0, 0, 'N' }, ++ { "debug", 0, 0, 'D' }, + { "summarize", 0, 0, 'm'}, + { "summary", 0, 0, 'm' }, + { 0, 0, 0, 0 } +@@ -1029,7 +1119,7 @@ + + oldargv = cloneargs(argc, argv); + +- while ((opt = GETOPT(argc, argv, "frRq1Ss::HlnAdvhNm" ++ while ((opt = GETOPT(argc, argv, "frRq1Ss::HlLnAdDvhNm" + #ifndef OMIT_GETOPT_LONG + , long_options, NULL + #endif +@@ -1068,6 +1158,12 @@ + case 'd': + SETFLAG(flags, F_DELETEFILES); + break; ++ case 'L': ++ SETFLAG(flags, F_HARDLINKFILES); ++ break; ++ case 'D': ++ SETFLAG(flags, F_DEBUGINFO); ++ break; + case 'v': + printf("fdupes %s\n", VERSION); + exit(0); +@@ -1102,6 +1198,16 @@ + exit(1); + } + ++ if (ISFLAG(flags, F_HARDLINKFILES) && ISFLAG(flags, F_DELETEFILES)) { ++ errormsg("options --linkhard and --delete are not compatible\n"); ++ exit(1); ++ } ++ ++ if (ISFLAG(flags, F_HARDLINKFILES) && ISFLAG(flags, F_CONSIDERHARDLINKS)) { ++ errormsg("options --linkhard and --hardlinks are not compatible\n"); ++ exit(1); ++ } ++ + if (ISFLAG(flags, F_RECURSEAFTER)) { + firstrecurse = nonoptafter("--recurse:", argc, oldargv, argv, optind); + +@@ -1187,12 +1293,23 @@ + + else + +- if (ISFLAG(flags, F_SUMMARIZEMATCHES)) +- summarizematches(files); +- +- else ++ if (ISFLAG(flags, F_HARDLINKFILES)) + +- printmatches(files); ++ if (ISFLAG(flags, F_DEBUGINFO)) ++ hardlinkfiles(files, 1); ++ else ++ hardlinkfiles(files, 0); ++ ++ else { ++ ++ if (ISFLAG(flags, F_SUMMARIZEMATCHES)) ++ summarizematches(files); ++ ++ else ++ ++ printmatches(files); ++ ++ } + + while (files) { + curfile = files->next;
signature.asc
Description: Digital signature