{"thread":{"id":"25977","subject":"[PATCH] logging branch deletion to help recovering from mistakes","startedAt":"2010-12-06T21:16:18Z","lastAt":"2010-12-07T20:55:26Z","messageCount":28,"participants":["Junio C Hamano","Štěpán Němec","Andreas Schwab","Nguyen Thai Ngoc Duy","Michael J Gruber","Casey Dahlin","Jakub Narebski","Jeff King","Jonathan Nieder","Shawn Pearce","Shawn O. Pearce"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"157431","messageId":"7vlj42siu5.fsf@alter.siamese.dyndns.org","threadId":"25977","inReplyTo":null,"subject":"[PATCH] logging branch deletion to help recovering from mistakes","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-12-06T21:16:18Z","receivedAt":"2010-12-06T21:16:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This adds core.logrefdeletion configuration variable (enabled by default\nin a repository with a work tree, just like core.logallrefupdates), and\nlogs deletion of refs via \"update-ref -d\", \"branch -d\", etc.\n\n\"git branch\" learns a new \"--list-deleted\" option to help users view the\nnames of branches and the commit objects that were at the tip of them when\nthe branches were deleted.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n\n * This recently came up at $dayjob.  The new option is not '--undelete'\n   and this is deliberate, as we do not have any information other than\n   the tip of the branch to recreate tracking and other frills.\n\n Documentation/config.txt             |    6 +\n Documentation/git-branch.txt         |    6 +\n builtin/branch.c                     |   21 ++++-\n cache.h                              |    1 +\n config.c                             |    5 +\n environment.c                        |    1 +\n refs.c                               |  167 +++++++++++++++++++++++++---------\n refs.h                               |    2 +\n t/t1400-update-ref.sh                |   53 ++++++++++-\n t/t7701-repack-unpack-unreachable.sh |    4 +\n 10 files changed, 219 insertions(+), 47 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex d82c0da..bdf90eb 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -351,6 +351,12 @@ This value is true by default in a repository that has\n a working directory associated with it, and false by\n default in a bare repository.\n \n+core.logRefDeletion::\n+\tEnable logging of eletion of refs (e.g. branches), allowing `git\n+\tbranch --list-deleted` to help you recover branches lost by\n+\trunning `git branch -d` by mistake.  This is enabled in a\n+\trepository that has a working tree associated with it by default.\n+\n core.repositoryFormatVersion::\n \tInternal variable identifying the repository format and layout\n \tversion.\ndiff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\nindex 1940256..07ec47b 100644\n--- a/Documentation/git-branch.txt\n+++ b/Documentation/git-branch.txt\n@@ -14,6 +14,7 @@ SYNOPSIS\n 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]\n 'git branch' (-m | -M) [<oldbranch>] <newbranch>\n 'git branch' (-d | -D) [-r] <branchname>...\n+'git branch' --list-deleted [<pattern>...]\n \n DESCRIPTION\n -----------\n@@ -70,6 +71,11 @@ OPTIONS\n -D::\n \tDelete a branch irrespective of its merged status.\n \n+--list-deleted::\n+\tList names of recently deleted branches together with the object\n+\tnames of the commits that were at the tip of them.  Glob patterns\n+\tcan be used to limit the branches that are shown.\n+\n -l::\n \tCreate the branch's reflog.  This activates recording of\n \tall changes made to the branch ref, enabling use of date\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex 87976f0..68604c2 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -15,6 +15,7 @@\n #include \"branch.h\"\n #include \"diff.h\"\n #include \"revision.h\"\n+#include \"string-list.h\"\n \n static const char * const builtin_branch_usage[] = {\n \t\"git branch [options] [-r | -a] [--merged | --no-merged]\",\n@@ -144,6 +145,19 @@ static int branch_merged(int kind, const char *name,\n \treturn merged;\n }\n \n+static int show_deleted(struct string_list_item *item, void *cb_data)\n+{\n+\tprintf(\"%s %s\\n\", (char *)item->util, item->string);\n+\treturn 0;\n+}\n+\n+static int list_deleted_branches(const char **argv)\n+{\n+\tstruct string_list *deleted = list_deleted_refs(\"refs/heads/\", argv);\n+\tfor_each_string_list(deleted, show_deleted, NULL);\n+\treturn 0;\n+}\n+\n static int delete_branches(int argc, const char **argv, int force, int kinds)\n {\n \tstruct commit *rev, *head_rev = NULL;\n@@ -612,7 +626,7 @@ static int opt_parse_merge_filter(const struct option *opt, const char *arg, int\n \n int cmd_branch(int argc, const char **argv, const char *prefix)\n {\n-\tint delete = 0, rename = 0, force_create = 0;\n+\tint delete = 0, rename = 0, force_create = 0, list_deleted = 0;\n \tint verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0;\n \tint reflog = 0;\n \tenum branch_track track;\n@@ -652,6 +666,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t\tOPT_BIT('M', NULL, &rename, \"move/rename a branch, even if target exists\", 2),\n \t\tOPT_BOOLEAN('l', NULL, &reflog, \"create the branch's reflog\"),\n \t\tOPT_BOOLEAN('f', \"force\", &force_create, \"force creation (when already exists)\"),\n+\t\tOPT_BOOLEAN(0, \"list-deleted\", &list_deleted, \"list deleted branches\"),\n \t\t{\n \t\t\tOPTION_CALLBACK, 0, \"no-merged\", &merge_filter_ref,\n \t\t\t\"commit\", \"print only not merged branches\",\n@@ -689,11 +704,13 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \n \targc = parse_options(argc, argv, prefix, options, builtin_branch_usage,\n \t\t\t     0);\n-\tif (!!delete + !!rename + !!force_create > 1)\n+\tif (!!list_deleted + !!delete + !!rename + !!force_create > 1)\n \t\tusage_with_options(builtin_branch_usage, options);\n \n \tif (delete)\n \t\treturn delete_branches(argc, argv, delete > 1, kinds);\n+\telse if (list_deleted)\n+\t\treturn list_deleted_branches(argv);\n \telse if (argc == 0)\n \t\treturn print_ref_list(kinds, detached, verbose, abbrev, with_commit);\n \telse if (rename && (argc == 1))\ndiff --git a/cache.h b/cache.h\nindex 2ef2fa3..0a82612 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -537,6 +537,7 @@ extern int ignore_case;\n extern int assume_unchanged;\n extern int prefer_symlink_refs;\n extern int log_all_ref_updates;\n+extern int log_ref_deletion;\n extern int warn_ambiguous_refs;\n extern int shared_repository;\n extern const char *apply_default_whitespace;\ndiff --git a/config.c b/config.c\nindex 4b0a820..cfa162a 100644\n--- a/config.c\n+++ b/config.c\n@@ -509,6 +509,11 @@ static int git_default_core_config(const char *var, const char *value)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"core.logrefdeletion\")) {\n+\t\tlog_ref_deletion = git_config_bool(var, value);\n+\t\treturn 0;\n+\t}\n+\n \tif (!strcmp(var, \"core.warnambiguousrefs\")) {\n \t\twarn_ambiguous_refs = git_config_bool(var, value);\n \t\treturn 0;\ndiff --git a/environment.c b/environment.c\nindex 2d0c315..12166d9 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -20,6 +20,7 @@ int assume_unchanged;\n int prefer_symlink_refs;\n int is_bare_repository_cfg = -1; /* unspecified */\n int log_all_ref_updates = -1; /* unspecified */\n+int log_ref_deletion = -1; /* unspecified */\n int warn_ambiguous_refs = 1;\n int repository_format_version;\n const char *git_commit_encoding;\ndiff --git a/refs.c b/refs.c\nindex e3c0511..afdd634 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -3,11 +3,14 @@\n #include \"object.h\"\n #include \"tag.h\"\n #include \"dir.h\"\n+#include \"string-list.h\"\n \n /* ISSYMREF=01 and ISPACKED=02 are public interfaces */\n #define REF_KNOWS_PEELED 04\n #define REF_BROKEN 010\n \n+#define BRANCH_DELETION_LOG \"DELETED-REFS\"\n+\n struct ref_list {\n \tstruct ref_list *next;\n \tunsigned char flag; /* ISSYMREF? ISPACKED? */\n@@ -1137,44 +1140,6 @@ static int repack_without_ref(const char *refname)\n \treturn commit_lock_file(&packlock);\n }\n \n-int delete_ref(const char *refname, const unsigned char *sha1, int delopt)\n-{\n-\tstruct ref_lock *lock;\n-\tint err, i = 0, ret = 0, flag = 0;\n-\n-\tlock = lock_ref_sha1_basic(refname, sha1, 0, &flag);\n-\tif (!lock)\n-\t\treturn 1;\n-\tif (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) {\n-\t\t/* loose */\n-\t\tconst char *path;\n-\n-\t\tif (!(delopt & REF_NODEREF)) {\n-\t\t\ti = strlen(lock->lk->filename) - 5; /* .lock */\n-\t\t\tlock->lk->filename[i] = 0;\n-\t\t\tpath = lock->lk->filename;\n-\t\t} else {\n-\t\t\tpath = git_path(\"%s\", refname);\n-\t\t}\n-\t\terr = unlink_or_warn(path);\n-\t\tif (err && errno != ENOENT)\n-\t\t\tret = 1;\n-\n-\t\tif (!(delopt & REF_NODEREF))\n-\t\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-\tunlink_or_warn(git_path(\"logs/%s\", lock->ref_name));\n-\tinvalidate_cached_refs();\n-\tunlock_ref(lock);\n-\treturn ret;\n-}\n-\n /*\n  * People using contrib's git-new-workdir have .git/logs/refs ->\n  * /some/other/path/.git/logs/refs, and that may live on another device.\n@@ -1361,11 +1326,13 @@ int log_ref_setup(const char *ref_name, char *logfile, int bufsize)\n \tint logfd, oflags = O_APPEND | O_WRONLY;\n \n \tgit_snpath(logfile, bufsize, \"logs/%s\", ref_name);\n-\tif (log_all_ref_updates &&\n-\t    (!prefixcmp(ref_name, \"refs/heads/\") ||\n-\t     !prefixcmp(ref_name, \"refs/remotes/\") ||\n-\t     !prefixcmp(ref_name, \"refs/notes/\") ||\n-\t     !strcmp(ref_name, \"HEAD\"))) {\n+\tif ((log_all_ref_updates &&\n+\t     (!prefixcmp(ref_name, \"refs/heads/\") ||\n+\t      !prefixcmp(ref_name, \"refs/remotes/\") ||\n+\t      !prefixcmp(ref_name, \"refs/notes/\") ||\n+\t      !strcmp(ref_name, \"HEAD\"))) ||\n+\t    (log_ref_deletion &&\n+\t     !strcmp(ref_name, BRANCH_DELETION_LOG))) {\n \t\tif (safe_create_leading_directories(logfile) < 0)\n \t\t\treturn error(\"unable to create directory for %s\",\n \t\t\t\t     logfile);\n@@ -1407,6 +1374,8 @@ static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,\n \n \tif (log_all_ref_updates < 0)\n \t\tlog_all_ref_updates = !is_bare_repository();\n+\tif (log_ref_deletion < 0)\n+\t\tlog_ref_deletion = !is_bare_repository();\n \n \tresult = log_ref_setup(ref_name, log_file, sizeof(log_file));\n \tif (result)\n@@ -1432,6 +1401,118 @@ static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,\n \treturn 0;\n }\n \n+struct filter_deleted {\n+\tconst char *pfx;\n+\tconst char **pattern;\n+\tsize_t pfxlen;\n+\tstruct string_list *list;\n+};\n+\n+static int collect_deleted(unsigned char *osha1, unsigned char *nsha1,\n+\t\t\t   const char *ident,\n+\t\t\t   unsigned long timestamp, int tz,\n+\t\t\t   const char *msg, void *cb_data)\n+{\n+\tstruct filter_deleted *filter = cb_data;\n+\tstruct string_list_item *item;\n+\tconst char *nameloc;\n+\tchar *namebody;\n+\tchar namebody_buf[1024];\n+\tsize_t namebodylen;\n+\n+\tif (prefixcmp(msg, \"delete \") ||\n+\t    memcmp(msg + 7, filter->pfx, filter->pfxlen) ||\n+\t    !is_null_sha1(nsha1))\n+\t\treturn 0;\n+\tnameloc = msg + 7 + filter->pfxlen;\n+\tnamebodylen = strlen(nameloc); /* counts final LF */\n+\tif (!namebodylen || nameloc[namebodylen - 1] != '\\n')\n+\t\treturn 0;\n+\tif (sizeof(namebody_buf) <= namebodylen)\n+\t\tnamebody = xmalloc(namebodylen);\n+\telse\n+\t\tnamebody = namebody_buf;\n+\tmemcpy(namebody, nameloc, namebodylen);\n+\tnamebody[namebodylen - 1] = '\\0';\n+\tif (filter->pattern[0]) {\n+\t\tint i, matches;\n+\t\tfor (i = matches = 0; !matches && filter->pattern[i]; i++)\n+\t\t\tif (fnmatch(filter->pattern[i], namebody, 0))\n+\t\t\t\tmatches = 1;\n+\t\tif (!matches)\n+\t\t\tgoto free_return;\n+\t}\n+\titem = string_list_insert(filter->list, namebody);\n+\tif (!item->util)\n+\t\titem->util = xmalloc(41);\n+\tstrcpy(item->util, sha1_to_hex(osha1));\n+free_return:\n+\tif (namebody != namebody_buf)\n+\t\tfree(namebody);\n+\treturn 0;\n+}\n+\n+struct string_list *list_deleted_refs(const char *pfx, const char **pattern)\n+{\n+\tstruct filter_deleted filter = { pfx, pattern };\n+\tfilter.list = xcalloc(1, sizeof(*(filter.list)));\n+\tfilter.list->strdup_strings = 1;\n+\tfilter.pfxlen = strlen(pfx);\n+\n+\tfor_each_reflog_ent(BRANCH_DELETION_LOG, collect_deleted, &filter);\n+\treturn filter.list;\n+}\n+\n+int delete_ref(const char *refname, const unsigned char *sha1, int delopt)\n+{\n+\tstruct ref_lock *lock;\n+\tint err, i = 0, ret = 0, flag = 0;\n+\tstruct strbuf logmsg = STRBUF_INIT;\n+\n+\tlock = lock_ref_sha1_basic(refname, sha1, 0, &flag);\n+\tif (!lock)\n+\t\treturn 1;\n+\tif (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) {\n+\t\t/* loose */\n+\t\tconst char *path;\n+\n+\t\tif (!(delopt & REF_NODEREF)) {\n+\t\t\ti = strlen(lock->lk->filename) - 5; /* .lock */\n+\t\t\tlock->lk->filename[i] = 0;\n+\t\t\tpath = lock->lk->filename;\n+\t\t} else {\n+\t\t\tpath = git_path(\"%s\", refname);\n+\t\t}\n+\t\terr = unlink_or_warn(path);\n+\t\tif (err && errno != ENOENT)\n+\t\t\tret = 1;\n+\n+\t\tif (!(delopt & REF_NODEREF))\n+\t\t\tlock->lk->filename[i] = '.';\n+\t}\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+\tunlink_or_warn(git_path(\"logs/%s\", lock->ref_name));\n+\tinvalidate_cached_refs();\n+\n+\tstrbuf_addf(&logmsg, \"delete %s\", refname);\n+\tif (log_ref_write(BRANCH_DELETION_LOG,\n+\t\t\t  lock->old_sha1, null_sha1, logmsg.buf))\n+\t\t/*\n+\t\t * there isn't much we can do at this point upon\n+\t\t * failing to record the branch deletion, and an error\n+\t\t * messages have been already issued.\n+\t\t */\n+\t\t;\n+\tunlock_ref(lock);\n+\treturn ret;\n+}\n+\n static int is_branch(const char *refname)\n {\n \treturn !strcmp(refname, \"HEAD\") || !prefixcmp(refname, \"refs/heads/\");\ndiff --git a/refs.h b/refs.h\nindex 5e7a9a5..9fcf7ac 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -87,6 +87,8 @@ typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, const\n int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data);\n int for_each_recent_reflog_ent(const char *ref, each_reflog_ent_fn fn, long, void *cb_data);\n \n+struct string_list *list_deleted_refs(const char *prefix, const char **pattern);\n+\n /*\n  * Calls the specified function for each reflog file until it returns nonzero,\n  * and returns the value\ndiff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh\nindex 54ba3df..ad9f461 100755\n--- a/t/t1400-update-ref.sh\n+++ b/t/t1400-update-ref.sh\n@@ -9,6 +9,7 @@ test_description='Test git update-ref and basic ref logging'\n Z=0000000000000000000000000000000000000000\n \n test_expect_success setup '\n+\tgit config core.logrefdeletion false &&\n \n \tfor name in A B C D E F\n \tdo\n@@ -38,7 +39,8 @@ test_expect_success \"fail to delete $m with stale ref\" '\n '\n test_expect_success \"delete $m\" '\n \tgit update-ref -d $m $B &&\n-\t! test -f .git/$m\n+\t! test -f .git/$m &&\n+\t! test -f .git/logs/DELETED-REFS\n '\n rm -f .git/$m\n \n@@ -46,7 +48,8 @@ test_expect_success \"delete $m without oldvalue verification\" \"\n \tgit update-ref $m $A &&\n \ttest $A = \\$(cat .git/$m) &&\n \tgit update-ref -d $m &&\n-\t! test -f .git/$m\n+\t! test -f .git/$m &&\n+\t! test -f .git/logs/DELETED-REFS\n \"\n rm -f .git/$m\n \n@@ -285,4 +288,50 @@ test_expect_success \\\n \t'git cat-file blob master@{2005-05-26 23:42}:F (expect OTHER)' \\\n \t'test OTHER = $(git cat-file blob \"master@{2005-05-26 23:42}:F\")'\n \n+test_expect_success 'reflog for deletion' '\n+\tgit config core.logrefdeletion yes &&\n+\n+\tgit branch frotz HEAD &&\n+\tgit branch nitfol $A &&\n+\tgit branch xyzzy $B &&\n+\n+\tgit branch -d frotz &&\n+\tgit branch -D nitfol &&\n+\tgit update-ref -d refs/heads/xyzzy &&\n+\t{\n+\t\techo \"$(git rev-parse HEAD) frotz\"\n+\t\techo \"$A nitfol\"\n+\t\techo \"$B xyzzy\"\n+\t} >expect &&\n+\tgit branch --list-deleted >actual &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'reflog for deletion disabled by default in a bare repo' '\n+\trm -fr test &&\n+\tmkdir test &&\n+\t(\n+\t\tcd test && git --bare init &&\n+\t\tgit fetch .. HEAD:refs/heads/master HEAD:refs/heads/slave &&\n+\t\tgit update-ref -d refs/heads/slave &&\n+\t\tgit branch --list-deleted >actual &&\n+\t\t>expect &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n+test_expect_success 'reflog for deletion can be enabled in a bare repo' '\n+\trm -fr test &&\n+\tmkdir test &&\n+\t(\n+\t\tcd test && git --bare init &&\n+\t\tgit config core.logrefdeletion yes &&\n+\t\tgit fetch .. HEAD:refs/heads/master HEAD:refs/heads/slave &&\n+\t\tgit update-ref -d refs/heads/slave &&\n+\t\tgit branch --list-deleted >actual &&\n+\t\techo \"$(git rev-parse master) slave\" >expect &&\n+\t\ttest_cmp expect actual\n+\t)\n+'\n+\n test_done\ndiff --git a/t/t7701-repack-unpack-unreachable.sh b/t/t7701-repack-unpack-unreachable.sh\nindex 200ab61..f6209d2 100755\n--- a/t/t7701-repack-unpack-unreachable.sh\n+++ b/t/t7701-repack-unpack-unreachable.sh\n@@ -9,6 +9,10 @@ csha1=\n tsha1=\n \n test_expect_success '-A with -d option leaves unreachable objects unpacked' '\n+\t# The test expects branch removal to lose the last reference to\n+\t# lost objects.  Disable branch deletion log to achieve this.\n+\tgit config core.logrefdeletion false &&\n+\n \techo content > file1 &&\n \tgit add . &&\n \ttest_tick &&\n"},{"id":"157439","messageId":"87zksi5zx8.fsf@gmail.com","threadId":"25977","inReplyTo":"7vlj42siu5.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] logging branch deletion to help recovering from mistakes","fromName":"Štěpán Němec","fromEmail":"stepnem@gmail.com","sentAt":"2010-12-06T21:55:47Z","receivedAt":"2010-12-06T21:55:47Z","isPatch":true,"sender":{"key":"stepnem@gmail.com","avatar":"https://avatars.githubusercontent.com/u/106838?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n[...]\n\nJust two cosmetic nits I noticed:\n\n> diff --git a/Documentation/config.txt b/Documentation/config.txt\n> index d82c0da..bdf90eb 100644\n> --- a/Documentation/config.txt\n> +++ b/Documentation/config.txt\n> @@ -351,6 +351,12 @@ This value is true by default in a repository that has\n>  a working directory associated with it, and false by\n>  default in a bare repository.\n>  \n> +core.logRefDeletion::\n> +\tEnable logging of eletion of refs (e.g. branches), allowing `git\n                          ^^^^^^^\n\n[...]\n\n> diff --git a/refs.c b/refs.c\n> index e3c0511..afdd634 100644\n> --- a/refs.c\n> +++ b/refs.c\n\n[...]\n\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\nCould also the first sentence start with a capital letter?\n\n[...]\n\n> +\t\tif (!(delopt & REF_NODEREF))\n> +\t\t\tlock->lk->filename[i] = '.';\n> +\t}\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\nSame here.\n\nThank you,\n\n  Štěpán\n"},{"id":"157456","messageId":"m2hbeqv6ip.fsf@igel.home","threadId":"25977","inReplyTo":"7vlj42siu5.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] logging branch deletion to help recovering from mistakes","fromName":"Andreas Schwab","fromEmail":"schwab@linux-m68k.org","sentAt":"2010-12-06T23:14:06Z","receivedAt":"2010-12-06T23:14:06Z","isPatch":true,"sender":{"key":"schwab@linux-m68k.org","avatar":"https://avatars.githubusercontent.com/u/2175493?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> +core.logRefDeletion::\n> +\tEnable logging of eletion of refs (e.g. branches), allowing `git\n\n                         +d\n\nAndreas.\n\n-- \nAndreas Schwab, schwab@linux-m68k.org\nGPG Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5\n\"And now for something completely different.\"\n"},{"id":"157472","messageId":"AANLkTikbsyFUzZeu8R6yAND6spV6OnvYL08gYZ+ZgJCh@mail.gmail.com","threadId":"25977","inReplyTo":"7vlj42siu5.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] logging branch deletion to help recovering from mistakes","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-07T01:18:28Z","receivedAt":"2010-12-07T01:18:28Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Dec 7, 2010 at 4:16 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> +#define BRANCH_DELETION_LOG \"DELETED-REFS\"\n> +\n\nShould this special log be mentioned in git-update-ref.txt or\ngitrepository-layout.txt?\n-- \nDuy\n"},{"id":"157476","messageId":"7vmxoiqeoq.fsf@alter.siamese.dyndns.org","threadId":"25977","inReplyTo":"AANLkTikbsyFUzZeu8R6yAND6spV6OnvYL08gYZ+ZgJCh@mail.gmail.com","subject":"Re: [PATCH] logging branch deletion to help recovering from mistakes","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-12-07T06:28:53Z","receivedAt":"2010-12-07T06:28:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n\n> On Tue, Dec 7, 2010 at 4:16 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> +#define BRANCH_DELETION_LOG \"DELETED-REFS\"\n>> +\n>\n> Should this special log be mentioned in git-update-ref.txt or\n> gitrepository-layout.txt?\n\nPerhaps, but I wasn't sure if this patch itself is a good idea to begin\nwith.  Not the problem it tries to solve, but its approach.\n\nFor example, this cannot be shown with \"reflog show\" or \"log -g\" due to\nthe way these frontends locate the reflog file to read (the logic wants to\nhave an underlying ref).\n"},{"id":"157483","messageId":"AANLkTinDyix3KEdLLGJEWQ8X+a3zQZOAiTh2mLf5wuvQ@mail.gmail.com","threadId":"25977","inReplyTo":"7vmxoiqeoq.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] logging branch deletion to help recovering from mistakes","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-07T11:37:01Z","receivedAt":"2010-12-07T11:37:01Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Dec 7, 2010 at 1:28 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>\n>> On Tue, Dec 7, 2010 at 4:16 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>>> +#define BRANCH_DELETION_LOG \"DELETED-REFS\"\n>>> +\n>>\n>> Should this special log be mentioned in git-update-ref.txt or\n>> gitrepository-layout.txt?\n>\n> Perhaps, but I wasn't sure if this patch itself is a good idea to begin\n> with.  Not the problem it tries to solve, but its approach.\n>\n> For example, this cannot be shown with \"reflog show\" or \"log -g\" due to\n> the way these frontends locate the reflog file to read (the logic wants to\n> have an underlying ref).\n>\n\nI think you have thought of this. What's wrong with keeping reflog\nwhen a branch is removed and appending \"delete\" line to the said\nreflog? I don't know how reflogs are managed, but those reflogs\nwithout associated branch will (or should) be cleaned when they are\nexpired.\n\nI stick with this idea because I also want to archive old branches and\nam thinking those reflogs ending with \"archive\" line will be kept\nforever, or until I feel like digging up them again.\n-- \nDuy\n"},{"id":"157488","messageId":"4CFE51FA.3060104@drmicha.warpmail.net","threadId":"25977","inReplyTo":"AANLkTinDyix3KEdLLGJEWQ8X+a3zQZOAiTh2mLf5wuvQ@mail.gmail.com","subject":"Re: [PATCH] logging branch deletion to help recovering from mistakes","fromName":"Michael J Gruber","fromEmail":"git@drmicha.warpmail.net","sentAt":"2010-12-07T15:25:46Z","receivedAt":"2010-12-07T15:25:46Z","isPatch":true,"sender":{"key":"git@grubix.eu","avatar":"https://avatars.githubusercontent.com/u/233215?v=4"},"body":"Nguyen Thai Ngoc Duy venit, vidit, dixit 07.12.2010 12:37:\n> On Tue, Dec 7, 2010 at 1:28 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>>\n>>> On Tue, Dec 7, 2010 at 4:16 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>>>> +#define BRANCH_DELETION_LOG \"DELETED-REFS\"\n>>>> +\n>>>\n>>> Should this special log be mentioned in git-update-ref.txt or\n>>> gitrepository-layout.txt?\n>>\n>> Perhaps, but I wasn't sure if this patch itself is a good idea to begin\n>> with.  Not the problem it tries to solve, but its approach.\n>>\n>> For example, this cannot be shown with \"reflog show\" or \"log -g\" due to\n>> the way these frontends locate the reflog file to read (the logic wants to\n>> have an underlying ref).\n>>\n> \n> I think you have thought of this. What's wrong with keeping reflog\n> when a branch is removed and appending \"delete\" line to the said\n> reflog? I don't know how reflogs are managed, but those reflogs\n> without associated branch will (or should) be cleaned when they are\n> expired.\n\nThe problem is the following:\n\nSay, you delete a branch and its reflog is kept (with a \"delete\" line\nappended).\n\nThen you create a new branch under the same name. What is supposed to\nhappen to the reflog? If you simply append, then old (unrelated) entries\nwill not expire through the imagined \"expire branch reflogs\" mechanism.\n\nNow, you rename that branch. We should really split the reflog in two\nnow, keeping the old name for the old parts and moving only the newer\nparts to the reflog with the new name.\n\nThis is all workable in principle but hints at a design flaw.\n\nMaybe it's easier to teach \"git reflog\" about \"DELETED_REFS\"?\n\nMichael\n"},{"id":"157493","messageId":"m3fwu9365i.fsf@localhost.localdomain","threadId":"25977","inReplyTo":"AANLkTinDyix3KEdLLGJEWQ8X+a3zQZOAiTh2mLf5wuvQ@mail.gmail.com","subject":"Re: [PATCH] logging branch deletion to help recovering from mistakes","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2010-12-07T16:22:54Z","receivedAt":"2010-12-07T16:22:54Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n> On Tue, Dec 7, 2010 at 1:28 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>>\n>>> On Tue, Dec 7, 2010 at 4:16 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>>>> +#define BRANCH_DELETION_LOG \"DELETED-REFS\"\n>>>> +\n>>>\n>>> Should this special log be mentioned in git-update-ref.txt or\n>>> gitrepository-layout.txt?\n>>\n>> Perhaps, but I wasn't sure if this patch itself is a good idea to begin\n>> with.  Not the problem it tries to solve, but its approach.\n>>\n>> For example, this cannot be shown with \"reflog show\" or \"log -g\" due to\n>> the way these frontends locate the reflog file to read (the logic wants to\n>> have an underlying ref).\n>>\n> \n> I think you have thought of this. What's wrong with keeping reflog\n> when a branch is removed and appending \"delete\" line to the said\n> reflog? I don't know how reflogs are managed, but those reflogs\n> without associated branch will (or should) be cleaned when they are\n> expired.\n>\n> I stick with this idea because I also want to archive old branches and\n> am thinking those reflogs ending with \"archive\" line will be kept\n> forever, or until I feel like digging up them again.\n\nThe problem with this idea is deleting branch 'foo' and creating 'foo/bar',\nor deleting branch 'foo/bar' and creating branch 'foo'.  Old reflog with\n\"delete\" line would block creating reflog for new branch.\n\n-- \nJakub Narebski\nPoland\nShadeHawk on #git\n"},{"id":"157492","messageId":"20101207162358.GT355@fearengine.rdu.redhat.com","threadId":"25977","inReplyTo":"7vlj42siu5.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] logging branch deletion to help recovering from mistakes","fromName":"Casey Dahlin","fromEmail":"cdahlin@redhat.com","sentAt":"2010-12-07T16:23:58Z","receivedAt":"2010-12-07T16:23:58Z","isPatch":true,"sender":{"key":"cdahlin@redhat.com","avatar":null},"body":"On Mon, Dec 06, 2010 at 01:16:18PM -0800, Junio C Hamano wrote:\n> This adds core.logrefdeletion configuration variable (enabled by default\n> in a repository with a work tree, just like core.logallrefupdates), and\n> logs deletion of refs via \"update-ref -d\", \"branch -d\", etc.\n> \n> \"git branch\" learns a new \"--list-deleted\" option to help users view the\n> names of branches and the commit objects that were at the tip of them when\n> the branches were deleted.\n> \n\nCould commits made onto a detached head also show up here? Or is that\nbetter thwarted with another mechanism?\n\n--CJD\n"},{"id":"157494","messageId":"AANLkTi=7P4AQOyhCMZTKrrK5hYUORqRUW0ec_gdSO-LM@mail.gmail.com","threadId":"25977","inReplyTo":"4CFE51FA.3060104@drmicha.warpmail.net","subject":"Re: [PATCH] logging branch deletion to help recovering from mistakes","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-07T16:25:31Z","receivedAt":"2010-12-07T16:25:31Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Dec 7, 2010 at 10:25 PM, Michael J Gruber\n<git@drmicha.warpmail.net> wrote:\n> Nguyen Thai Ngoc Duy venit, vidit, dixit 07.12.2010 12:37:\n>> On Tue, Dec 7, 2010 at 1:28 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>>> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>>>\n>>>> On Tue, Dec 7, 2010 at 4:16 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>>>>> +#define BRANCH_DELETION_LOG \"DELETED-REFS\"\n>>>>> +\n>>>>\n>>>> Should this special log be mentioned in git-update-ref.txt or\n>>>> gitrepository-layout.txt?\n>>>\n>>> Perhaps, but I wasn't sure if this patch itself is a good idea to begin\n>>> with.  Not the problem it tries to solve, but its approach.\n>>>\n>>> For example, this cannot be shown with \"reflog show\" or \"log -g\" due to\n>>> the way these frontends locate the reflog file to read (the logic wants to\n>>> have an underlying ref).\n>>>\n>>\n>> I think you have thought of this. What's wrong with keeping reflog\n>> when a branch is removed and appending \"delete\" line to the said\n>> reflog? I don't know how reflogs are managed, but those reflogs\n>> without associated branch will (or should) be cleaned when they are\n>> expired.\n>\n> The problem is the following:\n>\n> Say, you delete a branch and its reflog is kept (with a \"delete\" line\n> appended).\n>\n> Then you create a new branch under the same name. What is supposed to\n> happen to the reflog? If you simply append, then old (unrelated) entries\n> will not expire through the imagined \"expire branch reflogs\" mechanism.\n>\n> Now, you rename that branch. We should really split the reflog in two\n> now, keeping the old name for the old parts and moving only the newer\n> parts to the reflog with the new name.\n\nI don't see any problems with that. If I happen to create a branch\nwith the same name, most of the time, there is something related,\nunless for very generic names like \"tmp\". We can always notify users\nabout the accident resurrection of an old branch at branch creation,\nso they can remove the old reflog if they want.\n-- \nDuy\n"},{"id":"157495","messageId":"AANLkTi=Z1cM1A+z3xVYsNeOyJOXuYzFQoRemSrUDJ=04@mail.gmail.com","threadId":"25977","inReplyTo":"m3fwu9365i.fsf@localhost.localdomain","subject":"Re: [PATCH] logging branch deletion to help recovering from mistakes","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2010-12-07T16:26:47Z","receivedAt":"2010-12-07T16:26:47Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Dec 7, 2010 at 11:22 PM, Jakub Narebski <jnareb@gmail.com> wrote:\n> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>> On Tue, Dec 7, 2010 at 1:28 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>>> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>>>\n>>>> On Tue, Dec 7, 2010 at 4:16 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>>>>> +#define BRANCH_DELETION_LOG \"DELETED-REFS\"\n>>>>> +\n>>>>\n>>>> Should this special log be mentioned in git-update-ref.txt or\n>>>> gitrepository-layout.txt?\n>>>\n>>> Perhaps, but I wasn't sure if this patch itself is a good idea to begin\n>>> with.  Not the problem it tries to solve, but its approach.\n>>>\n>>> For example, this cannot be shown with \"reflog show\" or \"log -g\" due to\n>>> the way these frontends locate the reflog file to read (the logic wants to\n>>> have an underlying ref).\n>>>\n>>\n>> I think you have thought of this. What's wrong with keeping reflog\n>> when a branch is removed and appending \"delete\" line to the said\n>> reflog? I don't know how reflogs are managed, but those reflogs\n>> without associated branch will (or should) be cleaned when they are\n>> expired.\n>>\n>> I stick with this idea because I also want to archive old branches and\n>> am thinking those reflogs ending with \"archive\" line will be kept\n>> forever, or until I feel like digging up them again.\n>\n> The problem with this idea is deleting branch 'foo' and creating 'foo/bar',\n> or deleting branch 'foo/bar' and creating branch 'foo'.  Old reflog with\n> \"delete\" line would block creating reflog for new branch.\n\nThanks. That makes sense.\n-- \nDuy\n"},{"id":"157499","messageId":"20101207170623.GB21749@sigill.intra.peff.net","threadId":"25977","inReplyTo":"7vmxoiqeoq.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] logging branch deletion to help recovering from mistakes","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2010-12-07T17:06:23Z","receivedAt":"2010-12-07T17:06:23Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Dec 06, 2010 at 10:28:53PM -0800, Junio C Hamano wrote:\n\n> > Should this special log be mentioned in git-update-ref.txt or\n> > gitrepository-layout.txt?\n> \n> Perhaps, but I wasn't sure if this patch itself is a good idea to begin\n> with.  Not the problem it tries to solve, but its approach.\n> \n> For example, this cannot be shown with \"reflog show\" or \"log -g\" due to\n> the way these frontends locate the reflog file to read (the logic wants to\n> have an underlying ref).\n\nYeah, I think this is not _quite_ what people want in this area. A base\nrequirement from past discussions, I think, is that the whole reflog of\nthe deleted branch be saved rather than just the tip. And then \"reflog\nshow\" would make a lot more sense on such saved reflogs.\n\nI'm not sure in practice how important that distinction is, as we are\nnot saving deleted branch reflogs _at all_ right now, so the\nrequirements are mostly speculation at this point.\n\nThe most recent discussion I recall is this one:\n\n  http://thread.gmane.org/gmane.comp.version-control.git/144250/focus=145353\n\nwhere the general idea was to just keep deleted reflogs around, append\nto them if the branch was recreated, and use a consistent renaming\nscheme to avoid D/F naming conflicts (e.g., \"foo\" is a deleted ref, and\nyou create \"foo/bar\").\n\n-Peff\n"},{"id":"157504","messageId":"20101207174520.GB21483@burratino","threadId":"25977","inReplyTo":"20101207162358.GT355@fearengine.rdu.redhat.com","subject":"Re: [PATCH] logging branch deletion to help recovering from mistakes","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-12-07T17:45:20Z","receivedAt":"2010-12-07T17:45:20Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Casey Dahlin wrote:\n\n> Could commits made onto a detached head also show up here? Or is that\n> better thwarted with another mechanism?\n\nI think that's better thwarted with the HEAD reflog:\n\n\t$ git log -g HEAD\n"},{"id":"157505","messageId":"20101207175418.GU355@fearengine.rdu.redhat.com","threadId":"25977","inReplyTo":"20101207174520.GB21483@burratino","subject":"Re: [PATCH] logging branch deletion to help recovering from mistakes","fromName":"Casey Dahlin","fromEmail":"cdahlin@redhat.com","sentAt":"2010-12-07T17:54:18Z","receivedAt":"2010-12-07T17:54:18Z","isPatch":true,"sender":{"key":"cdahlin@redhat.com","avatar":null},"body":"On Tue, Dec 07, 2010 at 11:45:20AM -0600, Jonathan Nieder wrote:\n> Casey Dahlin wrote:\n> \n> > Could commits made onto a detached head also show up here? Or is that\n> > better thwarted with another mechanism?\n> \n> I think that's better thwarted with the HEAD reflog:\n> \n> \t$ git log -g HEAD\n\nI was more worried about changes that were made onto a detached head,\nand then the head was reattached, leaving the new commits dangling.\n\nThe end result is identical to a deleted branch, just wondering if we\nshould note it in the same place.\n\n--CJD\n"},{"id":"157506","messageId":"20101207180236.GC21483@burratino","threadId":"25977","inReplyTo":"20101207175418.GU355@fearengine.rdu.redhat.com","subject":"Re: [PATCH] logging branch deletion to help recovering from mistakes","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-12-07T18:02:36Z","receivedAt":"2010-12-07T18:02:36Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Casey Dahlin wrote:\n> On Tue, Dec 07, 2010 at 11:45:20AM -0600, Jonathan Nieder wrote:\n>> Casey Dahlin wrote:\n\n>>> Could commits made onto a detached head also show up here? Or is that\n>>> better thwarted with another mechanism?\n>>\n>> I think that's better thwarted with the HEAD reflog:\n>> \n>> \t$ git log -g HEAD\n>\n> I was more worried about changes that were made onto a detached head,\n> and then the head was reattached, leaving the new commits dangling.\n\nBut isn't that exactly what a detached HEAD is for?  If one wants\nthe experiments one does on detached HEAD to be kept around \"just\nin case\", wouldn't it make more sense to give them a (branch) name so\nthey can be separated from one another?\n\nIn other words, I do not see the connection yet.  Maybe it would be\nbest to propose another patch on top to do that?  (Patches often come\nwith documentation, which means clear explanation of use cases, which\nwould address my worry here.)\n"},{"id":"157507","messageId":"20101207181201.GB26137@sigill.intra.peff.net","threadId":"25977","inReplyTo":"20101207175418.GU355@fearengine.rdu.redhat.com","subject":"Re: [PATCH] logging branch deletion to help recovering from mistakes","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2010-12-07T18:12:01Z","receivedAt":"2010-12-07T18:12:01Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Dec 07, 2010 at 12:54:18PM -0500, Casey Dahlin wrote:\n\n> On Tue, Dec 07, 2010 at 11:45:20AM -0600, Jonathan Nieder wrote:\n> > Casey Dahlin wrote:\n> > \n> > > Could commits made onto a detached head also show up here? Or is that\n> > > better thwarted with another mechanism?\n> > \n> > I think that's better thwarted with the HEAD reflog:\n> > \n> > \t$ git log -g HEAD\n> \n> I was more worried about changes that were made onto a detached head,\n> and then the head was reattached, leaving the new commits dangling.\n> \n> The end result is identical to a deleted branch, just wondering if we\n> should note it in the same place.\n\nWe have enough information in the HEAD reflog already to reconstruct\nthose sorts of things.\n\nYou can detect entering and leaving the detached HEAD in the reflog. The\nreflog comments look something like this:\n\n  checkout: moving from $SOME_BRANCH to $SOME_SHA1\n  commit: $SOME_COMMIT_MESSAGE\n  checkout: moving from $SOME_OTHER_SHA1 to $BRANCH|$SHA1\n\nSo from that you can see that we entered a detached HEAD state, made a\ncommit, and that commit became dangling when we moved. One could write a\nscript to search for these cases (but note that most detached instances\njust involve rebasing, which is probably not interesting, as we install\nthe result into the branch tip at the end).\n\nI don't think this belongs in the same realm as \"deleted branches\", but\nI do think we could have a special option to \"git fsck\" to stick these\ninto lost-found.\n\n-Peff\n"},{"id":"157508","messageId":"AANLkTimnp3xCHp_3E7ry-5OQL3PFnYh=H8PhfzMN307C@mail.gmail.com","threadId":"25977","inReplyTo":"20101207170623.GB21749@sigill.intra.peff.net","subject":"Re: [PATCH] logging branch deletion to help recovering from mistakes","fromName":"Shawn Pearce","fromEmail":"spearce@spearce.org","sentAt":"2010-12-07T18:14:19Z","receivedAt":"2010-12-07T18:14:19Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"On Tue, Dec 7, 2010 at 9:06 AM, Jeff King <peff@peff.net> wrote:\n> On Mon, Dec 06, 2010 at 10:28:53PM -0800, Junio C Hamano wrote:\n>\n>> > Should this special log be mentioned in git-update-ref.txt or\n>> > gitrepository-layout.txt?\n>>\n>> Perhaps, but I wasn't sure if this patch itself is a good idea to begin\n>> with.  Not the problem it tries to solve, but its approach.\n>>\n>> For example, this cannot be shown with \"reflog show\" or \"log -g\" due to\n>> the way these frontends locate the reflog file to read (the logic wants to\n>> have an underlying ref).\n>\n> Yeah, I think this is not _quite_ what people want in this area. A base\n> requirement from past discussions, I think, is that the whole reflog of\n> the deleted branch be saved rather than just the tip. And then \"reflog\n> show\" would make a lot more sense on such saved reflogs.\n\nYup, that's what I recall too, folks (including myself) want to\nsave the reflog of the deleted branch, so it can be recovered if\nthe branch itself were to be recovered with an --undelete option.\n\n> I'm not sure in practice how important that distinction is, as we are\n> not saving deleted branch reflogs _at all_ right now, so the\n> requirements are mostly speculation at this point.\n>\n> The most recent discussion I recall is this one:\n>\n>  http://thread.gmane.org/gmane.comp.version-control.git/144250/focus=145353\n>\n> where the general idea was to just keep deleted reflogs around, append\n> to them if the branch was recreated, and use a consistent renaming\n> scheme to avoid D/F naming conflicts (e.g., \"foo\" is a deleted ref, and\n> you create \"foo/bar\").\n\nPer check-ref-format, ref names cannot contain two dots.  We could\narchive ref logs by renaming them, $GIT_DIR/logs/refs/heads/foo\nbecomes $GIT_DIR/logs/refs/heads/foo..deleted-1.  If foo is created\nand deleted again, it becomes foo..deleted-2.\n\nThis still causes problems for git reflog show / git log -g because\nthey want a current ref to enumerate the log of.\n\n\nA different approach might be to have $GIT_DIR/logs/refs/REF_ATTIC,\nand special case that in git reflog show / git log -g.  When a\nref is deleted, append its entire log onto REF_ATTIC, between two\nspecially formatted marker lines.  When recovering a branch, copy\nout the region from the REF_ATTIC log.\n\n-- \nShawn.\n"},{"id":"157510","messageId":"20101207182040.GA26770@sigill.intra.peff.net","threadId":"25977","inReplyTo":"AANLkTimnp3xCHp_3E7ry-5OQL3PFnYh=H8PhfzMN307C@mail.gmail.com","subject":"Re: [PATCH] logging branch deletion to help recovering from mistakes","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2010-12-07T18:20:40Z","receivedAt":"2010-12-07T18:20:40Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Dec 07, 2010 at 10:14:19AM -0800, Shawn O. Pearce wrote:\n\n> Per check-ref-format, ref names cannot contain two dots.  We could\n> archive ref logs by renaming them, $GIT_DIR/logs/refs/heads/foo\n> becomes $GIT_DIR/logs/refs/heads/foo..deleted-1.  If foo is created\n> and deleted again, it becomes foo..deleted-2.\n> \n> This still causes problems for git reflog show / git log -g because\n> they want a current ref to enumerate the log of.\n\nThat seems reasonable to me. The \"reflog show\" limitation is just a\nmatter of a simple code fix, though, isn't it? Is there a good reason\nfor this restriction to exist? And even if there is, it would be simple\nto special case it for ..deleted-* branches.\n\n> A different approach might be to have $GIT_DIR/logs/refs/REF_ATTIC,\n> and special case that in git reflog show / git log -g.  When a\n> ref is deleted, append its entire log onto REF_ATTIC, between two\n> specially formatted marker lines.  When recovering a branch, copy\n> out the region from the REF_ATTIC log.\n\nThat seems a lot less efficient, as we have to linearly search all of\nREF_ATTIC to get:\n\n  1. the reflog for one deleted branch\n\n  2. the list of deleted branches\n\nNeither of those is probably particularly performance critical, but it\njust seems like keeping the logs in files indexed by the original ref\nnames is a more natural fit.\n\n-Peff\n"},{"id":"157513","messageId":"20101207182342.GA3725@spearce.org","threadId":"25977","inReplyTo":"20101207182040.GA26770@sigill.intra.peff.net","subject":"Re: [PATCH] logging branch deletion to help recovering from mistakes","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2010-12-07T18:23:42Z","receivedAt":"2010-12-07T18:23:42Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Jeff King <peff@peff.net> wrote:\n> On Tue, Dec 07, 2010 at 10:14:19AM -0800, Shawn O. Pearce wrote:\n> \n> > Per check-ref-format, ref names cannot contain two dots.  We could\n> > archive ref logs by renaming them, $GIT_DIR/logs/refs/heads/foo\n> > becomes $GIT_DIR/logs/refs/heads/foo..deleted-1.  If foo is created\n> > and deleted again, it becomes foo..deleted-2.\n...\n> > A different approach might be to have $GIT_DIR/logs/refs/REF_ATTIC,\n> \n> That seems a lot less efficient, as we have to linearly search all of\n> REF_ATTIC to get:\n> \n>   1. the reflog for one deleted branch\n> \n>   2. the list of deleted branches\n> \n> Neither of those is probably particularly performance critical, but it\n> just seems like keeping the logs in files indexed by the original ref\n> names is a more natural fit.\n\nYea, I'm leaning more towards the foo..deleted-n idea too, for the\nsame reasons.  It also makes it easier to GC a deleted branch's\nreflog, we can examine the last record's timestamp in a reasonable\ntime bound and unlink the log if its really freaking old.\n\n-- \nShawn.\n"},{"id":"157514","messageId":"20101207182608.GV355@fearengine.rdu.redhat.com","threadId":"25977","inReplyTo":"20101207180236.GC21483@burratino","subject":"Re: [PATCH] logging branch deletion to help recovering from mistakes","fromName":"Casey Dahlin","fromEmail":"cdahlin@redhat.com","sentAt":"2010-12-07T18:26:09Z","receivedAt":"2010-12-07T18:26:09Z","isPatch":true,"sender":{"key":"cdahlin@redhat.com","avatar":null},"body":"On Tue, Dec 07, 2010 at 12:02:36PM -0600, Jonathan Nieder wrote:\n> Casey Dahlin wrote:\n> > On Tue, Dec 07, 2010 at 11:45:20AM -0600, Jonathan Nieder wrote:\n> >> Casey Dahlin wrote:\n> \n> >>> Could commits made onto a detached head also show up here? Or is that\n> >>> better thwarted with another mechanism?\n> >>\n> >> I think that's better thwarted with the HEAD reflog:\n> >> \n> >> \t$ git log -g HEAD\n> >\n> > I was more worried about changes that were made onto a detached head,\n> > and then the head was reattached, leaving the new commits dangling.\n> \n> But isn't that exactly what a detached HEAD is for?  If one wants\n> the experiments one does on detached HEAD to be kept around \"just\n> in case\", wouldn't it make more sense to give them a (branch) name so\n> they can be separated from one another?\n> \n\nAn experienced git user who's paying attention to what he's doing would\ndo things that way, but detached heads and what happens to commits\nthereupon are one of those concepts that tends to puzzle newbies, so\nmaking it harder to make mistakes with them is probably a good idea.\n\nEven still, there's always going to be those \"Oh, I actually wanted to\nkeep some of that\" moments, and that's what I'm talking about preparing\nfor.\n\n--CJD\n"},{"id":"157516","messageId":"20101207183507.GA27277@sigill.intra.peff.net","threadId":"25977","inReplyTo":"20101207182342.GA3725@spearce.org","subject":"Re: [PATCH] logging branch deletion to help recovering from mistakes","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2010-12-07T18:35:07Z","receivedAt":"2010-12-07T18:35:07Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Dec 07, 2010 at 10:23:42AM -0800, Shawn O. Pearce wrote:\n\n> Yea, I'm leaning more towards the foo..deleted-n idea too, for the\n> same reasons.  It also makes it easier to GC a deleted branch's\n> reflog, we can examine the last record's timestamp in a reasonable\n> time bound and unlink the log if its really freaking old.\n\nDo we need to actually do that? Shouldn't the entries in the reflog get\nexpired as part of the regular reflog gc? In that case, we would just\ndelete the file when it had zero entries.\n\n-Peff\n"},{"id":"157520","messageId":"20101207183739.GB3725@spearce.org","threadId":"25977","inReplyTo":"20101207183507.GA27277@sigill.intra.peff.net","subject":"Re: [PATCH] logging branch deletion to help recovering from mistakes","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2010-12-07T18:37:39Z","receivedAt":"2010-12-07T18:37:39Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Jeff King <peff@peff.net> wrote:\n> On Tue, Dec 07, 2010 at 10:23:42AM -0800, Shawn O. Pearce wrote:\n> \n> > Yea, I'm leaning more towards the foo..deleted-n idea too, for the\n> > same reasons.  It also makes it easier to GC a deleted branch's\n> > reflog, we can examine the last record's timestamp in a reasonable\n> > time bound and unlink the log if its really freaking old.\n> \n> Do we need to actually do that? Shouldn't the entries in the reflog get\n> expired as part of the regular reflog gc? In that case, we would just\n> delete the file when it had zero entries.\n\nYes, you are right.  We should instead let the normal reflog expire\naction do its work here, and delete the empty log file when it is\nfinally empty.\n\nI guess we also need repack and prune to enumerate these deleted\nreflogs and retain the objects their records point to.\n\n-- \nShawn.\n"},{"id":"157517","messageId":"20101207183930.GA27340@sigill.intra.peff.net","threadId":"25977","inReplyTo":"20101207183739.GB3725@spearce.org","subject":"Re: [PATCH] logging branch deletion to help recovering from mistakes","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2010-12-07T18:39:31Z","receivedAt":"2010-12-07T18:39:31Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Dec 07, 2010 at 10:37:39AM -0800, Shawn O. Pearce wrote:\n\n> Yes, you are right.  We should instead let the normal reflog expire\n> action do its work here, and delete the empty log file when it is\n> finally empty.\n> \n> I guess we also need repack and prune to enumerate these deleted\n> reflogs and retain the objects their records point to.\n\nDefinitely. I sort of assumed all of those things just traversed\n.git/logs blindly without regard to whether there was a ref, which would\nhandle this automagically. But maybe that is not the case.\n\nIs there a reason to require that each log is specifically tied to a\nref?\n\n-Peff\n"},{"id":"157519","messageId":"20101207184143.GC3725@spearce.org","threadId":"25977","inReplyTo":"20101207183930.GA27340@sigill.intra.peff.net","subject":"Re: [PATCH] logging branch deletion to help recovering from mistakes","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2010-12-07T18:41:43Z","receivedAt":"2010-12-07T18:41:43Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Jeff King <peff@peff.net> wrote:\n> On Tue, Dec 07, 2010 at 10:37:39AM -0800, Shawn O. Pearce wrote:\n> \n> > Yes, you are right.  We should instead let the normal reflog expire\n> > action do its work here, and delete the empty log file when it is\n> > finally empty.\n> > \n> > I guess we also need repack and prune to enumerate these deleted\n> > reflogs and retain the objects their records point to.\n> \n> Definitely. I sort of assumed all of those things just traversed\n> .git/logs blindly without regard to whether there was a ref, which would\n> handle this automagically. But maybe that is not the case.\n\nI think those enumerate the logs of refs that are also being\ntraversed.  Which means we would need to add new logic to enumerate\nthe deleted reflogs.\n \n> Is there a reason to require that each log is specifically tied to a\n> ref?\n\nHistorical bad assumptions?\n\nI mean, no, there really isn't a good reason that each log is\ntied to a ref.  Its probably reasonable to just enumerate the logs\ndirectory separate from the refs directory enumeration.  Its just\nsome more code.  Right now we discover logs by just relying on the\nref directory traversal code.\n\n-- \nShawn.\n"},{"id":"157524","messageId":"7v7hflqth1.fsf@alter.siamese.dyndns.org","threadId":"25977","inReplyTo":"20101207170623.GB21749@sigill.intra.peff.net","subject":"Re: [PATCH] logging branch deletion to help recovering from mistakes","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-12-07T19:21:46Z","receivedAt":"2010-12-07T19:21:46Z","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> On Mon, Dec 06, 2010 at 10:28:53PM -0800, Junio C Hamano wrote:\n>\n> Yeah, I think this is not _quite_ what people want in this area. A base\n> requirement from past discussions, I think, is that the whole reflog of\n> the deleted branch be saved rather than just the tip. And then \"reflog\n> show\" would make a lot more sense on such saved reflogs.\n\nI am more worried about stuff in branch.<name>.* that are discarded upon\n\"branch -d\".  Without the config items, you won't have a working:\n\n    $ branch -d frotz\n    $ branch --undelete frotz\n    $ git checkout frotz\n    $ git pull\n\nI would say it is fine to discard old reflog for \"frotz\" branch and tell\nusers of --undelete that even though their branch is undeleted, the reflog\nfor it is already expired when they deleted it the first time, but it is\nimpossible to implement \"branch --undelete\" without stashing away stuff\nother than reflog.\n"},{"id":"157526","messageId":"20101207193804.GA27685@sigill.intra.peff.net","threadId":"25977","inReplyTo":"7v7hflqth1.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] logging branch deletion to help recovering from mistakes","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2010-12-07T19:38:05Z","receivedAt":"2010-12-07T19:38:05Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Dec 07, 2010 at 11:21:46AM -0800, Junio C Hamano wrote:\n\n> I am more worried about stuff in branch.<name>.* that are discarded upon\n> \"branch -d\".  Without the config items, you won't have a working:\n> \n>     $ branch -d frotz\n>     $ branch --undelete frotz\n>     $ git checkout frotz\n>     $ git pull\n\nHmm, yeah, I didn't think about that. Two possible solutions:\n\n  1. Just leave it in .git/config. It is not hurting anything if the\n     branch does not exist, but it is cruft in a file the user might\n     look at.\n\n  2. Drop it into .git/config.dead/<branch_name>. When resurrecting a\n     branch, copy it back into .git/config.\n\nIn both cases, when the reflog for the deleted branch is pruned to\nnothing, we delete the relevant config, too.\n\nIn the second case, I think you would have to take special care for\nsomething like:\n\n  $ git branch frotz origin/master\n  $ git branch -d frotz\n  $ git remote rename origin foo\n  $ git branch --undelete frotz\n\nIn the non-deleted case, this transparently renames branch.frotz.remote\nfrom \"origin\" to \"foo\". In the deleted case, we would need to make sure\nthe dead config is updated, too.\n\n\nTo be honest, I have never been that interested in a \"branch --undelete\"\nfeature. I much more care about leaving the reflogs of deleted branches\naround, so I can \"git checkout -b foo bar@{1}\" later on[1]. That is, to\nme branch undeletion is not about bringing a branch back wholesale, but\nrather remembering commits so I can start a new branch there.\n\nBut I guess others might disagree.\n\n-Peff\n\n[1] Well, that and just piece of mind from knowing that \"branch -d\" is\n    not totally unrecoverable. Specifically, if we kept deleted reflogs\n    around, it would be safe(r) to turn on auto-prune on fetch, the lack\n    of which is something that seems to confuse new users.\n"},{"id":"157531","messageId":"7vfwu9pbyj.fsf@alter.siamese.dyndns.org","threadId":"25977","inReplyTo":"20101207180236.GC21483@burratino","subject":"Re: [PATCH] logging branch deletion to help recovering from mistakes","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-12-07T20:25:24Z","receivedAt":"2010-12-07T20:25:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jonathan Nieder <jrnieder@gmail.com> writes:\n\n> Casey Dahlin wrote:\n>> On Tue, Dec 07, 2010 at 11:45:20AM -0600, Jonathan Nieder wrote:\n>>> Casey Dahlin wrote:\n>\n>>>> Could commits made onto a detached head also show up here? Or is that\n>>>> better thwarted with another mechanism?\n>>>\n>>> I think that's better thwarted with the HEAD reflog:\n>>> \n>>> \t$ git log -g HEAD\n>>\n>> I was more worried about changes that were made onto a detached head,\n>> and then the head was reattached, leaving the new commits dangling.\n>\n> But isn't that exactly what a detached HEAD is for?  If one wants\n> the experiments one does on detached HEAD to be kept around \"just\n> in case\", wouldn't it make more sense to give them a (branch) name so\n> they can be separated from one another?\n\nWhat are you arguing after giving a correct answer.  \"git log -g HEAD\"\nkeeps track of what was at the tip of HEAD, be it pointing at a branch or\npointing diretly at a commit in a detached state, no?\n"},{"id":"157537","messageId":"20101207205526.GA25008@burratino","threadId":"25977","inReplyTo":"7vfwu9pbyj.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] logging branch deletion to help recovering from mistakes","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-12-07T20:55:26Z","receivedAt":"2010-12-07T20:55:26Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Junio C Hamano wrote:\n\n> \"git log -g HEAD\"\n> keeps track of what was at the tip of HEAD, be it pointing at a branch or\n> pointing diretly at a commit in a detached state, no?\n\nYes.\n\n1. Imagine I have an interesting branch and delete it:\n\n\t$ git branch interesting $(lots of hard work)\n\t$ git branch -D interesting\n\nOops.  If I want to recover that branch, I may have a lot of digging\nto do in the HEAD reflog.  It may not be there are all.  Your patch\nmitigates that by allowing a simple \"I didn't mean that\" command.\n\n\t$ git branch --undelete interesting\n\n2. Great.  Another way to lose a line of development, as Casey\nmentioned, is to not give it a branch name in the first place:\n\n\t$ git checkout HEAD^0\n\t...\n\t$ git checkout something-else\n\nOops.  Well, not so bad.  If I want to recover my old work, I can\nsimply use\n\n\t$ git checkout HEAD@{1}\n\n3. Now suppose I was not paying attention and made the mistake\nfrom (1) or (2) a week ago and didn't realize it.  Now I want to\nget back that code.\n\nIf it was situation (1), I can remember the name of the branch\nand do\n\n\t$ git branch --undelete interesting\n\nNo problem [1].  If it was situation (2), I need to dig through the\nHEAD reflog.  As Jeff explained, it is possible to script something up\nto help organize the search.  I think Casey was suggesting doing that\nwork at HEAD-reattachment time instead, so you could do\n\n\t$ git branch --undelete-detached-head=old-head\n\nto recover the last line of development made without a branch;\nmy response was that if this ends up frequently being useful\nthen I suspect something is wrong with the workflow.\n\nHoping that is clearer,\nJonathan\n\n[1] as long as the branch name was not reused\n"}]}