builtin-add.con commit merge-recursive: separate out xdl_merge() interface. (3e5261a)
   1/*
   2 * "git add" builtin command
   3 *
   4 * Copyright (C) 2006 Linus Torvalds
   5 */
   6#include "cache.h"
   7#include "builtin.h"
   8#include "dir.h"
   9#include "exec_cmd.h"
  10#include "cache-tree.h"
  11
  12static const char builtin_add_usage[] =
  13"git-add [-n] [-v] [-f] [--interactive | -i] [--] <filepattern>...";
  14
  15static const char *excludes_file;
  16
  17static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
  18{
  19        char *seen;
  20        int i, specs;
  21        struct dir_entry **src, **dst;
  22
  23        for (specs = 0; pathspec[specs];  specs++)
  24                /* nothing */;
  25        seen = xcalloc(specs, 1);
  26
  27        src = dst = dir->entries;
  28        i = dir->nr;
  29        while (--i >= 0) {
  30                struct dir_entry *entry = *src++;
  31                if (match_pathspec(pathspec, entry->name, entry->len,
  32                                   prefix, seen))
  33                        *dst++ = entry;
  34        }
  35        dir->nr = dst - dir->entries;
  36
  37        for (i = 0; i < specs; i++) {
  38                struct stat st;
  39                const char *match;
  40                if (seen[i])
  41                        continue;
  42
  43                match = pathspec[i];
  44                if (!match[0])
  45                        continue;
  46
  47                /* Existing file? We must have ignored it */
  48                if (!lstat(match, &st)) {
  49                        struct dir_entry *ent;
  50
  51                        ent = dir_add_name(dir, match, strlen(match));
  52                        ent->ignored = 1;
  53                        if (S_ISDIR(st.st_mode))
  54                                ent->ignored_dir = 1;
  55                        continue;
  56                }
  57                die("pathspec '%s' did not match any files", match);
  58        }
  59}
  60
  61static void fill_directory(struct dir_struct *dir, const char **pathspec)
  62{
  63        const char *path, *base;
  64        int baselen;
  65
  66        /* Set up the default git porcelain excludes */
  67        memset(dir, 0, sizeof(*dir));
  68        dir->exclude_per_dir = ".gitignore";
  69        path = git_path("info/exclude");
  70        if (!access(path, R_OK))
  71                add_excludes_from_file(dir, path);
  72        if (!access(excludes_file, R_OK))
  73                add_excludes_from_file(dir, excludes_file);
  74
  75        /*
  76         * Calculate common prefix for the pathspec, and
  77         * use that to optimize the directory walk
  78         */
  79        baselen = common_prefix(pathspec);
  80        path = ".";
  81        base = "";
  82        if (baselen) {
  83                char *common = xmalloc(baselen + 1);
  84                memcpy(common, *pathspec, baselen);
  85                common[baselen] = 0;
  86                path = base = common;
  87        }
  88
  89        /* Read the directory and prune it */
  90        read_directory(dir, path, base, baselen, pathspec);
  91        if (pathspec)
  92                prune_directory(dir, pathspec, baselen);
  93}
  94
  95static int git_add_config(const char *var, const char *value)
  96{
  97        if (!strcmp(var, "core.excludesfile")) {
  98                if (!value)
  99                        die("core.excludesfile without value");
 100                excludes_file = xstrdup(value);
 101                return 0;
 102        }
 103
 104        return git_default_config(var, value);
 105}
 106
 107static struct lock_file lock_file;
 108
 109static const char ignore_warning[] =
 110"The following paths are ignored by one of your .gitignore files:\n";
 111
 112int cmd_add(int argc, const char **argv, const char *prefix)
 113{
 114        int i, newfd;
 115        int verbose = 0, show_only = 0, ignored_too = 0;
 116        const char **pathspec;
 117        struct dir_struct dir;
 118        int add_interactive = 0;
 119
 120        for (i = 1; i < argc; i++) {
 121                if (!strcmp("--interactive", argv[i]) ||
 122                    !strcmp("-i", argv[i]))
 123                        add_interactive++;
 124        }
 125        if (add_interactive) {
 126                const char *args[] = { "add--interactive", NULL };
 127
 128                if (add_interactive != 1 || argc != 2)
 129                        die("add --interactive does not take any parameters");
 130                execv_git_cmd(args);
 131                exit(1);
 132        }
 133
 134        git_config(git_add_config);
 135
 136        newfd = hold_locked_index(&lock_file, 1);
 137
 138        for (i = 1; i < argc; i++) {
 139                const char *arg = argv[i];
 140
 141                if (arg[0] != '-')
 142                        break;
 143                if (!strcmp(arg, "--")) {
 144                        i++;
 145                        break;
 146                }
 147                if (!strcmp(arg, "-n")) {
 148                        show_only = 1;
 149                        continue;
 150                }
 151                if (!strcmp(arg, "-f")) {
 152                        ignored_too = 1;
 153                        continue;
 154                }
 155                if (!strcmp(arg, "-v")) {
 156                        verbose = 1;
 157                        continue;
 158                }
 159                usage(builtin_add_usage);
 160        }
 161        if (argc <= i) {
 162                fprintf(stderr, "Nothing specified, nothing added.\n");
 163                fprintf(stderr, "Maybe you wanted to say 'git add .'?\n");
 164                return 0;
 165        }
 166        pathspec = get_pathspec(prefix, argv + i);
 167
 168        fill_directory(&dir, pathspec);
 169
 170        if (show_only) {
 171                const char *sep = "", *eof = "";
 172                for (i = 0; i < dir.nr; i++) {
 173                        if (!ignored_too && dir.entries[i]->ignored)
 174                                continue;
 175                        printf("%s%s", sep, dir.entries[i]->name);
 176                        sep = " ";
 177                        eof = "\n";
 178                }
 179                fputs(eof, stdout);
 180                return 0;
 181        }
 182
 183        if (read_cache() < 0)
 184                die("index file corrupt");
 185
 186        if (!ignored_too) {
 187                int has_ignored = 0;
 188                for (i = 0; i < dir.nr; i++)
 189                        if (dir.entries[i]->ignored)
 190                                has_ignored = 1;
 191                if (has_ignored) {
 192                        fprintf(stderr, ignore_warning);
 193                        for (i = 0; i < dir.nr; i++) {
 194                                if (!dir.entries[i]->ignored)
 195                                        continue;
 196                                fprintf(stderr, "%s", dir.entries[i]->name);
 197                                if (dir.entries[i]->ignored_dir)
 198                                        fprintf(stderr, " (directory)");
 199                                fputc('\n', stderr);
 200                        }
 201                        fprintf(stderr,
 202                                "Use -f if you really want to add them.\n");
 203                        exit(1);
 204                }
 205        }
 206
 207        for (i = 0; i < dir.nr; i++)
 208                add_file_to_cache(dir.entries[i]->name, verbose);
 209
 210        if (active_cache_changed) {
 211                if (write_cache(newfd, active_cache, active_nr) ||
 212                    close(newfd) || commit_locked_index(&lock_file))
 213                        die("Unable to write new index file");
 214        }
 215
 216        return 0;
 217}