git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
* [PATCH] git-apply: apply submodule changes
@ 2007-08-10  9:30 Sven Verdoolaege
  2007-08-10 12:34 ` Johannes Schindelin
                   ` (3 more replies)
  0 siblings, 4 replies; 19+ messages in thread
From: Sven Verdoolaege @ 2007-08-10  9:30 UTC (permalink / raw
  To: Junio C Hamano, git; +Cc: Steffen Prohaska

Apply "Subproject commit HEX" changes produced by git-diff.
As usual in the current git, only the superproject itself is actually
modified (possibly creating empty directories for new submodules).

Signed-off-by: Sven Verdoolaege <skimo@kotnet.org>
---
This also makes rebase handle submodules.

 builtin-apply.c |   49 ++++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 38 insertions(+), 11 deletions(-)

diff --git a/builtin-apply.c b/builtin-apply.c
index da27075..24df282 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -1984,6 +1984,26 @@ static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
 	return 0;
 }
 
+static int read_file_or_gitlink(struct cache_entry *ce, char **buf_p,
+				unsigned long *size_p)
+{
+	if (!ce)
+		return 0;
+
+	if (S_ISGITLINK(ntohl(ce->ce_mode))) {
+		*buf_p = xmalloc(100);
+		*size_p = snprintf(*buf_p, 100,
+			"Subproject commit %s\n", sha1_to_hex(ce->sha1));
+	} else {
+		enum object_type type;
+		*buf_p = read_sha1_file(ce->sha1, &type, size_p);
+		if (!*buf_p)
+			return -1;
+	}
+
+	return 0;
+}
+
 static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
 {
 	char *buf;
@@ -1994,20 +2014,18 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
 	alloc = 0;
 	buf = NULL;
 	if (cached) {
-		if (ce) {
-			enum object_type type;
-			buf = read_sha1_file(ce->sha1, &type, &size);
-			if (!buf)
-				return error("read of %s failed",
-					     patch->old_name);
-			alloc = size;
-		}
+		if (read_file_or_gitlink(ce, &buf, &size))
+			return error("read of %s failed", patch->old_name);
+		alloc = size;
 	}
 	else if (patch->old_name) {
 		size = xsize_t(st->st_size);
 		alloc = size + 8192;
 		buf = xmalloc(alloc);
-		if (read_old_data(st, patch->old_name, &buf, &alloc, &size))
+		if (S_ISGITLINK(patch->old_mode))
+			size = snprintf(buf, alloc,
+				"Subproject commit %s\n", sha1_to_hex(ce->sha1));
+		else if (read_old_data(st, patch->old_name, &buf, &alloc, &size))
 			return error("read of %s failed", patch->old_name);
 	}
 
@@ -2098,7 +2116,7 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
 			}
 			if (!cached)
 				changed = ce_match_stat(ce, &st, 1);
-			if (changed)
+			if (changed && !S_ISGITLINK(patch->old_mode))
 				return error("%s: does not match index",
 					     old_name);
 			if (cached)
@@ -2387,7 +2405,9 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
 			die("unable to stat newly created file %s", path);
 		fill_stat_cache_info(ce, &st);
 	}
-	if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0)
+	if (S_ISGITLINK(mode))
+		get_sha1_hex(buf + strlen("Subproject commit "), ce->sha1);
+	else if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0)
 		die("unable to create backing store for newly created file %s", path);
 	if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)
 		die("unable to add cache entry for %s", path);
@@ -2398,6 +2418,13 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
 	int fd;
 	char *nbuf;
 
