threads / patch / 42497

patchadd: add --chmod=+x / --chmod=-x options

Subject: [PATCH] add: add --chmod=+x / --chmod=-x options

## tl;dr

8 messages between May 31, 2016 and Jun 8, 2016. Diffs are folded; open one to read it.

replies: 7people: 4as markdown or json

Edward Thomson· May 31, 2016, 22:08 UTC · lore

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 <ethomson@edwardthomson.com>
---
 builtin/add.c  | 12 +++++++++++-
 cache.h        |  2 ++
 read-cache.c   | 11 +++++++++--
 t/t3700-add.sh | 30 ++++++++++++++++++++++++++++++
 4 files changed, 52 insertions(+), 3 deletions(-)
Show changes to 4 files +52 −3

builtin/add.c, cache.h, read-cache.c, t/t3700-add.sh

diff --git a/builtin/add.c b/builtin/add.c
index 145f06e..44b6c97 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 *chmod_arg = NULL;
+
 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 */
@@ -263,6 +265,7 @@ 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")),
+	OPT_STRING( 0 , "chmod", &chmod_arg, N_("(+/-)x"), N_("override the executable bit of the listed files")),
 	OPT_END(),
 };
 
@@ -336,6 +339,11 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 	if (!show_only && ignore_missing)
 		die(_("Option --ignore-missing can only be used together with --dry-run"));
 
+	if (chmod_arg) {
+		if (strcmp(chmod_arg, "-x") && strcmp(chmod_arg, "+x"))
+			die(_("--chmod param must be either -x or +x"));
+	}
+
 	add_new_files = !take_worktree_changes && !refresh_only;
 	require_pathspec = !(take_worktree_changes || (0 < addremove_explicit));
 
@@ -346,7 +354,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)) |
+		 (chmod_arg && *chmod_arg == '+' ? ADD_CACHE_FORCE_EXECUTABLE : 0) |
+		 (chmod_arg && *chmod_arg == '-' ? 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..d12d143 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);
@@ -659,9 +661,14 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
 	else
 		ce->ce_flags |= CE_INTENT_TO_ADD;
 
-	if (trust_executable_bit && has_symlinks)
+	if (S_ISREG(st_mode) && (force_executable || force_notexecutable)) {
+		if (force_executable)
+			ce->ce_mode = create_ce_mode(0777);
+		else
+			ce->ce_mode = create_ce_mode(0666);
+	} else if (trust_executable_bit && has_symlinks) {
 		ce->ce_mode = create_ce_mode(st_mode);
-	else {
+	} 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..4865304 100755
--- a/t/t3700-add.sh
+++ b/t/t3700-add.sh
@@ -332,4 +332,34 @@ 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_expect_success POSIXPERM,SYMLINKS 'git add --chmod=+x with symlinks' '
+	git config core.filemode 1 &&
+	git config core.symlinks 1 &&
+	echo foo >foo2 &&
+	git add --chmod=+x foo2 &&
+	case "$(git ls-files --stage foo2)" in
+	100755" "*foo2) echo pass;;
+	*) echo fail; git ls-files --stage foo2; (exit 1);;
+	esac
+'
+
 test_done
-- 
2.7.4 (Apple Git-66)
Junio C Hamano· May 31, 2016, 22:34 UTC · re: Edward Thomson · lore

Re: [PATCH] add: add --chmod=+x / --chmod=-x options

Edward Thomson <ethomson@edwardthomson.com> writes:
> +static char *chmod_arg = NULL;
> +

I'll drop " = NULL", as it is our convention to let BSS take care of the zero initialization for global variables as much as possible.

Other than that, I did not see anything objectionable in this round, but I did notice that you kept the "this takes two bits out of 32" Dscho mentioned--I do not have a strong preference either way, so I'll queue it (at least tentatively) on 'pu'.

