notes.con commit Teach notes code to free its internal data structures on request (27d5756)
   1#include "cache.h"
   2#include "commit.h"
   3#include "notes.h"
   4#include "refs.h"
   5#include "utf8.h"
   6#include "strbuf.h"
   7#include "tree-walk.h"
   8
   9struct entry {
  10        unsigned char commit_sha1[20];
  11        unsigned char notes_sha1[20];
  12};
  13
  14struct hash_map {
  15        struct entry *entries;
  16        off_t count, size;
  17};
  18
  19static int initialized;
  20static struct hash_map hash_map;
  21
  22static int hash_index(struct hash_map *map, const unsigned char *sha1)
  23{
  24        int i = ((*(unsigned int *)sha1) % map->size);
  25
  26        for (;;) {
  27                unsigned char *current = map->entries[i].commit_sha1;
  28
  29                if (!hashcmp(sha1, current))
  30                        return i;
  31
  32                if (is_null_sha1(current))
  33                        return -1 - i;
  34
  35                if (++i == map->size)
  36                        i = 0;
  37        }
  38}
  39
  40static void add_entry(const unsigned char *commit_sha1,
  41                const unsigned char *notes_sha1)
  42{
  43        int index;
  44
  45        if (hash_map.count + 1 > hash_map.size >> 1) {
  46                int i, old_size = hash_map.size;
  47                struct entry *old = hash_map.entries;
  48
  49                hash_map.size = old_size ? old_size << 1 : 64;
  50                hash_map.entries = (struct entry *)
  51                        xcalloc(sizeof(struct entry), hash_map.size);
  52
  53                for (i = 0; i < old_size; i++)
  54                        if (!is_null_sha1(old[i].commit_sha1)) {
  55                                index = -1 - hash_index(&hash_map,
  56                                                old[i].commit_sha1);
  57                                memcpy(hash_map.entries + index, old + i,
  58                                        sizeof(struct entry));
  59                        }
  60                free(old);
  61        }
  62
  63        index = hash_index(&hash_map, commit_sha1);
  64        if (index < 0) {
  65                index = -1 - index;
  66                hash_map.count++;
  67        }
  68
  69        hashcpy(hash_map.entries[index].commit_sha1, commit_sha1);
  70        hashcpy(hash_map.entries[index].notes_sha1, notes_sha1);
  71}
  72
  73static void initialize_hash_map(const char *notes_ref_name)
  74{
  75        unsigned char sha1[20], commit_sha1[20];
  76        unsigned mode;
  77        struct tree_desc desc;
  78        struct name_entry entry;
  79        void *buf;
  80
  81        if (!notes_ref_name || read_ref(notes_ref_name, commit_sha1) ||
  82            get_tree_entry(commit_sha1, "", sha1, &mode))
  83                return;
  84
  85        buf = fill_tree_descriptor(&desc, sha1);
  86        if (!buf)
  87                die("Could not read %s for notes-index", sha1_to_hex(sha1));
  88
  89        while (tree_entry(&desc, &entry))
  90                if (!get_sha1(entry.path, commit_sha1))
  91                        add_entry(commit_sha1, entry.sha1);
  92        free(buf);
  93}
  94
  95static unsigned char *lookup_notes(const unsigned char *commit_sha1)
  96{
  97        int index;
  98
  99        if (!hash_map.size)
 100                return NULL;
 101
 102        index = hash_index(&hash_map, commit_sha1);
 103        if (index < 0)
 104                return NULL;
 105        return hash_map.entries[index].notes_sha1;
 106}
 107
 108void free_notes(void)
 109{
 110        free(hash_map.entries);
 111        memset(&hash_map, 0, sizeof(struct hash_map));
 112        initialized = 0;
 113}
 114
 115void get_commit_notes(const struct commit *commit, struct strbuf *sb,
 116                const char *output_encoding, int flags)
 117{
 118        static const char utf8[] = "utf-8";
 119        unsigned char *sha1;
 120        char *msg, *msg_p;
 121        unsigned long linelen, msglen;
 122        enum object_type type;
 123
 124        if (!initialized) {
 125                const char *env = getenv(GIT_NOTES_REF_ENVIRONMENT);
 126                if (env)
 127                        notes_ref_name = getenv(GIT_NOTES_REF_ENVIRONMENT);
 128                else if (!notes_ref_name)
 129                        notes_ref_name = GIT_NOTES_DEFAULT_REF;
 130                initialize_hash_map(notes_ref_name);
 131                initialized = 1;
 132        }
 133
 134        sha1 = lookup_notes(commit->object.sha1);
 135        if (!sha1)
 136                return;
 137
 138        if (!(msg = read_sha1_file(sha1, &type, &msglen)) || !msglen ||
 139                        type != OBJ_BLOB) {
 140                free(msg);
 141                return;
 142        }
 143
 144        if (output_encoding && *output_encoding &&
 145                        strcmp(utf8, output_encoding)) {
 146                char *reencoded = reencode_string(msg, output_encoding, utf8);
 147                if (reencoded) {
 148                        free(msg);
 149                        msg = reencoded;
 150                        msglen = strlen(msg);
 151                }
 152        }
 153
 154        /* we will end the annotation by a newline anyway */
 155        if (msglen && msg[msglen - 1] == '\n')
 156                msglen--;
 157
 158        if (flags & NOTES_SHOW_HEADER)
 159                strbuf_addstr(sb, "\nNotes:\n");
 160
 161        for (msg_p = msg; msg_p < msg + msglen; msg_p += linelen + 1) {
 162                linelen = strchrnul(msg_p, '\n') - msg_p;
 163
 164                if (flags & NOTES_INDENT)
 165                        strbuf_addstr(sb, "    ");
 166                strbuf_add(sb, msg_p, linelen);
 167                strbuf_addch(sb, '\n');
 168        }
 169
 170        free(msg);
 171}