Skip to content

Commit 4e55ed3

Browse files
ethomsongitster
authored andcommitted
add: add --chmod=+x / --chmod=-x options
The executable bit will not be detected (and therefore will not be set) for paths in a repository with `core.filemode` set to false, though the users may still wish to add files as executable for compatibility with other users who _do_ have `core.filemode` functionality. For example, Windows users adding shell scripts may wish to add them as executable for compatibility with users on non-Windows. Although this can be done with a plumbing command (`git update-index --add --chmod=+x foo`), teaching the `git-add` command allows users to set a file executable with a command that they're already familiar with. Signed-off-by: Edward Thomson <[email protected]> Helped-by: Johannes Schindelin <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 60bd4b1 commit 4e55ed3

File tree

6 files changed

+68
-20
lines changed

6 files changed

+68
-20
lines changed

builtin/add.c

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ static int patch_interactive, add_interactive, edit_interactive;
2626
static int take_worktree_changes;
2727

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

@@ -65,7 +65,8 @@ static void update_callback(struct diff_queue_struct *q,
6565
die(_("unexpected diff status %c"), p->status);
6666
case DIFF_STATUS_MODIFIED:
6767
case DIFF_STATUS_TYPE_CHANGED:
68-
if (add_file_to_index(&the_index, path, data->flags)) {
68+
if (add_file_to_index(&the_index, path,
69+
data->flags, data->force_mode)) {
6970
if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
7071
die(_("updating files failed"));
7172
data->add_errors++;
@@ -83,14 +84,15 @@ static void update_callback(struct diff_queue_struct *q,
8384
}
8485
}
8586

86-
int add_files_to_cache(const char *prefix,
87-
const struct pathspec *pathspec, int flags)
87+
int add_files_to_cache(const char *prefix, const struct pathspec *pathspec,
88+
int flags, int force_mode)
8889
{
8990
struct update_callback_data data;
9091
struct rev_info rev;
9192

9293
memset(&data, 0, sizeof(data));
9394
data.flags = flags;
95+
data.force_mode = force_mode;
9496

9597
init_revisions(&rev, prefix);
9698
setup_revisions(0, NULL, &rev, NULL);
@@ -238,6 +240,8 @@ static int ignore_add_errors, intent_to_add, ignore_missing;
238240
static int addremove = ADDREMOVE_DEFAULT;
239241
static int addremove_explicit = -1; /* unspecified */
240242

243+
static char *chmod_arg;
244+
241245
static int ignore_removal_cb(const struct option *opt, const char *arg, int unset)
242246
{
243247
/* if we are told to ignore, we are not adding removals */
@@ -263,6 +267,7 @@ static struct option builtin_add_options[] = {
263267
OPT_BOOL( 0 , "refresh", &refresh_only, N_("don't add, only refresh the index")),
264268
OPT_BOOL( 0 , "ignore-errors", &ignore_add_errors, N_("just skip files which cannot be added because of errors")),
265269
OPT_BOOL( 0 , "ignore-missing", &ignore_missing, N_("check if - even missing - files are ignored in dry run")),
270+
OPT_STRING( 0 , "chmod", &chmod_arg, N_("(+/-)x"), N_("override the executable bit of the listed files")),
266271
OPT_END(),
267272
};
268273

@@ -276,7 +281,7 @@ static int add_config(const char *var, const char *value, void *cb)
276281
return git_default_config(var, value, cb);
277282
}
278283

279-
static int add_files(struct dir_struct *dir, int flags)
284+
static int add_files(struct dir_struct *dir, int flags, int force_mode)
280285
{
281286
int i, exit_status = 0;
282287

@@ -289,7 +294,8 @@ static int add_files(struct dir_struct *dir, int flags)
289294
}
290295

291296
for (i = 0; i < dir->nr; i++)
292-
if (add_file_to_cache(dir->entries[i]->name, flags)) {
297+
if (add_file_to_index(&the_index, dir->entries[i]->name,
298+
flags, force_mode)) {
293299
if (!ignore_add_errors)
294300
die(_("adding files failed"));
295301
exit_status = 1;
@@ -302,7 +308,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
302308
int exit_status = 0;
303309
struct pathspec pathspec;
304310
struct dir_struct dir;
305-
int flags;
311+
int flags, force_mode;
306312
int add_new_files;
307313
int require_pathspec;
308314
char *seen = NULL;
@@ -336,6 +342,15 @@ int cmd_add(int argc, const char **argv, const char *prefix)
336342
if (!show_only && ignore_missing)
337343
die(_("Option --ignore-missing can only be used together with --dry-run"));
338344

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
352+
die(_("--chmod param '%s' must be either -x or +x"), chmod_arg);
353+
339354
add_new_files = !take_worktree_changes && !refresh_only;
340355
require_pathspec = !(take_worktree_changes || (0 < addremove_explicit));
341356

@@ -426,10 +441,10 @@ int cmd_add(int argc, const char **argv, const char *prefix)
426441

427442
plug_bulk_checkin();
428443

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

431446
if (add_new_files)
432-
exit_status |= add_files(&dir, flags);
447+
exit_status |= add_files(&dir, flags, force_mode);
433448

434449
unplug_bulk_checkin();
435450

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);
551+
add_files_to_cache(NULL, NULL, 0, 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
@@ -386,7 +386,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
386386
*/
387387
if (all || (also && pathspec.nr)) {
388388
hold_locked_index(&index_lock, 1);
389-
add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
389+
add_files_to_cache(also ? prefix : NULL, &pathspec, 0, 0);
390390
refresh_cache_or_die(refresh_flags);
391391
update_main_cache_tree(WRITE_TREE_SILENT);
392392
if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))

cache.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -367,8 +367,8 @@ 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))
371-
#define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags))
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)
372372
#define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL, NULL)
373373
#define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
374374
#define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
@@ -581,8 +581,8 @@ extern int remove_file_from_index(struct index_state *, const char *path);
581581
#define ADD_CACHE_IGNORE_ERRORS 4
582582
#define ADD_CACHE_IGNORE_REMOVAL 8
583583
#define ADD_CACHE_INTENT 16
584-
extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
585-
extern int add_file_to_index(struct index_state *, const char *path, int flags);
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);
586586
extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, unsigned int refresh_options);
587587
extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
588588
extern void set_object_name_for_intent_to_add_entry(struct cache_entry *ce);
@@ -1772,7 +1772,7 @@ void packet_trace_identity(const char *prog);
17721772
* return 0 if success, 1 - if addition of a file failed and
17731773
* ADD_FILES_IGNORE_ERRORS was specified in flags
17741774
*/
1775-
int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags);
1775+
int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags, int force_mode);
17761776

