builtin-add.con commit Merge branch 'eb/quilt' into next (7723522)
   1/*
   2 * "git add" builtin command
   3 *
   4 * Copyright (C) 2006 Linus Torvalds
   5 */
   6#include <fnmatch.h>
   7
   8#include "cache.h"
   9#include "builtin.h"
  10#include "dir.h"
  11#include "cache-tree.h"
  12
  13static const char builtin_add_usage[] =
  14"git-add [-n] [-v] <filepattern>...";
  15
  16static int common_prefix(const char **pathspec)
  17{
  18        const char *path, *slash, *next;
  19        int prefix;
  20
  21        if (!pathspec)
  22                return 0;
  23
  24        path = *pathspec;
  25        slash = strrchr(path, '/');
  26        if (!slash)
  27                return 0;
  28
  29        prefix = slash - path + 1;
  30        while ((next = *++pathspec) != NULL) {
  31                int len = strlen(next);
  32                if (len >= prefix && !memcmp(path, next, len))
  33                        continue;
  34                for (;;) {
  35                        if (!len)
  36                                return 0;
  37                        if (next[--len] != '/')
  38                                continue;
  39                        if (memcmp(path, next, len+1))
  40                                continue;
  41                        prefix = len + 1;
  42                        break;
  43                }
  44        }
  45        return prefix;
  46}
  47
  48static int match_one(const char *match, const char *name, int namelen)
  49{
  50        int matchlen;
  51
  52        /* If the match was just the prefix, we matched */
  53        matchlen = strlen(match);
  54        if (!matchlen)
  55                return 1;
  56
  57        /*
  58         * If we don't match the matchstring exactly,
  59         * we need to match by fnmatch
  60         */
  61        if (strncmp(match, name, matchlen))
  62                return !fnmatch(match, name, 0);
  63
  64        /*
  65         * If we did match the string exactly, we still
  66         * need to make sure that it happened on a path
  67         * component boundary (ie either the last character
  68         * of the match was '/', or the next character of
  69         * the name was '/' or the terminating NUL.
  70         */
  71        return  match[matchlen-1] == '/' ||
  72                name[matchlen] == '/' ||
  73                !name[matchlen];
  74}
  75
  76static int match(const char **pathspec, const char *name, int namelen, int prefix, char *seen)
  77{
  78        int retval;
  79        const char *match;
  80
  81        name += prefix;
  82        namelen -= prefix;
  83
  84        for (retval = 0; (match = *pathspec++) != NULL; seen++) {
  85                if (retval & *seen)
  86                        continue;
  87                match += prefix;
  88                if (match_one(match, name, namelen)) {
  89                        retval = 1;
  90                        *seen = 1;
  91                }
  92        }
  93        return retval;
  94}
  95
  96static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
  97{
  98        char *seen;
  99        int i, specs;
 100        struct dir_entry **src, **dst;
 101
 102        for (specs = 0; pathspec[specs];  specs++)
 103                /* nothing */;
 104        seen = xmalloc(specs);
 105        memset(seen, 0, specs);
 106
 107        src = dst = dir->entries;
 108        i = dir->nr;
 109        while (--i >= 0) {
 110                struct dir_entry *entry = *src++;
 111                if (!match(pathspec, entry->name, entry->len, prefix, seen)) {
 112                        free(entry);
 113                        continue;
 114                }
 115                *dst++ = entry;
 116        }
 117        dir->nr = dst - dir->entries;
 118
 119        for (i = 0; i < specs; i++) {
 120                struct stat st;
 121                const char *match;
 122                if (seen[i])
 123                        continue;
 124
 125                /* Existing file? We must have ignored it */
 126                match = pathspec[i];
 127                if (!match[0] || !lstat(match, &st))
 128                        continue;
 129                die("pathspec '%s' did not match any files", match);
 130        }
 131}
 132
 133static void fill_directory(struct dir_struct *dir, const char **pathspec)
 134{
 135        const char *path, *base;
 136        int baselen;
 137
 138        /* Set up the default git porcelain excludes */
 139        memset(dir, 0, sizeof(*dir));
 140        dir->exclude_per_dir = ".gitignore";
 141        path = git_path("info/exclude");
 142        if (!access(path, R_OK))
 143                add_excludes_from_file(dir, path);
 144
 145        /*
 146         * Calculate common prefix for the pathspec, and
 147         * use that to optimize the directory walk
 148         */
 149        baselen = common_prefix(pathspec);
 150        path = ".";
 151        base = "";
 152        if (baselen) {
 153                char *common = xmalloc(baselen + 1);
 154                common = xmalloc(baselen + 1);
 155                memcpy(common, *pathspec, baselen);
 156                common[baselen] = 0;
 157                path = base = common;
 158        }
 159
 160        /* Read the directory and prune it */
 161        read_directory(dir, path, base, baselen);
 162        if (pathspec)
 163                prune_directory(dir, pathspec, baselen);
 164}
 165
 166static int add_file_to_index(const char *path, int verbose)
 167{
 168        int size, namelen;
 169        struct stat st;
 170        struct cache_entry *ce;
 171
 172        if (lstat(path, &st))
 173                die("%s: unable to stat (%s)", path, strerror(errno));
 174
 175        if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
 176                die("%s: can only add regular files or symbolic links", path);
 177
 178        namelen = strlen(path);
 179        size = cache_entry_size(namelen);
 180        ce = xcalloc(1, size);
 181        memcpy(ce->name, path, namelen);
 182        ce->ce_flags = htons(namelen);
 183        fill_stat_cache_info(ce, &st);
 184
 185        ce->ce_mode = create_ce_mode(st.st_mode);
 186        if (!trust_executable_bit) {
 187                /* If there is an existing entry, pick the mode bits
 188                 * from it.
 189                 */
 190                int pos = cache_name_pos(path, namelen);
 191                if (pos >= 0)
 192                        ce->ce_mode = active_cache[pos]->ce_mode;
 193        }
 194
 195        if (index_path(ce->sha1, path, &st, 1))
 196                die("unable to index file %s", path);
 197        if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD))
 198                die("unable to add %s to index",path);
 199        if (verbose)
 200                printf("add '%s'\n", path);
 201        cache_tree_invalidate_path(active_cache_tree, path);
 202        return 0;
 203}
 204
 205static struct cache_file cache_file;
 206
 207int cmd_add(int argc, const char **argv, char **envp)
 208{
 209        int i, newfd;
 210        int verbose = 0, show_only = 0;
 211        const char *prefix = setup_git_directory();
 212        const char **pathspec;
 213        struct dir_struct dir;
 214
 215        git_config(git_default_config);
 216
 217        newfd = hold_index_file_for_update(&cache_file, get_index_file());
 218        if (newfd < 0)
 219                die("unable to create new cachefile");
 220
 221        if (read_cache() < 0)
 222                die("index file corrupt");
 223
 224        for (i = 1; i < argc; i++) {
 225                const char *arg = argv[i];
 226
 227                if (arg[0] != '-')
 228                        break;
 229                if (!strcmp(arg, "--")) {
 230                        i++;
 231                        break;
 232                }
 233                if (!strcmp(arg, "-n")) {
 234                        show_only = 1;
 235                        continue;
 236                }
 237                if (!strcmp(arg, "-v")) {
 238                        verbose = 1;
 239                        continue;
 240                }
 241                die(builtin_add_usage);
 242        }
 243        git_config(git_default_config);
 244        pathspec = get_pathspec(prefix, argv + i);
 245
 246        fill_directory(&dir, pathspec);
 247
 248        if (show_only) {
 249                const char *sep = "", *eof = "";
 250                for (i = 0; i < dir.nr; i++) {
 251                        printf("%s%s", sep, dir.entries[i]->name);
 252                        sep = " ";
 253                        eof = "\n";
 254                }
 255                fputs(eof, stdout);
 256                return 0;
 257        }
 258
 259        for (i = 0; i < dir.nr; i++)
 260                add_file_to_index(dir.entries[i]->name, verbose);
 261
 262        if (active_cache_changed) {
 263                if (write_cache(newfd, active_cache, active_nr) ||
 264                    commit_index_file(&cache_file))
 265                        die("Unable to write new index file");
 266        }
 267
 268        return 0;
 269}