{"thread":{"id":"6096","subject":"[RFC/PATCH] Enhance git-reflog with 'show' subcommand.","startedAt":"2006-12-24T10:02:15Z","lastAt":"2006-12-24T10:02:15Z","messageCount":1,"participants":["Shawn O. Pearce"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"30235","messageId":"20061224100215.GA25522@spearce.org","threadId":"6096","inReplyTo":null,"subject":"[RFC/PATCH] Enhance git-reflog with 'show' subcommand.","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2006-12-24T10:02:15Z","receivedAt":"2006-12-24T10:02:15Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"We now have a 'show' subcommand to 'git reflog' which dumps out the\ncontents of the reflog for each ref supplied on the command line,\nor 'HEAD' if no ref is supplied.\n\nEntries from the reflogs are sorted by reverse timestamp and then\nby reverse order of appearance within the reflog.  This replicates\nthe behavior of 'git log' where newer commits are shown first and\ncommits are ordered by timestamp when multiple refs are specified\non the command line.\n\nPoints in time in the reflog where a non-linear change occurred\n(due to for example a rebase, reset or amend) are highlighed by\ninserting 'R' in the first column of every line associated with\nthat log entry.\n\nFor example:\n\n  $ git reflog show sp/reflog-show\n    ref sp/reflog-show@{0}\n    commit 87d8af1.. -> f8ef360..\n    User: Shawn O. Pearce <spearce@spearce.org>\n    Date: Sun Dec 24 04:39:36 2006 -0500\n\n      commit: Enhance git-reflog with 'show' subcommand.\n\n  R ref sp/reflog-show@{1}\n  R commit 57e846c.. -> 87d8af1..\n  R User: Shawn O. Pearce <spearce@spearce.org>\n  R Date: Sun Dec 24 00:49:47 2006 -0500\n  R\n  R   reset --soft\n  R   to \"fix macos x build\"\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n\n This is an updated version of my earlier '[RFC] git reflog show'\n patch.  Lots of bugs have been fixed, I removed the totally\n unnecessary need for qsort(), and refs can now be supplied in\n shorthand format just like with 'git log'.\n\n One of the _really_ ugly parts of this patch is the duplication\n of the fmt table from sha1_refs.c.  The shorthand ref -> full ref\n name code should be refactored out first.  I'm being lazy and not\n doing it right now.  :-)\n\n I have taken Junio's suggestion of making non-linear changes stand\n out more by placing 'R' in the first column of every relevant line\n in the log, this visually stands out pretty good without applying\n any coloring.  Coloring would be nice for people who do that sort\n of thing.  ;-)\n\n Earlier Junio had suggested that the reflog display should use some\n form of graphical (albeit text mode) representation to indicate\n how entries relate to each other within the reflog.  I'm not sure\n that's possible when you get into complex logs where resets and\n rebases happen frequently.  I for one am not sure how I would\n draw some logs.\n\n builtin-reflog.c |  269 +++++++++++++++++++++++++++++++++++++++++++++++++++++-\n git.c            |    2 +-\n 2 files changed, 269 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin-reflog.c b/builtin-reflog.c\nindex de31967..6ee7fd3 100644\n--- a/builtin-reflog.c\n+++ b/builtin-reflog.c\n@@ -198,8 +198,273 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)\n \treturn status;\n }\n \n+struct cmd_reflog_list_ref {\n+\tstruct cmd_reflog_list_ref *next;\n+\tstruct cmd_reflog_list_ent *entries;\n+\tconst char *name;\n+\tunsigned int ent_cnt;\n+};\n+\n+struct cmd_reflog_list_ent {\n+\tstruct cmd_reflog_list_ent *next;\n+\tstruct commit *old;\n+\tstruct commit *new;\n+\tchar *cmd;\n+\tchar *user;\n+\tunsigned long timestamp;\n+\tint tz;\n+\tunsigned int pos;\n+\tunsigned linear:1;\n+\tunsigned char old_sha1[20];\n+\tunsigned char new_sha1[20];\n+};\n+\n+struct cmd_reflog_list_cb {\n+\tstruct cmd_reflog_list_ref *refs;\n+\tstruct cmd_reflog_list_ref *cur_ref;\n+\tunsigned include_linear_commits:1;\n+};\n+\n+static int list_reflog_ent(unsigned char *osha1, unsigned char *nsha1,\n+\t\tchar *data, void *cb_data)\n+{\n+\tstruct cmd_reflog_list_cb *cb = cb_data;\n+\tstruct cmd_reflog_list_ref *cur_ref = cb->cur_ref;\n+\tstruct cmd_reflog_list_ent *e;\n+\tstruct commit *old, *new;\n+\tunsigned long timestamp;\n+\tint tz;\n+\tchar *user_end, *cp, *ep;\n+\n+\tcur_ref->ent_cnt++;\n+\tcp = strchr(data, '>');\n+\tif (!cp || *++cp != ' ')\n+\t\treturn 0; /* corrupt line? */\n+\tuser_end = cp;\n+\ttimestamp = strtoul(cp, &ep, 10);\n+\tif (*ep++ != ' ')\n+\t\treturn 0; /* corrupt line? */\n+\ttz = strtol(ep, &cp, 10);\n+\tif (*cp++ != '\\t')\n+\t\treturn 0; /* corrupt line? */\n+\twhile (isspace(*cp))\n+\t\tcp++;\n+\n+\tif (is_null_sha1(osha1))\n+\t\told = NULL;\n+\telse\n+\t\told = lookup_commit_reference_gently(osha1, 1);\n+\n+\tif (is_null_sha1(nsha1))\n+\t\tnew = NULL;\n+\telse\n+\t\tnew = lookup_commit_reference_gently(nsha1, 1);\n+\n+\tif (!cb->include_linear_commits\n+\t\t&& old && new && new->parents\n+\t\t&& new->parents->item == old\n+\t\t&& !new->parents->next\n+\t\t&& cur_ref->entries && cur_ref->entries->new == old\n+\t\t&& (!strncmp(cp, \"commit\", 6) || !strncmp(cp, \"am: \", 4)))\n+\t\treturn 0;\n+\n+\te = xmalloc(sizeof(*e));\n+\te->cmd = xstrdup(cp);\n+\tcp = strrchr(e->cmd, '\\n');\n+\tif (cp)\n+\t\t*cp = 0;\n+\tif (*e->cmd) {\n+\t\tcp = e->cmd + strlen(e->cmd) - 1;\n+\t\twhile (isspace(*cp))\n+\t\t\t*cp-- = 0;\n+\t}\n+\te->user = xmalloc(user_end - data + 1);\n+\tstrncpy(e->user, data, user_end - data);\n+\te->user[user_end - data] = 0;\n+\te->timestamp = timestamp;\n+\te->tz = tz;\n+\te->pos = cur_ref->ent_cnt - 1;\n+\te->old = old;\n+\te->new = new;\n+\thashcpy(e->old_sha1, osha1);\n+\thashcpy(e->new_sha1, nsha1);\n+\n+\tif (new && cur_ref->entries && cur_ref->entries->new)\n+\t\te->linear = in_merge_bases(cur_ref->entries->new, new);\n+\telse if (!e->pos)\n+\t\te->linear = 1;\n+\telse\n+\t\te->linear = 0;\n+\n+\te->next = cur_ref->entries;\n+\tcur_ref->entries = e;\n+\treturn 0;\n+}\n+\n+static int list_reflog(const char *ref, const unsigned char *sha1,\n+\t\tint unused, void *cb_data)\n+{\n+\tstruct cmd_reflog_list_cb *cb = cb_data;\n+\n+\tcb->cur_ref = xmalloc(sizeof(*cb->cur_ref));\n+\tcb->cur_ref->name = xstrdup(ref);\n+\tcb->cur_ref->ent_cnt = 0;\n+\tcb->cur_ref->entries = NULL;\n+\tcb->cur_ref->next = cb->refs;\n+\tcb->refs = cb->cur_ref;\n+\n+\tfor_each_reflog_ent(ref, list_reflog_ent, cb);\n+\treturn 0;\n+}\n+\n+static void reflog_show_pfx(struct cmd_reflog_list_ent *e)\n+{\n+\tprintf(\"%c \", e->linear ? ' ' : 'R');\n+}\n+\n+static void reflog_show_cmtid(int abbrev,\n+\tunsigned char *sha1,\n+\tstruct commit *cmt)\n+{\n+\tif (cmt)\n+\t\tprintf(\"%s..\", find_unique_abbrev(sha1, abbrev));\n+\telse if (!is_null_sha1(sha1))\n+\t\tprintf(\"%s (missing)\", sha1_to_hex(sha1));\n+\telse\n+\t\tprintf(\"deleted\");\n+}\n+\n+static void reflog_show(int abbrev,\n+\tstruct cmd_reflog_list_ref *ref,\n+\tstruct cmd_reflog_list_ent *e)\n+{\n+\tconst char *name = ref->name;\n+\tif (!strncmp(\"refs/tags/\", name, 10))\n+\t\tname += 10;\n+\telse if (!strncmp(\"refs/heads/\", name, 11))\n+\t\tname += 11;\n+\telse if (!strncmp(\"refs/remotes/\", name, 13))\n+\t\tname += 13;\n+\n+\treflog_show_pfx(e);\n+\tprintf(\"ref %s@{%u}\\n\", name, ref->ent_cnt - e->pos - 1);\n+\n+\treflog_show_pfx(e);\n+\tprintf(\"commit \");\n+\treflog_show_cmtid(abbrev, e->old_sha1, e->old);\n+\tprintf(\" -> \");\n+\treflog_show_cmtid(abbrev, e->new_sha1, e->new);\n+\tprintf(\"\\n\");\n+\n+\treflog_show_pfx(e);\n+\tprintf(\"User: %s\\n\", e->user);\n+\n+\treflog_show_pfx(e);\n+\tprintf(\"Date: %s\\n\", show_date(e->timestamp, e->tz, 0));\n+\n+\treflog_show_pfx(e);\n+\tprintf(\"\\n\");\n+\n+\treflog_show_pfx(e);\n+\tprintf(\"  %s\\n\", e->cmd);\n+\n+\t/* Historically some commands have not logged very much detail.\n+\t * We should dump out the oneline of the new commit to help.\n+\t */\n+\tif (e->new && (!strcmp(e->cmd, \"reset --hard\")\n+\t\t|| !strcmp(e->cmd, \"reset --soft\")\n+\t\t|| !strncmp(e->cmd, \"fetch \", 6)\n+\t\t|| !strncmp(e->cmd, \"merge \", 6)\n+\t\t|| !strncmp(e->cmd, \"pull \", 5))) {\n+\t\tchar subject[256];\n+\t\tpretty_print_commit(CMIT_FMT_ONELINE, e->new, ~0,\n+\t\t\tsubject, sizeof(subject), 0,\n+\t\t\tNULL, NULL, 0);\n+\t\treflog_show_pfx(e);\n+\t\tprintf(\"  to \\\"%s\\\"\\n\", subject);\n+\t}\n+\n+\tprintf(\"\\n\");\n+}\n+\n+static const char reflog_show_usage[] =\n+\"git-reflog show [--all|<refs>...]\";\n+\n+static int cmd_reflog_show(int argc, const char **argv, const char *prefix)\n+{\n+\tstruct cmd_reflog_list_cb cb;\n+\tunsigned char sha1[20];\n+\tint i, do_all = 0, status = 0;\n+\n+\tcb.refs = NULL;\n+\tcb.cur_ref = NULL;\n+\tcb.include_linear_commits = 1;\n+\n+\tfor (i = 1; i < argc; i++) {\n+\t\tconst char *arg = argv[i];\n+\t\tif (!strcmp(arg, \"--all\"))\n+\t\t\tdo_all = 1;\n+\t\telse if (!strcmp(arg, \"--no-commits\"))\n+\t\t\tcb.include_linear_commits = 0;\n+\t\telse if (arg[0] == '-')\n+\t\t\tusage(reflog_show_usage);\n+\t\telse\n+\t\t\tbreak;\n+\t}\n+\tif (do_all)\n+\t\tstatus |= for_each_ref(list_reflog, &cb);\n+\tif (i == argc) {\n+\t\tconst char *ref = resolve_ref(\"HEAD\", sha1, 1, NULL);\n+\t\tif (ref)\n+\t\t\tstatus |= list_reflog(ref, sha1, 0, &cb);\n+\t\telse\n+\t\t\tstatus |= error(\"no such ref: HEAD\");\n+\t}\n+\twhile (i < argc) {\n+\t\tstatic const char *fmt[] = {\n+\t\t\t\"%.*s\",\n+\t\t\t\"refs/%.*s\",\n+\t\t\t\"refs/tags/%.*s\",\n+\t\t\t\"refs/heads/%.*s\",\n+\t\t\t\"refs/remotes/%.*s\",\n+\t\t\t\"refs/remotes/%.*s/HEAD\",\n+\t\t\tNULL\n+\t\t};\n+\t\tconst char *arg = argv[i++], **p;\n+\t\tfor (p = fmt; *p; p++) {\n+\t\t\tconst char *ref = mkpath(*p, strlen(arg), arg);\n+\t\t\tref = resolve_ref(ref, sha1, 1, NULL);\n+\t\t\tif (ref) {\n+\t\t\t\tstatus |= list_reflog(ref, sha1, 0, &cb);\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t\tif (!*p)\n+\t\t\tstatus |= error(\"no such ref: %s\", arg);\n+\t}\n+\n+\tfor (;;) {\n+\t\tstruct cmd_reflog_list_ref *r, *min_r = NULL;\n+\t\tstruct cmd_reflog_list_ent *e;\n+\t\tfor (r = cb.refs; r; r = r->next) {\n+\t\t\tif (!r->entries)\n+\t\t\t\tcontinue;\n+\t\t\tif (!min_r\n+\t\t\t\t|| r->entries->timestamp > min_r->entries->timestamp)\n+\t\t\t\tmin_r = r;\n+\t\t}\n+\t\tif (!min_r)\n+\t\t\tbreak;\n+\t\te = min_r->entries;\n+\t\tmin_r->entries = e->next;\n+\t\treflog_show(DEFAULT_ABBREV, min_r, e);\n+\t}\n+\n+\treturn status;\n+}\n+\n static const char reflog_usage[] =\n-\"git-reflog (expire | ...)\";\n+\"git-reflog (expire | show | ...)\";\n \n int cmd_reflog(int argc, const char **argv, const char *prefix)\n {\n@@ -207,6 +472,8 @@ int cmd_reflog(int argc, const char **argv, const char *prefix)\n \t\tusage(reflog_usage);\n \telse if (!strcmp(argv[1], \"expire\"))\n \t\treturn cmd_reflog_expire(argc - 1, argv + 1, prefix);\n+\telse if (!strcmp(argv[1], \"show\"))\n+\t\treturn cmd_reflog_show(argc - 1, argv + 1, prefix);\n \telse\n \t\tusage(reflog_usage);\n }\ndiff --git a/git.c b/git.c\nindex 50ebd86..27bbc75 100644\n--- a/git.c\n+++ b/git.c\n@@ -246,7 +246,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)\n \t\t{ \"prune-packed\", cmd_prune_packed, RUN_SETUP },\n \t\t{ \"push\", cmd_push, RUN_SETUP },\n \t\t{ \"read-tree\", cmd_read_tree, RUN_SETUP },\n-\t\t{ \"reflog\", cmd_reflog, RUN_SETUP },\n+\t\t{ \"reflog\", cmd_reflog, RUN_SETUP | USE_PAGER },\n \t\t{ \"repo-config\", cmd_repo_config },\n \t\t{ \"rerere\", cmd_rerere, RUN_SETUP },\n \t\t{ \"rev-list\", cmd_rev_list, RUN_SETUP },\n-- \n1.4.4.3.g389e\n"}]}