+	if (S_ISGITLINK(mode)) {
+		struct stat st;
+		if (!lstat(path, &st) && S_ISDIR(st.st_mode))
+			return 0;
+		return mkdir(path, 0777);
+	}
+
 	if (has_symlinks && S_ISLNK(mode))
 		/* Although buf:size is counted string, it also is NUL
 		 * terminated.
-- 
1.5.3.rc4.29.g74276-dirty

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH] git-apply: apply submodule changes
  2007-08-10  9:30 [PATCH] git-apply: apply submodule changes Sven Verdoolaege
@ 2007-08-10 12:34 ` Johannes Schindelin
  2007-08-10 12:39 ` Johannes Schindelin
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 19+ messages in thread
From: Johannes Schindelin @ 2007-08-10 12:34 UTC (permalink / raw
  To: Sven Verdoolaege; +Cc: Junio C Hamano, git, Steffen Prohaska

Hi,

On Fri, 10 Aug 2007, Sven Verdoolaege wrote:

> Apply "Subproject commit HEX" changes produced by git-diff.
> As usual in the current git, only the superproject itself is actually
> modified (possibly creating empty directories for new submodules).

For rebase and cherry-pick, it would be nice if git just ignored the 
changes in the submodules, provided that the submodule commit was not 
affected by the to-be-applied patches.

Hmm?

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH] git-apply: apply submodule changes
  2007-08-10  9:30 [PATCH] git-apply: apply submodule changes Sven Verdoolaege
  2007-08-10 12:34 ` Johannes Schindelin
@ 2007-08-10 12:39 ` Johannes Schindelin
  2007-08-10 13:57 ` [PATCH resend] " Sven Verdoolaege
  2007-08-12 14:23 ` [PATCH v3] " Sven Verdoolaege
  3 siblings, 0 replies; 19+ messages in thread
From: Johannes Schindelin @ 2007-08-10 12:39 UTC (permalink / raw
  To: Sven Verdoolaege; +Cc: Junio C Hamano, git, Steffen Prohaska

Hi,

On Fri, 10 Aug 2007, Sven Verdoolaege wrote:

> @@ -2387,7 +2405,9 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
>  			die("unable to stat newly created file %s", path);
>  		fill_stat_cache_info(ce, &st);
>  	}
> -	if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0)
> +	if (S_ISGITLINK(mode))
> +		get_sha1_hex(buf + strlen("Subproject commit "), ce->sha1);
> +	else if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0)
>  		die("unable to create backing store for newly created file %s", path);
>  	if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)
>  		die("unable to add cache entry for %s", path);

I guess that you need to catch an error from get_sha1_hex(), too.

I hope it is not to much to ask for a patch to t7400 to show what this 
patch fixes?

Ciao,
Dscho

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH resend] git-apply: apply submodule changes
  2007-08-10  9:30 [PATCH] git-apply: apply submodule changes Sven Verdoolaege
  2007-08-10 12:34 ` Johannes Schindelin
  2007-08-10 12:39 ` Johannes Schindelin
@ 2007-08-10 13:57 ` Sven Verdoolaege
  2007-08-11  5:43   ` Junio C Hamano
  2007-08-12 14:23 ` [PATCH v3] " Sven Verdoolaege
  3 siblings, 1 reply; 19+ messages in thread
From: Sven Verdoolaege @ 2007-08-10 13:57 UTC (permalink / raw
  To: Junio C Hamano, git; +Cc: Steffen Prohaska, Johannes.Schindelin

Apply "Subproject commit HEX" changes produced by git-diff.
As usual in the current git, only the superproject itself is actually
modified (possibly creating empty directories for new submodules).
Any checked-out submodule is left untouched and is not required to
be up-to-date.

Signed-off-by: Sven Verdoolaege <skimo@kotnet.org>
---
This second version has a test and an extra sanity check.

I seem to be experiencing some problems receiving emails,
so I'll reply to a message from Dscho here.

Johannes Schindelin <Johannes.Schindelin <at> gmx.de> writes:
> For rebase and cherry-pick, it would be nice if git just ignored the 
> changes in the submodules, provided that the submodule commit was not 
> affected by the to-be-applied patches.

I have no idea what you mean.

The checked out copies of the submodules are ignored completely
(if that is what you were talking about, then I hope this issue
is clarified by the updated commit message).  In the superproject,
the change to the submodule is obviously not ignored, since it's
an integral part of the patch.  However, git-apply will fail if
the original submodule commit does not correspond exactly to the
"from-file" submodule commit.
I don't think there is anything else we can do without a true
recursive git-diff/git-apply.

skimo

 builtin-apply.c            |   50 ++++++++++++++++++++++++++++++++++---------
 t/t7400-submodule-basic.sh |    8 +++++++
 2 files changed, 47 insertions(+), 11 deletions(-)

diff --git a/builtin-apply.c b/builtin-apply.c
index da27075..a38dbf1 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -1984,6 +1984,26 @@ static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
 	return 0;
 }
 
+static int read_file_or_gitlink(struct cache_entry *ce, char **buf_p,
+				unsigned long *size_p)
+{
+	if (!ce)
+		return 0;
+
+	if (S_ISGITLINK(ntohl(ce->ce_mode))) {
+		*buf_p = xmalloc(100);
+		*size_p = snprintf(*buf_p, 100,
+			"Subproject commit %s\n", sha1_to_hex(ce->sha1));
+	} else {
+		enum object_type type;
+		*buf_p = read_sha1_file(ce->sha1, &type, size_p);
+		if (!*buf_p)
+			return -1;
+	}
+
+	return 0;
+}
+
 static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
 {
 	char *buf;
@@ -1994,20 +2014,18 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
 	alloc = 0;
 	buf = NULL;
 	if (cached) {
-		if (ce) {
-			enum object_type type;
-			buf = read_sha1_file(ce->sha1, &type, &size);
-			if (!buf)
-				return error("read of %s failed",
-					     patch->old_name);
-			alloc = size;
-		}
+		if (read_file_or_gitlink(ce, &buf, &size))
+			return error("read of %s failed", patch->old_name);
+		alloc = size;
 	}
 	else if (patch->old_name) {
 		size = xsize_t(st->st_size);
 		alloc = size + 8192;
 		buf = xmalloc(alloc);
-		if (read_old_data(st, patch->old_name, &buf, &alloc, &size))
+		if (S_ISGITLINK(patch->old_mode))
+			size = snprintf(buf, alloc,
+				"Subproject commit %s\n", sha1_to_hex(ce->sha1));
+		else if (read_old_data(st, patch->old_name, &buf, &alloc, &size))
 			return error("read of %s failed", patch->old_name);
 	}
 
@@ -2098,7 +2116,7 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
 			}
 			if (!cached)
 				changed = ce_match_stat(ce, &st, 1);
-			if (changed)
+			if (changed && !S_ISGITLINK(patch->old_mode))
 				return error("%s: does not match index",
 					     old_name);
 			if (cached)
@@ -2387,7 +2405,10 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
 			die("unable to stat newly created file %s", path);
 		fill_stat_cache_info(ce, &st);
 	}
-	if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0)
+	if (S_ISGITLINK(mode)) {
+		if (get_sha1_hex(buf + strlen("Subproject commit "), ce->sha1))
+			die("corrupt patch for subproject %s", path);
+	} else if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0)
 		die("unable to create backing store for newly created file %s", path);
 	if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)
 		die("unable to add cache entry for %s", path);
@@ -2398,6 +2419,13 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
 	int fd;
 	char *nbuf;
 
+	if (S_ISGITLINK(mode)) {
+		struct stat st;
+		if (!lstat(path, &st) && S_ISDIR(st.st_mode))
+			return 0;
+		return mkdir(path, 0777);
+	}
+
 	if (has_symlinks && S_ISLNK(mode))
 		/* Although buf:size is counted string, it also is NUL
 		 * terminated.
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index e8ce7cd..cede2e7 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -175,4 +175,12 @@ test_expect_success 'checkout superproject with subproject already present' '
 	git-checkout master
 '
 
+test_expect_success 'rebase with subproject changes' '
+	git-checkout initial &&
+	echo t > t &&
+	git add t &&
+	git-commit -m "change t" &&
+	git-rebase HEAD master
+'
+
 test_done
-- 
1.5.3.rc4.29.g74276-dirty

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH resend] git-apply: apply submodule changes
  2007-08-10 13:57 ` [PATCH resend] " Sven Verdoolaege
@ 2007-08-11  5:43   ` Junio C Hamano
  2007-08-11  6:45     ` Sven Verdoolaege
  0 siblings, 1 reply; 19+ messages in thread
From: Junio C Hamano @ 2007-08-11  5:43 UTC (permalink / raw
  To: skimo; +Cc: git, Steffen Prohaska, Johannes.Schindelin

Sven Verdoolaege <skimo@kotnet.org> writes:

> @@ -1994,20 +2014,18 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
>  	alloc = 0;
>  	buf = NULL;
>  	if (cached) {
> -		if (ce) {
> -			enum object_type type;
> -			buf = read_sha1_file(ce->sha1, &type, &size);
> -			if (!buf)
> -				return error("read of %s failed",
> -					     patch->old_name);
> -			alloc = size;
> -		}
> +		if (read_file_or_gitlink(ce, &buf, &size))
> +			return error("read of %s failed", patch->old_name);
> +		alloc = size;
>  	}
>  	else if (patch->old_name) {
>  		size = xsize_t(st->st_size);
>  		alloc = size + 8192;
>  		buf = xmalloc(alloc);
> -		if (read_old_data(st, patch->old_name, &buf, &alloc, &size))
> +		if (S_ISGITLINK(patch->old_mode))
> +			size = snprintf(buf, alloc,
> +				"Subproject commit %s\n", sha1_to_hex(ce->sha1));
> +		else if (read_old_data(st, patch->old_name, &buf, &alloc, &size))

Who guarantees that ce is given to apply_data() in this codepath?

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH resend] git-apply: apply submodule changes
  2007-08-11  5:43   ` Junio C Hamano
@ 2007-08-11  6:45     ` Sven Verdoolaege
  2007-08-11  7:00       ` Junio C Hamano
  0 siblings, 1 reply; 19+ messages in thread
From: Sven Verdoolaege @ 2007-08-11  6:45 UTC (permalink / raw
  To: Junio C Hamano; +Cc: git, Steffen Prohaska, Johannes.Schindelin

On Fri, Aug 10, 2007 at 10:43:42PM -0700, Junio C Hamano wrote:
> Sven Verdoolaege <skimo@kotnet.org> writes:
> >  	else if (patch->old_name) {
> >  		size = xsize_t(st->st_size);
> >  		alloc = size + 8192;
> >  		buf = xmalloc(alloc);
> > -		if (read_old_data(st, patch->old_name, &buf, &alloc, &size))
> > +		if (S_ISGITLINK(patch->old_mode))
> > +			size = snprintf(buf, alloc,
> > +				"Subproject commit %s\n", sha1_to_hex(ce->sha1));
> > +		else if (read_old_data(st, patch->old_name, &buf, &alloc, &size))
> 
> Who guarantees that ce is given to apply_data() in this codepath?

Oops.  I guess it shows that I only tested it through git-rebase.
Would changing

		if (check_index) {

on line 2093 to

		if (check_index || S_ISGITLINK(patch->old_mode)) {

be acceptable?  Adding a conditional call to read_cache(), of course.
We're not going to be able to apply submodule patches without
an index, anyway.
Or should we just refuse to apply submodule patches if --index
has not been specified?

skimo

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH resend] git-apply: apply submodule changes
  2007-08-11  6:45     ` Sven Verdoolaege
@ 2007-08-11  7:00       ` Junio C Hamano
  0 siblings, 0 replies; 19+ messages in thread
From: Junio C Hamano @ 2007-08-11  7:00 UTC (permalink / raw
  To: Sven Verdoolaege; +Cc: git, Steffen Prohaska, Johannes.Schindelin

Sven Verdoolaege <skimo@liacs.nl> writes:

> Or should we just refuse to apply submodule patches if --index
> has not been specified?

If somebody sends you a patch that adds a new gitlink, I think
it is very natural to create a directory (or make sure a
directory exists) there when applying without --index.

My gut feeling is that it would probably be the most user
friendly to just warn but ignore if you do not have submodule
there and a patch wants to modify that path.  After all, the
fundamental idea behind our submodule support is that you as a
superproject person should not even have to have the submodule
checked out.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v3] git-apply: apply submodule changes
  2007-08-10  9:30 [PATCH] git-apply: apply submodule changes Sven Verdoolaege
                   ` (2 preceding siblings ...)
  2007-08-10 13:57 ` [PATCH resend] " Sven Verdoolaege
@ 2007-08-12 14:23 ` Sven Verdoolaege
  2007-08-12 18:16   ` Junio C Hamano
  3 siblings, 1 reply; 19+ messages in thread
From: Sven Verdoolaege @ 2007-08-12 14:23 UTC (permalink / raw
  To: Junio C Hamano, git; +Cc: Steffen Prohaska, Johannes.Schindelin

Apply "Subproject commit HEX" changes produced by git-diff.
As usual in the current git, only the superproject itself is actually
modified (possibly creating empty directories for new submodules).
Any checked-out submodule is left untouched and is not required to
be up-to-date.

Signed-off-by: Sven Verdoolaege <skimo@kotnet.org>
---
The third version also works without --index and documents
the behaviour of git-apply in the presence of submodule changes.

skimo

 Documentation/git-apply.txt |   14 +++++++++
 builtin-apply.c             |   69 +++++++++++++++++++++++++++++++++++-------
 t/t7400-submodule-basic.sh  |    8 +++++
 3 files changed, 79 insertions(+), 12 deletions(-)

diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt
index f03f661..804fdc3 100644
--- a/Documentation/git-apply.txt
+++ b/Documentation/git-apply.txt
@@ -171,6 +171,20 @@ apply.whitespace::
 	When no `--whitespace` flag is given from the command
 	line, this configuration item is used as the default.
 
+Submodules
+----------
+If the patch contains any changes to submodules then gitlink:git-apply[1]
+behaves as follows.
+
+If --index is specified (explicitly or implicitly), then the submodule
+commits must match the index exactly for the patch to apply.  If any
+of the submodules are checked-out, then these check-outs are completely
+ignored, i.e., they are not required to be up-to-date or clean and they
+are not updated.
+
+If --index is not specified, then the submodule commits in the patch
+are ignored and only the absence of presence of the corresponding
+subdirectory is checked and (if possible) updated.
 
 Author
 ------
diff --git a/builtin-apply.c b/builtin-apply.c
index da27075..eef596b 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -1984,6 +1984,40 @@ static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
 	return 0;
 }
 
+static int read_file_or_gitlink(struct cache_entry *ce, char **buf_p,
+				unsigned long *size_p)
+{
+	if (!ce)
+		return 0;
+
+	if (S_ISGITLINK(ntohl(ce->ce_mode))) {
+		*buf_p = xmalloc(100);
+		*size_p = snprintf(*buf_p, 100,
+			"Subproject commit %s\n", sha1_to_hex(ce->sha1));
+	} else {
+		enum object_type type;
+		*buf_p = read_sha1_file(ce->sha1, &type, size_p);
+		if (!*buf_p)
+			return -1;
+	}
+
+	return 0;
+}
+
+static int read_gitlink_or_skip(struct patch *patch, struct cache_entry *ce,
+				char *buf, unsigned long alloc)
+{
+	if (ce)
+		return snprintf(buf, alloc,
+				"Subproject commit %s\n", sha1_to_hex(ce->sha1));
+
+	/* We can't apply the submodule change without an index, so just
+	 * skip the patch itself and only create/remove directory.
+	 */
+	patch->fragments = NULL;
+	return 0;
+}
+
 static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
 {
 	char *buf;
@@ -1994,20 +2028,17 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
 	alloc = 0;
 	buf = NULL;
 	if (cached) {
-		if (ce) {
-			enum object_type type;
-			buf = read_sha1_file(ce->sha1, &type, &size);
-			if (!buf)
-				return error("read of %s failed",
-					     patch->old_name);
-			alloc = size;
-		}
+		if (read_file_or_gitlink(ce, &buf, &size))
+			return error("read of %s failed", patch->old_name);
+		alloc = size;
 	}
 	else if (patch->old_name) {
 		size = xsize_t(st->st_size);
 		alloc = size + 8192;
 		buf = xmalloc(alloc);
-		if (read_old_data(st, patch->old_name, &buf, &alloc, &size))
+		if (S_ISGITLINK(patch->old_mode))
+			size = read_gitlink_or_skip(patch, ce, buf, alloc);
+		else if (read_old_data(st, patch->old_name, &buf, &alloc, &size))
 			return error("read of %s failed", patch->old_name);
 	}
 
@@ -2098,7 +2129,7 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
 			}
 			if (!cached)
 				changed = ce_match_stat(ce, &st, 1);
