Introduce commit notes
authorJohannes Schindelin <Johannes.Schindelin@gmx.de>
Sat, 20 Dec 2008 12:05:14 +0000 (13:05 +0100)
committerJunio C Hamano <gitster@pobox.com>
Sun, 21 Dec 2008 10:47:21 +0000 (02:47 -0800)
Commit notes are blobs which are shown together with the commit
message. These blobs are taken from the notes ref, which you can
configure by the config variable core.notesRef, which in turn can
be overridden by the environment variable GIT_NOTES_REF.

The notes ref is a branch which contains "files" whose names are
the names of the corresponding commits (i.e. the SHA-1).

The rationale for putting this information into a ref is this: we
want to be able to fetch and possibly union-merge the notes,
maybe even look at the date when a note was introduced, and we
want to store them efficiently together with the other objects.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/config.txt
Makefile
cache.h
commit.c
config.c
environment.c
notes.c [new file with mode: 0644]
notes.h [new file with mode: 0644]
pretty.c
index 21ea16590b7fe017afbd2fe95c6586e784840bf8..b35a32abe115960a93c6aca0dbef9e9e56dcc585 100644 (file)
@@ -422,6 +422,21 @@ relatively high IO latencies.  With this set to 'true', git will do the
 index comparison to the filesystem data in parallel, allowing
 overlapping IO's.
 
+core.notesRef::
+       When showing commit messages, also show notes which are stored in
+       the given ref.  This ref is expected to contain paths of the form
+       ??/*, where the directory name consists of the first two
+       characters of the commit name, and the base name consists of
+       the remaining 38 characters.
++
+If such a path exists in the given ref, the referenced blob is read, and
+appended to the commit message, separated by a "Notes:" line.  If the
+given ref itself does not exist, it is not an error, but means that no
+notes should be print.
++
+This setting defaults to "refs/notes/commits", and can be overridden by
+the `GIT_NOTES_REF` environment variable.
+
 alias.*::
        Command aliases for the linkgit:git[1] command wrapper - e.g.
        after defining "alias.last = cat-file commit HEAD", the invocation
index aabf0130b99bee5204c8e668ba8f40caea77dae2..0bc96d8d32c67ef40bc3a8f3f67b057b9d9f4315 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -370,6 +370,7 @@ LIB_H += ll-merge.h
 LIB_H += log-tree.h
 LIB_H += mailmap.h
 LIB_H += merge-recursive.h
+LIB_H += notes.h
 LIB_H += object.h
 LIB_H += pack.h
 LIB_H += pack-refs.h
@@ -451,6 +452,7 @@ LIB_OBJS += match-trees.o
 LIB_OBJS += merge-file.o
 LIB_OBJS += merge-recursive.o
 LIB_OBJS += name-hash.o
+LIB_OBJS += notes.o
 LIB_OBJS += object.o
 LIB_OBJS += pack-check.o
 LIB_OBJS += pack-refs.o
diff --git a/cache.h b/cache.h
index 231c06d7726b575f6e522d5b0c0fe43557e8c651..6158d5546bdf4f8dbd16fa5e3107a3a214d612c9 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -367,6 +367,8 @@ static inline enum object_type object_type(unsigned int mode)
 #define GITATTRIBUTES_FILE ".gitattributes"
 #define INFOATTRIBUTES_FILE "info/attributes"
 #define ATTRIBUTE_MACRO_PREFIX "[attr]"
+#define GIT_NOTES_REF_ENVIRONMENT "GIT_NOTES_REF"
+#define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
 
 extern int is_bare_repository_cfg;
 extern int is_bare_repository(void);
@@ -538,6 +540,7 @@ enum rebase_setup_type {
 
 extern enum branch_track git_branch_track;
 extern enum rebase_setup_type autorebase;
+extern char *notes_ref_name;
 
 #define GIT_REPO_VERSION 0
 extern int repository_format_version;
index c99db162a48e0a47e73e6bfc2ae5fd4b7c9dfa1a..10e532afe1b383a2f49702f9ff0e932ccab43a43 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -5,6 +5,7 @@
 #include "utf8.h"
 #include "diff.h"
 #include "revision.h"
+#include "notes.h"
 
 int save_commit_buffer = 1;
 
index 790405a213b12a4d1c62d9354e1292e0dc6af057..e5d5b4bd0689d8c2932d501779356fc33a0d0379 100644 (file)
--- a/config.c
+++ b/config.c
@@ -469,6 +469,11 @@ static int git_default_core_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.notesref")) {
+               notes_ref_name = xstrdup(value);
+               return 0;
+       }
+
        if (!strcmp(var, "core.pager"))
                return git_config_string(&pager_program, var, value);
 
index e278bce0ea5f1ddda2dab9012663c6d1d4c6bd89..0edae21e74d53b89b55cd987ed5a6f6ff053e8ee 100644 (file)
@@ -45,6 +45,7 @@ enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
 
 /* Parallel index stat data preload? */
 int core_preload_index = 0;