Show 21 quoted lines
> @@ -346,7 +354,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)) |
> +		 (chmod_arg && *chmod_arg == '+' ? ADD_CACHE_FORCE_EXECUTABLE : 0) |
> +		 (chmod_arg && *chmod_arg == '-' ? 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
Johannes Schindelin· Jun 1, 2016, 07:23 UTC · re: Junio C Hamano · lore

Re: [PATCH] add: add --chmod=+x / --chmod=-x options

Hi Junio & Ed,
On Tue, 31 May 2016, Junio C Hamano wrote:
Show 12 quoted lines
> Edward Thomson <ethomson@edwardthomson.com> writes:
> 
> > +static char *chmod_arg = NULL;
> > +
> 
> I'll drop " = NULL", as it is our convention to let BSS take care of
> the zero initialization for global variables as much as possible.
> 
> Other than that, I did not see anything objectionable in this round,
> but I did notice that you kept the "this takes two bits out of 32"
> Dscho mentioned--I do not have a strong preference either way, so
> I'll queue it (at least tentatively) on 'pu'.

And here is an add-on patch (Ed, feel free to squash) that avoids those two bits, and even saves one line overall:

-- snipsnap --
Show changes to 5 files +37 −38

builtin/add.c, builtin/checkout.c, builtin/commit.c, cache.h, read-cache.c

diff --git a/builtin/add.c b/builtin/add.c
index 44b6c97..b1dddb4 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -26,7 +26,7 @@ static int patch_interactive, add_interactive, edit_interactive;
 static int take_worktree_changes;
 
 struct update_callback_data {
-	int flags;
+	int flags, force_mode;
 	int add_errors;
 };
 
@@ -65,7 +65,8 @@ static void update_callback(struct diff_queue_struct *q,
 			die(_("unexpected diff status %c"), p->status);
 		case DIFF_STATUS_MODIFIED:
 		case DIFF_STATUS_TYPE_CHANGED:
-			if (add_file_to_index(&the_index, path, data->flags)) {
+			if (add_file_to_index(&the_index, path,
+					data->flags, data->force_mode)) {
 				if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
 					die(_("updating files failed"));
 				data->add_errors++;
@@ -83,14 +84,15 @@ static void update_callback(struct diff_queue_struct *q,
 	}
 }
 
-int add_files_to_cache(const char *prefix,
-		       const struct pathspec *pathspec, int flags)
+int add_files_to_cache(const char *prefix, const struct pathspec *pathspec,
+	int flags, int force_mode)
 {
 	struct update_callback_data data;
 	struct rev_info rev;
 
 	memset(&data, 0, sizeof(data));
 	data.flags = flags;
+	data.force_mode = force_mode;
 
 	init_revisions(&rev, prefix);
 	setup_revisions(0, NULL, &rev, NULL);
@@ -238,7 +240,7 @@ static int ignore_add_errors, intent_to_add, ignore_missing;
 static int addremove = ADDREMOVE_DEFAULT;
 static int addremove_explicit = -1; /* unspecified */
 
-static char *chmod_arg = NULL;
+static char *chmod_arg;
 
 static int ignore_removal_cb(const struct option *opt, const char *arg, int unset)
 {
@@ -279,7 +281,7 @@ static int add_config(const char *var, const char *value, void *cb)
 	return git_default_config(var, value, cb);
 }
 
-static int add_files(struct dir_struct *dir, int flags)
+static int add_files(struct dir_struct *dir, int flags, int force_mode)
 {
 	int i, exit_status = 0;
 
@@ -292,7 +294,8 @@ static int add_files(struct dir_struct *dir, int flags)
 	}
 
 	for (i = 0; i < dir->nr; i++)
-		if (add_file_to_cache(dir->entries[i]->name, flags)) {
+		if (add_file_to_index(&the_index, dir->entries[i]->name,
+				flags, force_mode)) {
 			if (!ignore_add_errors)
 				die(_("adding files failed"));
 			exit_status = 1;
@@ -305,7 +308,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 	int exit_status = 0;
 	struct pathspec pathspec;
 	struct dir_struct dir;
-	int flags;
+	int flags, force_mode;
 	int add_new_files;
 	int require_pathspec;
 	char *seen = NULL;
@@ -339,10 +342,14 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 	if (!show_only && ignore_missing)
 		die(_("Option --ignore-missing can only be used together with --dry-run"));
 
-	if (chmod_arg) {
-		if (strcmp(chmod_arg, "-x") && strcmp(chmod_arg, "+x"))
-			die(_("--chmod param must be either -x or +x"));
-	}
+	if (!chmod_arg)
+		force_mode = 0;
+	else if (!strcmp(chmod_arg, "-x"))
+		force_mode = 0666;
+	else if (!strcmp(chmod_arg, "+x"))
+		force_mode = 0777;
+	else
+		die(_("--chmod param '%s' must be either -x or +x"), chmod_arg);
 
 	add_new_files = !take_worktree_changes && !refresh_only;
 	require_pathspec = !(take_worktree_changes || (0 < addremove_explicit));
@@ -354,9 +361,7 @@ 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)) |
-		 (chmod_arg && *chmod_arg == '+' ? ADD_CACHE_FORCE_EXECUTABLE : 0) |
-		 (chmod_arg && *chmod_arg == '-' ? ADD_CACHE_FORCE_NOTEXECUTABLE : 0);
+		  ? ADD_CACHE_IGNORE_REMOVAL : 0));
 
 	if (require_pathspec && argc == 0) {
 		fprintf(stderr, _("Nothing specified, nothing added.\n"));
@@ -436,10 +441,10 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 
 	plug_bulk_checkin();
 
-	exit_status |= add_files_to_cache(prefix, &pathspec, flags);
+	exit_status |= add_files_to_cache(prefix, &pathspec, flags, force_mode);
 
 	if (add_new_files)
-		exit_status |= add_files(&dir, flags);
+		exit_status |= add_files(&dir, flags, force_mode);
 
 	unplug_bulk_checkin();
 
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 3398c61..c3486bd 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -548,7 +548,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
 			 * entries in the index.
 			 */
 
-			add_files_to_cache(NULL, NULL, 0);
+			add_files_to_cache(NULL, NULL, 0, 0);
 			/*
 			 * NEEDSWORK: carrying over local changes
 			 * when branches have different end-of-line
diff --git a/builtin/commit.c b/builtin/commit.c
index 443ff91..163dbca 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -386,7 +386,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
 	 */
 	if (all || (also && pathspec.nr)) {
 		hold_locked_index(&index_lock, 1);
-		add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
+		add_files_to_cache(also ? prefix : NULL, &pathspec, 0, 0);
 		refresh_cache_or_die(refresh_flags);
 		update_main_cache_tree(WRITE_TREE_SILENT);
 		if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
diff --git a/cache.h b/cache.h
index da03cd9..c73becb 100644
--- a/cache.h
+++ b/cache.h
@@ -367,8 +367,8 @@ extern void free_name_hash(struct index_state *istate);
 #define rename_cache_entry_at(pos, new_name) rename_index_entry_at(&the_index, (pos), (new_name))
 #define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos))
 #define remove_file_from_cache(path) remove_file_from_index(&the_index, (path))
-#define add_to_cache(path, st, flags) add_to_index(&the_index, (path), (st), (flags))
-#define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags))
+#define add_to_cache(path, st, flags) add_to_index(&the_index, (path), (st), (flags), 0)
+#define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags), 0)
 #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL, NULL)
 #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
 #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
@@ -581,10 +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 int add_to_index(struct index_state *, const char *path, struct stat *, int flags, int force_mode);
+extern int add_file_to_index(struct index_state *, const char *path, int flags, int force_mode);
 extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, unsigned int refresh_options);
 extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
 extern void set_object_name_for_intent_to_add_entry(struct cache_entry *ce);
@@ -1774,7 +1772,7 @@ void packet_trace_identity(const char *prog);
  * return 0 if success, 1 - if addition of a file failed and
  * ADD_FILES_IGNORE_ERRORS was specified in flags
  */
-int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags);
+int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags, int force_mode);
 
 /* diff.c */
 extern int diff_auto_refresh_index;
diff --git a/read-cache.c b/read-cache.c
index d12d143..db27766 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -630,7 +630,7 @@ void set_object_name_for_intent_to_add_entry(struct cache_entry *ce)
 	hashcpy(ce->sha1, sha1);
 }
 
