builtin / add.con commit Merge branch 'jk/cat-file-batch-optim' (356df9b)
   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        rev.diffopt.use_color = 0;
 347        DIFF_OPT_SET(&rev.diffopt, IGNORE_DIRTY_SUBMODULES);
 348        out = open(file, O_CREAT | O_WRONLY, 0666);
 349        if (out < 0)
 350                die (_("Could not open '%s' for writing."), file);
 351        rev.diffopt.file = xfdopen(out, "w");
 352        rev.diffopt.close_file = 1;
 353        if (run_diff_files(&rev, 0))
 354                die (_("Could not write patch"));
 355
 356        launch_editor(file, NULL, NULL);
 357
 358        if (stat(file, &st))
 359                die_errno(_("Could not stat '%s'"), file);
 360        if (!st.st_size)
 361                die(_("Empty patch. Aborted."));
 362
 363        memset(&child, 0, sizeof(child));
 364        child.git_cmd = 1;
 365        child.argv = apply_argv;
 366        if (run_command(&child))
 367                die (_("Could not apply '%s'"), file);
 368
 369        unlink(file);
 370        free(file);
 371        return 0;
 372}
 373
 374static struct lock_file lock_file;
 375
 376static const char ignore_error[] =
 377N_("The following paths are ignored by one of your .gitignore files:\n");
 378
 379static int verbose, show_only, ignored_too, refresh_only;
 380static int ignore_add_errors, intent_to_add, ignore_missing;
 381
 382#define ADDREMOVE_DEFAULT 0 /* Change to 1 in Git 2.0 */
 383static int addremove = ADDREMOVE_DEFAULT;
 384static int addremove_explicit = -1; /* unspecified */
 385
 386static int ignore_removal_cb(const struct option *opt, const char *arg, int unset)
 387{
 388        /* if we are told to ignore, we are not adding removals */
 389        *(int *)opt->value = !unset ? 0 : 1;
 390        return 0;
 391}
 392
 393static struct option builtin_add_options[] = {
 394        OPT__DRY_RUN(&show_only, N_("dry run")),
 395        OPT__VERBOSE(&verbose, N_("be verbose")),
 396        OPT_GROUP(""),
 397        OPT_BOOL('i', "interactive", &add_interactive, N_("interactive picking")),
 398        OPT_BOOL('p', "patch", &patch_interactive, N_("select hunks interactively")),
 399        OPT_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")),
 400        OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files")),
 401        OPT_BOOL('u', "update", &take_worktree_changes, N_("update tracked files")),
 402        OPT_BOOL('N', "intent-to-add", &intent_to_add, N_("record only the fact that the path will be added later")),
 403        OPT_BOOL('A', "all", &addremove_explicit, N_("add changes from all tracked and untracked files")),
 404        { OPTION_CALLBACK, 0, "ignore-removal", &addremove_explicit,
 405          NULL /* takes no arguments */,
 406          N_("ignore paths removed in the working tree (same as --no-all)"),
 407          PARSE_OPT_NOARG, ignore_removal_cb },
 408        OPT_BOOL( 0 , "refresh", &refresh_only, N_("don't add, only refresh the index")),
 409        OPT_BOOL( 0 , "ignore-errors", &ignore_add_errors, N_("just skip files which cannot be added because of errors")),
 410        OPT_BOOL( 0 , "ignore-missing", &ignore_missing, N_("check if - even missing - files are ignored in dry run")),
 411        OPT_END(),
 412};
 413
 414static int add_config(const char *var, const char *value, void *cb)
 415{
 416        if (!strcmp(var, "add.ignoreerrors") ||
 417            !strcmp(var, "add.ignore-errors")) {
 418                ignore_add_errors = git_config_bool(var, value);
 419                return 0;
 420        }
 421        return git_default_config(var, value, cb);
 422}
 423
 424static int add_files(struct dir_struct *dir, int flags)
 425{
 426        int i, exit_status = 0;
 427
 428        if (dir->ignored_nr) {
 429                fprintf(stderr, _(ignore_error));
 430                for (i = 0; i < dir->ignored_nr; i++)
 431                        fprintf(stderr, "%s\n", dir->ignored[i]->name);
 432                fprintf(stderr, _("Use -f if you really want to add them.\n"));
 433                die(_("no files added"));
 434        }
 435
 436        for (i = 0; i < dir->nr; i++)
 437                if (add_file_to_cache(dir->entries[i]->name, flags)) {
 438                        if (!ignore_add_errors)
 439                                die(_("adding files failed"));
 440                        exit_status = 1;
 441                }
 442        return exit_status;
 443}
 444
 445int cmd_add(int argc, const char **argv, const char *prefix)
 446{
 447        int exit_status = 0;
 448        int newfd;
 449        const char **pathspec;
 450        struct dir_struct dir;
 451        int flags;
 452        int add_new_files;
 453        int require_pathspec;
 454        char *seen = NULL;
 455        int implicit_dot = 0;
 456        struct update_callback_data update_data;
 457
 458        git_config(add_config, NULL);
 459
 460        argc = parse_options(argc, argv, prefix, builtin_add_options,
 461                          builtin_add_usage, PARSE_OPT_KEEP_ARGV0);
 462        if (patch_interactive)
 463                add_interactive = 1;
 464        if (add_interactive)
 465                exit(interactive_add(argc - 1, argv + 1, prefix, patch_interactive));
 466
 467        if (edit_interactive)
 468                return(edit_patch(argc, argv, prefix));
 469        argc--;
 470        argv++;
 471
 472        if (0 <= addremove_explicit)
 473                addremove = addremove_explicit;
 474        else if (take_worktree_changes && ADDREMOVE_DEFAULT)
 475                addremove = 0; /* "-u" was given but not "-A" */
 476
 477        if (addremove && take_worktree_changes)
 478                die(_("-A and -u are mutually incompatible"));
 479
 480        /*
 481         * Warn when "git add pathspec..." was given without "-u" or "-A"
 482         * and pathspec... covers a removed path.
 483         */
 484        memset(&update_data, 0, sizeof(update_data));
 485        if (!take_worktree_changes && addremove_explicit < 0)
 486                update_data.warn_add_would_remove = 1;
 487
 488        if (!take_worktree_changes && addremove_explicit < 0 && argc)
 489                /*
 490                 * Turn "git add pathspec..." to "git add -A pathspec..."
 491                 * in Git 2.0 but not yet
 492                 */
 493                ; /* addremove = 1; */
 494
 495        if (!show_only && ignore_missing)
 496                die(_("Option --ignore-missing can only be used together with --dry-run"));
 497        if (addremove) {
 498                option_with_implicit_dot = "--all";
 499                short_option_with_implicit_dot = "-A";
 500        }
 501        if (take_worktree_changes) {
 502                option_with_implicit_dot = "--update";
 503                short_option_with_implicit_dot = "-u";
 504        }
 505        if (option_with_implicit_dot && !argc) {
 506                static const char *here[2] = { ".", NULL };
 507                argc = 1;
 508                argv = here;
 509                implicit_dot = 1;
 510        }
 511
 512        add_new_files = !take_worktree_changes && !refresh_only;
 513        require_pathspec = !take_worktree_changes;
 514
 515        newfd = hold_locked_index(&lock_file, 1);
 516
 517        flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
 518                 (show_only ? ADD_CACHE_PRETEND : 0) |
 519                 (intent_to_add ? ADD_CACHE_INTENT : 0) |
 520                 (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) |
 521                 (!(addremove || take_worktree_changes)
 522                  ? ADD_CACHE_IGNORE_REMOVAL : 0)) |
 523                 (implicit_dot ? ADD_CACHE_IMPLICIT_DOT : 0);
 524
 525        if (require_pathspec && argc == 0) {
 526                fprintf(stderr, _("Nothing specified, nothing added.\n"));
 527                fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n"));
 528                return 0;
 529        }
 530        pathspec = validate_pathspec(argv, prefix);
 531
 532        if (read_cache() < 0)
 533                die(_("index file corrupt"));
 534        treat_gitlinks(pathspec);
 535
 536        if (add_new_files) {
 537                int baselen;
 538
 539                /* Set up the default git porcelain excludes */
 540                memset(&dir, 0, sizeof(dir));
 541                if (!ignored_too) {
 542                        dir.flags |= DIR_COLLECT_IGNORED;
 543                        setup_standard_excludes(&dir);
 544                }
 545
 546                /* This picks up the paths that are not tracked */
 547                baselen = fill_directory(&dir, implicit_dot ? NULL : pathspec);
 548                if (pathspec)
 549                        seen = prune_directory(&dir, pathspec, baselen,
 550                                        implicit_dot ? WARN_IMPLICIT_DOT : 0);
 551        }
 552
 553        if (refresh_only) {
 554                refresh(verbose, pathspec);
 555                goto finish;
 556        }
 557        if (implicit_dot && prefix)
 558                refresh_cache(REFRESH_QUIET);
 559
 560        if (pathspec) {
 561                int i;
 562
 563                if (!seen)
 564                        seen = find_pathspecs_matching_against_index(pathspec);
 565                for (i = 0; pathspec[i]; i++) {
 566                        if (!seen[i] && pathspec[i][0]
 567                            && !file_exists(pathspec[i])) {
 568                                if (ignore_missing) {
 569                                        int dtype = DT_UNKNOWN;
 570                                        if (is_excluded(&dir, pathspec[i], &dtype))
 571                                                dir_add_ignored(&dir, pathspec[i], strlen(pathspec[i]));
 572                                } else
 573                                        die(_("pathspec '%s' did not match any files"),
 574                                            pathspec[i]);
 575                        }
 576                }
 577                free(seen);
 578        }
 579
 580        plug_bulk_checkin();
 581
 582        if ((flags & ADD_CACHE_IMPLICIT_DOT) && prefix) {
 583                /*
 584                 * Check for modified files throughout the worktree so
 585                 * update_callback has a chance to warn about changes
 586                 * outside the cwd.
 587                 */
 588                update_data.implicit_dot = prefix;
 589                update_data.implicit_dot_len = strlen(prefix);
 590                pathspec = NULL;
 591        }
 592        update_data.flags = flags & ~ADD_CACHE_IMPLICIT_DOT;
 593        update_files_in_cache(prefix, pathspec, &update_data);
 594
 595        exit_status |= !!update_data.add_errors;
 596        if (add_new_files)
 597                exit_status |= add_files(&dir, flags);
 598
 599        unplug_bulk_checkin();
 600
 601 finish:
 602        if (active_cache_changed) {
 603                if (write_cache(newfd, active_cache, active_nr) ||
 604                    commit_locked_index(&lock_file))
 605                        die(_("Unable to write new index file"));
 606        }
 607
 608        return exit_status;
 609}