+char *notes_ref_name;
 
 /* This is set by setup_git_dir_gently() and/or git_default_config() */
 char *git_work_tree_cfg;
diff --git a/notes.c b/notes.c
new file mode 100644 (file)
index 0000000..91ec77f
--- /dev/null
+++ b/notes.c
@@ -0,0 +1,68 @@
+#include "cache.h"
+#include "commit.h"
+#include "notes.h"
+#include "refs.h"
+#include "utf8.h"
+#include "strbuf.h"
+
+static int initialized;
+
+void get_commit_notes(const struct commit *commit, struct strbuf *sb,
+               const char *output_encoding)
+{
+       static const char *utf8 = "utf-8";
+       struct strbuf name = STRBUF_INIT;
+       const char *hex;
+       unsigned char sha1[20];
+       char *msg;
+       unsigned long msgoffset, msglen;
+       enum object_type type;
+
+       if (!initialized) {
+               const char *env = getenv(GIT_NOTES_REF_ENVIRONMENT);
+               if (env)
+                       notes_ref_name = getenv(GIT_NOTES_REF_ENVIRONMENT);
+               else if (!notes_ref_name)
+                       notes_ref_name = GIT_NOTES_DEFAULT_REF;
+               if (notes_ref_name && read_ref(notes_ref_name, sha1))
+                       notes_ref_name = NULL;
+               initialized = 1;
+       }
+
+       if (!notes_ref_name)
+               return;
+
+       strbuf_addf(&name, "%s:%s", notes_ref_name,
+                       sha1_to_hex(commit->object.sha1));
+       if (get_sha1(name.buf, sha1))
+               return;
+
+       if (!(msg = read_sha1_file(sha1, &type, &msglen)) || !msglen ||
+                       type != OBJ_BLOB)
+               return;
+
+       if (output_encoding && *output_encoding &&
+                       strcmp(utf8, output_encoding)) {
+               char *reencoded = reencode_string(msg, output_encoding, utf8);
+               if (reencoded) {
+                       free(msg);
+                       msg = reencoded;
+                       msglen = strlen(msg);
+               }
+       }
+
+       /* we will end the annotation by a newline anyway */
+       if (msglen && msg[msglen - 1] == '\n')
+               msglen--;
+
+       strbuf_addstr(sb, "\nNotes:\n");
+
+       for (msgoffset = 0; msgoffset < msglen;) {
+               int linelen = strchrnul(msg, '\n') - msg;
+
+               strbuf_addstr(sb, "    ");
+               strbuf_add(sb, msg + msgoffset, linelen);
+               msgoffset += linelen;
+       }
+       free(msg);
+}
diff --git a/notes.h b/notes.h
new file mode 100644 (file)
index 0000000..79d21b6
--- /dev/null
+++ b/notes.h
@@ -0,0 +1,7 @@
+#ifndef NOTES_H
+#define NOTES_H
+
+void get_commit_notes(const struct commit *commit, struct strbuf *sb,
+               const char *output_encoding);
+
+#endif
index f6ff31264b6908bac8bf71678e2eaf2e0cefc100..2d2872f3b5707c63c056c7591948db261a13b011 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -6,6 +6,7 @@
 #include "string-list.h"
 #include "mailmap.h"
 #include "log-tree.h"
+#include "notes.h"
 
 static char *user_format;
 
@@ -881,5 +882,9 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
         */
        if (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
                strbuf_addch(sb, '\n');
+
+       if (fmt != CMIT_FMT_ONELINE)
+               get_commit_notes(commit, sb, encoding);
+
        free(reencoded);
 }