builtin / add.con commit status: convert to use parse_pathspec (15b55ae)
   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 "pathspec.h"
  10#include "exec_cmd.h"
  11#include "cache-tree.h"
  12#include "run-command.h"
  13#include "parse-options.h"
  14#include "diff.h"
  15#include "diffcore.h"
  16#include "revision.h"
  17#include "bulk-checkin.h"
  18
  19static const char * const builtin_add_usage[] = {
  20        N_("git add [options] [--] <pathspec>..."),
  21        NULL
  22};
  23static int patch_interactive, add_interactive, edit_interactive;
  24static int take_worktree_changes;
  25
  26struct update_callback_data {
  27        int flags;
  28        int add_errors;
  29        const char *implicit_dot;
  30        size_t implicit_dot_len;
  31
  32        /* only needed for 2.0 transition preparation */
  33        int warn_add_would_remove;
  34};
  35
  36static const char *option_with_implicit_dot;
  37static const char *short_option_with_implicit_dot;
  38
  39static void warn_pathless_add(void)
  40{
  41        static int shown;
  42        assert(option_with_implicit_dot && short_option_with_implicit_dot);
  43
  44        if (shown)
  45                return;
  46        shown = 1;
  47
  48        /*
  49         * To be consistent with "git add -p" and most Git
  50         * commands, we should default to being tree-wide, but
  51         * this is not the original behavior and can't be
  52         * changed until users trained themselves not to type
  53         * "git add -u" or "git add -A". For now, we warn and
  54         * keep the old behavior. Later, the behavior can be changed
  55         * to tree-wide, keeping the warning for a while, and
  56         * eventually we can drop the warning.
  57         */
  58        warning(_("The behavior of 'git add %s (or %s)' with no path argument from a\n"
  59                  "subdirectory of the tree will change in Git 2.0 and should not be used anymore.\n"
  60                  "To add content for the whole tree, run:\n"
  61                  "\n"
  62                  "  git add %s :/\n"
  63                  "  (or git add %s :/)\n"
  64                  "\n"
  65                  "To restrict the command to the current directory, run:\n"
  66                  "\n"
  67                  "  git add %s .\n"
  68                  "  (or git add %s .)\n"
  69                  "\n"
  70                  "With the current Git version, the command is restricted to "
  71                  "the current directory.\n"
  72                  ""),
  73                option_with_implicit_dot, short_option_with_implicit_dot,
  74                option_with_implicit_dot, short_option_with_implicit_dot,
  75                option_with_implicit_dot, short_option_with_implicit_dot);
  76}
  77
  78static int fix_unmerged_status(struct diff_filepair *p,
  79                               struct update_callback_data *data)
  80{
  81        if (p->status != DIFF_STATUS_UNMERGED)
  82                return p->status;
  83        if (!(data->flags & ADD_CACHE_IGNORE_REMOVAL) && !p->two->mode)
  84                /*
  85                 * This is not an explicit add request, and the
  86                 * path is missing from the working tree (deleted)
  87                 */
  88                return DIFF_STATUS_DELETED;
  89        else
  90                /*
  91                 * Either an explicit add request, or path exists
  92                 * in the working tree.  An attempt to explicitly
  93                 * add a path that does not exist in the working tree
  94                 * will be caught as an error by the caller immediately.
  95                 */
  96                return DIFF_STATUS_MODIFIED;
  97}
  98
  99static const char *add_would_remove_warning = N_(
 100        "You ran 'git add' with neither '-A (--all)' or '--ignore-removal',\n"
 101"whose behaviour will change in Git 2.0 with respect to paths you removed.\n"
 102"Paths like '%s' that are\n"
 103"removed from your working tree are ignored with this version of Git.\n"
 104"\n"
 105"* 'git add --ignore-removal <pathspec>', which is the current default,\n"
 106"  ignores paths you removed from your working tree.\n"
 107"\n"
 108"* 'git add --all <pathspec>' will let you also record the removals.\n"
 109"\n"
 110"Run 'git status' to check the paths you removed from your working tree.\n");
 111
 112static void warn_add_would_remove(const char *path)
 113{
 114        warning(_(add_would_remove_warning), path);
 115}
 116
 117static void update_callback(struct diff_queue_struct *q,
 118                            struct diff_options *opt, void *cbdata)
 119{
 120        int i;
 121        struct update_callback_data *data = cbdata;
 122        const char *implicit_dot = data->implicit_dot;
 123        size_t implicit_dot_len = data->implicit_dot_len;
 124
 125        for (i = 0; i < q->nr; i++) {
 126                struct diff_filepair *p = q->queue[i];
 127                const char *path = p->one->path;
 128                /*
 129                 * Check if "git add -A" or "git add -u" was run from a
 130                 * subdirectory with a modified file outside that directory,
 131                 * and warn if so.
 132                 *
 133                 * "git add -u" will behave like "git add -u :/" instead of
 134                 * "git add -u ." in the future.  This warning prepares for
 135                 * that change.
 136                 */
 137                if (implicit_dot &&
 138                    strncmp_icase(path, implicit_dot, implicit_dot_len)) {
 139                        warn_pathless_add();
 140                        continue;
 141                }
 142                switch (fix_unmerged_status(p, data)) {
 143                default:
 144                        die(_("unexpected diff status %c"), p->status);
 145                case DIFF_STATUS_MODIFIED:
 146                case DIFF_STATUS_TYPE_CHANGED:
 147                        if (add_file_to_index(&the_index, path, data->flags)) {
 148                                if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
 149                                        die(_("updating files failed"));
 150                                data->add_errors++;
 151                        }
 152                        break;
 153                case DIFF_STATUS_DELETED:
 154                        if (data->warn_add_would_remove) {
 155                                warn_add_would_remove(path);
 156                                data->warn_add_would_remove = 0;
 157                        }
 158                        if (data->flags & ADD_CACHE_IGNORE_REMOVAL)
 159                                break;
 160                        if (!(data->flags & ADD_CACHE_PRETEND))
 161                                remove_file_from_index(&the_index, path);
 162                        if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE))
 163                                printf(_("remove '%s'\n"), path);
 164                        break;
 165                }
 166        }
 167}
 168
 169static void update_files_in_cache(const char *prefix, const char **pathspec,
 170                                  struct update_callback_data *data)
 171{
 172        struct rev_info rev;
 173
 174        init_revisions(&rev, prefix);
 175        setup_revisions(0, NULL, &rev, NULL);
 176        init_pathspec(&rev.prune_data, pathspec);
 177        rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
 178        rev.diffopt.format_callback = update_callback;
 179        rev.diffopt.format_callback_data = data;
 180        rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
 181        run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
 182}
 183
 184int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
 185{
 186        struct update_callback_data data;
 187
 188        memset(&data, 0, sizeof(data));
 189        data.flags = flags;
 190        update_files_in_cache(prefix, pathspec, &data);
 191        return !!data.add_errors;
 192}
 193
 194#define WARN_IMPLICIT_DOT (1u << 0)
 195static char *prune_directory(struct dir_struct *dir, const char **pathspec,
 196                             int prefix, unsigned flag)
 197{
 198        char *seen;
 199        int i, specs;
 200        struct dir_entry **src, **dst;
 201
 202        for (specs = 0; pathspec[specs];  specs++)
 203                /* nothing */;
 204        seen = xcalloc(specs, 1);
 205
 206        src = dst = dir->entries;
 207        i = dir->nr;
 208        while (--i >= 0) {
 209                struct dir_entry *entry = *src++;
 210                if (match_pathspec(pathspec, entry->name, entry->len,
 211                                   prefix, seen))
 212                        *dst++ = entry;
 213                else if (flag & WARN_IMPLICIT_DOT)
 214                        /*
 215                         * "git add -A" was run from a subdirectory with a
 216                         * new file outside that directory.
 217                         *
 218                         * "git add -A" will behave like "git add -A :/"
 219                         * instead of "git add -A ." in the future.
 220                         * Warn about the coming behavior change.
 221                         */
 222                        warn_pathless_add();
 223        }
 224        dir->nr = dst - dir->entries;
 225        add_pathspec_matches_against_index(pathspec, seen, specs);
 226        return seen;
 227}
 228
 229/*
 230 * Checks the index to see whether any path in pathspec refers to
 231 * something inside a submodule.  If so, dies with an error message.
 232 */
 233static void treat_gitlinks(const char **pathspec)
 234{
 235        int i;
 236
 237        if (!pathspec || !*pathspec)
 238                return;
 239
 240        for (i = 0; pathspec[i]; i++)
 241                pathspec[i] = check_path_for_gitlink(pathspec[i]);
 242}
 243
 244static void refresh(int verbose, const char **pathspec)
 245{
 246        char *seen;
 247        int i, specs;
 248
 249        for (specs = 0; pathspec[specs];  specs++)
 250                /* nothing */;
 251        seen = xcalloc(specs, 1);
 252        refresh_index(&the_index, verbose ? REFRESH_IN_PORCELAIN : REFRESH_QUIET,
 253                      pathspec, seen, _("Unstaged changes after refreshing the index:"));
 254        for (i = 0; i < specs; i++) {
 255                if (!seen[i])
 256                        die(_("pathspec '%s' did not match any files"), pathspec[i]);
 257        }
 258        free(seen);
 259}
 260
 261/*
 262 * Normalizes argv relative to prefix, via get_pathspec(), and then
 263 * runs die_if_path_beyond_symlink() on each path in the normalized
 264 * list.
 265 */
 266static const char **validate_pathspec(const char **argv, const char *prefix)
 267{
 268        const char **pathspec = get_pathspec(prefix, argv);
 269
 270        if (pathspec) {
 271                const char **p;
 272                for (p = pathspec; *p; p++) {
 273                        die_if_path_beyond_symlink(*p, prefix);
 274                }
 275        }
 276
 277        return pathspec;
 278}
 279
 280int run_add_interactive(const char *revision, const char *patch_mode,
 281                        const char **pathspec)
 282{
 283        int status, ac, pc = 0;
 284        const char **args;
 285
 286        if (pathspec)
 287                while (pathspec[pc])
 288                        pc++;
 289
 290        args = xcalloc(sizeof(const char *), (pc + 5));
 291        ac = 0;
 292        args[ac++] = "add--interactive";
 293        if (patch_mode)
 294                args[ac++] = patch_mode;
 295        if (revision)
 296                args[ac++] = revision;
 297        args[ac++] = "--";
 298        if (pc) {
 299                memcpy(&(args[ac]), pathspec, sizeof(const char *) * pc);
 300                ac += pc;
 301        }
 302        args[ac] = NULL;
 303
 304        status = run_command_v_opt(args, RUN_GIT_CMD);
 305        free(args);
 306        return status;
 307}
 308
 309int interactive_add(int argc, const char **argv, const char *prefix, int patch)
 310{
 311        const char **pathspec = NULL;
 312
 313        if (argc) {
 314                pathspec = validate_pathspec(argv, prefix);
 315                if (!pathspec)
 316                        return -1;
 317        }
 318
 319        return run_add_interactive(NULL,
 320                                   patch ? "--patch" : NULL,
 321                                   pathspec);
 322}
 323
 324static int edit_patch(int argc, const char **argv, const char *prefix)
 325{
 326        char *file = git_pathdup("ADD_EDIT.patch");
 327        const char *apply_argv[] = { "apply", "--recount", "--cached",
 328                NULL, NULL };
 329        struct child_process child;
 330        struct rev_info rev;
 331        int out;
 332        struct stat st;
 333
 334        apply_argv[3] = file;
 335
 336        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
 337
 338        if (read_cache() < 0)
 339                die (_("Could not read the index"));
 340
 341        init_revisions(&rev, prefix);
 342        rev.diffopt.context = 7;
 343
 344        argc = setup_revisions(argc, argv, &rev, NULL);
 345        rev.diffopt.output_format = DIFF_FORMAT_PATCH;
 346        DIFF_OPT_SET(&rev.diffopt, IGNORE_DIRTY_SUBMODULES);
 347        out = open(file, O_CREAT | O_WRONLY, 0666);
 348        if (out < 0)
 349                die (_("Could not open '%s' for writing."), file);
 350        rev.diffopt.file = xfdopen(out, "w");
 351        rev.diffopt.close_file = 1;
 352        if (run_diff_files(&rev, 0))
 353                die (_("Could not write patch"));
 354
 355        launch_editor(file, NULL, NULL);
 356
 357        if (stat(file, &st))
 358                die_errno(_("Could not stat '%s'"), file);
 359        if (!st.st_size)
 360                die(_("Empty patch. Aborted."));
 361
 362        memset(&child, 0, sizeof(child));
 363        child.git_cmd = 1;
 364        child.argv = apply_argv;
 365        if (run_command(&child))
 366                die (_("Could not apply '%s'"), file);
 367
 368        unlink(file);
 369        free(file);
 370        return 0;
 371}
 372
 373static struct lock_file lock_file;
 374
 375static const char ignore_error[] =
 376N_("The following paths are ignored by one of your .gitignore files:\n");
 377
 378static int verbose, show_only, ignored_too, refresh_only;
 379static int ignore_add_errors, intent_to_add, ignore_missing;
 380
 381#define ADDREMOVE_DEFAULT 0 /* Change to 1 in Git 2.0 */
 382static int addremove = ADDREMOVE_DEFAULT;
 383static int addremove_explicit = -1; /* unspecified */
 384
 385static int ignore_removal_cb(const struct option *opt, const char *arg, int unset)
 386{
 387        /* if we are told to ignore, we are not adding removals */
 388        *(int *)opt->value = !unset ? 0 : 1;
 389        return 0;
 390}
 391
 392static struct option builtin_add_options[] = {
 393        OPT__DRY_RUN(&show_only, N_("dry run")),
 394        OPT__VERBOSE(&verbose, N_("be verbose")),
 395        OPT_GROUP(""),
 396        OPT_BOOL('i', "interactive", &add_interactive, N_("interactive picking")),
 397        OPT_BOOL('p', "patch", &patch_interactive, N_("select hunks interactively")),
 398        OPT_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")),
 399        OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files")),
 400        OPT_BOOL('u', "update", &take_worktree_changes, N_("update tracked files")),
 401        OPT_BOOL('N', "intent-to-add", &intent_to_add, N_("record only the fact that the path will be added later")),
 402        OPT_BOOL('A', "all", &addremove_explicit, N_("add changes from all tracked and untracked files")),
 403        { OPTION_CALLBACK, 0, "ignore-removal", &addremove_explicit,
 404          NULL /* takes no arguments */,
 405          N_("ignore paths removed in the working tree (same as --no-all)"),
 406          PARSE_OPT_NOARG, ignore_removal_cb },
 407        OPT_BOOL( 0 , "refresh", &refresh_only, N_("don't add, only refresh the index")),
 408        OPT_BOOL( 0 , "ignore-errors", &ignore_add_errors, N_("just skip files which cannot be added because of errors")),
 409        OPT_BOOL( 0 , "ignore-missing", &ignore_missing, N_("check if - even missing - files are ignored in dry run")),
 410        OPT_END(),
 411};
 412
 413static int add_config(const char *var, const char *value, void *cb)
 414{
 415        if (!strcmp(var, "add.ignoreerrors") ||
 416            !strcmp(var, "add.ignore-errors")) {
 417                ignore_add_errors = git_config_bool(var, value);
 418                return 0;
 419        }
 420        return git_default_config(var, value, cb);
 421}
 422
 423static int add_files(struct dir_struct *dir, int flags)
 424{
 425        int i, exit_status = 0;
 426
 427        if (dir->ignored_nr) {
 428                fprintf(stderr, _(ignore_error));
 429                for (i = 0; i < dir->ignored_nr; i++)
 430                        fprintf(stderr, "%s\n", dir->ignored[i]->name);
 431                fprintf(stderr, _("Use -f if you really want to add them.\n"));
 432                die(_("no files added"));
 433        }
 434
 435        for (i = 0; i < dir->nr; i++)
 436                if (add_file_to_cache(dir->entries[i]->name, flags)) {
 437                        if (!ignore_add_errors)
 438                                die(_("adding files failed"));
 439                        exit_status = 1;
 440                }
 441        return exit_status;
 442}
 443
 444int cmd_add(int argc, const char **argv, const char *prefix)
 445{
 446        int exit_status = 0;
 447        int newfd;
 448        const char **pathspec;
 449        struct dir_struct dir;
 450        int flags;
 451        int add_new_files;
 452        int require_pathspec;
 453        char *seen = NULL;
 454        int implicit_dot = 0;
 455        struct update_callback_data update_data;
 456
 457        git_config(add_config, NULL);
 458
 459        argc = parse_options(argc, argv, prefix, builtin_add_options,
 460                          builtin_add_usage, PARSE_OPT_KEEP_ARGV0);
 461        if (patch_interactive)
 462                add_interactive = 1;
 463        if (add_interactive)
 464                exit(interactive_add(argc - 1, argv + 1, prefix, patch_interactive));
 465
 466        if (edit_interactive)
 467                return(edit_patch(argc, argv, prefix));
 468        argc--;
 469        argv++;
 470
 471        if (0 <= addremove_explicit)
 472                addremove = addremove_explicit;
 473        else if (take_worktree_changes && ADDREMOVE_DEFAULT)
 474                addremove = 0; /* "-u" was given but not "-A" */
 475
 476        if (addremove && take_worktree_changes)
 477                die(_("-A and -u are mutually incompatible"));
 478
 479        /*
 480         * Warn when "git add pathspec..." was given without "-u" or "-A"
 481         * and pathspec... covers a removed path.
 482         */
 483        memset(&update_data, 0, sizeof(update_data));
 484        if (!take_worktree_changes && addremove_explicit < 0)
 485                update_data.warn_add_would_remove = 1;
 486
 487        if (!take_worktree_changes && addremove_explicit < 0 && argc)
 488                /*
 489                 * Turn "git add pathspec..." to "git add -A pathspec..."
 490                 * in Git 2.0 but not yet
 491                 */
 492                ; /* addremove = 1; */
 493
 494        if (!show_only && ignore_missing)
 495                die(_("Option --ignore-missing can only be used together with --dry-run"));
 496        if (addremove) {
 497                option_with_implicit_dot = "--all";
 498                short_option_with_implicit_dot = "-A";
 499        }
 500        if (take_worktree_changes) {
 501                option_with_implicit_dot = "--update";
 502                short_option_with_implicit_dot = "-u";
 503        }
 504        if (option_with_implicit_dot && !argc) {
 505                static const char *here[2] = { ".", NULL };
 506                argc = 1;
 507                argv = here;
 508                implicit_dot = 1;
 509        }
 510
 511        add_new_files = !take_worktree_changes && !refresh_only;
 512        require_pathspec = !take_worktree_changes;
 513
 514        newfd = hold_locked_index(&lock_file, 1);
 515
 516        flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
 517                 (show_only ? ADD_CACHE_PRETEND : 0) |
 518                 (intent_to_add ? ADD_CACHE_INTENT : 0) |
 519                 (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) |
 520                 (!(addremove || take_worktree_changes)
 521                  ? ADD_CACHE_IGNORE_REMOVAL : 0)) |
 522                 (implicit_dot ? ADD_CACHE_IMPLICIT_DOT : 0);
 523
 524        if (require_pathspec && argc == 0) {
 525                fprintf(stderr, _("Nothing specified, nothing added.\n"));
 526                fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n"));
 527                return 0;
 528        }
 529        pathspec = validate_pathspec(argv, prefix);
 530
 531        if (read_cache() < 0)
 532                die(_("index file corrupt"));
 533        treat_gitlinks(pathspec);
 534
 535        if (add_new_files) {
 536                int baselen;
 537
 538                /* Set up the default git porcelain excludes */
 539                memset(&dir, 0, sizeof(dir));
 540                if (!ignored_too) {
 541                        dir.flags |= DIR_COLLECT_IGNORED;
 542                        setup_standard_excludes(&dir);
 543                }
 544
 545                /* This picks up the paths that are not tracked */
 546                baselen = fill_directory(&dir, implicit_dot ? NULL : pathspec);
 547                if (pathspec)
 548                        seen = prune_directory(&dir, pathspec, baselen,
 549                                        implicit_dot ? WARN_IMPLICIT_DOT : 0);
 550        }
 551
 552        if (refresh_only) {
 553                refresh(verbose, pathspec);
 554                goto finish;
 555        }
 556        if (implicit_dot && prefix)
 557                refresh_cache(REFRESH_QUIET);
 558
 559        if (pathspec) {
 560                int i;
 561
 562                if (!seen)
 563                        seen = find_pathspecs_matching_against_index(pathspec);
 564                for (i = 0; pathspec[i]; i++) {
 565                        if (!seen[i] && pathspec[i][0]
 566                            && !file_exists(pathspec[i])) {
 567                                if (ignore_missing) {
 568                                        int dtype = DT_UNKNOWN;
 569                                        if (is_excluded(&dir, pathspec[i], &dtype))
 570                                                dir_add_ignored(&dir, pathspec[i], strlen(pathspec[i]));
 571                                } else
 572                                        die(_("pathspec '%s' did not match any files"),
 573                                            pathspec[i]);
 574                        }
 575                }
 576                free(seen);
 577        }
 578
 579        plug_bulk_checkin();
 580
 581        if ((flags & ADD_CACHE_IMPLICIT_DOT) && prefix) {
 582                /*
 583                 * Check for modified files throughout the worktree so
 584                 * update_callback has a chance to warn about changes
 585                 * outside the cwd.
 586                 */
 587                update_data.implicit_dot = prefix;
 588                update_data.implicit_dot_len = strlen(prefix);
 589                pathspec = NULL;
 590        }
 591        update_data.flags = flags & ~ADD_CACHE_IMPLICIT_DOT;
 592        update_files_in_cache(prefix, pathspec, &update_data);
 593
 594        exit_status |= !!update_data.add_errors;
 595        if (add_new_files)
 596                exit_status |= add_files(&dir, flags);
 597
 598        unplug_bulk_checkin();
 599
 600 finish:
 601        if (active_cache_changed) {
 602                if (write_cache(newfd, active_cache, active_nr) ||
 603                    commit_locked_index(&lock_file))
 604                        die(_("Unable to write new index file"));
 605        }
 606
 607        return exit_status;
 608}