-			if (changed)
+			if (changed && !S_ISGITLINK(patch->old_mode))
 				return error("%s: does not match index",
 					     old_name);
 			if (cached)
@@ -2354,7 +2385,11 @@ static void remove_file(struct patch *patch, int rmdir_empty)
 		cache_tree_invalidate_path(active_cache_tree, patch->old_name);
 	}
 	if (!cached) {
-		if (!unlink(patch->old_name) && rmdir_empty) {
+		if (S_ISGITLINK(patch->old_mode)) {
+			if (rmdir(patch->old_name))
+				warning("unable to remove submodule %s",
+					patch->old_name);
+		} else if (!unlink(patch->old_name) && rmdir_empty) {
 			char *name = xstrdup(patch->old_name);
 			char *end = strrchr(name, '/');
 			while (end) {
@@ -2387,7 +2422,10 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
 			die("unable to stat newly created file %s", path);
 		fill_stat_cache_info(ce, &st);
 	}
-	if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0)
+	if (S_ISGITLINK(mode)) {
+		if (get_sha1_hex(buf + strlen("Subproject commit "), ce->sha1))
+			die("corrupt patch for subproject %s", path);
+	} else if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0)
 		die("unable to create backing store for newly created file %s", path);
 	if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)
 		die("unable to add cache entry for %s", path);
@@ -2398,6 +2436,13 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
 	int fd;
 	char *nbuf;
 
+	if (S_ISGITLINK(mode)) {
+		struct stat st;
+		if (!lstat(path, &st) && S_ISDIR(st.st_mode))
+			return 0;
+		return mkdir(path, 0777);
+	}
+
 	if (has_symlinks && S_ISLNK(mode))
 		/* Although buf:size is counted string, it also is NUL
 		 * terminated.
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index e8ce7cd..cede2e7 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -175,4 +175,12 @@ test_expect_success 'checkout superproject with subproject already present' '
 	git-checkout master
 '
 
+test_expect_success 'rebase with subproject changes' '
+	git-checkout initial &&
+	echo t > t &&
+	git add t &&
+	git-commit -m "change t" &&
+	git-rebase HEAD master
+'
+
 test_done
-- 
1.5.3.rc4.30.gd0c97-dirty

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH v3] git-apply: apply submodule changes
  2007-08-12 14:23 ` [PATCH v3] " Sven Verdoolaege
@ 2007-08-12 18:16   ` Junio C Hamano
  2007-08-12 18:50     ` Sven Verdoolaege
  0 siblings, 1 reply; 19+ messages in thread
From: Junio C Hamano @ 2007-08-12 18:16 UTC (permalink / raw
  To: skimo; +Cc: git, Steffen Prohaska, Johannes.Schindelin

Sven Verdoolaege <skimo@kotnet.org> writes:

> diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt
> index f03f661..804fdc3 100644
> --- a/Documentation/git-apply.txt
> +++ b/Documentation/git-apply.txt
> @@ -171,6 +171,20 @@ apply.whitespace::
>  	When no `--whitespace` flag is given from the command
>  	line, this configuration item is used as the default.
>  
> +Submodules
> +----------
> +If the patch contains any changes to submodules then gitlink:git-apply[1]
> +behaves as follows.

perhaps "as follows wrt the submodules"...

> diff --git a/builtin-apply.c b/builtin-apply.c
> index da27075..eef596b 100644
> --- a/builtin-apply.c
> +++ b/builtin-apply.c
> @@ -1984,6 +1984,40 @@ static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
>  	return 0;
>  }
>  
> +static int read_file_or_gitlink(struct cache_entry *ce, char **buf_p,
> +				unsigned long *size_p)
> +{
> +	if (!ce)
> +		return 0;
> +
> +	if (S_ISGITLINK(ntohl(ce->ce_mode))) {
> +		*buf_p = xmalloc(100);
> +		*size_p = snprintf(*buf_p, 100,
> +			"Subproject commit %s\n", sha1_to_hex(ce->sha1));
> +	} else {
> +		enum object_type type;
> +		*buf_p = read_sha1_file(ce->sha1, &type, size_p);
> +		if (!*buf_p)
> +			return -1;
> +	}
> +
> +	return 0;
> +}

Ok, read_file_or_gitlink() expects ce taken from the current
index and fills *buf_p with the preimage to be patched from it.

> +static int read_gitlink_or_skip(struct patch *patch, struct cache_entry *ce,
> +				char *buf, unsigned long alloc)
> +{
> +	if (ce)
> +		return snprintf(buf, alloc,
> +				"Subproject commit %s\n", sha1_to_hex(ce->sha1));
> +
> +	/* We can't apply the submodule change without an index, so just
> +	 * skip the patch itself and only create/remove directory.
> +	 */
> +	patch->fragments = NULL;
> +	return 0;
> +}