-int add_to_index(struct index_state *istate, const char *path, struct stat *st, int flags)
+int add_to_index(struct index_state *istate, const char *path, struct stat *st, int flags, int force_mode)
 {
 	int size, namelen, was_same;
 	mode_t st_mode = st->st_mode;
@@ -641,8 +641,6 @@ 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,14 +659,11 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
 	else
 		ce->ce_flags |= CE_INTENT_TO_ADD;
 
-	if (S_ISREG(st_mode) && (force_executable || force_notexecutable)) {
-		if (force_executable)
-			ce->ce_mode = create_ce_mode(0777);
-		else
-			ce->ce_mode = create_ce_mode(0666);
-	} else if (trust_executable_bit && has_symlinks) {
+	if (S_ISREG(st_mode) && force_mode)
+		ce->ce_mode = create_ce_mode(force_mode);
+	else if (trust_executable_bit && has_symlinks)
 		ce->ce_mode = create_ce_mode(st_mode);
-	} else {
+	else {
 		/* If there is an existing entry, pick the mode bits and type
 		 * from it, otherwise assume unexecutable regular file.
 		 */
@@ -727,12 +722,13 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
 	return 0;
 }
 
-int add_file_to_index(struct index_state *istate, const char *path, int flags)
+int add_file_to_index(struct index_state *istate, const char *path,
+	int flags, int force_mode)
 {
 	struct stat st;
 	if (lstat(path, &st))
 		die_errno("unable to stat '%s'", path);
-	return add_to_index(istate, path, &st, flags);
+	return add_to_index(istate, path, &st, flags, force_mode);
 }
 
 struct cache_entry *make_cache_entry(unsigned int mode,
Johannes Schindelin· Jun 1, 2016, 10:19 UTC · re: Johannes Schindelin · lore

Re: [PATCH] add: add --chmod=+x / --chmod=-x options

On Wed, 1 Jun 2016, Johannes Schindelin wrote:
> And here is an add-on patch (Ed, feel free to squash) that avoids those
> two bits, and even saves one line overall:
> 
> [...]

And here is a link for developers who prefer to work with Git directly (as opposed to working with Git through a mailbox):

	https://github.com/git/git/compare/dscho:force-chmod
Junio C Hamano· Jun 1, 2016, 16:00 UTC · re: Johannes Schindelin · lore

Re: [PATCH] add: add --chmod=+x / --chmod=-x options

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
Show 19 quoted lines
> Hi Junio & Ed,
>
> On Tue, 31 May 2016, Junio C Hamano wrote:
>
>> Edward Thomson <ethomson@edwardthomson.com> writes:
>> 
>> > +static char *chmod_arg = NULL;
>> > +
>> 
>> I'll drop " = NULL", as it is our convention to let BSS take care of
>> the zero initialization for global variables as much as possible.
>> 
>> Other than that, I did not see anything objectionable in this round,
>> but I did notice that you kept the "this takes two bits out of 32"
>> Dscho mentioned--I do not have a strong preference either way, so
>> I'll queue it (at least tentatively) on 'pu'.
>
> And here is an add-on patch (Ed, feel free to squash) that avoids those
> two bits, and even saves one line overall:

Unlike the "something like this" we saw earlier, this draws the boundary of responsibility between the caller and the API at a much more sensible place.

The difference between the versions with and without this update essentially is that the original had two bits in flags (among 32 total, 5 bits are currently used and the patch uses 2 more bits), and this instead changes the function signature of functions that took "flags" so that lal of them take one additional word. So in that sense, because we have enough bits to use, this is not a great improvement.

However.

The filemode that we force is not two independent bits, but a tristate: do not force, force executable and force non-executable. And from that point of view, the two seemingly-independent bits looked a bit strange, and I prefer the separation this update gives us slightly more for that reason. I didn't think carefully about the change to add_to_index(), but doing it this way _may_ make it easier for us to later extend it to force "this is file on filesystem but record it as a symlink", in which case, even if we do not plan to implement such an enhancement in a near future, I think this is the right direction to go in.

Thanks.
Show 243 quoted lines
>
> -- snipsnap --
> diff --git a/builtin/add.c b/builtin/add.c
> index 44b6c97..b1dddb4 100644
> --- a/builtin/add.c
> +++ b/builtin/add.c
> @@ -26,7 +26,7 @@ static int patch_interactive, add_interactive, edit_interactive;
>  static int take_worktree_changes;
>  
>  struct update_callback_data {
> -	int flags;
> +	int flags, force_mode;
>  	int add_errors;
>  };
>  
> @@ -65,7 +65,8 @@ static void update_callback(struct diff_queue_struct *q,
>  			die(_("unexpected diff status %c"), p->status);
>  		case DIFF_STATUS_MODIFIED:
>  		case DIFF_STATUS_TYPE_CHANGED:
> -			if (add_file_to_index(&the_index, path, data->flags)) {
> +			if (add_file_to_index(&the_index, path,
> +					data->flags, data->force_mode)) {
>  				if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
>  					die(_("updating files failed"));
>  				data->add_errors++;
> @@ -83,14 +84,15 @@ static void update_callback(struct diff_queue_struct *q,
>  	}
>  }
>  
> -int add_files_to_cache(const char *prefix,
> -		       const struct pathspec *pathspec, int flags)
> +int add_files_to_cache(const char *prefix, const struct pathspec *pathspec,
> +	int flags, int force_mode)
>  {
>  	struct update_callback_data data;
>  	struct rev_info rev;
>  
>  	memset(&data, 0, sizeof(data));
>  	data.flags = flags;
> +	data.force_mode = force_mode;
>  
>  	init_revisions(&rev, prefix);
>  	setup_revisions(0, NULL, &rev, NULL);
> @@ -238,7 +240,7 @@ static int ignore_add_errors, intent_to_add, ignore_missing;
>  static int addremove = ADDREMOVE_DEFAULT;
>  static int addremove_explicit = -1; /* unspecified */
>  
> -static char *chmod_arg = NULL;
> +static char *chmod_arg;
>  
>  static int ignore_removal_cb(const struct option *opt, const char *arg, int unset)
>  {
> @@ -279,7 +281,7 @@ static int add_config(const char *var, const char *value, void *cb)
>  	return git_default_config(var, value, cb);
>  }
>  
> -static int add_files(struct dir_struct *dir, int flags)
> +static int add_files(struct dir_struct *dir, int flags, int force_mode)
>  {
>  	int i, exit_status = 0;
>  
> @@ -292,7 +294,8 @@ static int add_files(struct dir_struct *dir, int flags)
>  	}
>  
>  	for (i = 0; i < dir->nr; i++)
> -		if (add_file_to_cache(dir->entries[i]->name, flags)) {
> +		if (add_file_to_index(&the_index, dir->entries[i]->name,
> +				flags, force_mode)) {
>  			if (!ignore_add_errors)
>  				die(_("adding files failed"));
>  			exit_status = 1;
> @@ -305,7 +308,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
>  	int exit_status = 0;
>  	struct pathspec pathspec;
>  	struct dir_struct dir;
> -	int flags;
> +	int flags, force_mode;
>  	int add_new_files;
>  	int require_pathspec;
>  	char *seen = NULL;
> @@ -339,10 +342,14 @@ int cmd_add(int argc, const char **argv, const char *prefix)
>  	if (!show_only && ignore_missing)
>  		die(_("Option --ignore-missing can only be used together with --dry-run"));
>  
> -	if (chmod_arg) {
> -		if (strcmp(chmod_arg, "-x") && strcmp(chmod_arg, "+x"))
> -			die(_("--chmod param must be either -x or +x"));
> -	}
> +	if (!chmod_arg)
> +		force_mode = 0;
> +	else if (!strcmp(chmod_arg, "-x"))
> +		force_mode = 0666;
> +	else if (!strcmp(chmod_arg, "+x"))
> +		force_mode = 0777;
> +	else
> +		die(_("--chmod param '%s' must be either -x or +x"), chmod_arg);
>  
>  	add_new_files = !take_worktree_changes && !refresh_only;
>  	require_pathspec = !(take_worktree_changes || (0 < addremove_explicit));
> @@ -354,9 +361,7 @@ 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)) |
> -		 (chmod_arg && *chmod_arg == '+' ? ADD_CACHE_FORCE_EXECUTABLE : 0) |
> -		 (chmod_arg && *chmod_arg == '-' ? ADD_CACHE_FORCE_NOTEXECUTABLE : 0);
> +		  ? ADD_CACHE_IGNORE_REMOVAL : 0));
>  
>  	if (require_pathspec && argc == 0) {
>  		fprintf(stderr, _("Nothing specified, nothing added.\n"));
> @@ -436,10 +441,10 @@ int cmd_add(int argc, const char **argv, const char *prefix)
>  
>  	plug_bulk_checkin();
>  
> -	exit_status |= add_files_to_cache(prefix, &pathspec, flags);
> +	exit_status |= add_files_to_cache(prefix, &pathspec, flags, force_mode);
>  
>  	if (add_new_files)
> -		exit_status |= add_files(&dir, flags);
> +		exit_status |= add_files(&dir, flags, force_mode);
>  
>  	unplug_bulk_checkin();
>  
> diff --git a/builtin/checkout.c b/builtin/checkout.c
> index 3398c61..c3486bd 100644
> --- a/builtin/checkout.c
> +++ b/builtin/checkout.c
> @@ -548,7 +548,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
>  			 * entries in the index.
>  			 */
>  
> -			add_files_to_cache(NULL, NULL, 0);
> +			add_files_to_cache(NULL, NULL, 0, 0);
>  			/*
>  			 * NEEDSWORK: carrying over local changes
>  			 * when branches have different end-of-line
> diff --git a/builtin/commit.c b/builtin/commit.c
> index 443ff91..163dbca 100644
> --- a/builtin/commit.c
> +++ b/builtin/commit.c
> @@ -386,7 +386,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
>  	 */
>  	if (all || (also && pathspec.nr)) {
>  		hold_locked_index(&index_lock, 1);
> -		add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
> +		add_files_to_cache(also ? prefix : NULL, &pathspec, 0, 0);
>  		refresh_cache_or_die(refresh_flags);
>  		update_main_cache_tree(WRITE_TREE_SILENT);
>  		if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
> diff --git a/cache.h b/cache.h
> index da03cd9..c73becb 100644
> --- a/cache.h
> +++ b/cache.h
> @@ -367,8 +367,8 @@ extern void free_name_hash(struct index_state *istate);
>  #define rename_cache_entry_at(pos, new_name) rename_index_entry_at(&the_index, (pos), (new_name))
>  #define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos))
>  #define remove_file_from_cache(path) remove_file_from_index(&the_index, (path))
> -#define add_to_cache(path, st, flags) add_to_index(&the_index, (path), (st), (flags))
> -#define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags))
> +#define add_to_cache(path, st, flags) add_to_index(&the_index, (path), (st), (flags), 0)
> +#define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags), 0)
>  #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL, NULL)
>  #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
>  #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
> @@ -581,10 +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 int add_to_index(struct index_state *, const char *path, struct stat *, int flags, int force_mode);
> +extern int add_file_to_index(struct index_state *, const char *path, int flags, int force_mode);
>  extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, unsigned int refresh_options);
>  extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
>  extern void set_object_name_for_intent_to_add_entry(struct cache_entry *ce);
> @@ -1774,7 +1772,7 @@ void packet_trace_identity(const char *prog);
>   * return 0 if success, 1 - if addition of a file failed and
>   * ADD_FILES_IGNORE_ERRORS was specified in flags
>   */
> -int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags);
> +int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags, int force_mode);
>  
>  /* diff.c */
>  extern int diff_auto_refresh_index;
> diff --git a/read-cache.c b/read-cache.c
> index d12d143..db27766 100644
> --- a/read-cache.c
> +++ b/read-cache.c
> @@ -630,7 +630,7 @@ void set_object_name_for_intent_to_add_entry(struct cache_entry *ce)
>  	hashcpy(ce->sha1, sha1);
>  }
>  
> -int add_to_index(struct index_state *istate, const char *path, struct stat *st, int flags)
> +int add_to_index(struct index_state *istate, const char *path, struct stat *st, int flags, int force_mode)
>  {
>  	int size, namelen, was_same;
>  	mode_t st_mode = st->st_mode;
> @@ -641,8 +641,6 @@ 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,14 +659,11 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
>  	else
>  		ce->ce_flags |= CE_INTENT_TO_ADD;
>  
> -	if (S_ISREG(st_mode) && (force_executable || force_notexecutable)) {
> -		if (force_executable)
> -			ce->ce_mode = create_ce_mode(0777);
> -		else
> -			ce->ce_mode = create_ce_mode(0666);
> -	} else if (trust_executable_bit && has_symlinks) {
> +	if (S_ISREG(st_mode) && force_mode)
> +		ce->ce_mode = create_ce_mode(force_mode);
> +	else if (trust_executable_bit && has_symlinks)
>  		ce->ce_mode = create_ce_mode(st_mode);
> -	} else {
> +	else {
>  		/* If there is an existing entry, pick the mode bits and type
>  		 * from it, otherwise assume unexecutable regular file.
>  		 */
> @@ -727,12 +722,13 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
>  	return 0;
>  }
>  
> -int add_file_to_index(struct index_state *istate, const char *path, int flags)
> +int add_file_to_index(struct index_state *istate, const char *path,
> +	int flags, int force_mode)
>  {
>  	struct stat st;
>  	if (lstat(path, &st))
>  		die_errno("unable to stat '%s'", path);
> -	return add_to_index(istate, path, &st, flags);
> +	return add_to_index(istate, path, &st, flags, force_mode);
>  }
>  
>  struct cache_entry *make_cache_entry(unsigned int mode,
Edward Thomson· Jun 7, 2016, 22:59 UTC · re: Junio C Hamano · lore

Re: [PATCH] add: add --chmod=+x / --chmod=-x options

On Wed, Jun 01, 2016 at 09:00:34AM -0700, Junio C Hamano wrote:
> 
> Unlike the "something like this" we saw earlier, this draws the
> boundary of responsibility between the caller and the API at a much
> more sensible place.

This makes sense to me - Junio, are you taking (or have you already taken) dscho's patch, or would you like me to squash it and resend?

Thanks- -ed

Junio C Hamano· Jun 8, 2016, 00:39 UTC · re: Edward Thomson · lore

Re: [PATCH] add: add --chmod=+x / --chmod=-x options

Edward Thomson <ethomson@edwardthomson.com> writes:
Show 8 quoted lines
> On Wed, Jun 01, 2016 at 09:00:34AM -0700, Junio C Hamano wrote:
>> 
>> Unlike the "something like this" we saw earlier, this draws the
>> boundary of responsibility between the caller and the API at a much
>> more sensible place.
>
> This makes sense to me - Junio, are you taking (or have you already
> taken) dscho's patch, or would you like me to squash it and resend?

I didn't plan to unilaterally squash them into one patch without hearing from you, so I haven't--Dscho's fix-up is queued directly on top, ready to be squashed in.

So either is fine by me, either you send a final version to replace the two patches on et/add-chmod-x topic, or you just tell me to go ahead.

Well, you practically said the latter already, so I'll do the squashing. Thanks.

Duy Nguyen· Jun 8, 2016, 11:46 UTC · re: Edward Thomson · lore

Re: [PATCH] add: add --chmod=+x / --chmod=-x options

On Wed, Jun 1, 2016 at 5:08 AM, Edward Thomson <ethomson@edwardthomson.com> wrote:

Show 5 quoted lines
> @@ -263,6 +265,7 @@ 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")),
> +       OPT_STRING( 0 , "chmod", &chmod_arg, N_("(+/-)x"), N_("override the executable bit of the listed files")),
If this is only about +/-x, would --[no-]executable be a better option name?
-- 
Duy

← back to recent threads