58de55e15ce8f00d65de440191285c32f9fc79e1
   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 int convert_to_archive(const char *path,
  40                              const void *src, size_t len,
  41                              struct strbuf *buf,
  42                              const struct commit *commit)
  43{
  44        static struct git_attr *attr_export_subst;
  45        struct git_attr_check check[1];
  46
  47        if (!commit)
  48                return 0;
  49
  50        if (!attr_export_subst)
  51                attr_export_subst = git_attr("export-subst", 12);
  52
  53        check[0].attr = attr_export_subst;
  54        if (git_checkattr(path, ARRAY_SIZE(check), check))
  55                return 0;
  56        if (!ATTR_TRUE(check[0].value))
  57                return 0;
  58
  59        format_subst(commit, src, len, buf);
  60        return 1;
  61}
  62
  63void *sha1_file_to_archive(const char *path, const unsigned char *sha1,
  64                           unsigned int mode, enum object_type *type,
  65                           unsigned long *sizep,
  66                           const struct commit *commit)
  67{
  68        void *buffer;
  69
  70        buffer = read_sha1_file(sha1, type, sizep);
  71        if (buffer && S_ISREG(mode)) {
  72                struct strbuf buf;
  73                size_t size = 0;
  74
  75                strbuf_init(&buf, 0);
  76                strbuf_attach(&buf, buffer, *sizep, *sizep + 1);
  77                convert_to_working_tree(path, buf.buf, buf.len, &buf);
  78                convert_to_archive(path, buf.buf, buf.len, &buf, commit);
  79                buffer = strbuf_detach(&buf, &size);
  80                *sizep = size;
  81        }
  82
  83        return buffer;
  84}
  85
  86int is_archive_path_ignored(const char *path)
  87{
  88        static struct git_attr *attr_export_ignore;
  89        struct git_attr_check check[1];
  90
  91        if (!attr_export_ignore)
  92                attr_export_ignore = git_attr("export-ignore", 13);
  93
  94        check[0].attr = attr_export_ignore;
  95        if (git_checkattr(path, ARRAY_SIZE(check), check))
  96                return 0;
  97        return ATTR_TRUE(check[0].value);
  98}
  99
 100struct archiver_context {
 101        struct archiver_args *args;
 102        write_archive_entry_fn_t write_entry;
 103};
 104
 105static int write_archive_entry(const unsigned char *sha1, const char *base,
 106                int baselen, const char *filename, unsigned mode, int stage,
 107                void *context)
 108{
 109        static struct strbuf path = STRBUF_INIT;
 110        struct archiver_context *c = context;
 111        struct archiver_args *args = c->args;
 112        write_archive_entry_fn_t write_entry = c->write_entry;
 113        int err;
 114        enum object_type type;
 115        unsigned long size;
 116        void *buffer;
 117
 118        strbuf_reset(&path);
 119        strbuf_grow(&path, PATH_MAX);
 120        strbuf_add(&path, base, baselen);
 121        strbuf_addstr(&path, filename);
 122
 123        if (is_archive_path_ignored(path.buf + args->baselen))
 124                return 0;
 125
 126        if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
 127                strbuf_addch(&path, '/');
 128                if (args->verbose)
 129                        fprintf(stderr, "%.*s\n", (int)path.len, path.buf);
 130                err = write_entry(args, sha1, path.buf, path.len, mode, NULL, 0);
 131                if (err)
 132                        return err;
 133                return READ_TREE_RECURSIVE;
 134        }
 135
 136        buffer = sha1_file_to_archive(path.buf + args->baselen, sha1, mode,
 137                        &type, &size, args->commit);
 138        if (!buffer)
 139                return error("cannot read %s", sha1_to_hex(sha1));
 140        if (args->verbose)
 141                fprintf(stderr, "%.*s\n", (int)path.len, path.buf);
 142        err = write_entry(args, sha1, path.buf, path.len, mode, buffer, size);
 143        free(buffer);
 144        return err;
 145}
 146
 147int write_archive_entries(struct archiver_args *args,
 148                write_archive_entry_fn_t write_entry)
 149{
 150        struct archiver_context context;
 151        int err;
 152
 153        if (args->baselen > 0 && args->base[args->baselen - 1] == '/') {
 154                size_t len = args->baselen;
 155
 156                while (len > 1 && args->base[len - 2] == '/')
 157                        len--;
 158                if (args->verbose)
 159                        fprintf(stderr, "%.*s\n", (int)len, args->base);
 160                err = write_entry(args, args->tree->object.sha1, args->base,
 161                                len, 040777, NULL, 0);
 162                if (err)
 163                        return err;
 164        }
 165
 166        context.args = args;
 167        context.write_entry = write_entry;
 168
 169        err =  read_tree_recursive(args->tree, args->base, args->baselen, 0,
 170                        args->pathspec, write_archive_entry, &context);
 171        if (err == READ_TREE_RECURSIVE)
 172                err = 0;
 173        return err;
 174}