Skip to content

Commit 36f6403

Browse files
committed
Merge branch 'tg/add-chmod+x-fix' into maint
"git add --chmod=+x <pathspec>" added recently only toggled the executable bit for paths that are either new or modified. This has been corrected to flip the executable bit for all paths that match the given pathspec. * tg/add-chmod+x-fix: t3700-add: do not check working tree file mode without POSIXPERM t3700-add: create subdirectory gently add: modify already added files when --chmod is given read-cache: introduce chmod_index_entry update-index: add test for chmod flags
2 parents bf3a55a + 40e0dc1 commit 36f6403

File tree

8 files changed

+136
-49
lines changed

8 files changed

+136
-49
lines changed

builtin/add.c

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,25 @@ static int patch_interactive, add_interactive, edit_interactive;
2626
static int take_worktree_changes;
2727

2828
struct update_callback_data {
29-
int flags, force_mode;
29+
int flags;
3030
int add_errors;
3131
};
3232

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+
3348
static int fix_unmerged_status(struct diff_filepair *p,
3449
struct update_callback_data *data)
3550
{
@@ -65,8 +80,7 @@ static void update_callback(struct diff_queue_struct *q,
6580
die(_("unexpected diff status %c"), p->status);
6681
case DIFF_STATUS_MODIFIED:
6782
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)) {
7084
if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
7185
die(_("updating files failed"));
7286
data->add_errors++;
@@ -84,15 +98,14 @@ static void update_callback(struct diff_queue_struct *q,
8498
}
8599
}
86100

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)
89103
{
90104
struct update_callback_data data;
91105
struct rev_info rev;
92106

93107
memset(&data, 0, sizeof(data));
94108
data.flags = flags;
95-
data.force_mode = force_mode;
96109

97110
init_revisions(&rev, prefix);
98111
setup_revisions(0, NULL, &rev, NULL);
@@ -281,7 +294,7 @@ static int add_config(const char *var, const char *value, void *cb)
281294
return git_default_config(var, value, cb);
282295
}
283296

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)
285298
{
286299
int i, exit_status = 0;
287300

@@ -294,8 +307,7 @@ static int add_files(struct dir_struct *dir, int flags, int force_mode)
294307
}
295308

296309
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)) {
299311
if (!ignore_add_errors)
300312
die(_("adding files failed"));
301313
exit_status = 1;
@@ -308,7 +320,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
308320
int exit_status = 0;
309321
struct pathspec pathspec;
310322
struct dir_struct dir;
311-
int flags, force_mode;
323+
int flags;
312324
int add_new_files;
313325
int require_pathspec;
314326
char *seen = NULL;
@@ -342,13 +354,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
342354
if (!show_only && ignore_missing)
343355
die(_("Option --ignore-missing can only be used together with --dry-run"));
344356

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]))
352359
die(_("--chmod param '%s' must be either -x or +x"), chmod_arg);
353360

354361
add_new_files = !take_worktree_changes && !refresh_only;
@@ -441,11 +448,13 @@ int cmd_add(int argc, const char **argv, const char *prefix)
441448

442449
plug_bulk_checkin();
443450

444-
exit_status |= add_files_to_cache(prefix, &pathspec, flags, force_mode);
451+
exit_status |= add_files_to_cache(prefix, &pathspec, flags);
445452

446453
if (add_new_files)
447-
exit_status |= add_files(&dir, flags, force_mode);
454+
exit_status |= add_files(&dir, flags);
448455

456+
if (chmod_arg && pathspec.nr)
457+
chmod_pathspec(&pathspec, chmod_arg[0]);
449458
unplug_bulk_checkin();
450459

451460
finish:

builtin/checkout.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
548548
* entries in the index.
549549
*/
550550

