{"thread":{"id":"17776","subject":"[PATCH] Documentation: pruning recipe for destructive filter-branch","startedAt":"2009-02-13T17:14:39Z","lastAt":"2009-02-15T19:33:37Z","messageCount":17,"participants":["Thomas Rast","Junio C Hamano","Jan Krüger","Johannes Schindelin"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"104529","messageId":"1234545279-23153-1-git-send-email-trast@student.ethz.ch","threadId":"17776","inReplyTo":null,"subject":"[PATCH] Documentation: pruning recipe for destructive filter-branch","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2009-02-13T17:14:39Z","receivedAt":"2009-02-13T17:14:39Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Add a section about how to shrink a repository's size after running\ngit-filter-branch to remove large blobs from history.\n\nThis comes up every week or so on IRC, and the commands required to\nhandle every case are not very newbie-friendly, so hopefully writing\nthem down somewhere leads to fewer questions.\n\nThanks to doener (Björn Steinbrink) for comments and corrections.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\nOr we could just add an option --prune-everything-I-have-a-backup-I-promise\nto git-filter-branch, so that users can get the same result with less effort.\n\n\n Documentation/git-filter-branch.txt |   36 +++++++++++++++++++++++++++++++++++\n 1 files changed, 36 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt\nindex 1fbbbb4..737c555 100644\n--- a/Documentation/git-filter-branch.txt\n+++ b/Documentation/git-filter-branch.txt\n@@ -339,6 +339,42 @@ git filter-branch --index-filter \\\n ---------------------------------------------------------------\n \n \n+\n+Checklist for Shrinking a Repository\n+------------------------------------\n+\n+git-filter-branch is often used to get rid of a subset of files,\n+usually with some combination of `\\--index-filter` and\n+`\\--subdirectory-filter`.  If you want to physically shrink the\n+repository afterwards, you have some choices:\n+\n+* Clone it with `git clone file:///path/to/repo`.  The clone will not\n+  have the removed objects.  See linkgit:git-clone[1].  (Note that\n+  cloning with a plain path just hardlinks everything!)\n+\n+If you really don't want to clone it, for whatever reasons, check the\n+following points (in this order).  This is a very destructive\n+approach, so *make a backup* or go back to cloning it.  You have been\n+warned.\n+\n+* Make sure you really removed all variants of a filename, if a blob\n+  was moved over its lifetime.  `git log \\--follow \\--all \\-- foo` can\n+  help you find renames.\n+\n+* Make sure you really filtered all refs: use `\\--tag-name-filter cat\n+  \\-- \\--all` when calling git-filter-branch.\n+\n+* Make sure you remove the original refs backed up by\n+  git-filter-branch: say `git for-each-ref \\--format=\"%(refname)\"\n+  refs/original/ | xargs -n 1 git update-ref -d`.\n+\n+* Expire all reflogs with `git reflog expire \\--expire=now \\--all`.\n+\n+* Repack to remove packed objects with `git repack -ad`.\n+\n+* Prune unpacked unreferenced objects with `git prune \\--expire=now`.\n+\n+\n Author\n ------\n Written by Petr \"Pasky\" Baudis <pasky@suse.cz>,\n-- \n1.6.2.rc0.274.g97213\n"},{"id":"104563","messageId":"7vr6214yi5.fsf@gitster.siamese.dyndns.org","threadId":"17776","inReplyTo":"1234545279-23153-1-git-send-email-trast@student.ethz.ch","subject":"Re: [PATCH] Documentation: pruning recipe for destructive filter-branch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-02-14T01:07:30Z","receivedAt":"2009-02-14T01:07:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Rast <trast@student.ethz.ch> writes:\n\n> diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt\n> index 1fbbbb4..737c555 100644\n> --- a/Documentation/git-filter-branch.txt\n> +++ b/Documentation/git-filter-branch.txt\n> @@ -339,6 +339,42 @@ git filter-branch --index-filter \\\n>  ---------------------------------------------------------------\n>  \n>  \n> +\n> +Checklist for Shrinking a Repository\n> +------------------------------------\n> +\n> +git-filter-branch is often used to get rid of a subset of files,\n> +usually with some combination of `\\--index-filter` and\n> +`\\--subdirectory-filter`.  If you want to physically shrink the\n> +repository afterwards, you have some choices:\n> +\n> +* Clone it with `git clone file:///path/to/repo`.  The clone will not\n> +  have the removed objects.  See linkgit:git-clone[1].  (Note that\n> +  cloning with a plain path just hardlinks everything!)\n> +\n> +If you really don't want to clone it, for whatever reasons, check the\n> +following points (in this order).  This is a very destructive\n> +approach, so *make a backup* or go back to cloning it.  You have been\n> +warned.\n\nSounds sane so far and even somewhat amusing.\n\n> +* Make sure you really removed all variants of a filename, if a blob\n> +  was moved over its lifetime.  `git log \\--follow \\--all \\-- foo` can\n> +  help you find renames.\n> +\n> +* Make sure you really filtered all refs: use `\\--tag-name-filter cat\n> +  \\-- \\--all` when calling git-filter-branch.\n\nDon't these two apply equally to \"git clone\" approach?  Only the remaining\nfour are specific to in-place pruning, if I am reading this correctly.\n\n> +* Make sure you remove the original refs backed up by\n> +  git-filter-branch: say `git for-each-ref \\--format=\"%(refname)\"\n> +  refs/original/ | xargs -n 1 git update-ref -d`.\n> +\n> +* Expire all reflogs with `git reflog expire \\--expire=now \\--all`.\n> +\n> +* Repack to remove packed objects with `git repack -ad`.\n> +\n> +* Prune unpacked unreferenced objects with `git prune \\--expire=now`.\n> +\n> +\n>  Author\n>  ------\n>  Written by Petr \"Pasky\" Baudis <pasky@suse.cz>,\n> -- \n> 1.6.2.rc0.274.g97213\n>\n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n"},{"id":"104564","messageId":"20090214025115.615119bf@perceptron","threadId":"17776","inReplyTo":"1234545279-23153-1-git-send-email-trast@student.ethz.ch","subject":"Re: [PATCH] Documentation: pruning recipe for destructive filter-branch","fromName":"Jan Krüger","fromEmail":"jk@jk.gs","sentAt":"2009-02-14T01:51:15Z","receivedAt":"2009-02-14T01:51:15Z","isPatch":true,"sender":{"key":"jk@jk.gs","avatar":"https://avatars.githubusercontent.com/u/1774?v=4"},"body":"Hi,\n\nOn Fri, 13 Feb 2009 18:14:39 +0100, Thomas Rast <trast@student.ethz.ch>\nwrote:\n\n> Add a section about how to shrink a repository's size after running\n> git-filter-branch to remove large blobs from history.\n\nGood idea.\n\n> +* Expire all reflogs with `git reflog expire \\--expire=now \\--all`.\n> +\n> +* Repack to remove packed objects with `git repack -ad`.\n> +\n> +* Prune unpacked unreferenced objects with `git prune \\--expire=now`.\n\nIs the --expire=now actually needed for prune? The way I read it\ngit-prune(1), it defaults to pruning everything anyway.\n\n-Jan\n"},{"id":"104591","messageId":"alpine.DEB.1.00.0902140642520.10279@pacific.mpi-cbg.de","threadId":"17776","inReplyTo":"20090214025115.615119bf@perceptron","subject":"Re: [PATCH] Documentation: pruning recipe for destructive filter-branch","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2009-02-14T05:46:18Z","receivedAt":"2009-02-14T05:46:18Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sat, 14 Feb 2009, Jan Krüger wrote:\n\n> On Fri, 13 Feb 2009 18:14:39 +0100, Thomas Rast <trast@student.ethz.ch>\n> wrote:\n> \n> > Add a section about how to shrink a repository's size after running\n> > git-filter-branch to remove large blobs from history.\n> \n> Good idea.\n\nIndeed.  It is funny when some people ask, but reading it several times a \nweek in #git makes that fun go away real quick.\n\n> > +* Expire all reflogs with `git reflog expire \\--expire=now \\--all`.\n> > +\n> > +* Repack to remove packed objects with `git repack -ad`.\n> > +\n> > +* Prune unpacked unreferenced objects with `git prune \\--expire=now`.\n> \n> Is the --expire=now actually needed for prune? The way I read it\n> git-prune(1), it defaults to pruning everything anyway.\n\nI hope not.  IIRC I set the default to 2 weeks.\n\n*goes to read the source* Ah, that default is only set in \"git gc\", and \n\"git prune\" still prunes everything.  So you're absolutely correct.\n\nBut maybe we do not want to direct the user to use \"prune\" here, but \nrather \"gc\"?\n\nCiao,\nDscho\n"},{"id":"104601","messageId":"20090214074954.7e423dd2@perceptron","threadId":"17776","inReplyTo":"alpine.DEB.1.00.0902140642520.10279@pacific.mpi-cbg.de","subject":"Re: [PATCH] Documentation: pruning recipe for destructive filter-branch","fromName":"Jan Krüger","fromEmail":"jk@jk.gs","sentAt":"2009-02-14T06:49:54Z","receivedAt":"2009-02-14T06:49:54Z","isPatch":true,"sender":{"key":"jk@jk.gs","avatar":"https://avatars.githubusercontent.com/u/1774?v=4"},"body":"Hi,\n\nOn Sat, 14 Feb 2009 06:46:18 +0100 (CET), Johannes Schindelin\n<Johannes.Schindelin@gmx.de> wrote:\n\n> > Is the --expire=now actually needed for prune? The way I read it\n> > git-prune(1), it defaults to pruning everything anyway.\n> \n> [...] Ah, that default is only set in \"git gc\", and \"git prune\" still\n> prunes everything. [...] But maybe we do not want to direct the user\n> to use \"prune\" here, but rather \"gc\"?\n\nI just read the source code of gc and noticed that the only way to\ninfluence its expiration limit for prune is to set it in the config file\n(gc.pruneExpire). Thus there is no immediate way to tell gc to prune\n*all* unreachable objects and we need to stick to the instructions that\nare in the patch (minus the unnecessary --expire=now for prune). The\nalternative would be to add a command line option to gc that\ncorresponds to gc.pruneExpire. I don't really care about that, but a\npatch for it is below.\n\n-Jan\n\n-----8<-----\n\nFrom 42226c6c542be4e9fff0817662115f180033e75a Mon Sep 17 00:00:00 2001\nFrom: =?utf-8?q?Jan=20Kr=C3=BCger?= <jk@jk.gs>\nDate: Sat, 14 Feb 2009 07:39:23 +0100\nSubject: [PATCH] gc: add --prune-expire option equivalent to gc.pruneExpire\nMIME-Version: 1.0\nContent-Type: text/plain; charset=utf-8\nContent-Transfer-Encoding: 8bit\n\nIn semi-regular intervals, people tend to want to get rid of huge\nobjects they accidentally created in a repository. Of course it\nshouldn't be too easy to do but also not too hard. Thus we allow them to\nget rid of unreferenced objects more easily once they have managed to\nmake the objects unreferenced, by allowing them to override the prune\nexpiration limit.\n\nSigned-off-by: Jan Krüger <jk@jk.gs>\n---\n Documentation/git-gc.txt |    5 +++++\n builtin-gc.c             |    5 +++--\n 2 files changed, 8 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt\nindex 7086eea..a4d35fb 100644\n--- a/Documentation/git-gc.txt\n+++ b/Documentation/git-gc.txt\n@@ -59,6 +59,11 @@ are consolidated into a single pack by using the `-A` option of\n 'git-repack'. Setting `gc.autopacklimit` to 0 disables\n automatic consolidation of packs.\n \n+--prune-expire <time>::\n+\tExpire unreachable objects older than <time>, rather than\n+\tconsidering the configuration variable or the default explained in\n+\tthe next section.\n+\n --quiet::\n \tSuppress all progress reports.\n \ndiff --git a/builtin-gc.c b/builtin-gc.c\nindex a201438..a62c762 100644\n--- a/builtin-gc.c\n+++ b/builtin-gc.c\n@@ -179,16 +179,17 @@ int cmd_gc(int argc, const char **argv, const char *prefix)\n \tint quiet = 0;\n \tchar buf[80];\n \n+\tgit_config(gc_config, NULL);\n+\n \tstruct option builtin_gc_options[] = {\n \t\tOPT_BOOLEAN(0, \"prune\", &prune, \"prune unreferenced objects (deprecated)\"),\n+\t\tOPT_STRING(0, \"prune-expire\", &prune_expire, \"time\", \"expire unreferenced objects older than <time>\"),\n \t\tOPT_BOOLEAN(0, \"aggressive\", &aggressive, \"be more thorough (increased runtime)\"),\n \t\tOPT_BOOLEAN(0, \"auto\", &auto_gc, \"enable auto-gc mode\"),\n \t\tOPT_BOOLEAN('q', \"quiet\", &quiet, \"suppress progress reports\"),\n \t\tOPT_END()\n \t};\n \n-\tgit_config(gc_config, NULL);\n-\n \tif (pack_refs < 0)\n \t\tpack_refs = !is_bare_repository();\n \n-- \n1.6.2.rc0.61.g5cd12.dirty\n"},{"id":"104630","messageId":"alpine.DEB.1.00.0902141248220.10279@pacific.mpi-cbg.de","threadId":"17776","inReplyTo":"20090214074954.7e423dd2@perceptron","subject":"Re: [PATCH] Documentation: pruning recipe for destructive filter-branch","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2009-02-14T11:48:57Z","receivedAt":"2009-02-14T11:48:57Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sat, 14 Feb 2009, Jan Krüger wrote:\n\n> Subject: [PATCH] gc: add --prune-expire option equivalent to gc.pruneExpire\n\nI like it, and if nobody beats me to it, I will add a test for this \noption tonight.\n\nCiao,\nDscho"},{"id":"104637","messageId":"1234621328-8998-1-git-send-email-trast@student.ethz.ch","threadId":"17776","inReplyTo":"alpine.DEB.1.00.0902141248220.10279@pacific.mpi-cbg.de","subject":"[PATCH v2] Documentation: pruning recipe for destructive filter-branch","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2009-02-14T14:22:08Z","receivedAt":"2009-02-14T14:22:08Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Add a section about how to shrink a repository's size after running\ngit-filter-branch to remove large blobs from history.\n\nThis comes up every week or so on IRC, and the commands required to\nhandle every case are not very newbie-friendly, so hopefully writing\nthem down somewhere leads to fewer questions.\n\nIt may seem contradictory to document fallbacks for older Gits in\nnewer docs, but we want to point people at this as a FAQ answer, and\nthey will frequently not have the newest version installed.\n\nThanks to Björn Steinbrink and Junio C Hamano for comments and\ncorrections.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\nJohannes Schindelin wrote:\n> On Sat, 14 Feb 2009, Jan Krüger wrote:\n> \n> > Subject: [PATCH] gc: add --prune-expire option equivalent to gc.pruneExpire\n> \n> I like it, and if nobody beats me to it, I will add a test for this \n> option tonight.\n\nCool, then let's use that in the docs.\n\n\n Documentation/git-filter-branch.txt |   36 +++++++++++++++++++++++++++++++++++\n 1 files changed, 36 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt\nindex 1fbbbb4..d148e58 100644\n--- a/Documentation/git-filter-branch.txt\n+++ b/Documentation/git-filter-branch.txt\n@@ -339,6 +339,42 @@ git filter-branch --index-filter \\\n ---------------------------------------------------------------\n \n \n+\n+Checklist for Shrinking a Repository\n+------------------------------------\n+\n+git-filter-branch is often used to get rid of a subset of files,\n+usually with some combination of `\\--index-filter` and\n+`\\--subdirectory-filter`.  If you want to physically shrink the\n+repository afterwards, you have some choices:\n+\n+* Make sure you really removed all variants of a filename, if a blob\n+  was moved over its lifetime.  `git log \\--name-only \\--follow \\--all\n+  \\-- filename` can help you find renames.\n+\n+* Make sure you really filtered all refs: use `\\--tag-name-filter cat\n+  \\-- \\--all` when calling git-filter-branch.\n+\n+* Clone it with `git clone file:///path/to/repo`.  The clone will not\n+  have the removed objects.  See linkgit:git-clone[1].  (Note that\n+  cloning with a plain path just hardlinks everything!)\n+\n+If you really don't want to clone it, for whatever reasons, check the\n+following points instead (in this order).  This is a very destructive\n+approach, so *make a backup* or go back to cloning it.  You have been\n+warned.\n+\n+* Remove the original refs backed up by git-filter-branch: say `git\n+  for-each-ref \\--format=\"%(refname)\" refs/original/ | xargs -n 1 git\n+  update-ref -d`.\n+\n+* Expire all reflogs with `git reflog expire \\--expire=now \\--all`.\n+\n+* Garbage collect all unreferenced objects with `git gc\n+  \\--prune-expire=now` (or if your git-gc is not new enough to have\n+  that option, `git repack -ad; git prune`).\n+\n+\n Author\n ------\n Written by Petr \"Pasky\" Baudis <pasky@suse.cz>,\n-- \n1.6.2.rc0.283.g181d4\n"},{"id":"104659","messageId":"alpine.DEB.1.00.0902141959060.10279@pacific.mpi-cbg.de","threadId":"17776","inReplyTo":"20090214074954.7e423dd2@perceptron","subject":"[PATCH] gc: make --prune useful again by accepting an optional parameter","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2009-02-14T19:02:22Z","receivedAt":"2009-02-14T19:02:22Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"With this patch, \"git gc --no-prune\" will not prune any loose (and\ndangling) object, and \"git gc --prune=5.minutes.ago\" will prune\nall loose objects older than 5 minutes.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n\n\tActually, when trying to add tests to Jan's patch, I realized that \n\tI already have a patch in my personal tree, which makes --prune \n\taccept an optional parameter...\n\n\tJan, what do you think?  If you prefer --prune-expire, can you \n\tplease adapt my test?\n\n Documentation/git-gc.txt |    9 ++++++++-\n builtin-gc.c             |   19 ++++++++++++-------\n t/t5304-prune.sh         |   20 ++++++++++++++++++++\n 3 files changed, 40 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt\nindex 7086eea..fcef5fb 100644\n--- a/Documentation/git-gc.txt\n+++ b/Documentation/git-gc.txt\n@@ -8,7 +8,7 @@ git-gc - Cleanup unnecessary files and optimize the local repository\n \n SYNOPSIS\n --------\n-'git gc' [--aggressive] [--auto] [--quiet]\n+'git gc' [--aggressive] [--auto] [--quiet] [--prune=<date>]\n \n DESCRIPTION\n -----------\n@@ -59,6 +59,13 @@ are consolidated into a single pack by using the `-A` option of\n 'git-repack'. Setting `gc.autopacklimit` to 0 disables\n automatic consolidation of packs.\n \n+--prune=<date>::\n+\tPrune loose objects older than date (default is 2 weeks ago).\n+\tThis option is on by default.\n+\n+--no-prune::\n+\tDo not prune any loose objects.\n+\n --quiet::\n \tSuppress all progress reports.\n \ndiff --git a/builtin-gc.c b/builtin-gc.c\nindex a201438..a962f38 100644\n--- a/builtin-gc.c\n+++ b/builtin-gc.c\n@@ -161,7 +161,8 @@ static int need_to_gc(void)\n \t */\n \tif (too_many_packs())\n \t\tappend_option(argv_repack,\n-\t\t\t      !strcmp(prune_expire, \"now\") ? \"-a\" : \"-A\",\n+\t\t\t      prune_expire && !strcmp(prune_expire, \"now\") ?\n+\t\t\t      \"-a\" : \"-A\",\n \t\t\t      MAX_ADD);\n \telse if (!too_many_loose_objects())\n \t\treturn 0;\n@@ -173,14 +174,15 @@ static int need_to_gc(void)\n \n int cmd_gc(int argc, const char **argv, const char *prefix)\n {\n-\tint prune = 0;\n \tint aggressive = 0;\n \tint auto_gc = 0;\n \tint quiet = 0;\n \tchar buf[80];\n \n \tstruct option builtin_gc_options[] = {\n-\t\tOPT_BOOLEAN(0, \"prune\", &prune, \"prune unreferenced objects (deprecated)\"),\n+\t\t{ OPTION_STRING, 0, \"prune\", &prune_expire, \"date\",\n+\t\t\t\"prune unreferenced objects (deprecated)\",\n+\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire },\n \t\tOPT_BOOLEAN(0, \"aggressive\", &aggressive, \"be more thorough (increased runtime)\"),\n \t\tOPT_BOOLEAN(0, \"auto\", &auto_gc, \"enable auto-gc mode\"),\n \t\tOPT_BOOLEAN('q', \"quiet\", &quiet, \"suppress progress reports\"),\n@@ -218,7 +220,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)\n \t\t\t\"\\\"git help gc\\\" for more information.\\n\");\n \t} else\n \t\tappend_option(argv_repack,\n-\t\t\t      !strcmp(prune_expire, \"now\") ? \"-a\" : \"-A\",\n+\t\t\t      prune_expire && !strcmp(prune_expire, \"now\")\n+\t\t\t      ? \"-a\" : \"-A\",\n \t\t\t      MAX_ADD);\n \n \tif (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD))\n@@ -230,9 +233,11 @@ int cmd_gc(int argc, const char **argv, const char *prefix)\n \tif (run_command_v_opt(argv_repack, RUN_GIT_CMD))\n \t\treturn error(FAILED_RUN, argv_repack[0]);\n \n-\targv_prune[2] = prune_expire;\n-\tif (run_command_v_opt(argv_prune, RUN_GIT_CMD))\n-\t\treturn error(FAILED_RUN, argv_prune[0]);\n+\tif (prune_expire) {\n+\t\targv_prune[2] = prune_expire;\n+\t\tif (run_command_v_opt(argv_prune, RUN_GIT_CMD))\n+\t\t\treturn error(FAILED_RUN, argv_prune[0]);\n+\t}\n \n \tif (run_command_v_opt(argv_rerere, RUN_GIT_CMD))\n \t\treturn error(FAILED_RUN, argv_rerere[0]);\ndiff --git a/t/t5304-prune.sh b/t/t5304-prune.sh\nindex 771c0a0..2e9c4a9 100755\n--- a/t/t5304-prune.sh\n+++ b/t/t5304-prune.sh\n@@ -112,4 +112,24 @@ test_expect_success 'prune: do not prune heads listed as an argument' '\n \n '\n \n+test_expect_success 'gc --no-prune && gc --prune=<date>' '\n+\n+\tbefore=$(git count-objects | sed \"s/ .*//\") &&\n+\tBLOB=$(echo aleph_0 | git hash-object -w --stdin) &&\n+\tBLOB_FILE=.git/objects/$(echo $BLOB | sed \"s/^../&\\//\") &&\n+\ttest $((1 + $before)) = $(git count-objects | sed \"s/ .*//\") &&\n+\ttest -f $BLOB_FILE &&\n+\ttest-chmtime =-$((86400*5001)) $BLOB_FILE &&\n+\tgit gc --no-prune &&\n+\ttest 1 = $(git count-objects | sed \"s/ .*//\") &&\n+\ttest -f $BLOB_FILE &&\n+\tgit gc --prune=5002.days.ago &&\n+\ttest 1 = $(git count-objects | sed \"s/ .*//\") &&\n+\ttest -f $BLOB_FILE &&\n+\tgit gc --prune=5000.days.ago &&\n+\ttest 0 = $(git count-objects | sed \"s/ .*//\") &&\n+\ttest ! -f $BLOB_FILE\n+\n+'\n+\n test_done\n-- \n1.6.2.rc0.367.g7f9a5\n"},{"id":"104661","messageId":"alpine.DEB.1.00.0902142011410.10279@pacific.mpi-cbg.de","threadId":"17776","inReplyTo":"1234621328-8998-1-git-send-email-trast@student.ethz.ch","subject":"Re: [PATCH v2] Documentation: pruning recipe for destructive filter-branch","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2009-02-14T19:12:27Z","receivedAt":"2009-02-14T19:12:27Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sat, 14 Feb 2009, Thomas Rast wrote:\n\n> Johannes Schindelin wrote:\n> > On Sat, 14 Feb 2009, Jan Krüger wrote:\n> > \n> > > Subject: [PATCH] gc: add --prune-expire option equivalent to gc.pruneExpire\n> > \n> > I like it, and if nobody beats me to it, I will add a test for this \n> > option tonight.\n> \n> Cool, then let's use that in the docs.\n\nThis would be needed in addition, incase that my --prune=<date> patch is \ntaken:\n\ndiff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt\nindex d148e58..a8ad884 100644\n--- a/Documentation/git-filter-branch.txt\n+++ b/Documentation/git-filter-branch.txt\n@@ -371,7 +371,7 @@ warned.\n * Expire all reflogs with `git reflog expire \\--expire=now \\--all`.\n \n * Garbage collect all unreferenced objects with `git gc\n-  \\--prune-expire=now` (or if your git-gc is not new enough to have\n+  \\--prune=now` (or if your git-gc is not new enough to have\n   that option, `git repack -ad; git prune`).\n \n \n-- \n1.6.2.rc0.367.g7f9a5\n"},{"id":"104672","messageId":"7vab8ou89h.fsf@gitster.siamese.dyndns.org","threadId":"17776","inReplyTo":"1234621328-8998-1-git-send-email-trast@student.ethz.ch","subject":"Re: [PATCH v2] Documentation: pruning recipe for destructive filter-branch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-02-14T19:29:46Z","receivedAt":"2009-02-14T19:29:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Rast <trast@student.ethz.ch> writes:\n\n> +git-filter-branch is often used to get rid of a subset of files,\n> +usually with some combination of `\\--index-filter` and\n> +`\\--subdirectory-filter`.  If you want to physically shrink the\n> +repository afterwards, you have some choices:\n> +\n> +* Make sure you really removed all variants of a filename, if a blob\n> +  was moved over its lifetime.  `git log \\--name-only \\--follow \\--all\n> +  \\-- filename` can help you find renames.\n> +\n> +* Make sure you really filtered all refs: use `\\--tag-name-filter cat\n> +  \\-- \\--all` when calling git-filter-branch.\n> +\n> +* Clone it with `git clone file:///path/to/repo`.  The clone will not\n> +  have the removed objects.  See linkgit:git-clone[1].  (Note that\n> +  cloning with a plain path just hardlinks everything!)\n> +\n> +If you really don't want to clone it, for whatever reasons, check the\n> +following points instead (in this order).  This is a very destructive\n> +approach, so *make a backup* or go back to cloning it.  You have been\n> +warned.\n> +\n> +* Remove the original refs backed up by git-filter-branch: say `git\n> +  for-each-ref \\--format=\"%(refname)\" refs/original/ | xargs -n 1 git\n> +  update-ref -d`.\n> +\n> +* Expire all reflogs with `git reflog expire \\--expire=now \\--all`.\n> +\n> +* Garbage collect all unreferenced objects with `git gc\n> +  \\--prune-expire=now` (or if your git-gc is not new enough to have\n> +  that option, `git repack -ad; git prune`).\n\nI am sorry, but the above does not flow well.  The first two common\npreparatory steps appear as if it is only for the \"clone\" method.\n\n\t... often used to get rid of ... and people expect the resulting\n        repository to be smaller than the original, but you need a few\n        more steps to actually make it smaller, because git tries hard \n        not to lose your objects until you tell it to.  First make sure\n\tthat:\n\n\t* you really removed all...\n\n\t* you reall filtered all...\n\n\tThen, there are two ways to get a smaller repository.  A safer way\n\tis to clone, that keeps your original intact.\n\n\t* Clone it with ...\n\n\tIf you do not want to ...\n\n\t* Remove the original refs...\n\n\t* Expire all...\n"},{"id":"104692","messageId":"1234645011-8092-1-git-send-email-trast@student.ethz.ch","threadId":"17776","inReplyTo":"7vab8ou89h.fsf@gitster.siamese.dyndns.org","subject":"[PATCH v3] Documentation: pruning recipe for destructive filter-branch","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2009-02-14T20:56:51Z","receivedAt":"2009-02-14T20:56:51Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Add a section about how to shrink a repository's size after running\ngit-filter-branch to remove large blobs from history.\n\nThis comes up every week or so on IRC, and the commands required to\nhandle every case are not very newbie-friendly, so hopefully writing\nthem down somewhere leads to fewer questions.\n\nIt may seem contradictory to document fallbacks for older Gits in\nnewer docs, but we want to point people at this as a FAQ answer, and\nthey will frequently not have the newest version installed.\n\nThanks to Björn Steinbrink and Junio C Hamano for comments and\ncorrections.\n\nSigned-off-by: Thomas Rast <trast@student.ethz.ch>\n---\n\nJunio C Hamano wrote:\n> I am sorry, but the above does not flow well.  The first two common\n> preparatory steps appear as if it is only for the \"clone\" method.\n\nYou're absolutely right.  I should have re-read it more thoroughly\nafter the rearrangement...\n\nI also changed the --prune-expire to --prune, and changed the\nsurrounding wording since it's now not a new option any more, just new\nsemantics for an old one.\n\nFurthermore, I noticed that asciidoc tries to turn file:///... into a\nlink, mangling the entire paragraph's formatting in the process.\nApparently +++quoting+++ helps.\n\n\n Documentation/git-filter-branch.txt |   41 +++++++++++++++++++++++++++++++++++\n 1 files changed, 41 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt\nindex 1fbbbb4..7ffe03f 100644\n--- a/Documentation/git-filter-branch.txt\n+++ b/Documentation/git-filter-branch.txt\n@@ -339,6 +339,47 @@ git filter-branch --index-filter \\\n ---------------------------------------------------------------\n \n \n+\n+Checklist for Shrinking a Repository\n+------------------------------------\n+\n+git-filter-branch is often used to get rid of a subset of files,\n+usually with some combination of `\\--index-filter` and\n+`\\--subdirectory-filter`.  People expect the resulting repository to\n+be smaller than the original, but you need a few more steps to\n+actually make it smaller, because git tries hard not to lose your\n+objects until you tell it to.  First make sure that:\n+\n+* You really removed all variants of a filename, if a blob was moved\n+  over its lifetime.  `git log \\--name-only \\--follow \\--all \\--\n+  filename` can help you find renames.\n+\n+* You really filtered all refs: use `\\--tag-name-filter cat \\--\n+  \\--all` when calling git-filter-branch.\n+\n+Then there are two ways to get a smaller repository.  A safer way is\n+to clone, that keeps your original intact.\n+\n+* Clone it with `git clone +++file:///path/to/repo+++`.  The clone\n+  will not have the removed objects.  See linkgit:git-clone[1].  (Note\n+  that cloning with a plain path just hardlinks everything!)\n+\n+If you really don't want to clone it, for whatever reasons, check the\n+following points instead (in this order).  This is a very destructive\n+approach, so *make a backup* or go back to cloning it.  You have been\n+warned.\n+\n+* Remove the original refs backed up by git-filter-branch: say `git\n+  for-each-ref \\--format=\"%(refname)\" refs/original/ | xargs -n 1 git\n+  update-ref -d`.\n+\n+* Expire all reflogs with `git reflog expire \\--expire=now \\--all`.\n+\n+* Garbage collect all unreferenced objects with `git gc \\--prune=now`\n+  (or if your git-gc is not new enough to support arguments to\n+  `\\--prune`, use `git repack -ad; git prune` instead).\n+\n+\n Author\n ------\n Written by Petr \"Pasky\" Baudis <pasky@suse.cz>,\n-- \n1.6.2.rc0.296.ge2122\n"},{"id":"104702","messageId":"200902142233.07190.trast@student.ethz.ch","threadId":"17776","inReplyTo":"alpine.DEB.1.00.0902141959060.10279@pacific.mpi-cbg.de","subject":"Re: [PATCH] gc: make --prune useful again by accepting an optional parameter","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2009-02-14T21:33:02Z","receivedAt":"2009-02-14T21:33:02Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Johannes Schindelin wrote:\n> With this patch, \"git gc --no-prune\" will not prune any loose (and\n> dangling) object, and \"git gc --prune=5.minutes.ago\" will prune\n> all loose objects older than 5 minutes.\n\nPrompted by discussion on IRC, I just spent some time staring at the\ncode, and for the code part and FWIW,\n\n  Acked-by: Thomas Rast <trast@student.ethz.ch>\n\nMeaning that for all I can see, it handles every combination of\nconfig, --prune and --no-prune correctly.\n\nI have some non-code nits however:\n\n> +--prune=<date>::\n> +\tPrune loose objects older than date (default is 2 weeks ago).\n> +\tThis option is on by default.\n\nThis isn't really true; to the user, it defaults to the value of\n'gc.pruneExpire'.  That this is done by defaulting the state of\n--prune to 2 weeks ago is an implementation detail.\n\n> +\t\t{ OPTION_STRING, 0, \"prune\", &prune_expire, \"date\",\n> +\t\t\t\"prune unreferenced objects (deprecated)\",\n> +\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire },\n\nYou still call the option \"deprecated\" :-)\n\n> diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh\n[...]\n> +test_expect_success 'gc --no-prune && gc --prune=<date>' '\n> +\n> +\tbefore=$(git count-objects | sed \"s/ .*//\") &&\n> +\tBLOB=$(echo aleph_0 | git hash-object -w --stdin) &&\n> +\tBLOB_FILE=.git/objects/$(echo $BLOB | sed \"s/^../&\\//\") &&\n> +\ttest $((1 + $before)) = $(git count-objects | sed \"s/ .*//\") &&\n> +\ttest -f $BLOB_FILE &&\n> +\ttest-chmtime =-$((86400*5001)) $BLOB_FILE &&\n> +\tgit gc --no-prune &&\n> +\ttest 1 = $(git count-objects | sed \"s/ .*//\") &&\n> +\ttest -f $BLOB_FILE &&\n> +\tgit gc --prune=5002.days.ago &&\n> +\ttest 1 = $(git count-objects | sed \"s/ .*//\") &&\n> +\ttest -f $BLOB_FILE &&\n> +\tgit gc --prune=5000.days.ago &&\n> +\ttest 0 = $(git count-objects | sed \"s/ .*//\") &&\n> +\ttest ! -f $BLOB_FILE\n> +\n> +'\n> +\n\nI'm not a huge fan of such contiguous blocks, as a failure is harder\nto pin to any of the several tests inside, especially with 'test' not\nshowing any output when it fails.\n\nAlso, it does test the interaction between having gc.pruneExpire set,\nbut passing --no-prune: the tests above leave gc.pruneExpire at\n\"2.days.ago\".  This wasn't immediately obvious however, and you should\nprobably set the config again just in case someone decides to insert a\ntest with a different value.\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n"},{"id":"104703","messageId":"3651.78.34.159.109.1234647192.squirrel@heapsort.de","threadId":"17776","inReplyTo":"alpine.DEB.1.00.0902141959060.10279@pacific.mpi-cbg.de","subject":"Re: [PATCH] gc: make --prune useful again by accepting an optional parameter","fromName":"Jan Krüger","fromEmail":"jk@jk.gs","sentAt":"2009-02-14T21:33:12Z","receivedAt":"2009-02-14T21:33:12Z","isPatch":true,"sender":{"key":"jk@jk.gs","avatar":"https://avatars.githubusercontent.com/u/1774?v=4"},"body":"Hi,\n\n> With this patch, \"git gc --no-prune\" will not prune any loose (and\n> dangling) object, and \"git gc --prune=5.minutes.ago\" will prune\n> all loose objects older than 5 minutes.\n\nI like it, so disregard my patch.\n\n> diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt\n> index 7086eea..fcef5fb 100644\n> --- a/Documentation/git-gc.txt\n> +++ b/Documentation/git-gc.txt\n> @@ -8,7 +8,7 @@ git-gc - Cleanup unnecessary files and optimize the local\n> repository\n>\n>  SYNOPSIS\n>  --------\n> -'git gc' [--aggressive] [--auto] [--quiet]\n> +'git gc' [--aggressive] [--auto] [--quiet] [--prune=<date>]\n\nMaybe add --no-prune too.\n\n>\n>  DESCRIPTION\n>  -----------\n> @@ -59,6 +59,13 @@ are consolidated into a single pack by using the `-A`\n> option of\n>  'git-repack'. Setting `gc.autopacklimit` to 0 disables\n>  automatic consolidation of packs.\n>\n> +--prune=<date>::\n> +\tPrune loose objects older than date (default is 2 weeks ago).\n> +\tThis option is on by default.\n\nMaybe refer to the config option at least briefly.\n\n> +\n> +--no-prune::\n> +\tDo not prune any loose objects.\n> +\n>  --quiet::\n>  \tSuppress all progress reports.\n>\n> diff --git a/builtin-gc.c b/builtin-gc.c\n> index a201438..a962f38 100644\n> --- a/builtin-gc.c\n> +++ b/builtin-gc.c\n> [snip]\n> @@ -173,14 +174,15 @@ static int need_to_gc(void)\n>\n>  int cmd_gc(int argc, const char **argv, const char *prefix)\n>  {\n> -\tint prune = 0;\n>  \tint aggressive = 0;\n>  \tint auto_gc = 0;\n>  \tint quiet = 0;\n>  \tchar buf[80];\n>\n>  \tstruct option builtin_gc_options[] = {\n> -\t\tOPT_BOOLEAN(0, \"prune\", &prune, \"prune unreferenced objects\n> (deprecated)\"),\n> +\t\t{ OPTION_STRING, 0, \"prune\", &prune_expire, \"date\",\n> +\t\t\t\"prune unreferenced objects (deprecated)\",\n\nIf you actually make it useful again shouldn't we stop marking it as\ndeprecated?\n\n-Jan\n"},{"id":"104705","messageId":"alpine.DEB.1.00.0902142301030.10279@pacific.mpi-cbg.de","threadId":"17776","inReplyTo":"200902142233.07190.trast@student.ethz.ch","subject":"Re: [PATCH] gc: make --prune useful again by accepting an optional parameter","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2009-02-14T22:05:00Z","receivedAt":"2009-02-14T22:05:00Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"\nThanks for both of your sets of comments.\n\nThis is the interdiff.\n\n---\n Documentation/git-gc.txt |    7 ++++---\n builtin-gc.c             |    2 +-\n t/t5304-prune.sh         |   24 +++++++++++++++++++++---\n 3 files changed, 26 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt\nindex fcef5fb..b292e98 100644\n--- a/Documentation/git-gc.txt\n+++ b/Documentation/git-gc.txt\n@@ -8,7 +8,7 @@ git-gc - Cleanup unnecessary files and optimize the local repository\n \n SYNOPSIS\n --------\n-'git gc' [--aggressive] [--auto] [--quiet] [--prune=<date>]\n+'git gc' [--aggressive] [--auto] [--quiet] [--prune=<date> | --no-prune]\n \n DESCRIPTION\n -----------\n@@ -60,8 +60,9 @@ are consolidated into a single pack by using the `-A` option of\n automatic consolidation of packs.\n \n --prune=<date>::\n-\tPrune loose objects older than date (default is 2 weeks ago).\n-\tThis option is on by default.\n+\tPrune loose objects older than date (default is 2 weeks ago,\n+\toverrideable by the config variable `gc.pruneExpire`).  This\n+\toption is on by default.\n \n --no-prune::\n \tDo not prune any loose objects.\ndiff --git a/builtin-gc.c b/builtin-gc.c\nindex 06bc83c..fc556ed 100644\n--- a/builtin-gc.c\n+++ b/builtin-gc.c\n@@ -181,7 +181,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)\n \n \tstruct option builtin_gc_options[] = {\n \t\t{ OPTION_STRING, 0, \"prune\", &prune_expire, \"date\",\n-\t\t\t\"prune unreferenced objects (deprecated)\",\n+\t\t\t\"prune unreferenced objects\",\n \t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire },\n \t\tOPT_BOOLEAN(0, \"aggressive\", &aggressive, \"be more thorough (increased runtime)\"),\n \t\tOPT_BOOLEAN(0, \"auto\", &auto_gc, \"enable auto-gc mode\"),\ndiff --git a/t/t5304-prune.sh b/t/t5304-prune.sh\nindex 2e9c4a9..55ed7c7 100755\n--- a/t/t5304-prune.sh\n+++ b/t/t5304-prune.sh\n@@ -112,7 +112,7 @@ test_expect_success 'prune: do not prune heads listed as an argument' '\n \n '\n \n-test_expect_success 'gc --no-prune && gc --prune=<date>' '\n+test_expect_success 'gc --no-prune' '\n \n \tbefore=$(git count-objects | sed \"s/ .*//\") &&\n \tBLOB=$(echo aleph_0 | git hash-object -w --stdin) &&\n@@ -120,14 +120,32 @@ test_expect_success 'gc --no-prune && gc --prune=<date>' '\n \ttest $((1 + $before)) = $(git count-objects | sed \"s/ .*//\") &&\n \ttest -f $BLOB_FILE &&\n \ttest-chmtime =-$((86400*5001)) $BLOB_FILE &&\n+\tgit config gc.pruneExpire 2.days.ago &&\n \tgit gc --no-prune &&\n \ttest 1 = $(git count-objects | sed \"s/ .*//\") &&\n+\ttest -f $BLOB_FILE\n+\n+'\n+\n+test_expect_success 'gc respects gc.pruneExpire' '\n+\n+\tgit config gc.pruneExpire 5002.days.ago &&\n+\tgit gc &&\n \ttest -f $BLOB_FILE &&\n+\tgit config gc.pruneExpire 5000.days.ago &&\n+\tgit gc &&\n+\ttest ! -f $BLOB_FILE\n+\n+'\n+\n+test_expect_success 'gc --prune=<date>' '\n+\n+\tBLOB=$(echo aleph_0 | git hash-object -w --stdin) &&\n+\tBLOB_FILE=.git/objects/$(echo $BLOB | sed \"s/^../&\\//\") &&\n+\ttest-chmtime =-$((86400*5001)) $BLOB_FILE &&\n \tgit gc --prune=5002.days.ago &&\n-\ttest 1 = $(git count-objects | sed \"s/ .*//\") &&\n \ttest -f $BLOB_FILE &&\n \tgit gc --prune=5000.days.ago &&\n-\ttest 0 = $(git count-objects | sed \"s/ .*//\") &&\n \ttest ! -f $BLOB_FILE\n \n '\n-- \n1.6.2.rc0.367.g7f9a5\n"},{"id":"104706","messageId":"alpine.DEB.1.00.0902142306400.10279@pacific.mpi-cbg.de","threadId":"17776","inReplyTo":"alpine.DEB.1.00.0902142301030.10279@pacific.mpi-cbg.de","subject":"[PATCH v2] gc: make --prune useful again by accepting an optional parameter","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2009-02-14T22:10:10Z","receivedAt":"2009-02-14T22:10:10Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"With this patch, \"git gc --no-prune\" will not prune any loose (and\ndangling) object, and \"git gc --prune=5.minutes.ago\" will prune\nall loose objects older than 5 minutes.\n\nThis patch benefitted from suggestions by Thomas Rast and Jan Krüger.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n Documentation/git-gc.txt |   10 +++++++++-\n builtin-gc.c             |   19 ++++++++++++-------\n t/t5304-prune.sh         |   38 ++++++++++++++++++++++++++++++++++++++\n 3 files changed, 59 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt\nindex 7086eea..b292e98 100644\n--- a/Documentation/git-gc.txt\n+++ b/Documentation/git-gc.txt\n@@ -8,7 +8,7 @@ git-gc - Cleanup unnecessary files and optimize the local repository\n \n SYNOPSIS\n --------\n-'git gc' [--aggressive] [--auto] [--quiet]\n+'git gc' [--aggressive] [--auto] [--quiet] [--prune=<date> | --no-prune]\n \n DESCRIPTION\n -----------\n@@ -59,6 +59,14 @@ are consolidated into a single pack by using the `-A` option of\n 'git-repack'. Setting `gc.autopacklimit` to 0 disables\n automatic consolidation of packs.\n \n+--prune=<date>::\n+\tPrune loose objects older than date (default is 2 weeks ago,\n+\toverrideable by the config variable `gc.pruneExpire`).  This\n+\toption is on by default.\n+\n+--no-prune::\n+\tDo not prune any loose objects.\n+\n --quiet::\n \tSuppress all progress reports.\n \ndiff --git a/builtin-gc.c b/builtin-gc.c\nindex a201438..8d990ed 100644\n--- a/builtin-gc.c\n+++ b/builtin-gc.c\n@@ -161,7 +161,8 @@ static int need_to_gc(void)\n \t */\n \tif (too_many_packs())\n \t\tappend_option(argv_repack,\n-\t\t\t      !strcmp(prune_expire, \"now\") ? \"-a\" : \"-A\",\n+\t\t\t      prune_expire && !strcmp(prune_expire, \"now\") ?\n+\t\t\t      \"-a\" : \"-A\",\n \t\t\t      MAX_ADD);\n \telse if (!too_many_loose_objects())\n \t\treturn 0;\n@@ -173,14 +174,15 @@ static int need_to_gc(void)\n \n int cmd_gc(int argc, const char **argv, const char *prefix)\n {\n-\tint prune = 0;\n \tint aggressive = 0;\n \tint auto_gc = 0;\n \tint quiet = 0;\n \tchar buf[80];\n \n \tstruct option builtin_gc_options[] = {\n-\t\tOPT_BOOLEAN(0, \"prune\", &prune, \"prune unreferenced objects (deprecated)\"),\n+\t\t{ OPTION_STRING, 0, \"prune\", &prune_expire, \"date\",\n+\t\t\t\"prune unreferenced objects\",\n+\t\t\tPARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire },\n \t\tOPT_BOOLEAN(0, \"aggressive\", &aggressive, \"be more thorough (increased runtime)\"),\n \t\tOPT_BOOLEAN(0, \"auto\", &auto_gc, \"enable auto-gc mode\"),\n \t\tOPT_BOOLEAN('q', \"quiet\", &quiet, \"suppress progress reports\"),\n@@ -218,7 +220,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)\n \t\t\t\"\\\"git help gc\\\" for more information.\\n\");\n \t} else\n \t\tappend_option(argv_repack,\n-\t\t\t      !strcmp(prune_expire, \"now\") ? \"-a\" : \"-A\",\n+\t\t\t      prune_expire && !strcmp(prune_expire, \"now\")\n+\t\t\t      ? \"-a\" : \"-A\",\n \t\t\t      MAX_ADD);\n \n \tif (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD))\n@@ -230,9 +233,11 @@ int cmd_gc(int argc, const char **argv, const char *prefix)\n \tif (run_command_v_opt(argv_repack, RUN_GIT_CMD))\n \t\treturn error(FAILED_RUN, argv_repack[0]);\n \n-\targv_prune[2] = prune_expire;\n-\tif (run_command_v_opt(argv_prune, RUN_GIT_CMD))\n-\t\treturn error(FAILED_RUN, argv_prune[0]);\n+\tif (prune_expire) {\n+\t\targv_prune[2] = prune_expire;\n+\t\tif (run_command_v_opt(argv_prune, RUN_GIT_CMD))\n+\t\t\treturn error(FAILED_RUN, argv_prune[0]);\n+\t}\n \n \tif (run_command_v_opt(argv_rerere, RUN_GIT_CMD))\n \t\treturn error(FAILED_RUN, argv_rerere[0]);\ndiff --git a/t/t5304-prune.sh b/t/t5304-prune.sh\nindex 771c0a0..55ed7c7 100755\n--- a/t/t5304-prune.sh\n+++ b/t/t5304-prune.sh\n@@ -112,4 +112,42 @@ test_expect_success 'prune: do not prune heads listed as an argument' '\n \n '\n \n+test_expect_success 'gc --no-prune' '\n+\n+\tbefore=$(git count-objects | sed \"s/ .*//\") &&\n+\tBLOB=$(echo aleph_0 | git hash-object -w --stdin) &&\n+\tBLOB_FILE=.git/objects/$(echo $BLOB | sed \"s/^../&\\//\") &&\n+\ttest $((1 + $before)) = $(git count-objects | sed \"s/ .*//\") &&\n+\ttest -f $BLOB_FILE &&\n+\ttest-chmtime =-$((86400*5001)) $BLOB_FILE &&\n+\tgit config gc.pruneExpire 2.days.ago &&\n+\tgit gc --no-prune &&\n+\ttest 1 = $(git count-objects | sed \"s/ .*//\") &&\n+\ttest -f $BLOB_FILE\n+\n+'\n+\n+test_expect_success 'gc respects gc.pruneExpire' '\n+\n+\tgit config gc.pruneExpire 5002.days.ago &&\n+\tgit gc &&\n+\ttest -f $BLOB_FILE &&\n+\tgit config gc.pruneExpire 5000.days.ago &&\n+\tgit gc &&\n+\ttest ! -f $BLOB_FILE\n+\n+'\n+\n+test_expect_success 'gc --prune=<date>' '\n+\n+\tBLOB=$(echo aleph_0 | git hash-object -w --stdin) &&\n+\tBLOB_FILE=.git/objects/$(echo $BLOB | sed \"s/^../&\\//\") &&\n+\ttest-chmtime =-$((86400*5001)) $BLOB_FILE &&\n+\tgit gc --prune=5002.days.ago &&\n+\ttest -f $BLOB_FILE &&\n+\tgit gc --prune=5000.days.ago &&\n+\ttest ! -f $BLOB_FILE\n+\n+'\n+\n test_done\n-- \n1.6.2.rc0.367.g7f9a5\n\n"},{"id":"104712","messageId":"200902142338.46632.trast@student.ethz.ch","threadId":"17776","inReplyTo":"alpine.DEB.1.00.0902142306400.10279@pacific.mpi-cbg.de","subject":"Re: [PATCH v2] gc: make --prune useful again by accepting an optional parameter","fromName":"Thomas Rast","fromEmail":"trast@student.ethz.ch","sentAt":"2009-02-14T22:38:43Z","receivedAt":"2009-02-14T22:38:43Z","isPatch":true,"sender":{"key":"tr@thomasrast.ch","avatar":"https://avatars.githubusercontent.com/u/153510?v=4"},"body":"Johannes Schindelin wrote:\n> With this patch, \"git gc --no-prune\" will not prune any loose (and\n> dangling) object, and \"git gc --prune=5.minutes.ago\" will prune\n> all loose objects older than 5 minutes.\n> \n> This patch benefitted from suggestions by Thomas Rast and Jan Krüger.\n> \n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  Documentation/git-gc.txt |   10 +++++++++-\n>  builtin-gc.c             |   19 ++++++++++++-------\n>  t/t5304-prune.sh         |   38 ++++++++++++++++++++++++++++++++++++++\n>  3 files changed, 59 insertions(+), 8 deletions(-)\n\nAck.\n\n-- \nThomas Rast\ntrast@{inf,student}.ethz.ch\n"},{"id":"104805","messageId":"7vprhjlcku.fsf@gitster.siamese.dyndns.org","threadId":"17776","inReplyTo":"alpine.DEB.1.00.0902142306400.10279@pacific.mpi-cbg.de","subject":"Re: [PATCH v2] gc: make --prune useful again by accepting an optional parameter","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-02-15T19:33:37Z","receivedAt":"2009-02-15T19:33:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> With this patch, \"git gc --no-prune\" will not prune any loose (and\n> dangling) object, and \"git gc --prune=5.minutes.ago\" will prune\n> all loose objects older than 5 minutes.\n>\n> This patch benefitted from suggestions by Thomas Rast and Jan Krüger.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThanks.\n\nBeing able to say --no-$opt to an OPT_STRING to NULLify a variable that\nhas non-NULL default value is somewhat amusing ;-)\n"}]}