builtin / add.con commit t: move "git add submodule" into test blocks (17f2f88)
   1/*
   2 * "git add" builtin command
   3 *
   4 * Copyright (C) 2006 Linus Torvalds
   5 */
   6#include "cache.h"
   7#include "builtin.h"
   8#include "lockfile.h"
   9#include "dir.h"
  10#include "pathspec.h"
  11#include "exec_cmd.h"
  12#include "cache-tree.h"
  13#include "run-command.h"
  14#include "parse-options.h"
  15#include "diff.h"
  16#include "diffcore.h"
  17#include "revision.h"
  18#include "bulk-checkin.h"
  19#include "argv-array.h"
  20#include "submodule.h"
  21
  22static const char * const builtin_add_usage[] = {
  23        N_("git add [<options>] [--] <pathspec>..."),
  24        NULL
  25};
  26static int patch_interactive, add_interactive, edit_interactive;
  27static int take_worktree_changes;
  28
  29struct update_callback_data {
  30        int flags;
  31        int add_errors;
  32};
  33
  34static void chmod_pathspec(struct pathspec *pathspec, int force_mode)
  35{
  36        int i;
  37
  38        for (i = 0; i < active_nr; i++) {
  39                struct cache_entry *ce = active_cache[i];
  40
  41                if (pathspec && !ce_path_match(ce, pathspec, NULL))
  42                        continue;
  43
  44                if (chmod_cache_entry(ce, force_mode) < 0)
  45                        fprintf(stderr, "cannot chmod '%s'", ce->name);
  46        }
  47}
  48
  49static int fix_unmerged_status(struct diff_filepair *p,
  50                               struct update_callback_data *data)
  51{
  52        if (p->status != DIFF_STATUS_UNMERGED)
  53                return p->status;
  54        if (!(data->flags & ADD_CACHE_IGNORE_REMOVAL) && !p->two->mode)
  55                /*
  56                 * This is not an explicit add request, and the
  57                 * path is missing from the working tree (deleted)
  58                 */
  59                return DIFF_STATUS_DELETED;
  60        else
  61                /*
  62                 * Either an explicit add request, or path exists
  63                 * in the working tree.  An attempt to explicitly
  64                 * add a path that does not exist in the working tree
  65                 * will be caught as an error by the caller immediately.
  66                 */
  67                return DIFF_STATUS_MODIFIED;
  68}
  69
  70static void update_callback(struct diff_queue_struct *q,
  71                            struct diff_options *opt, void *cbdata)
  72{
  73        int i;
  74        struct update_callback_data *data = cbdata;
  75
  76        for (i = 0; i < q->nr; i++) {
  77                struct diff_filepair *p = q->queue[i];
  78                const char *path = p->one->path;
  79                switch (fix_unmerged_status(p, data)) {
  80                default:
  81                        die(_("unexpected diff status %c"), p->status);
  82                case DIFF_STATUS_MODIFIED:
  83                case DIFF_STATUS_TYPE_CHANGED:
  84                        if (add_file_to_index(&the_index, path, data->flags)) {
  85                                if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
  86                                        die(_("updating files failed"));
  87                                data->add_errors++;
  88                        }
  89                        break;
  90                case DIFF_STATUS_DELETED:
  91                        if (data->flags & ADD_CACHE_IGNORE_REMOVAL)
  92                                break;
  93                        if (!(data->flags & ADD_CACHE_PRETEND))
  94                                remove_file_from_index(&the_index, path);
  95                        if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE))
  96                                printf(_("remove '%s'\n"), path);
  97                        break;
  98                }
  99        }
 100}
 101
 102int add_files_to_cache(const char *prefix,
 103                       const struct pathspec *pathspec, int flags)
 104{
 105        struct update_callback_data data;
 106        struct rev_info rev;
 107
 108        memset(&data, 0, sizeof(data));
 109        data.flags = flags;
 110
 111        init_revisions(&rev, prefix);
 112        setup_revisions(0, NULL, &rev, NULL);
 113        if (pathspec)
 114                copy_pathspec(&rev.prune_data, pathspec);
 115        rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
 116        rev.diffopt.format_callback = update_callback;
 117        rev.diffopt.format_callback_data = &data;
 118        rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
 119        run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
 120        return !!data.add_errors;
 121}
 122
 123static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec, int prefix)
 124{
 125        char *seen;
 126        int i;
 127        struct dir_entry **src, **dst;
 128
 129        seen = xcalloc(pathspec->nr, 1);
 130
 131        src = dst = dir->entries;
 132        i = dir->nr;
 133        while (--i >= 0) {
 134                struct dir_entry *entry = *src++;
 135                if (dir_path_match(entry, pathspec, prefix, seen))
 136                        *dst++ = entry;
 137        }
 138        dir->nr = dst - dir->entries;
 139        add_pathspec_matches_against_index(pathspec, &the_index, seen);
 140        return seen;
 141}
 142
 143static void refresh(int verbose, const struct pathspec *pathspec)
 144{
 145        char *seen;
 146        int i;
 147
 148        seen = xcalloc(pathspec->nr, 1);
 149        refresh_index(&the_index, verbose ? REFRESH_IN_PORCELAIN : REFRESH_QUIET,
 150                      pathspec, seen, _("Unstaged changes after refreshing the index:"));
 151        for (i = 0; i < pathspec->nr; i++) {
 152                if (!seen[i])
 153                        die(_("pathspec '%s' did not match any files"),
 154                            pathspec->items[i].match);
 155        }
 156        free(seen);
 157}
 158
 159int run_add_interactive(const char *revision, const char *patch_mode,
 160                        const struct pathspec *pathspec)
 161{
 162        int status, i;
 163        struct argv_array argv = ARGV_ARRAY_INIT;
 164
 165        argv_array_push(&argv, "add--interactive");
 166        if (patch_mode)
 167                argv_array_push(&argv, patch_mode);
 168        if (revision)
 169                argv_array_push(&argv, revision);
 170        argv_array_push(&argv, "--");
 171        for (i = 0; i < pathspec->nr; i++)
 172                /* pass original pathspec, to be re-parsed */
 173                argv_array_push(&argv, pathspec->items[i].original);
 174
 175        status = run_command_v_opt(argv.argv, RUN_GIT_CMD);
 176        argv_array_clear(&argv);
 177        return status;
 178}
 179
 180int interactive_add(int argc, const char **argv, const char *prefix, int patch)
 181{
 182        struct pathspec pathspec;
 183
 184        parse_pathspec(&pathspec, 0,
 185                       PATHSPEC_PREFER_FULL |
 186                       PATHSPEC_SYMLINK_LEADING_PATH |
 187                       PATHSPEC_PREFIX_ORIGIN,
 188                       prefix, argv);
 189
 190        return run_add_interactive(NULL,
 191                                   patch ? "--patch" : NULL,
 192                                   &pathspec);
 193}
 194
 195static int edit_patch(int argc, const char **argv, const char *prefix)
 196{
 197        char *file = git_pathdup("ADD_EDIT.patch");
 198        const char *apply_argv[] = { "apply", "--recount", "--cached",
 199                NULL, NULL };
 200        struct child_process child = CHILD_PROCESS_INIT;
 201        struct rev_info rev;
 202        int out;
 203        struct stat st;
 204
 205        apply_argv[3] = file;
 206
 207        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
 208
 209        if (read_cache() < 0)
 210                die(_("Could not read the index"));
 211
 212        init_revisions(&rev, prefix);
 213        rev.diffopt.context = 7;
 214
 215        argc = setup_revisions(argc, argv, &rev, NULL);
 216        rev.diffopt.output_format = DIFF_FORMAT_PATCH;
 217        rev.diffopt.use_color = 0;
 218        DIFF_OPT_SET(&rev.diffopt, IGNORE_DIRTY_SUBMODULES);
 219        out = open(file, O_CREAT | O_WRONLY, 0666);
 220        if (out < 0)
 221                die(_("Could not open '%s' for writing."), file);
 222        rev.diffopt.file = xfdopen(out, "w");
 223        rev.diffopt.close_file = 1;
 224        if (run_diff_files(&rev, 0))
 225                die(_("Could not write patch"));
 226
 227        if (launch_editor(file, NULL, NULL))
 228                die(_("editing patch failed"));
 229
 230        if (stat(file, &st))
 231                die_errno(_("Could not stat '%s'"), file);
 232        if (!st.st_size)
 233                die(_("Empty patch. Aborted."));
 234
 235        child.git_cmd = 1;
 236        child.argv = apply_argv;
 237        if (run_command(&child))
 238                die(_("Could not apply '%s'"), file);
 239
 240        unlink(file);
 241        free(file);
 242        return 0;
 243}
 244
 245static struct lock_file lock_file;
 246
 247static const char ignore_error[] =
 248N_("The following paths are ignored by one of your .gitignore files:\n");
 249
 250static int verbose, show_only, ignored_too, refresh_only;
 251static int ignore_add_errors, intent_to_add, ignore_missing;
 252static int warn_on_embedded_repo = 1;
 253
 254#define ADDREMOVE_DEFAULT 1
 255static int addremove = ADDREMOVE_DEFAULT;
 256static int addremove_explicit = -1; /* unspecified */
 257
 258static char *chmod_arg;
 259
 260static int ignore_removal_cb(const struct option *opt, const char *arg, int unset)
 261{
 262        /* if we are told to ignore, we are not adding removals */
 263        *(int *)opt->value = !unset ? 0 : 1;
 264        return 0;
 265}
 266
 267static struct option builtin_add_options[] = {
 268        OPT__DRY_RUN(&show_only, N_("dry run")),
 269        OPT__VERBOSE(&verbose, N_("be verbose")),
 270        OPT_GROUP(""),
 271        OPT_BOOL('i', "interactive", &add_interactive, N_("interactive picking")),
 272        OPT_BOOL('p', "patch", &patch_interactive, N_("select hunks interactively")),
 273        OPT_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")),
 274        OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files")),
 275        OPT_BOOL('u', "update", &take_worktree_changes, N_("update tracked files")),
 276        OPT_BOOL('N', "intent-to-add", &intent_to_add, N_("record only the fact that the path will be added later")),
 277        OPT_BOOL('A', "all", &addremove_explicit, N_("add changes from all tracked and untracked files")),
 278        { OPTION_CALLBACK, 0, "ignore-removal", &addremove_explicit,
 279          NULL /* takes no arguments */,
 280          N_("ignore paths removed in the working tree (same as --no-all)"),
 281          PARSE_OPT_NOARG, ignore_removal_cb },
 282        OPT_BOOL( 0 , "refresh", &refresh_only, N_("don't add, only refresh the index")),
 283        OPT_BOOL( 0 , "ignore-errors", &ignore_add_errors, N_("just skip files which cannot be added because of errors")),
 284        OPT_BOOL( 0 , "ignore-missing", &ignore_missing, N_("check if - even missing - files are ignored in dry run")),
 285        OPT_STRING( 0 , "chmod", &chmod_arg, N_("(+/-)x"), N_("override the executable bit of the listed files")),
 286        OPT_HIDDEN_BOOL(0, "warn-embedded-repo", &warn_on_embedded_repo,
 287                        N_("warn when adding an embedded repository")),
 288        OPT_END(),
 289};
 290
 291static int add_config(const char *var, const char *value, void *cb)
 292{
 293        if (!strcmp(var, "add.ignoreerrors") ||
 294            !strcmp(var, "add.ignore-errors")) {
 295                ignore_add_errors = git_config_bool(var, value);
 296                return 0;
 297        }
 298        return git_default_config(var, value, cb);
 299}
 300
 301static const char embedded_advice[] = N_(
 302"You've added another git repository inside your current repository.\n"
 303"Clones of the outer repository will not contain the contents of\n"
 304"the embedded repository and will not know how to obtain it.\n"
 305"If you meant to add a submodule, use:\n"
 306"\n"
 307"       git submodule add <url> %s\n"
 308"\n"
 309"If you added this path by mistake, you can remove it from the\n"
 310"index with:\n"
 311"\n"
 312"       git rm --cached %s\n"
 313"\n"
 314"See \"git help submodule\" for more information."
 315);
 316
 317static void check_embedded_repo(const char *path)
 318{
 319        struct strbuf name = STRBUF_INIT;
 320
 321        if (!warn_on_embedded_repo)
 322                return;
 323        if (!ends_with(path, "/"))
 324                return;
 325
 326        /* Drop trailing slash for aesthetics */
 327        strbuf_addstr(&name, path);
 328        strbuf_strip_suffix(&name, "/");
 329
 330        warning(_("adding embedded git repository: %s"), name.buf);
 331        if (advice_add_embedded_repo) {
 332                advise(embedded_advice, name.buf, name.buf);
 333                /* there may be multiple entries; advise only once */
 334                advice_add_embedded_repo = 0;
 335        }
 336
 337        strbuf_release(&name);
 338}
 339
 340static int add_files(struct dir_struct *dir, int flags)
 341{
 342        int i, exit_status = 0;
 343
 344        if (dir->ignored_nr) {
 345                fprintf(stderr, _(ignore_error));
 346                for (i = 0; i < dir->ignored_nr; i++)
 347                        fprintf(stderr, "%s\n", dir->ignored[i]->name);
 348                fprintf(stderr, _("Use -f if you really want to add them.\n"));
 349                exit_status = 1;
 350        }
 351
 352        for (i = 0; i < dir->nr; i++) {
 353                check_embedded_repo(dir->entries[i]->name);
 354                if (add_file_to_index(&the_index, dir->entries[i]->name, flags)) {
 355                        if (!ignore_add_errors)
 356                                die(_("adding files failed"));
 357                        exit_status = 1;
 358                }
 359        }
 360        return exit_status;
 361}
 362
 363int cmd_add(int argc, const char **argv, const char *prefix)
 364{
 365        int exit_status = 0;
 366        struct pathspec pathspec;
 367        struct dir_struct dir;
 368        int flags;
 369        int add_new_files;
 370        int require_pathspec;
 371        char *seen = NULL;
 372
 373        git_config(add_config, NULL);
 374
 375        argc = parse_options(argc, argv, prefix, builtin_add_options,
 376                          builtin_add_usage, PARSE_OPT_KEEP_ARGV0);
 377        if (patch_interactive)
 378                add_interactive = 1;
 379        if (add_interactive)
 380                exit(interactive_add(argc - 1, argv + 1, prefix, patch_interactive));
 381
 382        if (edit_interactive)
 383                return(edit_patch(argc, argv, prefix));
 384        argc--;
 385        argv++;
 386
 387        if (0 <= addremove_explicit)
 388                addremove = addremove_explicit;
 389        else if (take_worktree_changes && ADDREMOVE_DEFAULT)
 390                addremove = 0; /* "-u" was given but not "-A" */
 391
 392        if (addremove && take_worktree_changes)
 393                die(_("-A and -u are mutually incompatible"));
 394
 395        if (!take_worktree_changes && addremove_explicit < 0 && argc)
 396                /* Turn "git add pathspec..." to "git add -A pathspec..." */
 397                addremove = 1;
 398
 399        if (!show_only && ignore_missing)
 400                die(_("Option --ignore-missing can only be used together with --dry-run"));
 401
 402        if (chmod_arg && ((chmod_arg[0] != '-' && chmod_arg[0] != '+') ||
 403                          chmod_arg[1] != 'x' || chmod_arg[2]))
 404                die(_("--chmod param '%s' must be either -x or +x"), chmod_arg);
 405
 406        add_new_files = !take_worktree_changes && !refresh_only;
 407        require_pathspec = !(take_worktree_changes || (0 < addremove_explicit));
 408
 409        hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
 410
 411        flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
 412                 (show_only ? ADD_CACHE_PRETEND : 0) |
 413                 (intent_to_add ? ADD_CACHE_INTENT : 0) |
 414                 (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) |
 415                 (!(addremove || take_worktree_changes)
 416                  ? ADD_CACHE_IGNORE_REMOVAL : 0));
 417
 418        if (require_pathspec && argc == 0) {
 419                fprintf(stderr, _("Nothing specified, nothing added.\n"));
 420                fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n"));
 421                return 0;
 422        }
 423
 424        if (read_cache() < 0)
 425                die(_("index file corrupt"));
 426
 427        die_in_unpopulated_submodule(&the_index, prefix);
 428
 429        /*
 430         * Check the "pathspec '%s' did not match any files" block
 431         * below before enabling new magic.
 432         */
 433        parse_pathspec(&pathspec, 0,
 434                       PATHSPEC_PREFER_FULL |
 435                       PATHSPEC_SYMLINK_LEADING_PATH,
 436                       prefix, argv);
 437
 438        die_path_inside_submodule(&the_index, &pathspec);
 439
 440        if (add_new_files) {
 441                int baselen;
 442
 443                /* Set up the default git porcelain excludes */
 444                memset(&dir, 0, sizeof(dir));
 445                if (!ignored_too) {
 446                        dir.flags |= DIR_COLLECT_IGNORED;
 447                        setup_standard_excludes(&dir);
 448                }
 449
 450                /* This picks up the paths that are not tracked */
 451                baselen = fill_directory(&dir, &the_index, &pathspec);
 452                if (pathspec.nr)
 453                        seen = prune_directory(&dir, &pathspec, baselen);
 454        }
 455
 456        if (refresh_only) {
 457                refresh(verbose, &pathspec);
 458                goto finish;
 459        }
 460
 461        if (pathspec.nr) {
 462                int i;
 463
 464                if (!seen)
 465                        seen = find_pathspecs_matching_against_index(&pathspec, &the_index);
 466
 467                /*
 468                 * file_exists() assumes exact match
 469                 */
 470                GUARD_PATHSPEC(&pathspec,
 471                               PATHSPEC_FROMTOP |
 472                               PATHSPEC_LITERAL |
 473                               PATHSPEC_GLOB |
 474                               PATHSPEC_ICASE |
 475                               PATHSPEC_EXCLUDE);
 476
 477                for (i = 0; i < pathspec.nr; i++) {
 478                        const char *path = pathspec.items[i].match;
 479                        if (pathspec.items[i].magic & PATHSPEC_EXCLUDE)
 480                                continue;
 481                        if (!seen[i] && path[0] &&
 482                            ((pathspec.items[i].magic &
 483                              (PATHSPEC_GLOB | PATHSPEC_ICASE)) ||
 484                             !file_exists(path))) {
 485                                if (ignore_missing) {
 486                                        int dtype = DT_UNKNOWN;
 487                                        if (is_excluded(&dir, &the_index, path, &dtype))
 488                                                dir_add_ignored(&dir, &the_index,
 489                                                                path, pathspec.items[i].len);
 490                                } else
 491                                        die(_("pathspec '%s' did not match any files"),
 492                                            pathspec.items[i].original);
 493                        }
 494                }
 495                free(seen);
 496        }
 497
 498        plug_bulk_checkin();
 499
 500        exit_status |= add_files_to_cache(prefix, &pathspec, flags);
 501
 502        if (add_new_files)
 503                exit_status |= add_files(&dir, flags);
 504
 505        if (chmod_arg && pathspec.nr)
 506                chmod_pathspec(&pathspec, chmod_arg[0]);
 507        unplug_bulk_checkin();
 508
 509finish:
 510        if (active_cache_changed) {
 511                if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
 512                        die(_("Unable to write new index file"));
 513        }
 514
 515        return exit_status;
 516}