17771777
/* diff.c */
17781778
extern int diff_auto_refresh_index;

read-cache.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,7 @@ void set_object_name_for_intent_to_add_entry(struct cache_entry *ce)
630630
hashcpy(ce->sha1, sha1);
631631
}
632632

633-
int add_to_index(struct index_state *istate, const char *path, struct stat *st, int flags)
633+
int add_to_index(struct index_state *istate, const char *path, struct stat *st, int flags, int force_mode)
634634
{
635635
int size, namelen, was_same;
636636
mode_t st_mode = st->st_mode;
@@ -659,7 +659,9 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
659659
else
660660
ce->ce_flags |= CE_INTENT_TO_ADD;
661661

662-
if (trust_executable_bit && has_symlinks)
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)
663665
ce->ce_mode = create_ce_mode(st_mode);
664666
else {
665667
/* If there is an existing entry, pick the mode bits and type
@@ -720,12 +722,13 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
720722
return 0;
721723
}
722724

723-
int add_file_to_index(struct index_state *istate, const char *path, int flags)
725+
int add_file_to_index(struct index_state *istate, const char *path,
726+
int flags, int force_mode)
724727
{
725728
struct stat st;
726729
if (lstat(path, &st))
727730
die_errno("unable to stat '%s'", path);
728-
return add_to_index(istate, path, &st, flags);
731+
return add_to_index(istate, path, &st, flags, force_mode);
729732
}
730733

731734
struct cache_entry *make_cache_entry(unsigned int mode,

t/t3700-add.sh

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,4 +332,34 @@ test_expect_success 'git add --dry-run --ignore-missing of non-existing file out
332332
test_i18ncmp expect.err actual.err
333333
'
334334

335+
test_expect_success 'git add --chmod=+x stages a non-executable file with +x' '
336+
echo foo >foo1 &&
337+
git add --chmod=+x foo1 &&
338+
case "$(git ls-files --stage foo1)" in
339+
100755" "*foo1) echo pass;;
340+
*) echo fail; git ls-files --stage foo1; (exit 1);;
341+
esac
342+
'
343+
344+
test_expect_success 'git add --chmod=-x stages an executable file with -x' '
345+
echo foo >xfoo1 &&
346+
chmod 755 xfoo1 &&
347+
git add --chmod=-x xfoo1 &&
348+
case "$(git ls-files --stage xfoo1)" in
349+
100644" "*xfoo1) echo pass;;
350+
*) echo fail; git ls-files --stage xfoo1; (exit 1);;
351+
esac
352+
'
353+
354+
test_expect_success POSIXPERM,SYMLINKS 'git add --chmod=+x with symlinks' '
355+
git config core.filemode 1 &&
356+
git config core.symlinks 1 &&
357+
echo foo >foo2 &&
358+
git add --chmod=+x foo2 &&
359+
case "$(git ls-files --stage foo2)" in
360+
100755" "*foo2) echo pass;;
361+
*) echo fail; git ls-files --stage foo2; (exit 1);;
362+
esac
363+
'
364+
335365
test_done

0 commit comments

Comments
 (0)