Hmmmm...  see below.

>  static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
>  {
>  	char *buf;
> @@ -1994,20 +2028,17 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
>  	alloc = 0;
>  	buf = NULL;
>  	if (cached) {
> +		if (read_file_or_gitlink(ce, &buf, &size))
> +			return error("read of %s failed", patch->old_name);
> +		alloc = size;
>  	}

This part is consistent with the read_file_or_gitlink()
semantics above...

>  	else if (patch->old_name) {
>  		size = xsize_t(st->st_size);
>  		alloc = size + 8192;
>  		buf = xmalloc(alloc);
> -		if (read_old_data(st, patch->old_name, &buf, &alloc, &size))
> +		if (S_ISGITLINK(patch->old_mode))
> +			size = read_gitlink_or_skip(patch, ce, buf, alloc);
> +		else if (read_old_data(st, patch->old_name, &buf, &alloc, &size))
>  			return error("read of %s failed", patch->old_name);
>  	}

read_old_data() gets the lstat information from the current
filesystem data at old_name, and gives the preimage to be
patched, and naturally it bombs out if it is a directory, but
when we are applying a change to gitlink, the patch expects
old_name to be a directory.

So you introduced read_gitlink_or_skip() to work it around.  But
this makes me wonder...

 - what does ce have to do in this codepath?  read_old_data()
   does not care about what is in the index (in fact, in the
   index the entry can be a symlink when the path on the
   filesystem is a regular file, and it reads from the regular
   file as asked--it does not even look at ce by design).  
   if you have a regular file there in the current version, ce
   would say it is a regular file blob and you would not want
   read_gitlink_or_skip() to say "Subproject commit xyz...".

 - what is alloc at this point?  it is based on the size of
   directory st->st_size.

I think dropping fragments for a patch that tries to modify a
gitlink here is fine, but that can be done regardless of what ce
is.

The type-mismatch case to attempt to apply gitlink patch to a
regular blob is covered much earlier in check_patch().  It
complains if st_mode does not match patch->old_mode; I think you
need to adjust it a bit to:

 - allow gitlink patch to a path that currently has nothing (no
   submodule checked out) or a directory that has ".git/"
   (i.e. submodule checked out).

 - reject gitlink patch otherwise.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v3] git-apply: apply submodule changes
  2007-08-12 18:16   ` Junio C Hamano
@ 2007-08-12 18:50     ` Sven Verdoolaege
  2007-08-12 19:24       ` Junio C Hamano
  0 siblings, 1 reply; 19+ messages in thread
From: Sven Verdoolaege @ 2007-08-12 18:50 UTC (permalink / raw
  To: Junio C Hamano; +Cc: git, Steffen Prohaska, Johannes.Schindelin

On Sun, Aug 12, 2007 at 11:16:09AM -0700, Junio C Hamano wrote:
> Sven Verdoolaege <skimo@kotnet.org> writes:
> >  	else if (patch->old_name) {
> >  		size = xsize_t(st->st_size);
> >  		alloc = size + 8192;
> >  		buf = xmalloc(alloc);
> > -		if (read_old_data(st, patch->old_name, &buf, &alloc, &size))
> > +		if (S_ISGITLINK(patch->old_mode))
> > +			size = read_gitlink_or_skip(patch, ce, buf, alloc);
> > +		else if (read_old_data(st, patch->old_name, &buf, &alloc, &size))
> >  			return error("read of %s failed", patch->old_name);
> >  	}
> 
> read_old_data() gets the lstat information from the current
> filesystem data at old_name, and gives the preimage to be
> patched, and naturally it bombs out if it is a directory, but
> when we are applying a change to gitlink, the patch expects
> old_name to be a directory.
> 
> So you introduced read_gitlink_or_skip() to work it around.  But
> this makes me wonder...
> 
>  - what does ce have to do in this codepath?  read_old_data()
>    does not care about what is in the index (in fact, in the
>    index the entry can be a symlink when the path on the
>    filesystem is a regular file, and it reads from the regular
>    file as asked--it does not even look at ce by design).  
>    if you have a regular file there in the current version, ce
>    would say it is a regular file blob and you would not want
>    read_gitlink_or_skip() to say "Subproject commit xyz...".

Hmmm... the documentation says that if --index is in effect
then the file to be patched in the work tree is supposed to be 
up-to-date.  Then for files it shouldn't matter if the data
comes from the index or not.  For submodules it's crucial to
look at the index (if it is available), since that's the
only place you can find the current state of the submodule.
(Remember that git currently doesn't automatically update submodules.)

If I specify --index (e.g., through git rebase), then I really
wouldn't want the patch to apply if the old submodule commit
in the patch doesn't match the submodule commit in the index.
Would you?

>  - what is alloc at this point?  it is based on the size of
>    directory st->st_size.

I assume you mean "size".  For submodules, the value isn't used,
except to test that the "file" is empty (size==0) on delete.
So it seems safe to set it to zero for submodules in any case.

> I think dropping fragments for a patch that tries to modify a
> gitlink here is fine, but that can be done regardless of what ce
> is.

As explained above, I don't think it would a good idea to
just drop gitlink patches if --index is specified.

> The type-mismatch case to attempt to apply gitlink patch to a
> regular blob is covered much earlier in check_patch().  It
> complains if st_mode does not match patch->old_mode; I think you
> need to adjust it a bit to:
> 
>  - allow gitlink patch to a path that currently has nothing (no
>    submodule checked out) or a directory that has ".git/"
>    (i.e. submodule checked out).
> 
>  - reject gitlink patch otherwise.

Are you talking about the case where --index is specified?
Otherwise, I don't think we can make any assumptions on
what's inside the subdirectory.

skimo

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v3] git-apply: apply submodule changes
  2007-08-12 18:50     ` Sven Verdoolaege
@ 2007-08-12 19:24       ` Junio C Hamano
  2007-08-13  9:37         ` Sven Verdoolaege
  0 siblings, 1 reply; 19+ messages in thread
