git-commit-vandalism/notes.c

161 lines
3.5 KiB
C
Raw Normal View History

#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);
}