add: modify already added files when --chmod is given
When the chmod option was added to git add, it was hooked up to the diff machinery, meaning that it only works when the version in the index differs from the version on disk. As the option was supposed to mirror the chmod option in update-index, which always changes the mode in the index, regardless of the status of the file, make sure the option behaves the same way in git add. Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Thomas Gummerer committed
Sep 14, 2016 at 22:07 UTC
610d55af0f082f6b866dc858e144c03d8ed4424c
6 files changed
+91
-34
builtin/add.c
+28
-19
@@ -26,10 +26,25 @@ static int patch_interactive, add_interactive, edit_interactive;
26
static int take_worktree_changes;
27
28
struct update_callback_data {
29
- int flags, force_mode;
29
+ int flags;
30
int add_errors;
31
};
32
33
+static void chmod_pathspec(struct pathspec *pathspec, int force_mode)
34
+{
35
+ int i;
36
+
37
+ for (i = 0; i < active_nr; i++) {
38
+ struct cache_entry *ce = active_cache[i];
39
+
40
+ if (pathspec && !ce_path_match(ce, pathspec, NULL))
41
+ continue;
42
+
43
+ if (chmod_cache_entry(ce, force_mode) < 0)
44
+ fprintf(stderr, "cannot chmod '%s'", ce->name);
45
+ }
46
+}
47
+
48
static int fix_unmerged_status(struct diff_filepair *p,
49
struct update_callback_data *data)
50
{
@@ -65,8 +80,7 @@ static void update_callback(struct diff_queue_struct *q,
80
die(_("unexpected diff status %c"), p->status);
81
case DIFF_STATUS_MODIFIED:
82
case DIFF_STATUS_TYPE_CHANGED:
68
- if (add_file_to_index(&the_index, path,
69
- data->flags, data->force_mode)) {
83
+ if (add_file_to_index(&the_index, path, data->flags)) {
84
if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
85
die(_("updating files failed"));
86
data->add_errors++;
@@ -84,15 +98,14 @@ static void update_callback(struct diff_queue_struct *q,
98
}
99
}
100
87
-int add_files_to_cache(const char *prefix, const struct pathspec *pathspec,
88
- int flags, int force_mode)
101
+int add_files_to_cache(const char *prefix,
102
+ const struct pathspec *pathspec, int flags)
103
{
104
struct update_callback_data data;
105
struct rev_info rev;
106
107
memset(&data, 0, sizeof(data));
108
data.flags = flags;
95
- data.force_mode = force_mode;
109
110
init_revisions(&rev, prefix);
111
setup_revisions(0, NULL, &rev, NULL);
@@ -281,7 +294,7 @@ static int add_config(const char *var, const char *value, void *cb)
294
return git_default_config(var, value, cb);
295
}
296
284
-static int add_files(struct dir_struct *dir, int flags, int force_mode)
297
+static int add_files(struct dir_struct *dir, int flags)
298
{
299
int i, exit_status = 0;
300
@@ -294,8 +307,7 @@ static int add_files(struct dir_struct *dir, int flags, int force_mode)
307
}
308
309
for (i = 0; i < dir->nr; i++)
297
- if (add_file_to_index(&the_index, dir->entries[i]->name,
298
- flags, force_mode)) {
310
+ if (add_file_to_index(&the_index, dir->entries[i]->name, flags)) {
311
if (!ignore_add_errors)
312
die(_("adding files failed"));
313
exit_status = 1;
@@ -308,7 +320,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
320
int exit_status = 0;
321
struct pathspec pathspec;
322
struct dir_struct dir;
311
- int flags, force_mode;
323
+ int flags;
324
int add_new_files;
325
int require_pathspec;
326
char *seen = NULL;
@@ -342,13 +354,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
354
if (!show_only && ignore_missing)
355
die(_("Option --ignore-missing can only be used together with --dry-run"));
356
345
- if (!chmod_arg)
346
- force_mode = 0;
347
- else if (!strcmp(chmod_arg, "-x"))
348
- force_mode = 0666;
349
- else if (!strcmp(chmod_arg, "+x"))
350
- force_mode = 0777;
351
- else
357
+ if (chmod_arg && ((chmod_arg[0] != '-' && chmod_arg[0] != '+') ||
358
+ chmod_arg[1] != 'x' || chmod_arg[2]))
359
die(_("--chmod param '%s' must be either -x or +x"), chmod_arg);
360
361
add_new_files = !take_worktree_changes && !refresh_only;
@@ -441,11 +448,13 @@ int cmd_add(int argc, const char **argv, const char *prefix)
448
449
plug_bulk_checkin();
450
444
- exit_status |= add_files_to_cache(prefix, &pathspec, flags, force_mode);
451
+ exit_status |= add_files_to_cache(prefix, &pathspec, flags);
452
453
if (add_new_files)
447
- exit_status |= add_files(&dir, flags, force_mode);
454
+ exit_status |= add_files(&dir, flags);
455
456
+ if (chmod_arg && pathspec.nr)
457
+ chmod_pathspec(&pathspec, chmod_arg[0]);
458
unplug_bulk_checkin();
459
460
finish:
builtin/checkout.c
+1
-1
@@ -548,7 +548,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
548
* entries in the index.
549
*/
550
551
- add_files_to_cache(NULL, NULL, 0, 0);
551
+ add_files_to_cache(NULL, NULL, 0);
552
/*
553
* NEEDSWORK: carrying over local changes
554
* when branches have different end-of-line
builtin/commit.c
+1
-1
@@ -386,7 +386,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
386
*/
387
if (all || (also && pathspec.nr)) {
388
hold_locked_index(&index_lock, 1);
389
- add_files_to_cache(also ? prefix : NULL, &pathspec, 0, 0);
389
+ add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
390
refresh_cache_or_die(refresh_flags);
391
update_main_cache_tree(WRITE_TREE_SILENT);
392
if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
cache.h
+5
-5
@@ -367,8 +367,8 @@ extern void free_name_hash(struct index_state *istate);
367
#define rename_cache_entry_at(pos, new_name) rename_index_entry_at(&the_index, (pos), (new_name))
368
#define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos))
369
#define remove_file_from_cache(path) remove_file_from_index(&the_index, (path))
370
-#define add_to_cache(path, st, flags) add_to_index(&the_index, (path), (st), (flags), 0)
371
-#define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags), 0)
370
+#define add_to_cache(path, st, flags) add_to_index(&the_index, (path), (st), (flags))
371
+#define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags))
372
#define chmod_cache_entry(ce, flip) chmod_index_entry(&the_index, (ce), (flip))
373
#define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL, NULL)
374
#define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
@@ -582,8 +582,8 @@ extern int remove_file_from_index(struct index_state *, const char *path);
582
#define ADD_CACHE_IGNORE_ERRORS 4
583
#define ADD_CACHE_IGNORE_REMOVAL 8
584
#define ADD_CACHE_INTENT 16
585
-extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags, int force_mode);
586
-extern int add_file_to_index(struct index_state *, const char *path, int flags, int force_mode);
585
+extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
586
+extern int add_file_to_index(struct index_state *, const char *path, int flags);
587
extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, unsigned int refresh_options);
588
extern int chmod_index_entry(struct index_state *, struct cache_entry *ce, char flip);
589
extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
@@ -1774,7 +1774,7 @@ void packet_trace_identity(const char *prog);
1774
* return 0 if success, 1 - if addition of a file failed and
1775
* ADD_FILES_IGNORE_ERRORS was specified in flags
1776
*/
1777
-int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags, int force_mode);
1777
+int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags);
1778
1779
/* diff.c */
1780
extern int diff_auto_refresh_index;
read-cache.c
+6
-8
@@ -630,7 +630,7 @@ void set_object_name_for_intent_to_add_entry(struct cache_entry *ce)
630
hashcpy(ce->sha1, sha1);
631
}
632
633
-int add_to_index(struct index_state *istate, const char *path, struct stat *st, int flags, int force_mode)
633
+int add_to_index(struct index_state *istate, const char *path, struct stat *st, int flags)
634
{
635
int size, namelen, was_same;
636
mode_t st_mode = st->st_mode;
@@ -659,11 +659,10 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
659
else
660
ce->ce_flags |= CE_INTENT_TO_ADD;
661
662
- if (S_ISREG(st_mode) && force_mode)
663
- ce->ce_mode = create_ce_mode(force_mode);
664
- else if (trust_executable_bit && has_symlinks)
662
+
663
+ if (trust_executable_bit && has_symlinks) {
664
ce->ce_mode = create_ce_mode(st_mode);
666
- else {
665
+ } else {
666
/* If there is an existing entry, pick the mode bits and type
667
* from it, otherwise assume unexecutable regular file.
668
*/
@@ -722,13 +721,12 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
721
return 0;
722
}
723
725
-int add_file_to_index(struct index_state *istate, const char *path,
726
- int flags, int force_mode)
724
+int add_file_to_index(struct index_state *istate, const char *path, int flags)
725
{
726
struct stat st;
727
if (lstat(path, &st))
728
die_errno("unable to stat '%s'", path);
731
- return add_to_index(istate, path, &st, flags, force_mode);
729
+ return add_to_index(istate, path, &st, flags);
730
}
731
732
struct cache_entry *make_cache_entry(unsigned int mode,
t/t3700-add.sh
+50
@@ -349,4 +349,54 @@ test_expect_success POSIXPERM,SYMLINKS 'git add --chmod=+x with symlinks' '
349
test_mode_in_index 100755 foo2
350
'
351
352
+test_expect_success 'git add --chmod=[+-]x changes index with already added file' '
353
+ echo foo >foo3 &&
354
+ git add foo3 &&
355
+ git add --chmod=+x foo3 &&
356
+ test_mode_in_index 100755 foo3 &&
357
+ echo foo >xfoo3 &&
358
+ chmod 755 xfoo3 &&
359
+ git add xfoo3 &&
360
+ git add --chmod=-x xfoo3 &&
361
+ test_mode_in_index 100644 xfoo3
362
+'
363
+
364
+test_expect_success 'file status is changed after git add --chmod=+x' '
365
+ echo "AM foo4" >expected &&
366
+ echo foo >foo4 &&
367
+ git add foo4 &&
368
+ git add --chmod=+x foo4 &&
369
+ git status -s foo4 >actual &&
370
+ test_cmp expected actual
371
+'
372
+
373
+test_expect_success 'no file status change if no pathspec is given' '
374
+ >foo5 &&
375
+ >foo6 &&
376
+ git add foo5 foo6 &&
377
+ git add --chmod=+x &&
378
+ test_mode_in_index 100644 foo5 &&
379
+ test_mode_in_index 100644 foo6
380
+'
381
+
382
+test_expect_success 'no file status change if no pathspec is given in subdir' '
383
+ mkdir sub &&
384
+ (
385
+ cd sub &&
386
+ >sub-foo1 &&
387
+ >sub-foo2 &&
388
+ git add . &&
389
+ git add --chmod=+x &&
390
+ test_mode_in_index 100644 sub-foo1 &&
391
+ test_mode_in_index 100644 sub-foo2
392
+ )
393
+'
394
+
395
+test_expect_success 'all statuses changed in folder if . is given' '
396
+ git add --chmod=+x . &&
397
+ test $(git ls-files --stage | grep ^100644 | wc -l) -eq 0 &&
398
+ git add --chmod=-x . &&
399
+ test $(git ls-files --stage | grep ^100755 | wc -l) -eq 0
400
+'
401
+
402
test_done