From: Junio C Hamano @ 2007-08-12 19:24 UTC (permalink / raw
  To: skimo; +Cc: git, Steffen Prohaska, Johannes.Schindelin

Sven Verdoolaege <skimo@kotnet.org> writes:

>>  - what does ce have to do in this codepath?  read_old_data()
>>    does not care about what is in the index (in fact, in the
>>    index the entry can be a symlink when the path on the
>>    filesystem is a regular file, and it reads from the regular
>>    file as asked--it does not even look at ce by design).  
>>    if you have a regular file there in the current version, ce
>>    would say it is a regular file blob and you would not want
>>    read_gitlink_or_skip() to say "Subproject commit xyz...".
>
> Hmmm... the documentation says that if --index is in effect
> then the file to be patched in the work tree is supposed to be 
> up-to-date.

But that is the job of check_patch(), not this function, isn't it?

>> The type-mismatch case to attempt to apply gitlink patch to a
>> regular blob is covered much earlier in check_patch().  It
>> complains if st_mode does not match patch->old_mode; I think you
>> need to adjust it a bit to:
>> 
>>  - allow gitlink patch to a path that currently has nothing (no
>>    submodule checked out) or a directory that has ".git/"
>>    (i.e. submodule checked out).
>> 
>>  - reject gitlink patch otherwise.
>
> Are you talking about the case where --index is specified?

Talking about both cases, and the division of responsibility
between check_patch() and apply_data().

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v3] git-apply: apply submodule changes
  2007-08-12 19:24       ` Junio C Hamano
@ 2007-08-13  9:37         ` Sven Verdoolaege
  2007-08-13 17:13           ` [PATCH v4] " Sven Verdoolaege
  0 siblings, 1 reply; 19+ messages in thread
From: Sven Verdoolaege @ 2007-08-13  9:37 UTC (permalink / raw
  To: Junio C Hamano; +Cc: git, Steffen Prohaska, Johannes.Schindelin

On Sun, Aug 12, 2007 at 12:24:29PM -0700, Junio C Hamano wrote:
> Sven Verdoolaege <skimo@kotnet.org> writes:
> 
> >>  - what does ce have to do in this codepath?  read_old_data()
> >>    does not care about what is in the index (in fact, in the
> >>    index the entry can be a symlink when the path on the
> >>    filesystem is a regular file, and it reads from the regular
> >>    file as asked--it does not even look at ce by design).  
> >>    if you have a regular file there in the current version, ce
> >>    would say it is a regular file blob and you would not want
> >>    read_gitlink_or_skip() to say "Subproject commit xyz...".
> >
> > Hmmm... the documentation says that if --index is in effect
> > then the file to be patched in the work tree is supposed to be 
> > up-to-date.
> 
> But that is the job of check_patch(), not this function, isn't it?

Ah... so you want me to check that there has been no type change
to the submodule in check_patch (and then I can use my
read_gitlink_or_skip as is).  Is that right?

> >> The type-mismatch case to attempt to apply gitlink patch to a
> >> regular blob is covered much earlier in check_patch().  It
> >> complains if st_mode does not match patch->old_mode; I think you
> >> need to adjust it a bit to:
> >> 
> >>  - allow gitlink patch to a path that currently has nothing (no
> >>    submodule checked out) or a directory that has ".git/"
> >>    (i.e. submodule checked out).
> >> 
> >>  - reject gitlink patch otherwise.
> >
> > Are you talking about the case where --index is specified?
> 
> Talking about both cases, and the division of responsibility
> between check_patch() and apply_data().

I'll do that for the --index case, but I really think it doesn't
make sense for the other case.  If we're not in a git repo,
then the submodule, which may very well be present, is not
going to be a git repo either.

What could make sense is to enforce that in the --index case,
the submodule directory is either empty or a git repo and
that in the non --index case it is empty.

skimo

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v4] git-apply: apply submodule changes
  2007-08-13  9:37         ` Sven Verdoolaege
@ 2007-08-13 17:13           ` Sven Verdoolaege
  2007-08-13 20:26             ` Junio C Hamano
  2007-08-14 20:00             ` [PATCH v4] " Junio C Hamano
  0 siblings, 2 replies; 19+ messages in thread
From: Sven Verdoolaege @ 2007-08-13 17:13 UTC (permalink / raw
  To: Junio C Hamano; +Cc: git, Steffen Prohaska, Johannes.Schindelin

Apply "Subproject commit HEX" changes produced by git-diff.
As usual in the current git, only the superproject itself is actually
modified (possibly creating empty directories for new submodules).
Any checked-out submodule is left untouched and is not required to
be up-to-date.

Signed-off-by: Sven Verdoolaege <skimo@kotnet.org>
---
This version adds an extra check to verify that a submodule
still looks like a submodule in the work tree.

 Documentation/git-apply.txt |   14 +++++++
 builtin-apply.c             |   91 +++++++++++++++++++++++++++++++++++++------
 t/t7400-submodule-basic.sh  |    8 ++++
 3 files changed, 101 insertions(+), 12 deletions(-)

diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt
index f03f661..4c7e3a2 100644
--- a/Documentation/git-apply.txt
+++ b/Documentation/git-apply.txt
@@ -171,6 +171,20 @@ apply.whitespace::
 	When no `--whitespace` flag is given from the command
 	line, this configuration item is used as the default.
 
+Submodules
+----------
+If the patch contains any changes to submodules then gitlink:git-apply[1]
+treats these changes as follows.
+
+If --index is specified (explicitly or implicitly), then the submodule
+commits must match the index exactly for the patch to apply.  If any
+of the submodules are checked-out, then these check-outs are completely
+ignored, i.e., they are not required to be up-to-date or clean and they
+are not updated.
+
+If --index is not specified, then the submodule commits in the patch
+are ignored and only the absence of presence of the corresponding
+subdirectory is checked and (if possible) updated.
 
 Author
 ------
diff --git a/builtin-apply.c b/builtin-apply.c
index da27075..8ba20a6 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -12,6 +12,7 @@
 #include "blob.h"
 #include "delta.h"
 #include "builtin.h"
+#include "refs.h"
 
 /*
  *  --check turns on checking that the working tree matches the
@@ -1984,6 +1985,40 @@ static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
 	return 0;
 }
 
+static int read_file_or_gitlink(struct cache_entry *ce, char **buf_p,
+				unsigned long *size_p)
+{
+	if (!ce)
+		return 0;
+
+	if (S_ISGITLINK(ntohl(ce->ce_mode))) {
+		*buf_p = xmalloc(100);
+		*size_p = snprintf(*buf_p, 100,
+			"Subproject commit %s\n", sha1_to_hex(ce->sha1));
+	} else {
+		enum object_type type;
+		*buf_p = read_sha1_file(ce->sha1, &type, size_p);
+		if (!*buf_p)
+			return -1;
+	}
+
+	return 0;
+}
+
+static int read_gitlink_or_skip(struct patch *patch, struct cache_entry *ce,
+				char *buf, unsigned long alloc)
+{
+	if (ce)
+		return snprintf(buf, alloc,
+				"Subproject commit %s\n", sha1_to_hex(ce->sha1));
+
+	/* We can't apply the submodule change without an index, so just
+	 * skip the patch itself and only create/remove directory.
+	 */
+	patch->fragments = NULL;
+	return 0;
+}
+
 static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
 {
 	char *buf;
@@ -1994,20 +2029,17 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
 	alloc = 0;
 	buf = NULL;
 	if (cached) {
-		if (ce) {
-			enum object_type type;
-			buf = read_sha1_file(ce->sha1, &type, &size);
-			if (!buf)
-				return error("read of %s failed",
-					     patch->old_name);
-			alloc = size;
-		}
+		if (read_file_or_gitlink(ce, &buf, &size))
+			return error("read of %s failed", patch->old_name);
+		alloc = size;
 	}
 	else if (patch->old_name) {
 		size = xsize_t(st->st_size);
 		alloc = size + 8192;
 		buf = xmalloc(alloc);
-		if (read_old_data(st, patch->old_name, &buf, &alloc, &size))
+		if (S_ISGITLINK(patch->old_mode))
+			size = read_gitlink_or_skip(patch, ce, buf, alloc);
+		else if (read_old_data(st, patch->old_name, &buf, &alloc, &size))
 			return error("read of %s failed", patch->old_name);
 	}
 
@@ -2055,6 +2087,20 @@ static int check_to_create_blob(const char *new_name, int ok_if_exists)
 	return 0;
 }
 
+/* Check that the directory corresponding to a gitlink is either
+ * empty or a git repo.
+ */
+static int verify_gitlink_clean(const char *path)
+{
+	unsigned char sha1[20];
+
+	if (!rmdir(path)) {
+		mkdir(path, 0777);
+		return 0;
+	}
+	return resolve_gitlink_ref(path, "HEAD", sha1);
+}
+
 static int check_patch(struct patch *patch, struct patch *prev_patch)
 {
 	struct stat st;
@@ -2096,8 +2142,15 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
 				    lstat(old_name, &st))
 					return -1;
 			}
-			if (!cached)
+			if (!cached) {
 				changed = ce_match_stat(ce, &st, 1);
+				if (S_ISGITLINK(patch->old_mode)) {
+					changed &= TYPE_CHANGED;
+					if (!changed &&
+					    verify_gitlink_clean(patch->old_name))
+						changed |= TYPE_CHANGED;
+				}
+			}
 			if (changed)
 				return error("%s: does not match index",
 					     old_name);
@@ -2354,7 +2407,11 @@ static void remove_file(struct patch *patch, int rmdir_empty)
 		cache_tree_invalidate_path(active_cache_tree, patch->old_name);
 	}
 	if (!cached) {
-		if (!unlink(patch->old_name) && rmdir_empty) {
+		if (S_ISGITLINK(patch->old_mode)) {
+			if (rmdir(patch->old_name))
+				warning("unable to remove submodule %s",
+					patch->old_name);
+		} else if (!unlink(patch->old_name) && rmdir_empty) {
 			char *name = xstrdup(patch->old_name);
 			char *end = strrchr(name, '/');
 			while (end) {
@@ -2387,7 +2444,10 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
 			die("unable to stat newly created file %s", path);
 		fill_stat_cache_info(ce, &st);
 	}
-	if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0)
+	if (S_ISGITLINK(mode)) {
+		if (get_sha1_hex(buf + strlen("Subproject commit "), ce->sha1))
+			die("corrupt patch for subproject %s", path);
+	} else if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0)
 		die("unable to create backing store for newly created file %s", path);
 	if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)
 		die("unable to add cache entry for %s", path);
@@ -2398,6 +2458,13 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
 	int fd;
 	char *nbuf;
 
+	if (S_ISGITLINK(mode)) {
+		struct stat st;
+		if (!lstat(path, &st) && S_ISDIR(st.st_mode))
+			return 0;
+		return mkdir(path, 0777);
+	}
+
 	if (has_symlinks && S_ISLNK(mode))
 		/* Although buf:size is counted string, it also is NUL
 		 * terminated.
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index e8ce7cd..cede2e7 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -175,4 +175,12 @@ test_expect_success 'checkout superproject with subproject already present' '
 	git-checkout master
 '
 
+test_expect_success 'rebase with subproject changes' '
+	git-checkout initial &&
+	echo t > t &&
+	git add t &&
+	git-commit -m "change t" &&
+	git-rebase HEAD master
+'
+
 test_done
-- 
1.5.3.rc4.68.g4411e-dirty

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH v4] git-apply: apply submodule changes
  2007-08-13 17:13           ` [PATCH v4] " Sven Verdoolaege
