8c025bc03daf5472dbd0c447f2fa2c99ff93e046
   1#include "cache.h"
   2#include "refs.h"
   3#include "cache-tree.h"
   4#include "blame.h"
   5
   6void blame_origin_decref(struct blame_origin *o)
   7{
   8        if (o && --o->refcnt <= 0) {
   9                struct blame_origin *p, *l = NULL;
  10                if (o->previous)
  11                        blame_origin_decref(o->previous);
  12                free(o->file.ptr);
  13                /* Should be present exactly once in commit chain */
  14                for (p = o->commit->util; p; l = p, p = p->next) {
  15                        if (p == o) {
  16                                if (l)
  17                                        l->next = p->next;
  18                                else
  19                                        o->commit->util = p->next;
  20                                free(o);
  21                                return;
  22                        }
  23                }
  24                die("internal error in blame_origin_decref");
  25        }
  26}
  27
  28/*
  29 * Given a commit and a path in it, create a new origin structure.
  30 * The callers that add blame to the scoreboard should use
  31 * get_origin() to obtain shared, refcounted copy instead of calling
  32 * this function directly.
  33 */
  34static struct blame_origin *make_origin(struct commit *commit, const char *path)
  35{
  36        struct blame_origin *o;
  37        FLEX_ALLOC_STR(o, path, path);
  38        o->commit = commit;
  39        o->refcnt = 1;
  40        o->next = commit->util;
  41        commit->util = o;
  42        return o;
  43}
  44
  45/*
  46 * Locate an existing origin or create a new one.
  47 * This moves the origin to front position in the commit util list.
  48 */
  49struct blame_origin *get_origin(struct commit *commit, const char *path)
  50{
  51        struct blame_origin *o, *l;
  52
  53        for (o = commit->util, l = NULL; o; l = o, o = o->next) {
  54                if (!strcmp(o->path, path)) {
  55                        /* bump to front */
  56                        if (l) {
  57                                l->next = o->next;
  58                                o->next = commit->util;
  59                                commit->util = o;
  60                        }
  61                        return blame_origin_incref(o);
  62                }
  63        }
  64        return make_origin(commit, path);
  65}
  66
  67
  68
  69static void verify_working_tree_path(struct commit *work_tree, const char *path)
  70{
  71        struct commit_list *parents;
  72        int pos;
  73
  74        for (parents = work_tree->parents; parents; parents = parents->next) {
  75                const struct object_id *commit_oid = &parents->item->object.oid;
  76                struct object_id blob_oid;
  77                unsigned mode;
  78
  79                if (!get_tree_entry(commit_oid->hash, path, blob_oid.hash, &mode) &&
  80                    sha1_object_info(blob_oid.hash, NULL) == OBJ_BLOB)
  81                        return;
  82        }
  83
  84        pos = cache_name_pos(path, strlen(path));
  85        if (pos >= 0)
  86                ; /* path is in the index */
  87        else if (-1 - pos < active_nr &&
  88                 !strcmp(active_cache[-1 - pos]->name, path))
  89                ; /* path is in the index, unmerged */
  90        else
  91                die("no such path '%s' in HEAD", path);
  92}
  93
  94static struct commit_list **append_parent(struct commit_list **tail, const struct object_id *oid)
  95{
  96        struct commit *parent;
  97
  98        parent = lookup_commit_reference(oid->hash);
  99        if (!parent)
 100                die("no such commit %s", oid_to_hex(oid));
 101        return &commit_list_insert(parent, tail)->next;
 102}
 103
 104static void append_merge_parents(struct commit_list **tail)
 105{
 106        int merge_head;
 107        struct strbuf line = STRBUF_INIT;
 108
 109        merge_head = open(git_path_merge_head(), O_RDONLY);
 110        if (merge_head < 0) {
 111                if (errno == ENOENT)
 112                        return;
 113                die("cannot open '%s' for reading", git_path_merge_head());
 114        }
 115
 116        while (!strbuf_getwholeline_fd(&line, merge_head, '\n')) {
 117                struct object_id oid;
 118                if (line.len < GIT_SHA1_HEXSZ || get_oid_hex(line.buf, &oid))
 119                        die("unknown line in '%s': %s", git_path_merge_head(), line.buf);
 120                tail = append_parent(tail, &oid);
 121        }
 122        close(merge_head);
 123        strbuf_release(&line);
 124}
 125
 126/*
 127 * This isn't as simple as passing sb->buf and sb->len, because we
 128 * want to transfer ownership of the buffer to the commit (so we
 129 * must use detach).
 130 */
 131static void set_commit_buffer_from_strbuf(struct commit *c, struct strbuf *sb)
 132{
 133        size_t len;
 134        void *buf = strbuf_detach(sb, &len);
 135        set_commit_buffer(c, buf, len);
 136}
 137
 138/*
 139 * Prepare a dummy commit that represents the work tree (or staged) item.
 140 * Note that annotating work tree item never works in the reverse.
 141 */
 142struct commit *fake_working_tree_commit(struct diff_options *opt,
 143                                        const char *path,
 144                                        const char *contents_from)
 145{
 146        struct commit *commit;
 147        struct blame_origin *origin;
 148        struct commit_list **parent_tail, *parent;
 149        struct object_id head_oid;
 150        struct strbuf buf = STRBUF_INIT;
 151        const char *ident;
 152        time_t now;
 153        int size, len;
 154        struct cache_entry *ce;
 155        unsigned mode;
 156        struct strbuf msg = STRBUF_INIT;
 157
 158        read_cache();
 159        time(&now);
 160        commit = alloc_commit_node();
 161        commit->object.parsed = 1;
 162        commit->date = now;
 163        parent_tail = &commit->parents;
 164
 165        if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL))
 166                die("no such ref: HEAD");
 167
 168        parent_tail = append_parent(parent_tail, &head_oid);
 169        append_merge_parents(parent_tail);
 170        verify_working_tree_path(commit, path);
 171
 172        origin = make_origin(commit, path);
 173
 174        ident = fmt_ident("Not Committed Yet", "not.committed.yet", NULL, 0);
 175        strbuf_addstr(&msg, "tree 0000000000000000000000000000000000000000\n");
 176        for (parent = commit->parents; parent; parent = parent->next)
 177                strbuf_addf(&msg, "parent %s\n",
 178                            oid_to_hex(&parent->item->object.oid));
 179        strbuf_addf(&msg,
 180                    "author %s\n"
 181                    "committer %s\n\n"
 182                    "Version of %s from %s\n",
 183                    ident, ident, path,
 184                    (!contents_from ? path :
 185                     (!strcmp(contents_from, "-") ? "standard input" : contents_from)));
 186        set_commit_buffer_from_strbuf(commit, &msg);
 187
 188        if (!contents_from || strcmp("-", contents_from)) {
 189                struct stat st;
 190                const char *read_from;
 191                char *buf_ptr;
 192                unsigned long buf_len;
 193
 194                if (contents_from) {
 195                        if (stat(contents_from, &st) < 0)
 196                                die_errno("Cannot stat '%s'", contents_from);
 197                        read_from = contents_from;
 198                }
 199                else {
 200                        if (lstat(path, &st) < 0)
 201                                die_errno("Cannot lstat '%s'", path);
 202                        read_from = path;
 203                }
 204                mode = canon_mode(st.st_mode);
 205
 206                switch (st.st_mode & S_IFMT) {
 207                case S_IFREG:
 208                        if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
 209                            textconv_object(read_from, mode, &null_oid, 0, &buf_ptr, &buf_len))
 210                                strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);
 211                        else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
 212                                die_errno("cannot open or read '%s'", read_from);
 213                        break;
 214                case S_IFLNK:
 215                        if (strbuf_readlink(&buf, read_from, st.st_size) < 0)
 216                                die_errno("cannot readlink '%s'", read_from);
 217                        break;
 218                default:
 219                        die("unsupported file type %s", read_from);
 220                }
 221        }
 222        else {
 223                /* Reading from stdin */
 224                mode = 0;
 225                if (strbuf_read(&buf, 0, 0) < 0)
 226                        die_errno("failed to read from stdin");
 227        }
 228        convert_to_git(path, buf.buf, buf.len, &buf, 0);
 229        origin->file.ptr = buf.buf;
 230        origin->file.size = buf.len;
 231        pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_oid.hash);
 232
 233        /*
 234         * Read the current index, replace the path entry with
 235         * origin->blob_sha1 without mucking with its mode or type
 236         * bits; we are not going to write this index out -- we just
 237         * want to run "diff-index --cached".
 238         */
 239        discard_cache();
 240        read_cache();
 241
 242        len = strlen(path);
 243        if (!mode) {
 244                int pos = cache_name_pos(path, len);
 245                if (0 <= pos)
 246                        mode = active_cache[pos]->ce_mode;
 247                else
 248                        /* Let's not bother reading from HEAD tree */
 249                        mode = S_IFREG | 0644;
 250        }
 251        size = cache_entry_size(len);
 252        ce = xcalloc(1, size);
 253        oidcpy(&ce->oid, &origin->blob_oid);
 254        memcpy(ce->name, path, len);
 255        ce->ce_flags = create_ce_flags(0);
 256        ce->ce_namelen = len;
 257        ce->ce_mode = create_ce_mode(mode);
 258        add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
 259
 260        cache_tree_invalidate_path(&the_index, path);
 261
 262        return commit;
 263}