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