@ 2007-08-13 20:26             ` Junio C Hamano
       [not found]               ` <7vd4xqeilh.fsf@assigned-by-dhcp.cox.net>
  2007-08-14 20:00             ` [PATCH v4] " Junio C Hamano
  1 sibling, 1 reply; 19+ messages in thread
From: Junio C Hamano @ 2007-08-13 20:26 UTC (permalink / raw
  To: skimo; +Cc: git, Steffen Prohaska, Johannes.Schindelin

Sven Verdoolaege <skimo@kotnet.org> writes:

> +static int read_gitlink_or_skip(struct patch *patch, struct cache_entry *ce,
> +				char *buf, unsigned long alloc)
> +{
> +	if (ce)
> +		return snprintf(buf, alloc,
> +				"Subproject commit %s\n", sha1_to_hex(ce->sha1));
> +
> +	/* We can't apply the submodule change without an index, so just
> +	 * skip the patch itself and only create/remove directory.
> +	 */
> +	patch->fragments = NULL;
> +	return 0;
> +}
> @@ -1994,20 +2029,17 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
>  	alloc = 0;
>  	buf = NULL;
>  	if (cached) {
> +		if (read_file_or_gitlink(ce, &buf, &size))
> +			return error("read of %s failed", patch->old_name);
> +		alloc = size;
> ...
>  	else if (patch->old_name) {
>  		size = xsize_t(st->st_size);
>  		alloc = size + 8192;
>  		buf = xmalloc(alloc);
> -		if (read_old_data(st, patch->old_name, &buf, &alloc, &size))
> +		if (S_ISGITLINK(patch->old_mode))
> +			size = read_gitlink_or_skip(patch, ce, buf, alloc);
> +		else if (read_old_data(st, patch->old_name, &buf, &alloc, &size))
>  			return error("read of %s failed", patch->old_name);
>  	}

I think the logic here sounds sane.  The read_file_or_gitlink()
abstraction in the first if() part was so nice that I was hoping
we could do something similar in this "else if" part, without
hiding the assignment to patch->fragments as an obscure side
effect of calling read_gitlink_or_skip().  That assignment is a
gitlink specific hack so it might be easier to read if this part
is written like:

	} else if (patch->old_name && S_ISGITLINK(patch->old_mode)) {
		if (ce)
                	size = snprintf(....)
		else {
                	/* we cannot apply gitlink mods without index */
			size = 0;
			patch->fragments = NULL;
		}
        } else if (patch->old_name) {
		... original code here ...

> @@ -2055,6 +2087,20 @@ static int check_to_create_blob(const char *new_name, int ok_if_exists)
>  	return 0;
>  }
>  
> +/* Check that the directory corresponding to a gitlink is either
> + * empty or a git repo.
> + */
> +static int verify_gitlink_clean(const char *path)
> +{
> +	unsigned char sha1[20];
> +
> +	if (!rmdir(path)) {
> +		mkdir(path, 0777);
> +		return 0;
> +	}
> +	return resolve_gitlink_ref(path, "HEAD", sha1);
> +}

Is it the responsibility of the caller of this function to make
sure that path is either absent or is a directory?  Can there be
a regular file at path, which would cause rmdir to fail?  I do
not think it is sensible to call resolve_gitlink_ref() on such a
path, so let's say the caller will make sure that path is
gitlink in the current (i.e. in index) tree before calling this
function.

>  static int check_patch(struct patch *patch, struct patch *prev_patch)
>  {
>  	struct stat st;
> @@ -2096,8 +2142,15 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
>  				    lstat(old_name, &st))
>  					return -1;
>  			}
> -			if (!cached)
> +			if (!cached) {
>  				changed = ce_match_stat(ce, &st, 1);

Here, ce_match_stat() sees if the index and work tree match.
You could have a regular file there but you haven't checked
(which is not a crime, yet).

> +				if (S_ISGITLINK(patch->old_mode)) {

I think the rmdir/mkdir sequence should be done only when ce is
a gitlink.  Perhaps it is just the matter of:

                                if (S_ISGITLINK(patch->old_mode) &&
                                    S_ISGITLINK(ntohl(ce->ce_mode))) {
                                        ...
                                }

> +					changed &= TYPE_CHANGED;
> +					if (!changed &&
> +					    verify_gitlink_clean(patch->old_name))
> +						changed |= TYPE_CHANGED;
> +				}
> +			}

This part is very confusing.  You discard all changes other than
TYPE_CHANGED, and give TYPE_CHANGED and nothing else if gitlink
is not clean.  I suspect "changed &= ~TYPE_CHANGED" might be
what you meant, but I do not know what you are trying to do
here.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4] git-apply: apply submodule changes
       [not found]               ` <7vd4xqeilh.fsf@assigned-by-dhcp.cox.net>
@ 2007-08-14  8:39                 ` Sven Verdoolaege
  2007-08-14  9:09                   ` Junio C Hamano
  0 siblings, 1 reply; 19+ messages in thread
From: Sven Verdoolaege @ 2007-08-14  8:39 UTC (permalink / raw
  To: Junio C Hamano; +Cc: git, Steffen Prohaska, Johannes.Schindelin

On Mon, Aug 13, 2007 at 11:27:38PM -0700, Junio C Hamano wrote:
>  * write_out_one_result() calls remove_file() and create_file()
>    to match the work tree to the result you prepared with
>    apply_data().
> 
>    - remove_file() is changed not to do any for gitlink.  We
>      _might_ want to try rmdir() if there is an otherwise empty
>      directory there, but currently we cannot do much to the
>      failure on that, so I did not bother with it.

We could at least warn about it, which is what my patch did.

>    - create_file() does three things:
>      - create a file in the work tree to match the result;
>      - update the index with the patch result;
>      - invalidate cache-tree entry for the path.
> 
>      For the first task, create_one_file() is usually used to
>      create a blob (either regular file or a symlink).  For
>      gitlinks, we do not affect the work tree for now, just like
>      checkout_entry().

It creates the subdirectory, though, and git-apply should do so
too since it expects the subdirectory to be there for subsequent
patches (at least in the --index case).

> diff --git a/builtin-apply.c b/builtin-apply.c

Did you remove the documentation on purpose ?

> +static int verify_index_match(struct cache_entry *ce, struct stat *st)
> +{
> +	if (!ce_match_stat(ce, st, 1))
> +		return 0;
> +	if (S_ISGITLINK(ntohl(ce->ce_mode))) {
> +		if (S_ISDIR(st->st_mode))
> +			return 0;
> +	}

Not a big deal, but ce_match_stat already checks for that.
That's why I was checking for TYPE_CHANGED in its return
value.

> @@ -2096,16 +2142,22 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
>  				    lstat(old_name, &st))
>  					return -1;
>  			}
> -			if (!cached)
> -				changed = ce_match_stat(ce, &st, 1);
> -			if (changed)
> +			if (!cached && verify_index_match(ce, &st))
>  				return error("%s: does not match index",
>  					     old_name);
>  			if (cached)
>  				st_mode = ntohl(ce->ce_mode);
> +		} else if (stat_ret < 0) {
> +			if (errno == ENOENT && S_ISGITLINK(patch->old_mode))
> +				/*
> +				 * It is Ok not to have the submodule
> +				 * checked out at all.
> +				 */
> +				;
> +			else
> +				return error("%s: %s", old_name,
> +					     strerror(errno));
>  		}

