Coverage Report

Created: 2024-09-16 06:12

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