Users on deficient filesystems that lack an execute bit may still wish to add files to the repository with the appropriate execute bit set (or not). Although this can be done in two steps (`git add foo && git update-index --chmod=+x foo`), providing the `--chmod=+x` option to the add command allows users to set a file executable in a single command that they're already familiar with. Signed-off-by: Edward Thomson <ethomson@xxxxxxxxxxxxxxxxx> --- builtin/add.c | 18 +++++++++++++++++- cache.h | 2 ++ read-cache.c | 6 ++++++ t/t3700-add.sh | 19 +++++++++++++++++++ 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/builtin/add.c b/builtin/add.c index 145f06e..2a9abf7 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -238,6 +238,8 @@ static int ignore_add_errors, intent_to_add, ignore_missing; static int addremove = ADDREMOVE_DEFAULT; static int addremove_explicit = -1; /* unspecified */ +static char should_chmod = 0; + static int ignore_removal_cb(const struct option *opt, const char *arg, int unset) { /* if we are told to ignore, we are not adding removals */ @@ -245,6 +247,15 @@ static int ignore_removal_cb(const struct option *opt, const char *arg, int unse return 0; } +static int chmod_cb(const struct option *opt, const char *arg, int unset) +{ + char *flip = opt->value; + if ((arg[0] != '-' && arg[0] != '+') || arg[1] != 'x' || arg[2]) + return error("option 'chmod' expects \"+x\" or \"-x\""); + *flip = arg[0]; + return 0; +} + static struct option builtin_add_options[] = { OPT__DRY_RUN(&show_only, N_("dry run")), OPT__VERBOSE(&verbose, N_("be verbose")), @@ -263,6 +274,9 @@ static struct option builtin_add_options[] = { OPT_BOOL( 0 , "refresh", &refresh_only, N_("don't add, only refresh the index")), OPT_BOOL( 0 , "ignore-errors", &ignore_add_errors, N_("just skip files which cannot be added because of errors")), OPT_BOOL( 0 , "ignore-missing", &ignore_missing, N_("check if - even missing - files are ignored in dry run")), + { OPTION_CALLBACK, 0, "chmod", &should_chmod, N_("(+/-)x"), + N_("override the executable bit of the listed files"), + PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP, chmod_cb}, OPT_END(), }; @@ -346,7 +360,9 @@ int cmd_add(int argc, const char **argv, const char *prefix) (intent_to_add ? ADD_CACHE_INTENT : 0) | (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) | (!(addremove || take_worktree_changes) - ? ADD_CACHE_IGNORE_REMOVAL : 0)); + ? ADD_CACHE_IGNORE_REMOVAL : 0)) | + (should_chmod == '+' ? ADD_CACHE_FORCE_EXECUTABLE : 0) | + (should_chmod == '-' ? ADD_CACHE_FORCE_NOTEXECUTABLE : 0); if (require_pathspec && argc == 0) { fprintf(stderr, _("Nothing specified, nothing added.\n")); diff --git a/cache.h b/cache.h index 6049f86..da03cd9 100644 --- a/cache.h +++ b/cache.h @@ -581,6 +581,8 @@ extern int remove_file_from_index(struct index_state *, const char *path); #define ADD_CACHE_IGNORE_ERRORS 4 #define ADD_CACHE_IGNORE_REMOVAL 8 #define ADD_CACHE_INTENT 16 +#define ADD_CACHE_FORCE_EXECUTABLE 32 +#define ADD_CACHE_FORCE_NOTEXECUTABLE 64 extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags); extern int add_file_to_index(struct index_state *, const char *path, int flags); extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, unsigned int refresh_options); diff --git a/read-cache.c b/read-cache.c index d9fb78b..81bf186 100644 --- a/read-cache.c +++ b/read-cache.c @@ -641,6 +641,8 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st, int intent_only = flags & ADD_CACHE_INTENT; int add_option = (ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE| (intent_only ? ADD_CACHE_NEW_ONLY : 0)); + int force_executable = flags & ADD_CACHE_FORCE_EXECUTABLE; + int force_notexecutable = flags & ADD_CACHE_FORCE_NOTEXECUTABLE; if (!S_ISREG(st_mode) && !S_ISLNK(st_mode) && !S_ISDIR(st_mode)) return error("%s: can only add regular files, symbolic links or git-directories", path); @@ -661,6 +663,10 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st, if (trust_executable_bit && has_symlinks) ce->ce_mode = create_ce_mode(st_mode); + else if (force_executable) + ce->ce_mode = create_ce_mode(0777); + else if (force_notexecutable) + ce->ce_mode = create_ce_mode(0666); else { /* If there is an existing entry, pick the mode bits and type * from it, otherwise assume unexecutable regular file. diff --git a/t/t3700-add.sh b/t/t3700-add.sh index f14a665..e551eaf 100755 --- a/t/t3700-add.sh +++ b/t/t3700-add.sh @@ -332,4 +332,23 @@ test_expect_success 'git add --dry-run --ignore-missing of non-existing file out test_i18ncmp expect.err actual.err ' +test_expect_success 'git add --chmod=+x stages a non-executable file with +x' ' + echo foo >foo1 && + git add --chmod=+x foo1 && + case "$(git ls-files --stage foo1)" in + 100755" "*foo1) echo pass;; + *) echo fail; git ls-files --stage foo1; (exit 1);; + esac +' + +test_expect_success 'git add --chmod=-x stages an executable file with -x' ' + echo foo >xfoo1 && + chmod 755 xfoo1 && + git add --chmod=-x xfoo1 && + case "$(git ls-files --stage xfoo1)" in + 100644" "*xfoo1) echo pass;; + *) echo fail; git ls-files --stage xfoo1; (exit 1);; + esac +' + test_done -- 2.6.4 (Apple Git-63) -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html