Shouldn't you be consistent with the --index case and require the
subdirectory to exist?

skimo

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4] git-apply: apply submodule changes
  2007-08-14  8:39                 ` Sven Verdoolaege
@ 2007-08-14  9:09                   ` Junio C Hamano
  2007-08-15 17:22                     ` [PATCH v6] " Sven Verdoolaege
  0 siblings, 1 reply; 19+ messages in thread
From: Junio C Hamano @ 2007-08-14  9:09 UTC (permalink / raw
  To: skimo; +Cc: git, Steffen Prohaska, Johannes.Schindelin

Sven Verdoolaege <skimo@kotnet.org> writes:

>> diff --git a/builtin-apply.c b/builtin-apply.c
>
> Did you remove the documentation on purpose ?

No, I just wanted to get a feedback on a (possibly partial)
cleanup, as I couldn't make heads or tails of your patch
especially around that TYPE_CHANGED part, and also the part to
write out the results.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4] git-apply: apply submodule changes
  2007-08-13 17:13           ` [PATCH v4] " Sven Verdoolaege
  2007-08-13 20:26             ` Junio C Hamano
@ 2007-08-14 20:00             ` Junio C Hamano
  1 sibling, 0 replies; 19+ messages in thread
From: Junio C Hamano @ 2007-08-14 20:00 UTC (permalink / raw
  To: skimo; +Cc: git, Steffen Prohaska, Johannes.Schindelin

Sven Verdoolaege <skimo@kotnet.org> writes:

> @@ -2096,8 +2142,15 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
>  				    lstat(old_name, &st))
>  					return -1;
>  			}
> -			if (!cached)
> +			if (!cached) {
>  				changed = ce_match_stat(ce, &st, 1);
> +				if (S_ISGITLINK(patch->old_mode)) {
> +					changed &= TYPE_CHANGED;
> +					if (!changed &&
> +					    verify_gitlink_clean(patch->old_name))
> +						changed |= TYPE_CHANGED;
> +				}
> +			}

In this codepath, we know the patch wants to either modify the
path at old_name or remove old_name.  If we are going to affect
the work tree, we have run lstat on it, and ran checkout_entry() 
if we did not have anything there and did lstat() again.

