{"thread":{"id":"6181","subject":"[RFC/PATCH] Add builtin git-last-modified.","startedAt":"2007-01-01T07:55:15Z","lastAt":"2007-01-01T07:55:15Z","messageCount":1,"participants":["Robert Fitzsimons"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"30613","messageId":"20070101075515.GK6558@localhost","threadId":"6181","inReplyTo":null,"subject":"[RFC/PATCH] Add builtin git-last-modified.","fromName":"Robert Fitzsimons","fromEmail":"robfitz@273k.net","sentAt":"2007-01-01T07:55:15Z","receivedAt":"2007-01-01T07:55:15Z","isPatch":true,"sender":{"key":"robfitz@273k.net","avatar":null},"body":"This is a plumbing command which can be used by gitweb.  It will scan\na list of commits and/or refs and return the time in seconds for the\nyoungest commit.  This value can then be used to generate and check\nthe Last-Modified and Is-Modified-Since HTTP headers.\n\nSigned-off-by: Robert Fitzsimons <robfitz@273k.net>\n---\n\n\nThe following two commands lines are similar:\n\n$ git for-each-ref --count=1 --sort=-committerdate '--format=%(committerdate)' refs/heads\n$ git last-modified --branches\n\nThe last-modified version is a bit faster (5-10%).\n\nI'm not 100% sure how this command and if this command should be used\nby gitweb.  But I'll post the patch anyway for comments.\n\nRobert\n\n\n\n .gitignore                          |    1 +\n Documentation/git-last-modified.txt |   49 ++++++++++++++++++++++++++++\n Makefile                            |    3 +-\n builtin-last-modified.c             |   61 +++++++++++++++++++++++++++++++++++\n builtin.h                           |    1 +\n git.c                               |    1 +\n t/t3810-last-modified.sh            |   57 ++++++++++++++++++++++++++++++++\n 7 files changed, 172 insertions(+), 1 deletions(-)\n\ndiff --git a/.gitignore b/.gitignore\nindex 2904f12..ffca6c5 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -58,6 +58,7 @@ git-lost-found\n git-ls-files\n git-ls-remote\n git-ls-tree\n+git-last-modified\n git-mailinfo\n git-mailsplit\n git-merge\ndiff --git a/Documentation/git-last-modified.txt b/Documentation/git-last-modified.txt\nnew file mode 100644\nindex 0000000..4584b5d\n--- /dev/null\n+++ b/Documentation/git-last-modified.txt\n@@ -0,0 +1,49 @@\n+git-last-modified(1)\n+================\n+\n+NAME\n+----\n+git-last-modified - Get the last modified time in seconds\n+\n+\n+SYNOPSIS\n+--------\n+'git-last-modified' [ \\--all ]\n+\t\t    [ \\--branches ]\n+\t\t    [ \\--tags ]\n+\t\t    [ \\--remotes ]\n+\t\t    <commmit>...\n+\n+DESCRIPTION\n+-----------\n+\n+Get the last modified time in seconds for the youngest commit object\n+based on the search criteria.  The time value is from the 'commit'\n+header of the commit object, ignoring the timezone.  The time is\n+formatted as an ASCII decimal number and is the number of seconds\n+since epoch.\n+\n+This is a plumbing command used by gitweb to generate and check the\n+Last-Modified and Is-Modified-Since HTTP headers.\n+\n+OPTIONS\n+-------\n+--all::\n+\tSearch all refs found in `$GIT_DIR/refs`.\n+\n+--branches::\n+\tSearch branch refs found in `$GIT_DIR/refs/heads`.\n+\n+--tags::\n+\tSearch tag refs found in `$GIT_DIR/refs/tags`.\n+\n+--remotes::\n+\tSearch remote refs found in `$GIT_DIR/refs/remotes`.\n+\n+<commit>...::\n+\tSearch list of commits and refs.\n+\n+GIT\n+---\n+Part of the gitlink:git[7] suite\n+\ndiff --git a/Makefile b/Makefile\nindex fa1a022..1f169ac 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -312,7 +312,8 @@ BUILTIN_OBJS = \\\n \tbuiltin-verify-pack.o \\\n \tbuiltin-write-tree.o \\\n \tbuiltin-show-ref.o \\\n-\tbuiltin-pack-refs.o\n+\tbuiltin-pack-refs.o \\\n+\tbuiltin-last-modified.o\n \n GITLIBS = $(LIB_FILE) $(XDIFF_LIB)\n EXTLIBS = -lz\ndiff --git a/builtin-last-modified.c b/builtin-last-modified.c\nnew file mode 100644\nindex 0000000..38cfb80\n--- /dev/null\n+++ b/builtin-last-modified.c\n@@ -0,0 +1,61 @@\n+#include \"cache.h\"\n+#include \"commit.h\"\n+#include \"refs.h\"\n+#include \"builtin.h\"\n+\n+static int lastmod_date(const unsigned char *sha1, unsigned long *date)\n+{\n+\tstruct commit *commit = lookup_commit_reference_gently(sha1, 1);\n+\n+\tif (!commit)\n+\t\treturn 1;\n+\n+\tif (commit->date > *date)\n+\t\t*date = commit->date;\n+\n+\treturn 0;\n+}\n+\n+static int process_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data)\n+{\n+\treturn lastmod_date(sha1, (unsigned long *)cb_data);\n+}\n+\n+int cmd_last_modified(int argc, const char **argv, const char *prefix)\n+{\n+\tint i;\n+\tunsigned char sha1[20];\n+\tunsigned long date = 0;\n+\n+\tgit_config(git_default_config);\n+\n+\tfor (i = 1; i < argc; i++) {\n+\t\tconst char *arg = argv[i];\n+\n+\t\tif (!strcmp(arg, \"--all\")) {\n+\t\t\tfor_each_ref(process_ref, &date);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"--branches\")) {\n+\t\t\tfor_each_branch_ref(process_ref, &date);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"--tags\")) {\n+\t\t\tfor_each_tag_ref(process_ref, &date);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"--remotes\")) {\n+\t\t\tfor_each_remote_ref(process_ref, &date);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (get_sha1(arg, sha1))\n+\t\t\tdie(\"last-modified: could not get sha1 for '%s'\", arg);\n+\t\tif (lastmod_date(sha1, &date))\n+\t\t\tdie(\"last-modified: bad revision '%s'\", arg);\n+\t}\n+\n+\tprintf(\"%lu\\n\", date);\n+\n+\treturn 0;\n+}\ndiff --git a/builtin.h b/builtin.h\nindex df72d09..862cadb 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -75,5 +75,6 @@ extern int cmd_write_tree(int argc, const char **argv, const char *prefix);\n extern int cmd_verify_pack(int argc, const char **argv, const char *prefix);\n extern int cmd_show_ref(int argc, const char **argv, const char *prefix);\n extern int cmd_pack_refs(int argc, const char **argv, const char *prefix);\n+extern int cmd_last_modified(int argc, const char **argv, const char *prefix);\n \n #endif\ndiff --git a/git.c b/git.c\nindex c82ca45..e6242e6 100644\n--- a/git.c\n+++ b/git.c\n@@ -269,6 +269,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)\n \t\t{ \"verify-pack\", cmd_verify_pack },\n \t\t{ \"show-ref\", cmd_show_ref, RUN_SETUP },\n \t\t{ \"pack-refs\", cmd_pack_refs, RUN_SETUP },\n+\t\t{ \"last-modified\", cmd_last_modified, RUN_SETUP },\n \t};\n \tint i;\n \ndiff --git a/t/t3810-last-modified.sh b/t/t3810-last-modified.sh\nnew file mode 100755\nindex 0000000..d85283e\n--- /dev/null\n+++ b/t/t3810-last-modified.sh\n@@ -0,0 +1,57 @@\n+#!/bin/sh\n+\n+test_description='git-last-modified'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+    for n in 1 2 3 4 5 ; do \\\n+        echo $n > a ; \\\n+        git-add a ; \\\n+        git-commit -m \"$n\" ; \\\n+    done ; \\\n+    git-tag -m \"t1\" t1 ; \\\n+    git-checkout -b o ; \\\n+    for n in o1 o1 o3 o4 o5 ; do \\\n+        echo $n > b ; \\\n+        git-add b ; \\\n+        git-commit -m \"$n\" ; \\\n+    done ; \\\n+    git-tag -m \"t2\" t2 ; \\\n+    git-checkout master\n+'\n+\n+test_expect_success 'no options' '\n+    test $(git-last-modified) -eq 0\n+'\n+\n+test_expect_success 'rev HEAD' '\n+    test $(git-last-modified HEAD) -gt 0 &&\n+    test $(git-last-modified refs/heads/master) -gt 0 &&\n+    test $(git-last-modified --all) -gt 0 &&\n+    test $(git-last-modified --branches) -gt 0 &&\n+    test $(git-last-modified --tags) -gt 0 &&\n+    test $(git-last-modified --remotes) -eq 0\n+'\n+\n+test_expect_success 'rev master -lt o' '\n+    test $(git-last-modified master) -lt $(git-last-modified o)\n+'\n+\n+test_expect_success 'mixed --branches HEAD t1 o' '\n+    test $(git-last-modified --branches HEAD t1 o) -gt 0\n+'\n+\n+test_expect_failure 'bad sha1' '\n+    git-last-modified 000000\n+'\n+\n+test_expect_failure 'bad rev' '\n+    git-last-modified xyz\n+'\n+\n+test_expect_failure 'bad --branches rev' '\n+    git-last-modified xyz\n+'\n+\n+test_done\n-- \n1.5.0.rc0.g2708-dirty\n"}]}