551-
add_files_to_cache(NULL, NULL, 0, 0);
551+
add_files_to_cache(NULL, NULL, 0);
552552
/*
553553
* NEEDSWORK: carrying over local changes
554554
* when branches have different end-of-line

builtin/commit.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
387387
*/
388388
if (all || (also && pathspec.nr)) {
389389
hold_locked_index(&index_lock, 1);
390-
add_files_to_cache(also ? prefix : NULL, &pathspec, 0, 0);
390+
add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
391391
refresh_cache_or_die(refresh_flags);
392392
update_main_cache_tree(WRITE_TREE_SILENT);
393393
if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))

builtin/update-index.c

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -419,30 +419,18 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
419419
return 0;
420420
}
421421

422-
static void chmod_path(int flip, const char *path)
422+
static void chmod_path(char flip, const char *path)
423423
{
424424
int pos;
425425
struct cache_entry *ce;
426-
unsigned int mode;
427426

428427
pos = cache_name_pos(path, strlen(path));
429428
if (pos < 0)
430429
goto fail;
431430
ce = active_cache[pos];
432-
mode = ce->ce_mode;
433-
if (!S_ISREG(mode))
434-
goto fail;
435-
switch (flip) {
436-
case '+':
437-
ce->ce_mode |= 0111; break;
438-
case '-':
439-
ce->ce_mode &= ~0111; break;
440-
default:
431+
if (chmod_cache_entry(ce, flip) < 0)
441432
goto fail;
442-
}
443-
cache_tree_invalidate_path(&the_index, path);
444-
ce->ce_flags |= CE_UPDATE_IN_BASE;
445-
active_cache_changed |= CE_ENTRY_CHANGED;
433+
446434
report("chmod %cx '%s'", flip, path);
447435
return;
448436
fail:

cache.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -367,8 +367,9 @@ extern void free_name_hash(struct index_state *istate);
367367
#define rename_cache_entry_at(pos, new_name) rename_index_entry_at(&the_index, (pos), (new_name))
368368
#define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos))
369369
#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))
372373
#define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL, NULL)
373374
#define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
374375
#define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
@@ -581,9 +582,10 @@ extern int remove_file_from_index(struct index_state *, const char *path);
581582
#define ADD_CACHE_IGNORE_ERRORS 4
582583
#define ADD_CACHE_IGNORE_REMOVAL 8
583584
#define ADD_CACHE_INTENT 16
584-
extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags, int force_mode);
585-
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);
586587
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);
587589
extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
588590
extern void set_object_name_for_intent_to_add_entry(struct cache_entry *ce);
589591
extern int index_name_is_other(const struct index_state *, const char *, int);
@@ -1828,7 +1830,7 @@ void packet_trace_identity(const char *prog);
18281830
* return 0 if success, 1 - if addition of a file failed and
18291831
* ADD_FILES_IGNORE_ERRORS was specified in flags
18301832
*/
1831-
int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags, int force_mode);
1833+
int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags);
18321834

18331835
/* diff.c */
18341836
extern int diff_auto_refresh_index;

read-cache.c

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -627,7 +627,7 @@ void set_object_name_for_intent_to_add_entry(struct cache_entry *ce)
627627
hashcpy(ce->sha1, sha1);
628628
}
629629