I think the check "S_ISGITLINK(patch->old_mode)" is wrong
(that's where my confusion while reading your patch came from).
It has to check ce's mode, not patch->old_mode, because we are
verifying if the index matches with the work tree in this
codepath.  If you fix it to S_ISGITLINK(ntohl(ce->ce_mode)),
I think I can see what you are trying to do.

When ce is not a gitlink, you keep the original behaviour, which
is assuring that you did not break things for people who do not
use gitlink.

I am still having trouble with the TYPE_CHANGED bits.  You
discard everything other than TYPE_CHANGED, and 

 - if ce_match_stat() returned TYPE_CHANGED, then that is given
   to later processing to cause us to fail "oops, path is not up
   to date";

 - if ce_match_stat() did not return TYPE_CHANGED, that means we
   found a directory at the path (ce_match_stat_basic() says
   so).  In such a case you call verify_gitlink_clean(), but it
   essentially says "make sure there is either an empty
   directory or some repository".  Maybe we do not even have to
   have this extra check?

When ce is a gitlink, ce_match_stat() says DATA_CHANGED if the
commit in the work tree of the subproject is different.  From
the earlier discussions, we do want to discard DATA_CHANGED for
this codepath.

So it looks almost Ok after spending a few days looking at this
code.  Finally.

However, if it takes _me_ three days to understand this hunk,
(admittably, the parameter to S_ISGITLINK() completely confused
me originally, and I also had other things to do, so it was not
"72 hours"), I do not think the code with your patch is
maintainable by anybody.  At least we would need to have a few
words of comment to describe what is going on there.

	if (!cached) {
        	changed = ce_match_stat(ce, &st, 1);
                if (S_ISGITLINK(ntohl(ce->ce_mode)))
                	/*
			 * ce_match_stat() reports the
			 * difference between the commit object
                         * name in the index and what is checked
			 * out in the work tree of subproject;
                         * because we do not recurse, we do not
			 * want to insist on them matching with
                         * each other.
                         */
                	changed &= ~DATA_CHANGED;
	}
        if (changed)
        	return error("%s: does not match index", old_name);

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v6] git-apply: apply submodule changes
  2007-08-14  9:09                   ` Junio C Hamano
@ 2007-08-15 17:22                     ` Sven Verdoolaege
  2007-08-16  0:02                       ` Junio C Hamano
  0 siblings, 1 reply; 19+ messages in thread
From: Sven Verdoolaege @ 2007-08-15 17:22 UTC (permalink / raw
  To: Junio C Hamano; +Cc: git, Steffen Prohaska, Johannes.Schindelin

Apply "Subproject commit HEX" changes produced by git-diff.
As usual in the current git, only the superproject itself is actually
modified (possibly creating empty directories for new submodules).
Any checked-out submodule is left untouched and is not required to
be up-to-date.

With clean-ups from Junio C Hamano.

Signed-off-by: Sven Verdoolaege <skimo@kotnet.org>
---
On Tue, Aug 14, 2007 at 02:09:35AM -0700, Junio C Hamano wrote:
> Sven Verdoolaege <skimo@kotnet.org> writes:
> >> diff --git a/builtin-apply.c b/builtin-apply.c
> >
> > Did you remove the documentation on purpose ?
> 
> No, I just wanted to get a feedback on a (possibly partial)
> cleanup, as I couldn't make heads or tails of your patch
> especially around that TYPE_CHANGED part, and also the part to
> write out the results.

I agree that the TYPE_CHANGED thing may have been confusing,
so I kept your version (although I switched the tests around,
since there is no point in checking if the stat info matches
if you're going to ignore the result anyway).

Other than that, the only change wrt to your version is that
I added back the creation and (attempt at) removal of the
corresponding subdirectory.

I'm not sure if you intented to remove the check for
either an empty dir or a git repo.  You asked me to add
it before, but then you removed it in your version.
I didn't add it back again.

skimo

 Documentation/git-apply.txt |   14 +++++
 builtin-apply.c             |  112 +++++++++++++++++++++++++++++++++----------
 t/t7400-submodule-basic.sh  |   17 +++++++
 3 files changed, 118 insertions(+), 25 deletions(-)

diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt
index f03f661..4c7e3a2 100644
--- a/Documentation/git-apply.txt
+++ b/Documentation/git-apply.txt
@@ -171,6 +171,20 @@ apply.whitespace::
 	When no `--whitespace` flag is given from the command
 	line, this configuration item is used as the default.
 
+Submodules
+----------
+If the patch contains any changes to submodules then gitlink:git-apply[1]
+treats these changes as follows.
+
+If --index is specified (explicitly or implicitly), then the submodule
+commits must match the index exactly for the patch to apply.  If any
+of the submodules are checked-out, then these check-outs are completely
+ignored, i.e., they are not required to be up-to-date or clean and they
+are not updated.
+
+If --index is not specified, then the submodule commits in the patch
+are ignored and only the absence of presence of the corresponding
+subdirectory is checked and (if possible) updated.
 
 Author
 ------
diff --git a/builtin-apply.c b/builtin-apply.c
index da27075..8055c7d 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -1984,6 +1984,25 @@ static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
 	return 0;
 }
 
+static int read_file_or_gitlink(struct cache_entry *ce, char **buf_p,
+				unsigned long *size_p)
+{
+	if (!ce)
+		return 0;
+
+	if (S_ISGITLINK(ntohl(ce->ce_mode))) {
+		*buf_p = xmalloc(100);
+		*size_p = snprintf(*buf_p, 100,
+			"Subproject commit %s\n", sha1_to_hex(ce->sha1));
+	} else {
+		enum object_type type;
+		*buf_p = read_sha1_file(ce->sha1, &type, size_p);
+		if (!*buf_p)
+			return -1;
+	}
+	return 0;
+}
+
 static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
 {
 	char *buf;
@@ -1994,22 +2013,32 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
 	alloc = 0;
 	buf = NULL;
 	if (cached) {
-		if (ce) {
-			enum object_type type;
-			buf = read_sha1_file(ce->sha1, &type, &size);
-			if (!buf)
+		if (read_file_or_gitlink(ce, &buf, &size))
+			return error("read of %s failed", patch->old_name);
+		alloc = size;
+	} else if (patch->old_name) {
+		if (S_ISGITLINK(patch->old_mode)) {
+			if (ce)
+				read_file_or_gitlink(ce, &buf, &size);
+			else {
+				/*
+				 * There is no way to apply subproject
+				 * patch without looking at the index.
+				 */
+				patch->fragments = NULL;
+				size = 0;
+			}
+		}
+		else {
+			size = xsize_t(st->st_size);
+			alloc = size + 8192;
+			buf = xmalloc(alloc);
+			if (read_old_data(st, patch->old_name,
+					  &buf, &alloc, &size))
 				return error("read of %s failed",
 					     patch->old_name);
-			alloc = size;
 		}
 	}
-	else if (patch->old_name) {
-		size = xsize_t(st->st_size);
-		alloc = size + 8192;
-		buf = xmalloc(alloc);
-		if (read_old_data(st, patch->old_name, &buf, &alloc, &size))
-			return error("read of %s failed", patch->old_name);
-	}
 
 	desc.size = size;
 	desc.alloc = alloc;
@@ -2055,6 +2084,16 @@ static int check_to_create_blob(const char *new_name, int ok_if_exists)
 	return 0;
 }
 
+static int verify_index_match(struct cache_entry *ce, struct stat *st)
+{
+	if (S_ISGITLINK(ntohl(ce->ce_mode))) {
+		if (!S_ISDIR(st->st_mode))
+			return -1;
+		return 0;
+	}
+	return ce_match_stat(ce, st, 1);
+}
+
 static int check_patch(struct patch *patch, struct patch *prev_patch)
 {
 	struct stat st;
@@ -2065,8 +2105,14 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
 	int ok_if_exists;
 
 	patch->rejected = 1; /* we will drop this after we succeed */
+
+	/*
+	 * Make sure that we do not have local modifications from the
+	 * index when we are looking at the index.  Also make sure
+	 * we have the preimage file to be patched in the work tree,
+	 * unless --cached, which tells git to apply only in the index.
+	 */
 	if (old_name) {
-		int changed = 0;
 		int stat_ret = 0;
 		unsigned st_mode = 0;
 
@@ -2096,15 +2142,12 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
 				    lstat(old_name, &st))
 					return -1;
 			}
-			if (!cached)
-				changed = ce_match_stat(ce, &st, 1);
-			if (changed)
+			if (!cached && verify_index_match(ce, &st))
 				return error("%s: does not match index",
 					     old_name);
 			if (cached)
 				st_mode = ntohl(ce->ce_mode);
-		}
-		else if (stat_ret < 0)
+		} else if (stat_ret < 0)
 			return error("%s: %s", old_name, strerror(errno));
 
 		if (!cached)
@@ -2354,7 +2397,11 @@ static void remove_file(struct patch *patch, int rmdir_empty)
 		cache_tree_invalidate_path(active_cache_tree, patch->old_name);
 	}
 	if (!cached) {
-		if (!unlink(patch->old_name) && rmdir_empty) {
+		if (S_ISGITLINK(patch->old_mode)) {
+			if (rmdir(patch->old_name))
+				warning("unable to remove submodule %s",
+					patch->old_name);
+		} else if (!unlink(patch->old_name) && rmdir_empty) {
 			char *name = xstrdup(patch->old_name);
 			char *end = strrchr(name, '/');
 			while (end) {
@@ -2382,13 +2429,21 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
 	memcpy(ce->name, path, namelen);
 	ce->ce_mode = create_ce_mode(mode);
 	ce->ce_flags = htons(namelen);
-	if (!cached) {
-		if (lstat(path, &st) < 0)
-			die("unable to stat newly created file %s", path);
-		fill_stat_cache_info(ce, &st);
+	if (S_ISGITLINK(mode)) {
+		const char *s = buf;
+
+		if (get_sha1_hex(s + strlen("Subproject commit "), ce->sha1))
+			die("corrupt patch for subproject %s", path);
+	} else {
+		if (!cached) {
+			if (lstat(path, &st) < 0)
+				die("unable to stat newly created file %s",
+				    path);
+			fill_stat_cache_info(ce, &st);
+		}
+		if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0)
+			die("unable to create backing store for newly created file %s", path);
 	}
-	if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0)
-		die("unable to create backing store for newly created file %s", path);
 	if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)
 		die("unable to add cache entry for %s", path);
 }
@@ -2398,6 +2453,13 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
 	int fd;
 	char *nbuf;
 
+	if (S_ISGITLINK(mode)) {
+		struct stat st;
+		if (!lstat(path, &st) && S_ISDIR(st.st_mode))
+			return 0;
+		return mkdir(path, 0777);
+	}
+
 	if (has_symlinks && S_ISLNK(mode))
 		/* Although buf:size is counted string, it also is NUL
 		 * terminated.
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index e8ce7cd..9d142ed 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -175,4 +175,21 @@ test_expect_success 'checkout superproject with subproject already present' '
 	git-checkout master
 '
 
+test_expect_success 'apply submodule diff' '
+	git branch second &&
+	(
+		cd lib &&
+		echo s >s &&
+		git add s &&
+		git commit -m "change subproject"
+	) &&
+	git update-index --add lib &&
+	git-commit -m "change lib" &&
+	git-format-patch -1 --stdout >P.diff &&
+	git checkout second &&
+	git apply --index P.diff &&
+	D=$(git diff --cached master) &&
+	test -z "$D"
+'
+
 test_done
-- 
1.5.3.rc5.1.g7de89

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH v6] git-apply: apply submodule changes
  2007-08-15 17:22                     ` [PATCH v6] " Sven Verdoolaege
@ 2007-08-16  0:02                       ` Junio C Hamano
  0 siblings, 0 replies; 19+ messages in thread
From: Junio C Hamano @ 2007-08-16  0:02 UTC (permalink / raw
  To: skimo; +Cc: git, Steffen Prohaska, Johannes.Schindelin

Sven Verdoolaege <skimo@kotnet.org> writes:

> I agree that the TYPE_CHANGED thing may have been confusing,
> so I kept your version (although I switched the tests around,
> since there is no point in checking if the stat info matches
> if you're going to ignore the result anyway).
>
> Other than that, the only change wrt to your version is that
> I added back the creation and (attempt at) removal of the
> corresponding subdirectory.

Makes much more sense than what I wrote.  Thanks.

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2007-08-16  0:03 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-08-10  9:30 [PATCH] git-apply: apply submodule changes Sven Verdoolaege
2007-08-10 12:34 ` Johannes Schindelin
2007-08-10 12:39 ` Johannes Schindelin
2007-08-10 13:57 ` [PATCH resend] " Sven Verdoolaege
2007-08-11  5:43   ` Junio C Hamano
2007-08-11  6:45     ` Sven Verdoolaege
2007-08-11  7:00       ` Junio C Hamano
2007-08-12 14:23 ` [PATCH v3] " Sven Verdoolaege
2007-08-12 18:16   ` Junio C Hamano
2007-08-12 18:50     ` Sven Verdoolaege
2007-08-12 19:24       ` Junio C Hamano
2007-08-13  9:37         ` Sven Verdoolaege
2007-08-13 17:13           ` [PATCH v4] " Sven Verdoolaege
2007-08-13 20:26             ` Junio C Hamano
     [not found]               ` <7vd4xqeilh.fsf@assigned-by-dhcp.cox.net>
2007-08-14  8:39                 ` Sven Verdoolaege
2007-08-14  9:09                   ` Junio C Hamano
2007-08-15 17:22                     ` [PATCH v6] " Sven Verdoolaege
2007-08-16  0:02                       ` Junio C Hamano
2007-08-14 20:00             ` [PATCH v4] " Junio C Hamano

Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).