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