archive.con commit Documentation: clarify diff --cc (ea3d988)
   1#include "cache.h"
   2#include "commit.h"
   3#include "attr.h"
   4#include "archive.h"
   5
   6static void format_subst(const struct commit *commit,
   7                         const char *src, size_t len,
   8                         struct strbuf *buf)
   9{
  10        char *to_free = NULL;
  11        struct strbuf fmt;
  12
  13        if (src == buf->buf)
  14                to_free = strbuf_detach(buf, NULL);
  15        strbuf_init(&fmt, 0);
  16        for (;;) {
  17                const char *b, *c;
  18
  19                b = memmem(src, len, "$Format:", 8);
  20                if (!b)
  21                        break;
  22                c = memchr(b + 8, '$', (src + len) - b - 8);
  23                if (!c)
  24                        break;
  25
  26                strbuf_reset(&fmt);
  27                strbuf_add(&fmt, b + 8, c - b - 8);
  28
  29                strbuf_add(buf, src, b - src);
  30                format_commit_message(commit, fmt.buf, buf);
  31                len -= c + 1 - src;
  32                src  = c + 1;
  33        }
  34        strbuf_add(buf, src, len);
  35        strbuf_release(&fmt);
  36        free(to_free);
  37}
  38
  39static void *sha1_file_to_archive(const char *path, const unsigned char *sha1,
  40                unsigned int mode, enum object_type *type,
  41                unsigned long *sizep, const struct commit *commit)
  42{
  43        void *buffer;
  44
  45        buffer = read_sha1_file(sha1, type, sizep);
  46        if (buffer && S_ISREG(mode)) {
  47                struct strbuf buf;
  48                size_t size = 0;
  49
  50                strbuf_init(&buf, 0);
  51                strbuf_attach(&buf, buffer, *sizep, *sizep + 1);
  52                convert_to_working_tree(path, buf.buf, buf.len, &buf);
  53                if (commit)
  54                        format_subst(commit, buf.buf, buf.len, &buf);
  55                buffer = strbuf_detach(&buf, &size);
  56                *sizep = size;
  57        }
  58
  59        return buffer;
  60}
  61
  62static void setup_archive_check(struct git_attr_check *check)
  63{
  64        static struct git_attr *attr_export_ignore;
  65        static struct git_attr *attr_export_subst;
  66
  67        if (!attr_export_ignore) {
  68                attr_export_ignore = git_attr("export-ignore", 13);
  69                attr_export_subst = git_attr("export-subst", 12);
  70        }
  71        check[0].attr = attr_export_ignore;
  72        check[1].attr = attr_export_subst;
  73}
  74
  75struct archiver_context {
  76        struct archiver_args *args;
  77        write_archive_entry_fn_t write_entry;
  78};
  79
  80static int write_archive_entry(const unsigned char *sha1, const char *base,
  81                int baselen, const char *filename, unsigned mode, int stage,
  82                void *context)
  83{
  84        static struct strbuf path = STRBUF_INIT;
  85        struct archiver_context *c = context;
  86        struct archiver_args *args = c->args;
  87        write_archive_entry_fn_t write_entry = c->write_entry;
  88        struct git_attr_check check[2];
  89        const char *path_without_prefix;
  90        int convert = 0;
  91        int err;
  92        enum object_type type;
  93        unsigned long size;
  94        void *buffer;
  95
  96        strbuf_reset(&path);
  97        strbuf_grow(&path, PATH_MAX);
  98        strbuf_add(&path, base, baselen);
  99        strbuf_addstr(&path, filename);
 100        path_without_prefix = path.buf + args->baselen;
 101
 102        setup_archive_check(check);
 103        if (!git_checkattr(path_without_prefix, ARRAY_SIZE(check), check)) {
 104                if (ATTR_TRUE(check[0].value))
 105                        return 0;
 106                convert = ATTR_TRUE(check[1].value);
 107        }
 108
 109        if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
 110                strbuf_addch(&path, '/');
 111                if (args->verbose)
 112                        fprintf(stderr, "%.*s\n", (int)path.len, path.buf);
 113                err = write_entry(args, sha1, path.buf, path.len, mode, NULL, 0);
 114                if (err)
 115                        return err;
 116                return READ_TREE_RECURSIVE;
 117        }
 118
 119        buffer = sha1_file_to_archive(path_without_prefix, sha1, mode,
 120                        &type, &size, convert ? args->commit : NULL);
 121        if (!buffer)
 122                return error("cannot read %s", sha1_to_hex(sha1));
 123        if (args->verbose)
 124                fprintf(stderr, "%.*s\n", (int)path.len, path.buf);
 125        err = write_entry(args, sha1, path.buf, path.len, mode, buffer, size);
 126        free(buffer);
 127        return err;
 128}
 129
 130int write_archive_entries(struct archiver_args *args,
 131                write_archive_entry_fn_t write_entry)
 132{
 133        struct archiver_context context;
 134        int err;
 135
 136        if (args->baselen > 0 && args->base[args->baselen - 1] == '/') {
 137                size_t len = args->baselen;
 138
 139                while (len > 1 && args->base[len - 2] == '/')
 140                        len--;
 141                if (args->verbose)
 142                        fprintf(stderr, "%.*s\n", (int)len, args->base);
 143                err = write_entry(args, args->tree->object.sha1, args->base,
 144                                len, 040777, NULL, 0);
 145                if (err)
 146                        return err;
 147        }
 148
 149        context.args = args;
 150        context.write_entry = write_entry;
 151
 152        err =  read_tree_recursive(args->tree, args->base, args->baselen, 0,
 153                        args->pathspec, write_archive_entry, &context);
 154        if (err == READ_TREE_RECURSIVE)
 155                err = 0;
 156        return err;
 157}