630-
int add_to_index(struct index_state *istate, const char *path, struct stat *st, int flags, int force_mode)
630+
int add_to_index(struct index_state *istate, const char *path, struct stat *st, int flags)
631631
{
632632
int size, namelen, was_same;
633633
mode_t st_mode = st->st_mode;
@@ -656,11 +656,10 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
656656
else
657657
ce->ce_flags |= CE_INTENT_TO_ADD;
658658

659-
if (S_ISREG(st_mode) && force_mode)
660-
ce->ce_mode = create_ce_mode(force_mode);
661-
else if (trust_executable_bit && has_symlinks)
659+
660+
if (trust_executable_bit && has_symlinks) {
662661
ce->ce_mode = create_ce_mode(st_mode);
663-
else {
662+
} else {
664663
/* If there is an existing entry, pick the mode bits and type
665664
* from it, otherwise assume unexecutable regular file.
666665
*/
@@ -719,13 +718,12 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
719718
return 0;
720719
}
721720

722-
int add_file_to_index(struct index_state *istate, const char *path,
723-
int flags, int force_mode)
721+
int add_file_to_index(struct index_state *istate, const char *path, int flags)
724722
{
725723
struct stat st;
726724
if (lstat(path, &st))
727725
die_errno("unable to stat '%s'", path);
728-
return add_to_index(istate, path, &st, flags, force_mode);
726+
return add_to_index(istate, path, &st, flags);
729727
}
730728

731729
struct cache_entry *make_cache_entry(unsigned int mode,
@@ -756,6 +754,35 @@ struct cache_entry *make_cache_entry(unsigned int mode,
756754
return ret;
757755
}
758756

757+
/*
758+
* Chmod an index entry with either +x or -x.
759+
*
760+
* Returns -1 if the chmod for the particular cache entry failed (if it's
761+
* not a regular file), -2 if an invalid flip argument is passed in, 0
762+
* otherwise.
763+
*/
764+
int chmod_index_entry(struct index_state *istate, struct cache_entry *ce,
765+
char flip)
766+
{
767+
if (!S_ISREG(ce->ce_mode))
768+
return -1;
769+
switch (flip) {
770+
case '+':
771+
ce->ce_mode |= 0111;
772+
break;
773+
case '-':
774+
ce->ce_mode &= ~0111;
775+
break;
776+
default:
777+
return -2;
778+
}
779+
cache_tree_invalidate_path(istate, ce->name);
780+
ce->ce_flags |= CE_UPDATE_IN_BASE;
781+
istate->cache_changed |= CE_ENTRY_CHANGED;
782+
783+
return 0;
784+
}
785+
759786
int ce_same_name(const struct cache_entry *a, const struct cache_entry *b)
760787
{
761788
int len = ce_namelen(a);

t/t2107-update-index-basic.sh

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,17 @@ test_expect_success '.lock files cleaned up' '
8080
)
8181
'
8282

83+
test_expect_success '--chmod=+x and chmod=-x in the same argument list' '
84+
>A &&
85+
>B &&
86+
git add A B &&
87+
git update-index --chmod=+x A --chmod=-x B &&
88+
cat >expect <<-\EOF &&
89+
100755 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 A
90+
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 B
91+
EOF
92+
git ls-files --stage A B >actual &&
93+
test_cmp expect actual
94+
'
95+
8396
test_done

t/t3700-add.sh

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,4 +349,52 @@ test_expect_success POSIXPERM,SYMLINKS 'git add --chmod=+x with symlinks' '
349349
test_mode_in_index 100755 foo2
350350
'
351351

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 POSIXPERM 'git add --chmod=[+-]x does not change the working tree' '
365+
echo foo >foo4 &&
366+
git add foo4 &&
367+
git add --chmod=+x foo4 &&
368+
! test -x foo4
369+
'
370+
371+
test_expect_success 'no file status change if no pathspec is given' '
372+
>foo5 &&
373+
>foo6 &&
374+
git add foo5 foo6 &&
375+
git add --chmod=+x &&
376+
test_mode_in_index 100644 foo5 &&
377+
test_mode_in_index 100644 foo6
378+
'
379+
380+
test_expect_success 'no file status change if no pathspec is given in subdir' '
381+
mkdir -p sub &&
382+
(
383+
cd sub &&
384+
>sub-foo1 &&
385+
>sub-foo2 &&
386+
git add . &&
387+
git add --chmod=+x &&
388+
test_mode_in_index 100644 sub-foo1 &&
389+
test_mode_in_index 100644 sub-foo2
390+
)
391+
'
392+
393+
test_expect_success 'all statuses changed in folder if . is given' '
394+
git add --chmod=+x . &&
395+
test $(git ls-files --stage | grep ^100644 | wc -l) -eq 0 &&
396+
git add --chmod=-x . &&
397+
test $(git ls-files --stage | grep ^100755 | wc -l) -eq 0
398+
'
399+
352400
test_done

0 commit comments

Comments
 (0)