{"thread":{"id":"5922","subject":"[PATCH 1/2] Delete ref $frotz by moving ref file to \"deleted-$frotz~ref\".","startedAt":"2006-10-14T13:39:49Z","lastAt":"2006-10-17T09:24:24Z","messageCount":6,"participants":["Christian Couder","Junio C Hamano","Shawn Pearce","Jakub Narebski"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"28761","messageId":"20061014153949.2994a114.chriscool@tuxfamily.org","threadId":"5922","inReplyTo":null,"subject":"[PATCH 1/2] Delete ref $frotz by moving ref file to \"deleted-$frotz~ref\".","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2006-10-14T13:39:49Z","receivedAt":"2006-10-14T13:39:49Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"The idea is that moving:\n\n$GIT_DIR/refs/<refpath>/frotz\n\nto:\n\n$GIT_DIR/deleted-refs/<refpath>/frotz~ref\n\nmaybe cheaper and safer than repacking the refs without the\ndeleted one.\n\nOn the other hand now when resolving a ref we have to check\nif a related deleted ref file exists.\n\nThe new \"delete_ref\" function is similar to \"write_ref_sha1\".\n\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\n---\n refs.c |  160 ++++++++++++++++++++++++++++++++++++++++------------------------\n refs.h |    2 +\n 2 files changed, 102 insertions(+), 60 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 89cffaf..6a8da58 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -176,6 +176,38 @@ static struct ref_list *get_loose_refs(v\n \treturn cached_refs.loose;\n }\n \n+static char *get_del_ref_name(const char *ref_name)\n+{\n+\tsize_t ref_name_len = strlen(ref_name);\n+\tchar *delref_name = xmalloc(ref_name_len + 13);\n+\n+\tstrncpy(delref_name, \"deleted-\", 8);\n+\tstrncpy(delref_name + 8, ref_name, ref_name_len);\n+\tstrcpy(delref_name + 8 + ref_name_len, \"~ref\");\n+\n+\treturn delref_name;\n+}\n+\n+static int lstat_del_ref_file(const char *ref_name, struct stat *st)\n+{\n+\tchar *delref_name = get_del_ref_name(ref_name);\n+\tchar *delref_file = git_path(\"%s\", delref_name);\n+\n+\tint ret = lstat(delref_file, st);\n+\n+\tfree(delref_name);\n+\treturn ret;\n+}\n+\n+static char *get_del_ref_file(const char *ref_name)\n+{\n+\tchar *delref_name = get_del_ref_name(ref_name);\n+\tchar *delref_file = xstrdup(git_path(\"%s\", delref_name));\n+\n+\tfree(delref_name);\n+\treturn delref_file;\n+}\n+\n /* We allow \"recursive\" symbolic refs. Only within reason, though */\n #define MAXDEPTH 5\n \n@@ -204,6 +236,17 @@ const char *resolve_ref(const char *ref,\n \t\t * born.  It is NOT OK if we are resolving for\n \t\t * reading.\n \t\t */\n+\n+\t\tif (!lstat_del_ref_file(ref, &st)) {\n+\t\t\t/* The ref has been deleted. */\n+\t\t\tif (flag)\n+\t\t\t\t*flag |= REF_ISDELETED;\n+\t\t\tif (reading)\n+\t\t\t\treturn NULL;\n+\t\t\thashclr(sha1);\n+\t\t\treturn ref;\n+\t\t}\n+\n \t\tif (lstat(path, &st) < 0) {\n \t\t\tstruct ref_list *list = get_packed_refs();\n \t\t\twhile (list) {\n@@ -541,12 +584,14 @@ static struct ref_lock *lock_ref_sha1_ba\n \tstruct ref_lock *lock;\n \tstruct stat st;\n \tint last_errno = 0;\n+\tint default_flag;\n+\tint *ref_flag = flag ? flag : &default_flag;\n \tint mustexist = (old_sha1 && !is_null_sha1(old_sha1));\n \n \tlock = xcalloc(1, sizeof(struct ref_lock));\n \tlock->lock_fd = -1;\n \n-\tref = resolve_ref(ref, lock->old_sha1, mustexist, flag);\n+\tref = resolve_ref(ref, lock->old_sha1, mustexist, ref_flag);\n \tif (!ref && errno == EISDIR) {\n \t\t/* we are trying to lock foo but we used to\n \t\t * have foo/bar which now does not exist;\n@@ -559,7 +604,7 @@ static struct ref_lock *lock_ref_sha1_ba\n \t\t\terror(\"there are still refs under '%s'\", orig_ref);\n \t\t\tgoto error_return;\n \t\t}\n-\t\tref = resolve_ref(orig_ref, lock->old_sha1, mustexist, flag);\n+\t\tref = resolve_ref(orig_ref, lock->old_sha1, mustexist, ref_flag);\n \t}\n \tif (!ref) {\n \t\tlast_errno = errno;\n@@ -568,7 +613,8 @@ static struct ref_lock *lock_ref_sha1_ba\n \t\tgoto error_return;\n \t}\n \tif (is_null_sha1(lock->old_sha1)) {\n-\t\t/* The ref did not exist and we are creating it.\n+\t\t/* The ref did not exist (or has been deleted)\n+\t\t * and we are (re)creating it.\n \t\t * Make sure there is no existing ref that is packed\n \t\t * whose name begins with our refname, nor a ref whose\n \t\t * name is a proper prefix of our refname.\n@@ -580,15 +626,19 @@ static struct ref_lock *lock_ref_sha1_ba\n \t\t\tint len = strlen(list->name);\n \t\t\tint cmplen = (namlen < len) ? namlen : len;\n \t\t\tconst char *lead = (namlen < len) ? list->name : ref;\n+\t\t\tstruct stat st;\n \n \t\t\tif (!strncmp(ref, list->name, cmplen) &&\n-\t\t\t    lead[cmplen] == '/') {\n+\t\t\t    lead[cmplen] == '/' &&\n+\t\t\t    lstat_del_ref_file(list->name, &st)) {\n \t\t\t\terror(\"'%s' exists; cannot create '%s'\",\n \t\t\t\t      list->name, ref);\n \t\t\t\tgoto error_return;\n \t\t\t}\n \t\t\tlist = list->next;\n \t\t}\n+\t\tif (*ref_flag & REF_ISDELETED)\n+\t\t\tlock->del_file = get_del_ref_file(ref);\n \t}\n \n \tlock->lk = xcalloc(1, sizeof(struct lock_file));\n@@ -627,77 +677,60 @@ struct ref_lock *lock_any_ref_for_update\n \treturn lock_ref_sha1_basic(ref, old_sha1, NULL);\n }\n \n-static struct lock_file packlock;\n-\n-static int repack_without_ref(const char *refname)\n+/* Remove the ref \"refs/$frotz\" by creating \"deleted-refs/$frotz~ref\". */\n+int delete_ref(const char *ref_name, unsigned char *sha1)\n {\n-\tstruct ref_list *list, *packed_ref_list;\n-\tint fd;\n-\tint found = 0;\n+\tint flag = 0;\n+\tstatic char term = '\\n';\n+\tstruct ref_lock *lock = lock_ref_sha1_basic(ref_name, sha1, &flag);\n \n-\tpacked_ref_list = get_packed_refs();\n-\tfor (list = packed_ref_list; list; list = list->next) {\n-\t\tif (!strcmp(refname, list->name)) {\n-\t\t\tfound = 1;\n-\t\t\tbreak;\n-\t\t}\n+\tif (!lock)\n+\t\treturn -1;\n+\tif (flag & REF_ISDELETED) {\n+\t\tunlock_ref(lock);\n+\t\treturn 0; /* Already deleted is ok. */\n \t}\n-\tif (!found)\n-\t\treturn 0;\n-\tmemset(&packlock, 0, sizeof(packlock));\n-\tfd = hold_lock_file_for_update(&packlock, git_path(\"packed-refs\"), 0);\n-\tif (fd < 0)\n-\t\treturn error(\"cannot delete '%s' from packed refs\", refname);\n-\n-\tfor (list = packed_ref_list; list; list = list->next) {\n-\t\tchar line[PATH_MAX + 100];\n-\t\tint len;\n-\n-\t\tif (!strcmp(refname, list->name))\n-\t\t\tcontinue;\n-\t\tlen = snprintf(line, sizeof(line), \"%s %s\\n\",\n-\t\t\t       sha1_to_hex(list->sha1), list->name);\n-\t\t/* this should not happen but just being defensive */\n-\t\tif (len > sizeof(line))\n-\t\t\tdie(\"too long a refname '%s'\", list->name);\n-\t\twrite_or_die(fd, line, len);\n+\tif (write(lock->lock_fd, sha1_to_hex(sha1), 40) != 40 ||\n+\t    write(lock->lock_fd, &term, 1) != 1\n+\t\t|| close(lock->lock_fd) < 0) {\n+\t\terror(\"Couldn't write %s\", lock->lk->filename);\n+\t\tunlock_ref(lock);\n+\t\treturn -1;\n \t}\n-\treturn commit_lock_file(&packlock);\n-}\n-\n-int delete_ref(const char *refname, unsigned char *sha1)\n-{\n-\tstruct ref_lock *lock;\n-\tint err, i, ret = 0, flag = 0;\n-\n-\tlock = lock_ref_sha1_basic(refname, sha1, &flag);\n-\tif (!lock)\n-\t\treturn 1;\n+\tinvalidate_cached_refs();\n \tif (!(flag & REF_ISPACKED)) {\n \t\t/* loose */\n-\t\ti = strlen(lock->lk->filename) - 5; /* .lock */\n+\t\tint i = strlen(lock->lk->filename) - 5; /* .lock */\n \t\tlock->lk->filename[i] = 0;\n-\t\terr = unlink(lock->lk->filename);\n-\t\tif (err) {\n-\t\t\tret = 1;\n+\t\tif (unlink(lock->lk->filename)) {\n \t\t\terror(\"unlink(%s) failed: %s\",\n \t\t\t      lock->lk->filename, strerror(errno));\n+\t\t\tunlock_ref(lock);\n+\t\t\treturn -1;\n \t\t}\n \t\tlock->lk->filename[i] = '.';\n \t}\n-\t/* removing the loose one could have resurrected an earlier\n-\t * packed one.  Also, if it was not loose we need to repack\n-\t * without it.\n-\t */\n-\tret |= repack_without_ref(refname);\n-\n-\terr = unlink(lock->log_file);\n-\tif (err && errno != ENOENT)\n+\tif (unlink(lock->log_file) && errno != ENOENT)\n \t\tfprintf(stderr, \"warning: unlink(%s) failed: %s\",\n \t\t\tlock->log_file, strerror(errno));\n-\tinvalidate_cached_refs();\n+\tif (!lock->del_file)\n+\t\tlock->del_file = get_del_ref_file(ref_name);\n+\tif (safe_create_leading_directories(lock->del_file)) {\n+\t\terror(\"unable to create directory for %s: %s\",\n+\t\t      lock->del_file, strerror(errno));\n+\t\tunlock_ref(lock);\n+\t\treturn -1;\n+\t}\n+\tif (rename(lock->lk->filename, lock->del_file)) {\n+\t\terror(\"rename(%s -> %s) failed: %s\",\n+\t\t      lock->lk->filename, lock->del_file,\n+\t\t      strerror(errno));\n+\t\tunlock_ref(lock);\n+\t\treturn -1;\n+\t}\n+\tlock->lock_fd = -1;\n \tunlock_ref(lock);\n-\treturn ret;\n+\treturn 0;\n }\n \n void unlock_ref(struct ref_lock *lock)\n@@ -710,6 +743,7 @@ void unlock_ref(struct ref_lock *lock)\n \t}\n \tfree(lock->ref_name);\n \tfree(lock->log_file);\n+\tfree(lock->del_file);\n \tfree(lock);\n }\n \n@@ -786,6 +820,12 @@ int write_ref_sha1(struct ref_lock *lock\n \t\tunlock_ref(lock);\n \t\treturn -1;\n \t}\n+\tif (lock->del_file && unlink(lock->del_file) && errno != ENOENT) {\n+\t\terror(\"unlink(%s) failed: %s\",\n+\t\t      lock->del_file, strerror(errno));\n+\t\tunlock_ref(lock);\n+\t\treturn -1;\n+\t}\n \tif (commit_lock_file(lock->lk)) {\n \t\terror(\"Couldn't set %s\", lock->ref_name);\n \t\tunlock_ref(lock);\ndiff --git a/refs.h b/refs.h\nindex a57d437..5aa7755 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -4,6 +4,7 @@ #define REFS_H\n struct ref_lock {\n \tchar *ref_name;\n \tchar *log_file;\n+\tchar *del_file;\n \tstruct lock_file *lk;\n \tunsigned char old_sha1[20];\n \tint lock_fd;\n@@ -16,6 +17,7 @@ struct ref_lock {\n  */\n #define REF_ISSYMREF 01\n #define REF_ISPACKED 02\n+#define REF_ISDELETED 04\n typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data);\n extern int head_ref(each_ref_fn, void *);\n extern int for_each_ref(each_ref_fn, void *);\n-- \n1.4.3.rc2.gbcf275-dirty\n"},{"id":"28770","messageId":"7vr6xa91kj.fsf@assigned-by-dhcp.cox.net","threadId":"5922","inReplyTo":"20061014153949.2994a114.chriscool@tuxfamily.org","subject":"Re: [PATCH 1/2] Delete ref $frotz by moving ref file to \"deleted-$frotz~ref\".","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-10-14T18:47:40Z","receivedAt":"2006-10-14T18:47:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Christian Couder <chriscool@tuxfamily.org> writes:\n\n> The idea is that moving:\n>\n> $GIT_DIR/refs/<refpath>/frotz\n>\n> to:\n>\n> $GIT_DIR/deleted-refs/<refpath>/frotz~ref\n>\n> maybe cheaper and safer than repacking the refs without the\n> deleted one.\n\nBefore actually implementing delete_ref(), we discussed this\n\"deleted-refs/\" idea. but I do not think it is a direction we\nwould want to go.\n\nRef deletion is an operation that happens far far rarer than\nupdates and lookups, and I deliberately chose to optimize for\nthe latter.\n\nThere are valid reasons to delete refs, and one most frequent\none would be to discard throw-away temporary branches you may\nhave needed to switch to when your work was interrupted.  But\neven counting that kind of deletion, I imagine that you would\nnot be creating and removing more than one branch per every 10\ncommits you would create, and I also imagine you would be\ninvoking not less than 5 operations that inspect project\nhistory, such as git-log and git-diff, between commits you make.\n\nAn operation to build a new commit itself needs at least two\nlookups (one to see what's current upfront, and another to see\nnobody touched it upon lockless update).  Most history-related\noperations at least need to look at one (typically, HEAD), and\nany refname you use to spell the name of an object or revision\nrange (e.g. \"v2.6.17..v2.6.18~10\" needs to look at tags/v2.6.17\nand tags/v2.6.18).  Optimizing for deletion path at the expense\nof giving even a tiny penalty to lookup path is optimizing for a\nwrong case, and that is why I rejected deleted-refs/ idea when I\noriginally did the delete_ref() implementation.\n\nHaving said that, I would definitely think there still are rooms\nfor optimization in the current implementation.  For example, I\ndo not recall offhand if I made the code to unconditionally\nrepack without the deleted one, or only repack when we know the\nref being deleted exists in the packed refs file.  The latter\nobviously would be more efficient and if we currently do not do\nthat, making it do so is a very welcomed change.  Especially,\ngiven that the latest code does not pack branch heads by\ndefault, when a temporary throw-away branch is discarded, it is\nfar more likely that it is not packed and we do not need to\nrepack.\n\nIndependent from this topic of \"removing deleted from packed\" vs\n\"using deleted-refs/\", I think we still do not handle .git/logs/\nhierarchy correctly in the current code when ref deletion is\ninvolved.  We already made it to correctly unlink/rmdir/mkdir\non-demand for .git/refs/ hierarchy and I think we need to have\ncode that parallels that for the .git/logs/ side.\n"},{"id":"28898","messageId":"200610170626.25654.chriscool@tuxfamily.org","threadId":"5922","inReplyTo":"7vr6xa91kj.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 1/2] Delete ref $frotz by moving ref file to \"deleted-$frotz~ref\".","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2006-10-17T04:26:25Z","receivedAt":"2006-10-17T04:26:25Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"Junio C Hamano wrote:\n> Christian Couder <chriscool@tuxfamily.org> writes:\n> > The idea is that moving:\n> >\n> > $GIT_DIR/refs/<refpath>/frotz\n> >\n> > to:\n> >\n> > $GIT_DIR/deleted-refs/<refpath>/frotz~ref\n> >\n> > maybe cheaper and safer than repacking the refs without the\n> > deleted one.\n>\n> Before actually implementing delete_ref(), we discussed this\n> \"deleted-refs/\" idea. but I do not think it is a direction we\n> would want to go.\n>\n> Ref deletion is an operation that happens far far rarer than\n> updates and lookups, and I deliberately chose to optimize for\n> the latter.\n\nI think that the ref deletion usage depends on the policy of people using \ngit, and there may be people that delete a ref very often.\n\nFor example, when git becomes a major SCM, there may be people working on \nbig projects that want to create a new branch for each new bug and then \ndelete the branch when the code on the bug branch has been integrated into \na new release and the bug is closed.\n\n> There are valid reasons to delete refs, and one most frequent\n> one would be to discard throw-away temporary branches you may\n> have needed to switch to when your work was interrupted.  But\n> even counting that kind of deletion, I imagine that you would\n> not be creating and removing more than one branch per every 10\n> commits you would create, and I also imagine you would be\n> invoking not less than 5 operations that inspect project\n> history, such as git-log and git-diff, between commits you make.\n\nThe operations that inspect project history may use a ref cache or something \nso that a lookup on the disk may not be needed. So only the ref creation or \nupdate rate versus delete rate may matter.\n\n> An operation to build a new commit itself needs at least two\n> lookups (one to see what's current upfront, and another to see\n> nobody touched it upon lockless update).  Most history-related\n> operations at least need to look at one (typically, HEAD), and\n> any refname you use to spell the name of an object or revision\n> range (e.g. \"v2.6.17..v2.6.18~10\" needs to look at tags/v2.6.17\n> and tags/v2.6.18).  Optimizing for deletion path at the expense\n> of giving even a tiny penalty to lookup path is optimizing for a\n> wrong case, and that is why I rejected deleted-refs/ idea when I\n> originally did the delete_ref() implementation.\n\nThe lookup code is already using cached packed refs. It could also use \ncached loose and deleted refs, so the lookup penalty would be very very \nsmall. By the way, the OS may already cache loose and deleted ref file stat \ninformation, so that may right now be a very small penalty.\n\nAnd at least, algorythmically speaking, with my patch the deletion path is \nnow independent of the number of existing refs, so it's much better (while \nthe lookup path stay the same).\n\nIf there are thousand of refs and a heavy I/O load, rewritting the packed \nref file for each deletion means writing on disk something that may not fit \nin the disk cache. It may be very bad.\n\nMy patch also has a few added benefits like making it possible to have a \nread only packed ref file, while still letting people delete refs. It also \nmake it possible to resurect a deleted ref, or to control branch deletion \nrights on a per ref directory basis (though that may not be very usefull).\n\nAnd the fact that the packed ref file (which may be read only for added \nsafety) is not rewritten each time a ref is deleted make things much safer \nif there are many users working on the same git repository.\n\nChristian.\n"},{"id":"28901","messageId":"7v4pu3tuye.fsf@assigned-by-dhcp.cox.net","threadId":"5922","inReplyTo":"200610170626.25654.chriscool@tuxfamily.org","subject":"Re: [PATCH 1/2] Delete ref $frotz by moving ref file to \"deleted-$frotz~ref\".","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-10-17T04:41:45Z","receivedAt":"2006-10-17T04:41:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Christian Couder <chriscool@tuxfamily.org> writes:\n\n> For example, when git becomes a major SCM, there may be people working on \n> big projects that want to create a new branch for each new bug and then \n> delete the branch when the code on the bug branch has been integrated into \n> a new release and the bug is closed.\n\nI would say that is a very valid way to work with git,\nregardless of the size of project.  Now, how often would you\ncreate such a per-bug branch and delete one, compared to the\nnumber of operations that would require ref lookups?  Your\nexample actually supports what I've said -- optimizing for\ndeletion at the cost of more expensive lookups is wrong.\n\n> The operations that inspect project history may use a ref cache or something \n> so that a lookup on the disk may not be needed. So only the ref creation or \n> update rate versus delete rate may matter.\n\nStop and think about what you are saying.  What's a ref cache?\nWe do not have such a beast today (unless you equate it with\npacked-refs file), and you would need to design and implement\nit, but think about how you make that operate.  You would need\nto invalidate it when you delete a ref using the deleted-ref/\napproach; that's not much different from repacking packed-refs\nfile without the ref you just deleted, no?\n\nOf course you can argue that instead of repacking you always\nstat deleted-ref/ hierarchy; in other words, you can argue that\nyou can make deletion path faster by penalizing the lookup path.\n\nSo I do not think using \"ref cache\" (whatever it is, and however\nit operates) does not change the situation a bit.\n\n> If there are thousand of refs and a heavy I/O load, rewritting the packed \n> ref file for each deletion means writing on disk something that may not fit \n> in the disk cache. It may be very bad.\n\nIf the goal is to optimize for deletion path, then that is\ntrue.  My point is that we do not want to optimize for deletion\npath at the expense of more costly lookup path.\n"},{"id":"28904","messageId":"20061017050714.GA21210@spearce.org","threadId":"5922","inReplyTo":"7v4pu3tuye.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 1/2] Delete ref $frotz by moving ref file to \"deleted-$frotz~ref\".","fromName":"Shawn Pearce","fromEmail":"spearce@spearce.org","sentAt":"2006-10-17T05:07:14Z","receivedAt":"2006-10-17T05:07:14Z","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> Christian Couder <chriscool@tuxfamily.org> writes:\n> \n> > For example, when git becomes a major SCM, there may be people working on \n> > big projects that want to create a new branch for each new bug and then \n> > delete the branch when the code on the bug branch has been integrated into \n> > a new release and the bug is closed.\n> \n> I would say that is a very valid way to work with git,\n> regardless of the size of project.  Now, how often would you\n> create such a per-bug branch and delete one, compared to the\n> number of operations that would require ref lookups?  Your\n> example actually supports what I've said -- optimizing for\n> deletion at the cost of more expensive lookups is wrong.\n\nI agree completely with Junio.  I make a lot of temporary \"throw\naway\" branches in Git; often they live on disk for 5/10 minutes at\nmost before getting deleted again.  I also make a smaller number\n(but still significant) of longer lived branches that hang around\nfor days or weeks before getting deleted.\n\nIn the former case (throw away) I wouldn't want those refs added\nto the packed refs file.  They just don't live around long enough\nto make it worth it.  And when I delete them I want them gone.\nSo moving them off to a 'deleted-refs' directory to indicate they\nare gone is just delaying the removal.  Not something I want.\n\nIn the latter case (longer lived) I don't mind if I have to sit\nthough an extra 500 ms to rewrite the entire packed refs file\nduring a ref delete operation.  I lived with the branch for weeks;\nI can probably spare a second to finally get rid of it once its\ngone upstream.  Heck, the push to move that branch upstream might\nactually take longer to unpack the loose objects contained on that\nbranch than the packed ref delete, even on 1000s of refs.\n \n> If the goal is to optimize for deletion path, then that is\n> true.  My point is that we do not want to optimize for deletion\n> path at the expense of more costly lookup path.\n\nAbsolutely.  I figure I do ref lookups at least 3x the number of ref\ndeletes I perform.  And that's just thinking about the sequence of\ncommands I commonly perform against my \"throw away\" branches which\nlive for at most 10 minutes, let alone my longer lived branches\nthat hang around for weeks.\n\n-- \nShawn.\n"},{"id":"28924","messageId":"eh27f9$uog$1@sea.gmane.org","threadId":"5922","inReplyTo":"200610170626.25654.chriscool@tuxfamily.org","subject":"Re: [PATCH 1/2] Delete ref $frotz by moving ref file to \"deleted-$frotz~ref\".","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-10-17T09:24:24Z","receivedAt":"2006-10-17T09:24:24Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Christian Couder wrote:\n\n>> Ref deletion is an operation that happens far far rarer than\n>> updates and lookups, and I deliberately chose to optimize for\n>> the latter.\n> \n> I think that the ref deletion usage depends on the policy of people using \n> git, and there may be people that delete a ref very often.\n> \n> For example, when git becomes a major SCM, there may be people working on \n> big projects that want to create a new branch for each new bug and then \n> delete the branch when the code on the bug branch has been integrated into \n> a new release and the bug is closed.\n\nSo do not pack branches then, pack only tags.\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"}]}