Skip to content

Commit

Permalink
Merge branch 'js/notes'
Browse files Browse the repository at this point in the history
* js/notes:
  git-notes: fix printing of multi-line notes
  notes: fix core.notesRef documentation
  Add an expensive test for git-notes
  Speed up git notes lookup
  Add a script to edit/inspect notes
  Introduce commit notes

Conflicts:
	pretty.c
  • Loading branch information
Junio C Hamano committed Feb 6, 2009
2 parents 5d680a6 + 22a3d06 commit 7b75b33
Show file tree
Hide file tree
Showing 15 changed files with 504 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ git-mktag
git-mktree
git-name-rev
git-mv
git-notes
git-pack-redundant
git-pack-objects
git-pack-refs
Expand Down
13 changes: 13 additions & 0 deletions Documentation/config.txt
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,19 @@ 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 files named
after the full SHA-1 of the commit they annotate.
+
If such a file 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 printed.
+
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
Expand Down
46 changes: 46 additions & 0 deletions Documentation/git-notes.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
git-notes(1)
============

NAME
----
git-notes - Add/inspect commit notes

SYNOPSIS
--------
[verse]
'git-notes' (edit | show) [commit]

DESCRIPTION
-----------
This command allows you to add notes to commit messages, without
changing the commit. To discern these notes from the message stored
in the commit object, the notes are indented like the message, after
an unindented line saying "Notes:".

To disable commit notes, you have to set the config variable
core.notesRef to the empty string. Alternatively, you can set it
to a different ref, something like "refs/notes/bugzilla". This setting
can be overridden by the environment variable "GIT_NOTES_REF".


SUBCOMMANDS
-----------

edit::
Edit the notes for a given commit (defaults to HEAD).

show::
Show the notes for a given commit (defaults to HEAD).


Author
------
Written by Johannes Schindelin <johannes.schindelin@gmx.de>

Documentation
-------------
Documentation by Johannes Schindelin

GIT
---
Part of the gitlink:git[7] suite
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ SCRIPT_SH += git-merge-octopus.sh
SCRIPT_SH += git-merge-one-file.sh
SCRIPT_SH += git-merge-resolve.sh
SCRIPT_SH += git-mergetool.sh
SCRIPT_SH += git-notes.sh
SCRIPT_SH += git-parse-remote.sh
SCRIPT_SH += git-pull.sh
SCRIPT_SH += git-quiltimport.sh
Expand Down Expand Up @@ -377,6 +378,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
Expand Down Expand Up @@ -459,6 +461,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
Expand Down
3 changes: 3 additions & 0 deletions cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,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);
Expand Down Expand Up @@ -542,6 +544,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;
Expand Down
1 change: 1 addition & 0 deletions command-list.txt
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ git-mktag plumbingmanipulators
git-mktree plumbingmanipulators
git-mv mainporcelain common
git-name-rev plumbinginterrogators
git-notes mainporcelain
git-pack-objects plumbingmanipulators
git-pack-redundant plumbinginterrogators
git-pack-refs ancillarymanipulators
Expand Down
1 change: 1 addition & 0 deletions commit.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "utf8.h"
#include "diff.h"
#include "revision.h"
#include "notes.h"

int save_commit_buffer = 1;

Expand Down
5 changes: 5 additions & 0 deletions config.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
1 change: 1 addition & 0 deletions environment.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
65 changes: 65 additions & 0 deletions git-notes.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/bin/sh

USAGE="(edit | show) [commit]"
. git-sh-setup

test -n "$3" && usage

test -z "$1" && usage
ACTION="$1"; shift

test -z "$GIT_NOTES_REF" && GIT_NOTES_REF="$(git config core.notesref)"
test -z "$GIT_NOTES_REF" && GIT_NOTES_REF="refs/notes/commits"

COMMIT=$(git rev-parse --verify --default HEAD "$@") ||
die "Invalid commit: $@"

MESSAGE="$GIT_DIR"/new-notes-$COMMIT
trap '
test -f "$MESSAGE" && rm "$MESSAGE"
' 0

case "$ACTION" in
edit)
GIT_NOTES_REF= git log -1 $COMMIT | sed "s/^/#/" > "$MESSAGE"

GIT_INDEX_FILE="$MESSAGE".idx
export GIT_INDEX_FILE

CURRENT_HEAD=$(git show-ref "$GIT_NOTES_REF" | cut -f 1 -d ' ')
if [ -z "$CURRENT_HEAD" ]; then
PARENT=
else
PARENT="-p $CURRENT_HEAD"
git read-tree "$GIT_NOTES_REF" || die "Could not read index"
git cat-file blob :$COMMIT >> "$MESSAGE" 2> /dev/null
fi

