{"thread":{"id":"6429","subject":"[PATCH] prune-packed: new option --min-age=N","startedAt":"2007-01-18T17:18:30Z","lastAt":"2007-01-22T09:32:35Z","messageCount":30,"participants":["Matthias Lederhofer","Shawn O. Pearce","Junio C Hamano","Nicolas Pitre","Simon 'corecode' Schubert","Jeff King","Steven Grimm","Linus Torvalds","Jakub Narebski"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"32007","messageId":"20070118171830.GA13521@moooo.ath.cx","threadId":"6429","inReplyTo":null,"subject":"[PATCH] prune-packed: new option --min-age=N","fromName":"Matthias Lederhofer","fromEmail":"matled@gmx.net","sentAt":"2007-01-18T17:18:30Z","receivedAt":"2007-01-18T17:18:30Z","isPatch":true,"sender":{"key":"matled@gmx.net","avatar":null},"body":"This option makes prune-packed ignore all packs younger than N seconds\n(using mtime).\n\nSigned-off-by: Matthias Lederhofer <matled@gmx.net>\n---\nThis option allows to remove the unpacked version of an object only if\nit has been packed for a minimum time.  It is intended to work around\nthe problem of freshly packed objects which should not be deleted\nbecause someone might still try to open the unpacked version.\n\nif [ $(git count-objects | cut -d ' ' -f 3) -gt 10240 ]; then\n    git repack\nfi\ngit prune-packed --min-age=$((24*60*60))\n---\n builtin-prune-packed.c |   48 +++++++++++++++++++++++++++++++++++++++++++-----\n builtin-prune.c        |    2 +-\n builtin.h              |    2 +-\n 3 files changed, 45 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin-prune-packed.c b/builtin-prune-packed.c\nindex a57b76d..4359a41 100644\n--- a/builtin-prune-packed.c\n+++ b/builtin-prune-packed.c\n@@ -7,7 +7,8 @@ static const char prune_packed_usage[] =\n #define DRY_RUN 01\n #define VERBOSE 02\n \n-static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts)\n+static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts,\n+\t\t      char **ignore_packed)\n {\n \tstruct dirent *de;\n \tchar hex[40];\n@@ -20,7 +21,7 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts)\n \t\tmemcpy(hex+2, de->d_name, 38);\n \t\tif (get_sha1_hex(hex, sha1))\n \t\t\tcontinue;\n-\t\tif (!has_sha1_pack(sha1, NULL))\n+\t\tif (!has_sha1_pack(sha1, ignore_packed))\n \t\t\tcontinue;\n \t\tmemcpy(pathname + len, de->d_name, 38);\n \t\tif (opts & DRY_RUN)\n@@ -32,12 +33,46 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts)\n \trmdir(pathname);\n }\n \n-void prune_packed_objects(int opts)\n+static char **prune_ignore(int min_age, int opts)\n+{\n+\tint ignore_max = 10;\n+\tint ignore_count = 0;\n+\tchar **ignore_packed = xcalloc(ignore_max, sizeof(char*));\n+\tint now = time(NULL);\n+\tstruct packed_git *p;\n+\n+\tprepare_packed_git();\n+\tfor (p = packed_git; p; p = p->next) {\n+\t\tstruct stat s;\n+\t\tif (stat(p->pack_name, &s)) {\n+\t\t\twarn(\"unable to stat %s\", p->pack_name);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (now-s.st_mtime >= min_age)\n+\t\t\tcontinue;\n+\t\tif (opts & VERBOSE)\n+\t\t\tfprintf(stderr, \"ignoring pack %s\\n\", p->pack_name);\n+\t\tif (ignore_count >= ignore_max-1) {\n+\t\t\tignore_max += 10;\n+\t\t\tignore_packed = xrealloc(ignore_packed,\n+\t\t\t\tignore_max*sizeof(char*));\n+\t\t}\n+\t\tignore_packed[ignore_count++] = p->pack_name;\n+\t}\n+\tignore_packed[ignore_count] = NULL;\n+\treturn ignore_packed;\n+}\n+\n+void prune_packed_objects(int opts, int min_age)\n {\n \tint i;\n \tstatic char pathname[PATH_MAX];\n \tconst char *dir = get_object_directory();\n \tint len = strlen(dir);\n+\tchar **ignore_packed = NULL;\n+\n+\tif (min_age > 0)\n+\t\tignore_packed = prune_ignore(min_age, opts);\n \n \tif (len > PATH_MAX - 42)\n \t\tdie(\"impossible object directory\");\n@@ -54,7 +89,7 @@ void prune_packed_objects(int opts)\n \t\t\t\t((i+1) * 100) / 256);\n \t\tif (!d)\n \t\t\tcontinue;\n-\t\tprune_dir(i, d, pathname, len + 3, opts);\n+\t\tprune_dir(i, d, pathname, len + 3, opts, ignore_packed);\n \t\tclosedir(d);\n \t}\n \tif (opts == VERBOSE)\n@@ -65,6 +100,7 @@ int cmd_prune_packed(int argc, const char **argv, const char *prefix)\n {\n \tint i;\n \tint opts = VERBOSE;\n+\tint min_age = 0;\n \n \tfor (i = 1; i < argc; i++) {\n \t\tconst char *arg = argv[i];\n@@ -74,6 +110,8 @@ int cmd_prune_packed(int argc, const char **argv, const char *prefix)\n \t\t\t\topts |= DRY_RUN;\n \t\t\telse if (!strcmp(arg, \"-q\"))\n \t\t\t\topts &= ~VERBOSE;\n+\t\t\telse if (!strncmp(arg, \"--min-age=\", 10))\n+\t\t\t\tmin_age = atoi(arg+10);\n \t\t\telse\n \t\t\t\tusage(prune_packed_usage);\n \t\t\tcontinue;\n@@ -82,6 +120,6 @@ int cmd_prune_packed(int argc, const char **argv, const char *prefix)\n \t\tusage(prune_packed_usage);\n \t}\n \tsync();\n-\tprune_packed_objects(opts);\n+\tprune_packed_objects(opts, min_age);\n \treturn 0;\n }\ndiff --git a/builtin-prune.c b/builtin-prune.c\nindex 6f0ba0d..46a0fe5 100644\n--- a/builtin-prune.c\n+++ b/builtin-prune.c\n@@ -100,6 +100,6 @@ int cmd_prune(int argc, const char **argv, const char *prefix)\n \tprune_object_dir(get_object_directory());\n \n \tsync();\n-\tprune_packed_objects(show_only);\n+\tprune_packed_objects(show_only, 0);\n \treturn 0;\n }\ndiff --git a/builtin.h b/builtin.h\nindex 818c7bf..e9929da 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -11,7 +11,7 @@ extern int mailinfo(FILE *in, FILE *out, int ks, const char *encoding, const cha\n extern int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec, int skip);\n extern void stripspace(FILE *in, FILE *out);\n extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);\n-extern void prune_packed_objects(int);\n+extern void prune_packed_objects(int, int);\n \n extern int cmd_add(int argc, const char **argv, const char *prefix);\n extern int cmd_annotate(int argc, const char **argv, const char *prefix);\n-- \n1.4.4.4\n"},{"id":"32008","messageId":"20070118172408.GG15428@spearce.org","threadId":"6429","inReplyTo":"20070118171830.GA13521@moooo.ath.cx","subject":"Re: [PATCH] prune-packed: new option --min-age=N","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2007-01-18T17:24:08Z","receivedAt":"2007-01-18T17:24:08Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Matthias Lederhofer <matled@gmx.net> wrote:\n> This option allows to remove the unpacked version of an object only if\n> it has been packed for a minimum time.  It is intended to work around\n> the problem of freshly packed objects which should not be deleted\n> because someone might still try to open the unpacked version.\n\nAre we not rescanning for new packs if we fail to find an\nobject in the object directory?  I thought we were doing this in\nread_sha1_file.  *looks at code* Indeed, we are...\n\nIs this somehow not working?\n\n-- \nShawn.\n"},{"id":"32009","messageId":"20070118174244.GA14287@moooo.ath.cx","threadId":"6429","inReplyTo":"20070118172408.GG15428@spearce.org","subject":"Re: [PATCH] prune-packed: new option --min-age=N","fromName":"Matthias Lederhofer","fromEmail":"matled@gmx.net","sentAt":"2007-01-18T17:42:44Z","receivedAt":"2007-01-18T17:42:44Z","isPatch":true,"sender":{"key":"matled@gmx.net","avatar":null},"body":"Shawn O. Pearce <spearce@spearce.org> wrote:\n> Matthias Lederhofer <matled@gmx.net> wrote:\n> > This option allows to remove the unpacked version of an object only if\n> > it has been packed for a minimum time.  It is intended to work around\n> > the problem of freshly packed objects which should not be deleted\n> > because someone might still try to open the unpacked version.\n> \n> Are we not rescanning for new packs if we fail to find an\n> object in the object directory?  I thought we were doing this in\n> read_sha1_file.  *looks at code* Indeed, we are...\n> \n> Is this somehow not working?\nI just remembered people were warning about automatic prune-packed\n(e.g. cron), so this patch does not make much sense anymore.\n\nAny comments about adding such an option to git prune for unpacked\nobjects?  This would allow to run git prune automatically on\nrepositories while new objects are created.\n"},{"id":"32010","messageId":"20070118175134.GH15428@spearce.org","threadId":"6429","inReplyTo":"20070118174244.GA14287@moooo.ath.cx","subject":"Re: [PATCH] prune-packed: new option --min-age=N","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2007-01-18T17:51:34Z","receivedAt":"2007-01-18T17:51:34Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Matthias Lederhofer <matled@gmx.net> wrote:\n> I just remembered people were warning about automatic prune-packed\n> (e.g. cron), so this patch does not make much sense anymore.\n\nI think we've resolved it.  There may be some hole we aren't aware\nof, but I doubt one exists.  The last hole was packs could be\nremoved by repack while they were still being brought in during\npush or 'fetch -k', but that was fixed with the .keep file.\n \n> Any comments about adding such an option to git prune for unpacked\n> objects?  This would allow to run git prune automatically on\n> repositories while new objects are created.\n\nAn age makes sense on just plain prune.  If the loose object was\nlast modified several hours ago and its not being referenced by\nanything currently, its probably safe to remove.\n\nIf you are going to implement this I would suggest making the default\nage 2 hours and allow the user to configure it from a gc.pruneexpire\nconfiguration option, much like gc.reflogexpire.\n\n-- \nShawn.\n"},{"id":"32025","messageId":"20070118222919.GA22060@moooo.ath.cx","threadId":"6429","inReplyTo":"20070118175134.GH15428@spearce.org","subject":"[RFC] prune: --expire=seconds","fromName":"Matthias Lederhofer","fromEmail":"matled@gmx.net","sentAt":"2007-01-18T22:29:19Z","receivedAt":"2007-01-18T22:29:19Z","isPatch":false,"sender":{"key":"matled@gmx.net","avatar":null},"body":"This option specifies the minimum age of an object before it\nmay be removed by prune.  The default value is 2 hours and\nmay be changed using gc.pruneexpire.\n\nSigned-off-by: Matthias Lederhofer <matled@gmx.net>\n---\nShawn O. Pearce <spearce@spearce.org> wrote:\n> If you are going to implement this I would suggest making the default\n> age 2 hours and allow the user to configure it from a gc.pruneexpire\n> configuration option, much like gc.reflogexpire.\n\nHere it is, I've set the default value to 2 hours as you suggested.\nAny other comments if the default should be a value >0 or 0 to keep\nthe old behaviour?\n---\n builtin-prune.c |   25 ++++++++++++++++++++++++-\n 1 files changed, 24 insertions(+), 1 deletions(-)\n\ndiff --git a/builtin-prune.c b/builtin-prune.c\nindex 6f0ba0d..f46892d 100644\n--- a/builtin-prune.c\n+++ b/builtin-prune.c\n@@ -5,8 +5,10 @@\n #include \"builtin.h\"\n #include \"reachable.h\"\n \n-static const char prune_usage[] = \"git-prune [-n]\";\n+static const char prune_usage[] = \"git-prune [-n] [--expire=seconds]\";\n static int show_only;\n+static int prune_expire;\n+static time_t now;\n \n static int prune_object(char *path, const char *filename, const unsigned char *sha1)\n {\n@@ -38,6 +40,7 @@ static int prune_dir(int i, char *path)\n \t\tchar name[100];\n \t\tunsigned char sha1[20];\n \t\tint len = strlen(de->d_name);\n+\t\tstruct stat st;\n \n \t\tswitch (len) {\n \t\tcase 2:\n@@ -60,6 +63,11 @@ static int prune_dir(int i, char *path)\n \t\t\tif (lookup_object(sha1))\n \t\t\t\tcontinue;\n \n+\t\t\tif (prune_expire > 0 &&\n+\t\t\t    !stat(mkpath(\"%s/%s\", path, de->d_name), &st) &&\n+\t\t\t    now-st.st_mtime < prune_expire)\n+\t\t\t\tcontinue;\n+\n \t\t\tprune_object(path, de->d_name, sha1);\n \t\t\tcontinue;\n \t\t}\n@@ -79,10 +87,21 @@ static void prune_object_dir(const char *path)\n \t}\n }\n \n+static void git_prune_config(const char *var, const char *value)\n+{\n+\tif (!strcmp(var, \"gc.pruneexpire\")) {\n+\t\tprune_expire = git_config_int(var, value);\n+\t\treturn 0;\n+\t}\n+\treturn git_default_config(var, value);\n+}\n+\n int cmd_prune(int argc, const char **argv, const char *prefix)\n {\n \tint i;\n \tstruct rev_info revs;\n+\tprune_expire = 2*60*60;\n+\tnow = time(NULL);\n \n \tfor (i = 1; i < argc; i++) {\n \t\tconst char *arg = argv[i];\n@@ -90,6 +109,10 @@ int cmd_prune(int argc, const char **argv, const char *prefix)\n \t\t\tshow_only = 1;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strncmp(arg, \"--expire=\", 9)) {\n+\t\t\tprune_expire = atoi(arg+9);\n+\t\t\tcontinue;\n+\t\t}\n \t\tusage(prune_usage);\n \t}\n \n-- \n1.4.4.4\n"},{"id":"32026","messageId":"7vy7o0klt1.fsf@assigned-by-dhcp.cox.net","threadId":"6429","inReplyTo":"20070118222919.GA22060@moooo.ath.cx","subject":"Re: [RFC] prune: --expire=seconds","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-01-18T22:32:58Z","receivedAt":"2007-01-18T22:32:58Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Matthias Lederhofer <matled@gmx.net> writes:\n\n> This option specifies the minimum age of an object before it\n> may be removed by prune.  The default value is 2 hours and\n> may be changed using gc.pruneexpire.\n>\n> Signed-off-by: Matthias Lederhofer <matled@gmx.net>\n> ---\n> Shawn O. Pearce <spearce@spearce.org> wrote:\n>> If you are going to implement this I would suggest making the default\n>> age 2 hours and allow the user to configure it from a gc.pruneexpire\n>> configuration option, much like gc.reflogexpire.\n>\n> Here it is, I've set the default value to 2 hours as you suggested.\n> Any other comments if the default should be a value >0 or 0 to keep\n> the old behaviour?\n\nI am not sure if this is needed, as Shawn explained earlier\nrounds of loose-objects safety work.\n\nIf this is something we would want, it might make sense if we\nallowed \"prune --expire='1.day'\" syntax ;-).\n"},{"id":"32033","messageId":"20070119034404.GA17521@spearce.org","threadId":"6429","inReplyTo":"7vy7o0klt1.fsf@assigned-by-dhcp.cox.net","subject":"Re: [RFC] prune: --expire=seconds","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2007-01-19T03:44:04Z","receivedAt":"2007-01-19T03:44:04Z","isPatch":false,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Junio C Hamano <junkio@cox.net> wrote:\n> Matthias Lederhofer <matled@gmx.net> writes:\n> \n> > This option specifies the minimum age of an object before it\n> > may be removed by prune.  The default value is 2 hours and\n> > may be changed using gc.pruneexpire.\n> \n> I am not sure if this is needed, as Shawn explained earlier\n> rounds of loose-objects safety work.\n\nI think we need this fix.  We still have a race condition between\nthe loose object creation and the ref update.\n\nWe've closed this hole completely in the large push case (objects\n>=receive.unpackLimit) and 'fetch -k' case by creating .keep files\nbefore the .pack file, updating refs, then deleting the .keep file;\nand by making sure git-repack leaves packs with .keeps alone.  So\nwe cannot lose an object here.\n\nBut update-index/add/merge-recursive/write-tree/commit-tree, etc.\nas well as small pushes (objects <receive.unpackLimit) and fetch\nwithout -k option still have a race condition.  The objects will\nbe created/unpacked into the loose objects directory with nothing\nreferencing them, and a prune which gets to run just before before\nthe ref update becomes visible would probably whack those objects.\n\nGiven that 'git gc' is the encouraged way to maintain a repository,\nand that 'repack -a -d' is safe, and prune-packed is equally safe,\nI think we should try to make prune safe too.  Matthias' patch\ndoes this by giving the ref update process a fairly large window\nto perform its action within.\n \n> If this is something we would want, it might make sense if we\n> allowed \"prune --expire='1.day'\" syntax ;-).\n\nYes, I agree.\n\nMatthias you can take a look at builtin-reflog.c's argument handling\nfor an example.  I think you just need to use approxidate() in both\nyour config function and in your command line argument handling.\nThen the default becomes '2.hours.ago' instead of just \"2\" (at\nleast from a documentation perspective).\n\nThough the more I think about this perhaps the default should be\n'1.day'.  24 hours is a hellva large window for any current ref\nupdate to complete in, even if the ref update was some massive rsync\nwhich is doing a such a large volume of data on a small bandwidth\nlink that it takes 20 hours to complete.  Besides, users could\nalways force it to be much lower with the command line option if\nthey really need to prune _right_now_.\n\n-- \nShawn.\n"},{"id":"32054","messageId":"20070119104935.GA5189@moooo.ath.cx","threadId":"6429","inReplyTo":"20070119034404.GA17521@spearce.org","subject":"[PATCH] prune: --expire=time","fromName":"Matthias Lederhofer","fromEmail":"matled@gmx.net","sentAt":"2007-01-19T10:49:35Z","receivedAt":"2007-01-19T10:49:35Z","isPatch":true,"sender":{"key":"matled@gmx.net","avatar":null},"body":"This option specifies the minimum age of an object before it\nmay be removed by prune.  The default value is 24 hours and\nmay be changed using gc.pruneexpire.\n\nSigned-off-by: Matthias Lederhofer <matled@gmx.net>\n---\nShawn O. Pearce <spearce@spearce.org> wrote:\n> Given that 'git gc' is the encouraged way to maintain a repository,\n> and that 'repack -a -d' is safe, and prune-packed is equally safe,\n> I think we should try to make prune safe too.  Matthias' patch\n> does this by giving the ref update process a fairly large window\n> to perform its action within.\nAh, git repack -a -d is safe now too?\n\n> Junio C Hamano <junkio@cox.net> wrote:\n> > If this is something we would want, it might make sense if we\n> > allowed \"prune --expire='1.day'\" syntax ;-).\n> \n> Yes, I agree.\n> \n> Matthias you can take a look at builtin-reflog.c's argument handling\n> for an example.  I think you just need to use approxidate() in both\n> your config function and in your command line argument handling.\n> Then the default becomes '2.hours.ago' instead of just \"2\" (at\n> least from a documentation perspective).\n> \n> Though the more I think about this perhaps the default should be\n> '1.day'.  24 hours is a hellva large window for any current ref\n> update to complete in, even if the ref update was some massive rsync\n> which is doing a such a large volume of data on a small bandwidth\n> link that it takes 20 hours to complete.  Besides, users could\n> always force it to be much lower with the command line option if\n> they really need to prune _right_now_.\nThanks for the advice, this is much better than specifying seconds.\nHere is the new version.\n\nThings I'm not sure about, any further comments/discussion?\n- default value for gc.pruneexpire\n- special value(s) for gc.pruneexpire/--expire which mean 'do not\n  check for the age', currently it is 'off'\n---\n builtin-prune.c |   31 ++++++++++++++++++++++++++++++-\n 1 files changed, 30 insertions(+), 1 deletions(-)\n\ndiff --git a/builtin-prune.c b/builtin-prune.c\nindex 6f0ba0d..410b3b2 100644\n--- a/builtin-prune.c\n+++ b/builtin-prune.c\n@@ -5,8 +5,9 @@\n #include \"builtin.h\"\n #include \"reachable.h\"\n \n-static const char prune_usage[] = \"git-prune [-n]\";\n+static const char prune_usage[] = \"git-prune [-n] [--expire=time]\";\n static int show_only;\n+static int prune_expire;\n \n static int prune_object(char *path, const char *filename, const unsigned char *sha1)\n {\n@@ -38,6 +39,7 @@ static int prune_dir(int i, char *path)\n \t\tchar name[100];\n \t\tunsigned char sha1[20];\n \t\tint len = strlen(de->d_name);\n+\t\tstruct stat st;\n \n \t\tswitch (len) {\n \t\tcase 2:\n@@ -60,6 +62,11 @@ static int prune_dir(int i, char *path)\n \t\t\tif (lookup_object(sha1))\n \t\t\t\tcontinue;\n \n+\t\t\tif (prune_expire > 0 &&\n+\t\t\t    !stat(mkpath(\"%s/%s\", path, de->d_name), &st) &&\n+\t\t\t    st.st_mtime > prune_expire)\n+\t\t\t\tcontinue;\n+\n \t\t\tprune_object(path, de->d_name, sha1);\n \t\t\tcontinue;\n \t\t}\n@@ -79,10 +86,25 @@ static void prune_object_dir(const char *path)\n \t}\n }\n \n+static int git_prune_config(const char *var, const char *value)\n+{\n+\tif (!strcmp(var, \"gc.pruneexpire\")) {\n+\t\tif (!strcmp(value, \"off\"))\n+\t\t\tprune_expire = 0;\n+\t\telse\n+\t\t\tprune_expire = approxidate(value);\n+\t\treturn 0;\n+\t}\n+\treturn git_default_config(var, value);\n+}\n+\n int cmd_prune(int argc, const char **argv, const char *prefix)\n {\n \tint i;\n \tstruct rev_info revs;\n+\tprune_expire = time(NULL)-24*60*60;\n+\n+\tgit_config(git_prune_config);\n \n \tfor (i = 1; i < argc; i++) {\n \t\tconst char *arg = argv[i];\n@@ -90,6 +112,13 @@ int cmd_prune(int argc, const char **argv, const char *prefix)\n \t\t\tshow_only = 1;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strncmp(arg, \"--expire=\", 9)) {\n+\t\t\tif (!strcmp(arg+9, \"off\"))\n+\t\t\t\tprune_expire = 0;\n+\t\t\telse\n+\t\t\t\tprune_expire = approxidate(arg+9);\n+\t\t\tcontinue;\n+\t\t}\n \t\tusage(prune_usage);\n \t}\n \n-- \n1.4.4.4\n"},{"id":"32069","messageId":"Pine.LNX.4.64.0701191039420.3011@xanadu.home","threadId":"6429","inReplyTo":"20070119104935.GA5189@moooo.ath.cx","subject":"Re: [PATCH] prune: --expire=time","fromName":"Nicolas Pitre","fromEmail":"nico@cam.org","sentAt":"2007-01-19T15:41:34Z","receivedAt":"2007-01-19T15:41:34Z","isPatch":true,"sender":{"key":"nico@fluxnic.net","avatar":"https://avatars.githubusercontent.com/u/702790?v=4"},"body":"On Fri, 19 Jan 2007, Matthias Lederhofer wrote:\n\n> Ah, git repack -a -d is safe now too?\n\nYes.\n\n\nNicolas\n"},{"id":"32074","messageId":"7vfya6hll3.fsf@assigned-by-dhcp.cox.net","threadId":"6429","inReplyTo":"20070119104935.GA5189@moooo.ath.cx","subject":"Re: [PATCH] prune: --expire=time","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-01-19T19:18:16Z","receivedAt":"2007-01-19T19:18:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Matthias Lederhofer <matled@gmx.net> writes:\n\n> Shawn O. Pearce <spearce@spearce.org> wrote:\n>\n>> Junio C Hamano <junkio@cox.net> wrote:\n>> > If this is something we would want, it might make sense if we\n>> > allowed \"prune --expire='1.day'\" syntax ;-).\n>> \n>> Yes, I agree.\n>\n> Here is the new version.\n\nThanks.\n\n> Things I'm not sure about, any further comments/discussion?\n> - default value for gc.pruneexpire\n> - special value(s) for gc.pruneexpire/--expire which mean 'do not\n>   check for the age', currently it is 'off'\n\nNo single timeout value can be the right timeout for everybody,\nso a big debate is not useful here.  I think 1 day as you and\nShawn did makes sense.\n\n\tgit prune --expire=off\n\nfelt a bit confusing to me at the first glance.  Does it turn\noff the expiration mechanism, retaining all cruft, or turns off\nthe mechanism to give grace period for recent objects?  The\nanswer is obviosuly the latter as \"retain all cruft\" is\nmeaningless, but still it somehow feels funny.  It might be\neasier to explain if it was:\n\n\tgit prune --expire=now\n\nMaybe an alternative:\n\n\tgit prune --retain=1.day\n\tgit prune --retain=off\n\nperhaps?  I dunno.\n\nWe seem to use \"unsigned long\" as a stand-in type for time_t in\nthe rest of the code.  I'd feel better if prune_expire was also\n\"unsigned long\".  We probably should talk about making them all\ntime_t at some point but not right now.\n\nI was tempted to say that we might want to teach approxidate\n\"now\" (add one entry to struct special in date.c), but I do not\nthink it is useful for this application (you want prune_expire\nset to zero not the time we run time(NULL)), and I do not think\nof any other application that wants \"now\".\n"},{"id":"32150","messageId":"20070120111832.GA30368@moooo.ath.cx","threadId":"6429","inReplyTo":"7vfya6hll3.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] prune: --expire=time","fromName":"Matthias Lederhofer","fromEmail":"matled@gmx.net","sentAt":"2007-01-20T11:18:32Z","receivedAt":"2007-01-20T11:18:32Z","isPatch":true,"sender":{"key":"matled@gmx.net","avatar":null},"body":"Junio C Hamano <junkio@cox.net> wrote:\n> \tgit prune --expire=off\n> \n> felt a bit confusing to me at the first glance.  Does it turn\n> off the expiration mechanism, retaining all cruft, or turns off\n> the mechanism to give grace period for recent objects?  The\n> answer is obviosuly the latter as \"retain all cruft\" is\n> meaningless, but still it somehow feels funny.  It might be\n> easier to explain if it was:\n> \n> \tgit prune --expire=now\n> \n> Maybe an alternative:\n> \n> \tgit prune --retain=1.day\n> \tgit prune --retain=off\n> \n> perhaps?  I dunno.\nPerhaps we could also use 'none' or 'all, e.g. --retain=none or\n--expire=all.\n"},{"id":"32152","messageId":"45B205BB.7010101@fs.ei.tum.de","threadId":"6429","inReplyTo":"7vfya6hll3.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] prune: --expire=time","fromName":"Simon 'corecode' Schubert","fromEmail":"corecode@fs.ei.tum.de","sentAt":"2007-01-20T12:06:19Z","receivedAt":"2007-01-20T12:06:19Z","isPatch":true,"sender":{"key":"corecode@fs.ei.tum.de","avatar":"https://gravatar.com/avatar/eff9dbf0cdac0d1e6a6cd7ed0e50763edcb376b493b5253a35ff167918ad79e1?d=mp&s=160"},"body":"Junio C Hamano wrote:\n>> Things I'm not sure about, any further comments/discussion?\n>> - default value for gc.pruneexpire\n>> - special value(s) for gc.pruneexpire/--expire which mean 'do not\n>>   check for the age', currently it is 'off'\n> \n> No single timeout value can be the right timeout for everybody,\n> so a big debate is not useful here.  I think 1 day as you and\n> Shawn did makes sense.\n\nNot that I want to sabotage this discussion, but you have a very valid point.  A timeout can always be crossed, and then bad things[tm] happen.\n\nMy idea is to create a marker file when creating (yet) unconnected loose objects, i.e. on commit/push/fetch.  After the ref was updated or on abort, this marker would be removed.  Prune then can simply search for the oldest marker and only remove objects older than this marker.\n\nOf course this also can mean that a marker file somehow stays and prune fails to clean properly, but that's still better than accidentially cleaning too much.  In the case of dangling marker files, the admin would simply remove them.  rm .git/marker/* when the repo is quiet.\n\ncheers\n  simon\n\n-- \nServe - BSD     +++  RENT this banner advert  +++    ASCII Ribbon   /\"\\\nWork - Mac      +++  space for low €€€ NOW!1  +++      Campaign     \\ /\nParty Enjoy Relax   |   http://dragonflybsd.org      Against  HTML   \\\nDude 2c 2 the max   !   http://golden-apple.biz       Mail + News   / \\\n\n"},{"id":"32189","messageId":"7vlkjw50nl.fsf@assigned-by-dhcp.cox.net","threadId":"6429","inReplyTo":"20070120111832.GA30368@moooo.ath.cx","subject":"Re: [PATCH] prune: --expire=time","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-01-21T06:55:42Z","receivedAt":"2007-01-21T06:55:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Matthias Lederhofer <matled@gmx.net> writes:\n\n> Junio C Hamano <junkio@cox.net> wrote:\n>...\n>> Maybe an alternative:\n>> \n>> \tgit prune --retain=1.day\n>> \tgit prune --retain=off\n>> \n>> perhaps?  I dunno.\n>\n> Perhaps we could also use 'none' or 'all, e.g. --retain=none or\n> --expire=all.\n\nI am considering to commit the attached instead.\n\nThe command \"prune\" is about expiring loose objects that are\nunreachable, and the option introduces a grace period for the\nexpiration process.  Other places that we do use the word\n'expire' to specify the time do mean the expiration timeout.\n\nAs long as you do not rewind/rebase too much, there is not much\nyou can gain from 'git-prune' ('git-repack -a -d' would give you\nmuch more disk savings and performance gain).  I do not think it\nmakes sense to run uncontrolled 'git-prune' from automated cron\njobs.  Even if you rewind/rebase often, 1.5.0 will protect the\nobjects reflog entries refer to, so there will be even less to\nbe gained from 'git-prune'.  I am having a feeling that it might\neven make sense not to run 'git-prune' from 'git-gc'.\n\nWhile I sympathize with what Simon says to certain degree, I\ntend to think the complication it needs to introduce is really\nnot worth it.  Perfect is the enemy of the good.\n\nBy the way.\n\nWhile updating the documentation, I noticed that we lost the\n'extra heads' support when git-prune was rewritten as a built-in\nin commit ba84a797 (July 6th 2006).  The example (commented out\nin the patch) is a valid way to safely prune a repository whose\nobjects are borrowed via the alternates mechanism by some other\nrepository, albeit not really scalable.\n\nThe way we might want to address this would be when 'clone -s'\nmakes a new repository that borrows from an existing repository,\nwe could make a symlink under .git/refs/borrowers/ in the\noriginal repository that points at .git/refs directory of the\ncloned repository -- you can do that by hand today and it would\nbe much nicer than having to specify the other repository when\nrunning 'git prune' as the example suggests.\n\nFor this reason, I would say losing that 'extra heads' support\nfrom git-prune, which happened half year ago, was Ok.\n\nSo far, we have been telling not to rewind refs in repositories\nwhose objects are borrowed by other repositories via the\nalternates mechanism, and I think that advice is still a very\nreasonable one to give, but we probably should make it more\nprominent.  As long as we do that, we would not even need the\n'refs/borrowers/*' symlinks.\n\n-- >8 --\nFrom: Matthias Lederhofer <matled@gmx.net>\n[PATCH] prune: --grace=time\n\nThis option gives grace period to objects that are unreachable\nfrom the refs from getting pruned.\n\nThe default value is 24 hours and may be changed using\ngc.prunegrace.\n\nSigned-off-by: Matthias Lederhofer <matled@gmx.net>\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n Documentation/git-prune.txt |    9 ++++++++-\n builtin-prune.c             |   31 ++++++++++++++++++++++++++++++-\n 2 files changed, 38 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-prune.txt b/Documentation/git-prune.txt\nindex a11e303..fbd344d 100644\n--- a/Documentation/git-prune.txt\n+++ b/Documentation/git-prune.txt\n@@ -8,7 +8,7 @@ git-prune - Prunes all unreachable objects from the object database\n \n SYNOPSIS\n --------\n-'git-prune' [-n] [--] [<head>...]\n+'git-prune' [-n] [--grace=<time>]\n \n DESCRIPTION\n -----------\n@@ -28,6 +28,12 @@ OPTIONS\n \tDo not remove anything; just report what it would\n \tremove.\n \n+--grace=<time>::\n+\tDo not prune loose objects that are younger than the\n+\tspecified time.  This gives a grace period to newly\n+\tcreated objects from getting pruned.\n+\n+////////////////////////////////////////////\n \\--::\n \tDo not interpret any more arguments as options.\n \n@@ -46,6 +52,7 @@ borrows from your repository via its\n ------------\n $ git prune $(cd ../another && $(git-rev-parse --all))\n ------------\n+////////////////////////////////////////////\n \n Author\n ------\ndiff --git a/builtin-prune.c b/builtin-prune.c\nindex 6f0ba0d..7929af1 100644\n--- a/builtin-prune.c\n+++ b/builtin-prune.c\n@@ -5,8 +5,9 @@\n #include \"builtin.h\"\n #include \"reachable.h\"\n \n-static const char prune_usage[] = \"git-prune [-n]\";\n+static const char prune_usage[] = \"git-prune [-n] [--grace=time]\";\n static int show_only;\n+static int prune_grace_period;\n \n static int prune_object(char *path, const char *filename, const unsigned char *sha1)\n {\n@@ -38,6 +39,7 @@ static int prune_dir(int i, char *path)\n \t\tchar name[100];\n \t\tunsigned char sha1[20];\n \t\tint len = strlen(de->d_name);\n+\t\tstruct stat st;\n \n \t\tswitch (len) {\n \t\tcase 2:\n@@ -60,6 +62,11 @@ static int prune_dir(int i, char *path)\n \t\t\tif (lookup_object(sha1))\n \t\t\t\tcontinue;\n \n+\t\t\tif (prune_grace_period > 0 &&\n+\t\t\t    !stat(mkpath(\"%s/%s\", path, de->d_name), &st) &&\n+\t\t\t    st.st_mtime > prune_grace_period)\n+\t\t\t\tcontinue;\n+\n \t\t\tprune_object(path, de->d_name, sha1);\n \t\t\tcontinue;\n \t\t}\n@@ -79,10 +86,25 @@ static void prune_object_dir(const char *path)\n \t}\n }\n \n+static int git_prune_config(const char *var, const char *value)\n+{\n+\tif (!strcmp(var, \"gc.prunegrace\")) {\n+\t\tif (!strcmp(value, \"off\"))\n+\t\t\tprune_grace_period = 0;\n+\t\telse\n+\t\t\tprune_grace_period = approxidate(value);\n+\t\treturn 0;\n+\t}\n+\treturn git_default_config(var, value);\n+}\n+\n int cmd_prune(int argc, const char **argv, const char *prefix)\n {\n \tint i;\n \tstruct rev_info revs;\n+\tprune_grace_period = time(NULL)-24*60*60;\n+\n+\tgit_config(git_prune_config);\n \n \tfor (i = 1; i < argc; i++) {\n \t\tconst char *arg = argv[i];\n@@ -90,6 +112,13 @@ int cmd_prune(int argc, const char **argv, const char *prefix)\n \t\t\tshow_only = 1;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strncmp(arg, \"--grace=\", 8)) {\n+\t\t\tif (!strcmp(arg+8, \"off\"))\n+\t\t\t\tprune_grace_period = 0;\n+\t\t\telse\n+\t\t\t\tprune_grace_period = approxidate(arg+8);\n+\t\t\tcontinue;\n+\t\t}\n \t\tusage(prune_usage);\n \t}\n \n-- \n1.5.0.rc1.g40ab\n"},{"id":"32191","messageId":"20070121075339.GA10632@spearce.org","threadId":"6429","inReplyTo":"7vlkjw50nl.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] prune: --expire=time","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2007-01-21T07:53:39Z","receivedAt":"2007-01-21T07:53:39Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Junio C Hamano <junkio@cox.net> wrote:\n> The way we might want to address this would be when 'clone -s'\n> makes a new repository that borrows from an existing repository,\n> we could make a symlink under .git/refs/borrowers/ in the\n> original repository that points at .git/refs directory of the\n> cloned repository -- you can do that by hand today and it would\n> be much nicer than having to specify the other repository when\n> running 'git prune' as the example suggests.\n\nUnrelated topic: We may also want to consider doing the reverse,\nthat is symlink '.git/refs/borrows-from' of the cloned repository\nto '.git/refs' in the original repository.  This way pushing into\nthe cloned repository can avoid uploading objects which the cloned\nrepository already has access to via its objects/info/alternates\nsetup.\n\nThis would especially be nice with project forks, such as on\nrepo.or.cz.  I don't need to upload all of 'master' if it has\nalready been uploaded by pasky's mirror job, for example.\n\n-- \nShawn.\n"},{"id":"32196","messageId":"20070121103724.GA23256@moooo.ath.cx","threadId":"6429","inReplyTo":"7vlkjw50nl.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] prune: --expire=time","fromName":"Matthias Lederhofer","fromEmail":"matled@gmx.net","sentAt":"2007-01-21T10:37:24Z","receivedAt":"2007-01-21T10:37:24Z","isPatch":true,"sender":{"key":"matled@gmx.net","avatar":null},"body":"Junio C Hamano <junkio@cox.net> wrote:\n> I am considering to commit the attached instead.\nLooks fine.  Just one question:  You said normally unsigned long would\nbe used for time_t but time_t itself seems to be signed.  Using\nunsigned long instead of int for prune_grace_period (which is used as\ntime_t here) results in 'warning: comparison between signed and\nunsigned'.  Perhaps you want to change it here anyway to be consistent\nwith the rest of the code (approxidate returns unsigned long too).\n"},{"id":"32200","messageId":"7vejpo39zg.fsf@assigned-by-dhcp.cox.net","threadId":"6429","inReplyTo":"20070121103724.GA23256@moooo.ath.cx","subject":"Re: [PATCH] prune: --expire=time","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-01-21T11:17:07Z","receivedAt":"2007-01-21T11:17:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Matthias Lederhofer <matled@gmx.net> writes:\n\n> Junio C Hamano <junkio@cox.net> wrote:\n>> I am considering to commit the attached instead.\n> Looks fine.  Just one question:  You said normally unsigned long would\n> be used for time_t but time_t itself seems to be signed.  Using\n> unsigned long instead of int for prune_grace_period (which is used as\n> time_t here) results in 'warning: comparison between signed and\n> unsigned'.  Perhaps you want to change it here anyway to be consistent\n> with the rest of the code (approxidate returns unsigned long too).\n\nAlthough I've merged this and pushed out v1.5.0-rc2, I am\nstarting to think this whole implementation of grace period is\nunfortunately busted and does not buy us much.  Running\ngit-prune in an uncontrolled way from a cron job is still not\nsafe.\n\nSuppose there is a repository that has one old blob that is not\nreferenced from any existing ref (in other words, its been more\nthan the grace period since 'prune' was run in the repository,\nand one of its heads were rewound which lost the last reference\nto the blob).  You are pushing a new commit into it, whose tree\nhas that blob as one of the files.\n\nYou construct a pack, and unpack-objects starts to run to\nextract the objects you send in the said repository.  The pack\nyou are sending does contain the blob (because no refs reached\nit in the repository), but unpack-objects safety measure means\nthe blob is not re-extracted to overwrite the existing old blob.\n\nNow, an automated prune runs and finishes reading the available\nrefs before your push concludes and updates the ref with your\nnew commit.\n\nWhat happens?\n\nIf we wanted to apply this grace period conservatively,\nprotecting young objects is not enough.  You need to protect\neverything they refer to as well.  In the above scenario, you\nwould protect the new commit object and probably the tree\nobjects contained within, but the code happily will lose the\nblob that was already sitting there.\n"},{"id":"32232","messageId":"20070121220114.GA24729@coredump.intra.peff.net","threadId":"6429","inReplyTo":"7vejpo39zg.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] prune: --expire=time","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2007-01-21T22:01:15Z","receivedAt":"2007-01-21T22:01:15Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sun, Jan 21, 2007 at 03:17:07AM -0800, Junio C Hamano wrote:\n\n> If we wanted to apply this grace period conservatively,\n> protecting young objects is not enough.  You need to protect\n> everything they refer to as well.  In the above scenario, you\n\nThat's not sufficient either. You might not _have_ the young objects\nyet, think the blob is dangling, and delete it. Meanwhile, the tree that\nreferences it arrives. IOW,\n  1. blob B arrives, but already exists\n  2. prune deletes unreference and old blob B\n  3. tree T arrives, referencing blob B\nI think this might be safe if you add objects in a top-down way (i.e., T\nbefore B). However, that doesn't make sense for the commit operation, in\nwhich you add blobs (with git-add), and then eventually construct a\ntree.\n\n-Peff\n"},{"id":"32261","messageId":"45B415B1.30407@midwinter.com","threadId":"6429","inReplyTo":"20070121220114.GA24729@coredump.intra.peff.net","subject":"Re: [PATCH] prune: --expire=time","fromName":"Steven Grimm","fromEmail":"koreth@midwinter.com","sentAt":"2007-01-22T01:38:57Z","receivedAt":"2007-01-22T01:38:57Z","isPatch":true,"sender":{"key":"koreth@midwinter.com","avatar":"https://gravatar.com/avatar/71b4d2e8b62f168bdc9e9205341159e3567003b4f9e2127c617c5fa0a1f5bad2?d=mp&s=160"},"body":"Jeff King wrote:\n> That's not sufficient either. You might not _have_ the young objects\n> yet, think the blob is dangling, and delete it. Meanwhile, the tree that\n> references it arrives. IOW,\n>   1. blob B arrives, but already exists\n>   2. prune deletes unreference and old blob B\n>   3. tree T arrives, referencing blob B\n> I think this might be safe if you add objects in a top-down way (i.e., T\n> before B). However, that doesn't make sense for the commit operation, in\n> which you add blobs (with git-add), and then eventually construct a\n> tree.\n>   \n\nShouldn't the repository be locked against operations like prune while a \ncommit is in progress anyway? That seems like it's pretty prudent and \nreasonable to me -- doing otherwise is just asking for a zillion little \nrace conditions. Prune should be a rare enough operation that having it \nabort (or better, block) while a commit is going on wouldn't be a big \nproblem, I'd think.\n\n-Steve\n"},{"id":"32265","messageId":"20070122015252.GA26934@coredump.intra.peff.net","threadId":"6429","inReplyTo":"45B415B1.30407@midwinter.com","subject":"Re: [PATCH] prune: --expire=time","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2007-01-22T01:52:52Z","receivedAt":"2007-01-22T01:52:52Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sun, Jan 21, 2007 at 05:38:57PM -0800, Steven Grimm wrote:\n\n> >before B). However, that doesn't make sense for the commit operation, in\n> >which you add blobs (with git-add), and then eventually construct a\n> >tree.\n> >  \n> \n> Shouldn't the repository be locked against operations like prune while a \n> commit is in progress anyway? That seems like it's pretty prudent and \n> reasonable to me -- doing otherwise is just asking for a zillion little \n> race conditions. Prune should be a rare enough operation that having it \n> abort (or better, block) while a commit is going on wouldn't be a big \n> problem, I'd think.\n\nI was a bit loose with my phrase 'commit operation'. What I really mean\nis:\n\n$ git add file   ;# (1)\n$ hack hack hack ;# (2)\n$ git commit     ;# (3)\n\nAfter step (1), you have a blob in your db. If you already had that\nblob, then you have the old blob. You don't get the updated tree and\ncommit until step (3). Step (2) can be hours or days. Do you really want\nto lock the repository that long?\n\nPotentially we could 'touch' the blob in step (1) to update its\ntimestamp. But if we update timestamps for things like commit, then that\nmight mean 'touch'ing tens of thousands of objects for a commit which\n_should_ only require making a few objects.\n\n-Peff\n"},{"id":"32266","messageId":"7v1wlnyfzr.fsf@assigned-by-dhcp.cox.net","threadId":"6429","inReplyTo":"45B415B1.30407@midwinter.com","subject":"Re: [PATCH] prune: --expire=time","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-01-22T02:03:52Z","receivedAt":"2007-01-22T02:03:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Steven Grimm <koreth@midwinter.com> writes:\n\n> Shouldn't the repository be locked against operations like prune while\n> a commit is in progress anyway? That seems like it's pretty prudent\n> and reasonable to me -- doing otherwise is just asking for a zillion\n> little race conditions.\n\nThe primary problem is not \"while a commit is in progress\"\nanyway.  I do not think of a single sane reason to run git-prune\nfrom a cron job in a repository that is used for an active\ndevelopment.  It would make sense for management types to run\ncount-objects from a cron job and yell at offenders to repack,\nbut even then the primary disk saving and performance\nenhancement would come from repacking and not from pruning.\nEspecially, 1.5.0 and onwards the objects reachable from reflog\nare protected from pruning and reflogs are enabled by default\nfor developer repositories with working trees, even rewind and\nrebae would not create crufts (the only two exceptions that\ncreate cruft are running \"git add\" more than once on the same\nfile between commits to leak blobs and intermediate tree objects\nrecursive merge needs to make when there are more than one\ncommon ancestors).\n\nIt is a possibility to have a single timestamp file that any\ncommand that intends to eventually update refs should touch\nbefore it starts creating any object.  Then prune can stat the\nfile and remember its timestamp before it starts reading the\nrefs, and then before starting to actually prune the objects it\ncan stat the file again and if the timestamp is different it can\nabort.  Commit, receive-pack (invoked by git-push from the\nremote side), fetch-pack (invoked by clone, fetch and pull), etc.\nall needs to touch the file upfront before they create even a\nsingle object.  But that would create a very hot spot on the\nfilesystem, and I am not sure what its ramifications are.\n\nMy take on this issue is rather different.  I do agree with you\nthat prune is a rare enough operation, and I think it should not\npenalize the primary thing developers would want to do many\ntimes an hour.  I think its much simpler and saner to teach\npeople not to run prune in an uncontrolled way.\n\nWe may want to remove the call to git-prune from git-gc, and\ntell people that if they want to run something from a cron job,\nrun git-gc and not git-prune.\n"},{"id":"32267","messageId":"7vwt3fx1am.fsf@assigned-by-dhcp.cox.net","threadId":"6429","inReplyTo":"20070122015252.GA26934@coredump.intra.peff.net","subject":"Re: [PATCH] prune: --expire=time","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-01-22T02:06:41Z","receivedAt":"2007-01-22T02:06:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> I was a bit loose with my phrase 'commit operation'. What I really mean\n> is:\n>\n> $ git add file   ;# (1)\n> $ hack hack hack ;# (2)\n> $ git commit     ;# (3)\n>\n> After step (1), you have a blob in your db. If you already had that\n> blob, then you have the old blob. You don't get the updated tree and\n> commit until step (3). Step (2) can be hours or days. Do you really want\n> to lock the repository that long?\n\nThis case does not need any locking, as blobs reachable from\nindex are considered as \"reachable\" for the purpose of pruning.\n"},{"id":"32268","messageId":"Pine.LNX.4.64.0701211813410.14248@woody.osdl.org","threadId":"6429","inReplyTo":"7vwt3fx1am.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] prune: --expire=time","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2007-01-22T02:23:29Z","receivedAt":"2007-01-22T02:23:29Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sun, 21 Jan 2007, Junio C Hamano wrote:\n> \n> This case does not need any locking, as blobs reachable from\n> index are considered as \"reachable\" for the purpose of pruning.\n\nWell, strictly speaking, there's a race there. The new index gets written \nout _after_ the blob has been created. Also, in many cases, we end up \nusing completely temporary indexes (\"git commit filename\") that \"git \nprune\" doesn't know or understand.\n\nAll of which is really nothing new. \"git prune\" has always been dangerous. \nYou cannot, and must not, run it concurrently with other git operations.\n\nAlso, in the absense of undo operations, there is really nothing to ever \nprune. Of course, the git.git archive itself has effectively taughth \npeople bad habits, since \"pu\" ends up continually rebasing itself. \n\nHowever, now that rebasing ends up being visible in the branch reflog, \nwe're back to the \"normally nothing to ever prune\" situation, and as such, \nthe only object pruning that _should_ take place is basically as part of \n\"git repack -a -d\" (which unlike a prune is actually safe, since it only \nprunes objects that are reachable from a pack).\n\nSo to recap: \"git prune\" simply isn't a safe thing to do. Don't do it \nwithout thinking. I'm not at all sure it's a good idea that \"git gc\" does \nit for you, since it just encourages mindless pruning that probably \nshouldn't happen in the first place.\n\nNeeding to prune is generally to be taken as a sign of something being \nwrong.\n\n(And yeah, the grace period makes it \"safe\". Assuming everybody involved \nhas even half-way reliable clocks. So IN PRACTICE it is all fine, and I \ndoubt you can lose anything except by really doing something insane. If \nyou want to kill your archive, it's easier to do \"rm .git/objects/*/*a*\" \nthan it is to try to do it with strange \"git prune\" setups, but still..)\n\n\t\tLinus\n"},{"id":"32271","messageId":"7virezwzpz.fsf@assigned-by-dhcp.cox.net","threadId":"6429","inReplyTo":"Pine.LNX.4.64.0701211813410.14248@woody.osdl.org","subject":"Re: [PATCH] prune: --expire=time","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-01-22T02:40:40Z","receivedAt":"2007-01-22T02:40:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@osdl.org> writes:\n\n> However, now that rebasing ends up being visible in the branch reflog, \n> we're back to the \"normally nothing to ever prune\" situation, and as such, \n> the only object pruning that _should_ take place is basically as part of \n> \"git repack -a -d\" (which unlike a prune is actually safe, since it only \n> prunes objects that are reachable from a pack).\n>\n> So to recap: \"git prune\" simply isn't a safe thing to do. Don't do it \n> without thinking. I'm not at all sure it's a good idea that \"git gc\" does \n> it for you, since it just encourages mindless pruning that probably \n> shouldn't happen in the first place.\n\nI guess we are in agreement on this.\n\n-- >8 --\n[PATCH] git-gc: do not run prune mindlessly.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n Documentation/git-gc.txt |    1 -\n git-gc.sh                |    1 -\n 2 files changed, 0 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt\nindex 2bcc949..f53ca97 100644\n--- a/Documentation/git-gc.txt\n+++ b/Documentation/git-gc.txt\n@@ -50,7 +50,6 @@ kept.  This defaults to 15 days.\n \n See Also\n --------\n-gitlink:git-prune[1]\n gitlink:git-reflog[1]\n gitlink:git-repack[1]\n gitlink:git-rerere[1]\ndiff --git a/git-gc.sh b/git-gc.sh\nindex 6de55f7..7716f62 100755\n--- a/git-gc.sh\n+++ b/git-gc.sh\n@@ -11,5 +11,4 @@ SUBDIRECTORY_OK=Yes\n git-pack-refs --prune &&\n git-reflog expire --all &&\n git-repack -a -d -l &&\n-git-prune &&\n git-rerere gc || exit\n-- \n1.5.0.rc2\n"},{"id":"32272","messageId":"Pine.LNX.4.64.0701211851310.14248@woody.osdl.org","threadId":"6429","inReplyTo":"7virezwzpz.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] prune: --expire=time","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2007-01-22T02:58:22Z","receivedAt":"2007-01-22T02:58:22Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sun, 21 Jan 2007, Junio C Hamano wrote:\n> >\n> > So to recap: \"git prune\" simply isn't a safe thing to do. Don't do it \n> > without thinking. I'm not at all sure it's a good idea that \"git gc\" does \n> > it for you, since it just encourages mindless pruning that probably \n> > shouldn't happen in the first place.\n> \n> I guess we are in agreement on this.\n\nWell, having complained about \"git prune\", I at the same time have to \nadmit that I worry about loose objects (and scary messages from \ngit-fsck-objects) potentially confusing new people.\n\nSo \"git prune\" _does_ remove stuff that happens normally. It removes stuff \nthat accumulates (even with reflog) thanks to commands that were \ninterrupted with ^C, and it also removes the auto-merge turds that the \nrecursive merge can create when it does its internal pseudo-commit for \nmore complex merges.\n\nSo I don't think running \"prune\" from within \"git gc\" is necessarily \nwrong per se - I just don't think it's a good idea to do so by _default_, \nexactly because of the issues it can have. \n\nSo hiding \"git prune\" behind \"git gc\" is probably a good thing (make \npeople learn just one thing they need to interface to), but maybe we need \na \"--prune\" flag to the gc command, and then perhaps just document that \nyou should be careful.\n\n\t\tLinus\n"},{"id":"32275","messageId":"20070122032655.GA5223@coredump.intra.peff.net","threadId":"6429","inReplyTo":"7virezwzpz.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH] v1.5.0.txt: update description of git-gc","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2007-01-22T03:26:55Z","receivedAt":"2007-01-22T03:26:55Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"It doesn't call git-prune, and it does call a lot of other things.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\nOn Sun, Jan 21, 2007 at 06:40:40PM -0800, Junio C Hamano wrote:\n> [PATCH] git-gc: do not run prune mindlessly.\n\nThis updates the release notes to reflect this change (and fixes a few\nother inaccuracies and omissions).\n\n v1.5.0.txt |    4 ++--\n 1 files changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/v1.5.0.txt b/v1.5.0.txt\nindex 1b8ecf0..8a2c9cc 100644\n--- a/v1.5.0.txt\n+++ b/v1.5.0.txt\n@@ -191,8 +191,8 @@ Updates in v1.5.0 since v1.4.4 series\n    unreachable, as there is a one-day grace period built-in.\n \n  - There is a toplevel garbage collector script, 'git-gc', that\n-   is an easy way to run 'git-repack -a -d', 'git-reflog gc',\n-   and 'git-prune'.\n+   runs periodic cleanup functions, including 'git-repack -a -d',\n+   'git-reflog expire', 'git-pack-refs --prune', and 'git-rerere gc'.\n \n \n * Detached HEAD\n-- \n1.5.0.rc1.gda86\n"},{"id":"32276","messageId":"7v3b63wsh3.fsf@assigned-by-dhcp.cox.net","threadId":"6429","inReplyTo":"Pine.LNX.4.64.0701211851310.14248@woody.osdl.org","subject":"Re: [PATCH] prune: --expire=time","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-01-22T05:17:12Z","receivedAt":"2007-01-22T05:17:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@osdl.org> writes:\n\n> ... I at the same time have to \n> admit that I worry about loose objects (and scary messages from \n> git-fsck-objects) potentially confusing new people.\n\nI've been annoyed by those scary messages fsck-objects enough\nand was wondering if we could make it less scary.  Especially\nannoying is that the message about missing blobs and trees that\nare only referred to by dangling commits.\n\n> So hiding \"git prune\" behind \"git gc\" is probably a good thing (make \n> people learn just one thing they need to interface to), but maybe we need \n> a \"--prune\" flag to the gc command, and then perhaps just document that \n> you should be careful.\n\nI am still undecided which one should be the default.\n\nFor interactive use by developers who work in their own\nrepositories, git-prune is safe because nothing else would be\nworking on their repositories at the time.  While I do not think\nwe should recommend using git-gc from a cron job, if they want\nto do so, giving an extra --no-prune option in their cron script\nwould be much less annoying.\n\n-- >8 --\n[PATCH] git-gc: do not run prune mindlessly.\n\nYou should pass --no-prune if you ever want to run git-gc from\na cron job.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n Documentation/git-gc.txt |   15 +++++++++++++--\n git-gc.sh                |   18 ++++++++++++++++--\n 2 files changed, 29 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt\nindex 2bcc949..7b650a7 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'\n+'git-gc' [--no-prune]\n \n DESCRIPTION\n -----------\n@@ -21,6 +21,18 @@ Users are encouraged to run this task on a regular basis within\n each repository to maintain good disk space utilization and good\n operating performance.\n \n+OPTIONS\n+-------\n+\n+--no-prune::\n+\tUsually `git-gc` packs refs, expires old reflog entries,\n+\tpacks loose objects, removes unreferenced loose objects,\n+\tand removes old 'rerere' records.  Among these, removal\n+\tof unreferenced loose objects is an unsafe operation\n+\twhile other git operations are in progress.  This option\n+\tdisables this unsafe step.\n+\n+\n Configuration\n -------------\n \n@@ -50,7 +62,6 @@ kept.  This defaults to 15 days.\n \n See Also\n --------\n-gitlink:git-prune[1]\n gitlink:git-reflog[1]\n gitlink:git-repack[1]\n gitlink:git-rerere[1]\ndiff --git a/git-gc.sh b/git-gc.sh\nindex 6de55f7..ecd4b0e 100755\n--- a/git-gc.sh\n+++ b/git-gc.sh\n@@ -4,12 +4,26 @@\n #\n # Cleanup unreachable files and optimize the repository.\n \n-USAGE=''\n+USAGE='git-gc [--no-prune]'\n SUBDIRECTORY_OK=Yes\n . git-sh-setup\n \n+no_prune=\n+while case $# in 0) break ;; esac\n+do\n+\tcase \"$1\" in\n+\t--no-prune)\n+\t\tno_prune=:\n+\t\t;;\n+\t--)\n+\t\tusage\n+\t\t;;\n+\tesac\n+\tshift\n+done\n+\n git-pack-refs --prune &&\n git-reflog expire --all &&\n git-repack -a -d -l &&\n-git-prune &&\n+$no_prune git-prune &&\n git-rerere gc || exit\n-- \n1.5.0.rc2\n"},{"id":"32280","messageId":"Pine.LNX.4.64.0701212137370.14248@woody.osdl.org","threadId":"6429","inReplyTo":"7v3b63wsh3.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] prune: --expire=time","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2007-01-22T06:26:41Z","receivedAt":"2007-01-22T06:26:41Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sun, 21 Jan 2007, Junio C Hamano wrote:\n> \n> I've been annoyed by those scary messages fsck-objects enough\n> and was wondering if we could make it less scary.  Especially\n> annoying is that the message about missing blobs and trees that\n> are only referred to by dangling commits.\n\nYeah. Something like this, which really just changes the order in which we \nprint out the warnings, might be a good idea.\n\nThis patch does that, and also adds a lot of commentary on what it is \ndoing. It also splits out the \"object unreachable\" case from the reachable \ncase, since they end up being really really different. An unreachable \nobject we don't even care about broken links for etc.\n\nThe diffstat says that it adds a lot more lines than it removed, but all \nthe really new lines are either just the added comments, or a result of \njust splitting the object checking up into a few separate functions. The \nactual code is largely the same (but with the improved semantics).\n\nI actually tested this a bit by trying to force a few corruption cases. \nAnd the changes _look_ obvious, and yes, it improved reporting (I think). \nAt the same time, git-fsck-objects is too important to have just one pair \nof eyes looking at it, so I would suggest others really double-check my \nchanges and the logic.\n\n(Btw, the \"parsed but unparsed because it was in a pack-file\" test should \nprobably be tightened up a bit. That part is not new code, it's the old \ncode in a new place with a newly added comment about what it is all about. \nSo it's not a regression, it's just something I thought I'd point out when \nI looked at the code).\n\nAdd my sign-off on the patch as appropriate. I do think it's mergeable, \nbut I'd _really_ like somebody else to double-check me here.\n\nNOTE! One of the new things is that it doesn't complain about missing \nunreachable objects at all any more. It used to complain about missing \nobjects even if they were only missing because _another_ unreachable \nobject wanted to access them, which was obviously just useless noise (it \ncould happen if you ^C in the middle of a fetch, for example.\n\nAnyway, somebody should double- and triple-check me.\n\n\t\tLinus\n\n---\n\n fsck-objects.c |  133 ++++++++++++++++++++++++++++++++++++++++----------------\n 1 files changed, 95 insertions(+), 38 deletions(-)\n\ndiff --git a/fsck-objects.c b/fsck-objects.c\nindex 81f00db..ecfb014 100644\n--- a/fsck-objects.c\n+++ b/fsck-objects.c\n@@ -54,6 +54,99 @@ static int objwarning(struct object *obj, const char *err, ...)\n \treturn -1;\n }\n \n+/*\n+ * Check a single reachable object\n+ */\n+static void check_reachable_object(struct object *obj)\n+{\n+\tconst struct object_refs *refs;\n+\n+\t/*\n+\t * We obviously want the object to be parsed,\n+\t * except if it was in a pack-file and we didn't\n+\t * do a full fsck\n+\t */\n+\tif (!obj->parsed) {\n+\t\tif (has_sha1_file(obj->sha1))\n+\t\t\treturn; /* it is in pack - forget about it */\n+\t\tprintf(\"missing %s %s\\n\", typename(obj->type), sha1_to_hex(obj->sha1));\n+\t\treturn;\n+\t}\n+\n+\t/*\n+\t * Check that everything that we try to reference is also good.\n+\t */\n+\trefs = lookup_object_refs(obj);\n+\tif (refs) {\n+\t\tunsigned j;\n+\t\tfor (j = 0; j < refs->count; j++) {\n+\t\t\tstruct object *ref = refs->ref[j];\n+\t\t\tif (ref->parsed ||\n+\t\t\t    (has_sha1_file(ref->sha1)))\n+\t\t\t\tcontinue;\n+\t\t\tprintf(\"broken link from %7s %s\\n\",\n+\t\t\t       typename(obj->type), sha1_to_hex(obj->sha1));\n+\t\t\tprintf(\"              to %7s %s\\n\",\n+\t\t\t       typename(ref->type), sha1_to_hex(ref->sha1));\n+\t\t}\n+\t}\n+}\n+\n+/*\n+ * Check a single unreachable object\n+ */\n+static void check_unreachable_object(struct object *obj)\n+{\n+\t/*\n+\t * Missing unreachable object? Ignore it. It's not like\n+\t * we miss it (since it can't be reached), nor do we want\n+\t * to complain about it being unreachable (since it does\n+\t * not exist).\n+\t */\n+\tif (!obj->parsed)\n+\t\treturn;\n+\n+\t/*\n+\t * Unreachable object that exists? Show it if asked to,\n+\t * since this is something that is prunable.\n+\t */\n+\tif (show_unreachable) {\n+\t\tprintf(\"unreachable %s %s\\n\", typename(obj->type), sha1_to_hex(obj->sha1));\n+\t\treturn;\n+\t}\n+\n+\t/*\n+\t * \"!used\" means that nothing at all points to it, including\n+\t * other unreacahble objects. In other words, it's the \"tip\"\n+\t * of some set of unreachable objects, usually a commit that\n+\t * got dropped.\n+\t *\n+\t * Such starting points are more interesting than some random\n+\t * set of unreachable objects, so we show them even if the user\n+\t * hasn't asked for _all_ unreachable objects. If you have\n+\t * deleted a branch by mistake, this is a prime candidate to\n+\t * start looking at, for example.\n+\t */\n+\tif (!obj->used) {\n+\t\tprintf(\"dangling %s %s\\n\", typename(obj->type),\n+\t\t       sha1_to_hex(obj->sha1));\n+\t\treturn;\n+\t}\n+\n+\t/*\n+\t * Otherwise? It's there, it's unreachable, and some other unreachable\n+\t * object points to it. Ignore it - it's not interesting, and we showed\n+\t * all the interesting cases above.\n+\t */\n+}\n+\n+static void check_object(struct object *obj)\n+{\n+\tif (obj->flags & REACHABLE)\n+\t\tcheck_reachable_object(obj);\n+\telse\n+\t\tcheck_unreachable_object(obj);\n+}\n \n static void check_connectivity(void)\n {\n@@ -62,46 +155,10 @@ static void check_connectivity(void)\n \t/* Look up all the requirements, warn about missing objects.. */\n \tmax = get_max_object_index();\n \tfor (i = 0; i < max; i++) {\n-\t\tconst struct object_refs *refs;\n \t\tstruct object *obj = get_indexed_object(i);\n \n-\t\tif (!obj)\n-\t\t\tcontinue;\n-\n-\t\tif (!obj->parsed) {\n-\t\t\tif (has_sha1_file(obj->sha1))\n-\t\t\t\t; /* it is in pack */\n-\t\t\telse\n-\t\t\t\tprintf(\"missing %s %s\\n\",\n-\t\t\t\t       typename(obj->type), sha1_to_hex(obj->sha1));\n-\t\t\tcontinue;\n-\t\t}\n-\n-\t\trefs = lookup_object_refs(obj);\n-\t\tif (refs) {\n-\t\t\tunsigned j;\n-\t\t\tfor (j = 0; j < refs->count; j++) {\n-\t\t\t\tstruct object *ref = refs->ref[j];\n-\t\t\t\tif (ref->parsed ||\n-\t\t\t\t    (has_sha1_file(ref->sha1)))\n-\t\t\t\t\tcontinue;\n-\t\t\t\tprintf(\"broken link from %7s %s\\n\",\n-\t\t\t\t       typename(obj->type), sha1_to_hex(obj->sha1));\n-\t\t\t\tprintf(\"              to %7s %s\\n\",\n-\t\t\t\t       typename(ref->type), sha1_to_hex(ref->sha1));\n-\t\t\t}\n-\t\t}\n-\n-\t\tif (show_unreachable && !(obj->flags & REACHABLE)) {\n-\t\t\tprintf(\"unreachable %s %s\\n\",\n-\t\t\t       typename(obj->type), sha1_to_hex(obj->sha1));\n-\t\t\tcontinue;\n-\t\t}\n-\n-\t\tif (!obj->used) {\n-\t\t\tprintf(\"dangling %s %s\\n\", typename(obj->type),\n-\t\t\t       sha1_to_hex(obj->sha1));\n-\t\t}\n+\t\tif (obj)\n+\t\t\tcheck_object(obj);\n \t}\n }\n \n"},{"id":"32281","messageId":"20070122065745.GA4669@spearce.org","threadId":"6429","inReplyTo":"Pine.LNX.4.64.0701212137370.14248@woody.osdl.org","subject":"Re: [PATCH] prune: --expire=time","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2007-01-22T06:57:45Z","receivedAt":"2007-01-22T06:57:45Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Linus Torvalds <torvalds@osdl.org> wrote:\n> Add my sign-off on the patch as appropriate. I do think it's mergeable, \n> but I'd _really_ like somebody else to double-check me here.\n\nYour patch looks right to me.  And I like the idea of reducing the\noutput to only the really important stuff.  Nice work.\n\n-- \nShawn.\n"},{"id":"32283","messageId":"7vd557v8l6.fsf@assigned-by-dhcp.cox.net","threadId":"6429","inReplyTo":"Pine.LNX.4.64.0701212137370.14248@woody.osdl.org","subject":"Re: [PATCH] prune: --expire=time","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-01-22T07:12:05Z","receivedAt":"2007-01-22T07:12:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@osdl.org> writes:\n\n> On Sun, 21 Jan 2007, Junio C Hamano wrote:\n>> \n>> I've been annoyed by those scary messages fsck-objects enough\n>> and was wondering if we could make it less scary.  Especially\n>> annoying is that the message about missing blobs and trees that\n>> are only referred to by dangling commits.\n> ...\n> Add my sign-off on the patch as appropriate. I do think it's mergeable, \n> but I'd _really_ like somebody else to double-check me here.\n\nI think this is very sensible.  Even without all the added\ncomments, the refactoring makes the code much more readable.\n\n-- >8 --\nFrom: Linus Torvalds <torvalds@osdl.org>\nDate: Sun, 21 Jan 2007 22:26:41 -0800 (PST)\nSubject: [PATCH] fsck-objects: refactor checking for connectivity\n\nThis separates the connectivity check into separate codepaths,\none for reachable objects and the other for unreachable ones,\nwhile adding a lot of comments to explain what is going on.\n\nWhen checking an unreachable object, unlike a reachable one, we\ndo not have to complain if it does not exist (we used to\ncomplain about a missing blob even when the only thing that\nreferences it is a tree that is dangling).  Also we do not have\nto check and complain about objects that are referenced by an\nunreachable object.\n\nThis makes the messages from fsck-objects a lot less noisy and\nmore useful.\n\nSigned-off-by: Linus Torvalds <torvalds@osdl.org>\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n <<your patch here>>\n"},{"id":"32285","messageId":"ep20a7$504$1@sea.gmane.org","threadId":"6429","inReplyTo":"7v3b63wsh3.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] prune: --expire=time","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2007-01-22T09:32:35Z","receivedAt":"2007-01-22T09:32:35Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Junio C Hamano wrote:\n\n> @@ -50,7 +62,6 @@ kept.  This defaults to 15 days.\n>  \n>  See Also\n>  --------\n> -gitlink:git-prune[1]\n>  gitlink:git-reflog[1]\n>  gitlink:git-repack[1]\n>  gitlink:git-rerere[1]\n\nHuh?\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"}]}