${VISUAL:-${EDITOR:-vi}} "$MESSAGE"

grep -v ^# < "$MESSAGE" | git stripspace > "$MESSAGE".processed
mv "$MESSAGE".processed "$MESSAGE"
if [ -s "$MESSAGE" ]; then
BLOB=$(git hash-object -w "$MESSAGE") ||
die "Could not write into object database"
git update-index --add --cacheinfo 0644 $BLOB $COMMIT ||
die "Could not write index"
else
test -z "$CURRENT_HEAD" &&
die "Will not initialise with empty tree"
git update-index --force-remove $COMMIT ||
die "Could not update index"
fi

TREE=$(git write-tree) || die "Could not write tree"
NEW_HEAD=$(echo Annotate $COMMIT | git commit-tree $TREE $PARENT) ||
die "Could not annotate"
git update-ref -m "Annotate $COMMIT" \
"$GIT_NOTES_REF" $NEW_HEAD $CURRENT_HEAD
;;
show)
git show "$GIT_NOTES_REF":$COMMIT
;;
*)
usage
esac
160 changes: 160 additions & 0 deletions notes.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
#include "cache.h"
#include "commit.h"
#include "notes.h"
#include "refs.h"
#include "utf8.h"
#include "strbuf.h"
#include "tree-walk.h"

struct entry {
unsigned char commit_sha1[20];
unsigned char notes_sha1[20];
};

struct hash_map {
struct entry *entries;
off_t count, size;
};

static int initialized;
static struct hash_map hash_map;

static int hash_index(struct hash_map *map, const unsigned char *sha1)
{
int i = ((*(unsigned int *)sha1) % map->size);

for (;;) {
unsigned char *current = map->entries[i].commit_sha1;

if (!hashcmp(sha1, current))
return i;

if (is_null_sha1(current))
return -1 - i;

if (++i == map->size)
i = 0;
}
}

static void add_entry(const unsigned char *commit_sha1,
const unsigned char *notes_sha1)
{
int index;

if (hash_map.count + 1 > hash_map.size >> 1) {
int i, old_size = hash_map.size;
struct entry *old = hash_map.entries;

hash_map.size = old_size ? old_size << 1 : 64;
hash_map.entries = (struct entry *)
xcalloc(sizeof(struct entry), hash_map.size);

for (i = 0; i < old_size; i++)
if (!is_null_sha1(old[i].commit_sha1)) {
index = -1 - hash_index(&hash_map,
old[i].commit_sha1);
memcpy(hash_map.entries + index, old + i,
sizeof(struct entry));
}
free(old);
}

index = hash_index(&hash_map, commit_sha1);
if (index < 0) {
index = -1 - index;
hash_map.count++;
}

hashcpy(hash_map.entries[index].commit_sha1, commit_sha1);
hashcpy(hash_map.entries[index].notes_sha1, notes_sha1);
}

static void initialize_hash_map(const char *notes_ref_name)
{
unsigned char sha1[20], commit_sha1[20];
unsigned mode;
struct tree_desc desc;
struct name_entry entry;
void *buf;

if (!notes_ref_name || read_ref(notes_ref_name, commit_sha1) ||
get_tree_entry(commit_sha1, "", sha1, &mode))
return;

buf = fill_tree_descriptor(&desc, sha1);
if (!buf)
die("Could not read %s for notes-index", sha1_to_hex(sha1));

while (tree_entry(&desc, &entry))
if (!get_sha1(entry.path, commit_sha1))
add_entry(commit_sha1, entry.sha1);
free(buf);
}

static unsigned char *lookup_notes(const unsigned char *commit_sha1)
{
int index;

if (!hash_map.size)
return NULL;

index = hash_index(&hash_map, commit_sha1);
if (index < 0)
return NULL;
return hash_map.entries[index].notes_sha1;
}

void get_commit_notes(const struct commit *commit, struct strbuf *sb,
const char *output_encoding)
{
static const char *utf8 = "utf-8";
unsigned char *sha1;
char *msg, *msg_p;
unsigned long linelen, 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;
initialize_hash_map(notes_ref_name);
initialized = 1;
}

sha1 = lookup_notes(commit->object.sha1);
if (!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 (msg_p = msg; msg_p < msg + msglen; msg_p += linelen + 1) {
linelen = strchrnul(msg_p, '\n') - msg_p;

strbuf_addstr(sb, " ");
strbuf_add(sb, msg_p, linelen);
strbuf_addch(sb, '\n');
}

free(msg);
}
7 changes: 7 additions & 0 deletions notes.h
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit 7b75b33

Please sign in to comment.