git-apply: apply submodule changes

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>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Sven Verdoolaege 2007-08-15 19:22:09 +02:00 committed by Junio C Hamano
parent da899deb24
commit e06c5a6c7b
3 changed files with 117 additions and 25 deletions

View File

@ -171,6 +171,20 @@ apply.whitespace::
When no `--whitespace` flag is given from the command When no `--whitespace` flag is given from the command
line, this configuration item is used as the default. 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 Author
------ ------

View File

@ -1984,6 +1984,25 @@ static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
return 0; 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) static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
{ {
char *buf; char *buf;
@ -1994,22 +2013,32 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
alloc = 0; alloc = 0;
buf = NULL; buf = NULL;
if (cached) { if (cached) {
if (ce) { if (read_file_or_gitlink(ce, &buf, &size))
enum object_type type; return error("read of %s failed", patch->old_name);
buf = read_sha1_file(ce->sha1, &type, &size); alloc = size;
if (!buf) } 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", return error("read of %s failed",
patch->old_name); 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.size = size;
desc.alloc = alloc; desc.alloc = alloc;
@ -2055,6 +2084,16 @@ static int check_to_create_blob(const char *new_name, int ok_if_exists)
return 0; 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) static int check_patch(struct patch *patch, struct patch *prev_patch)
{ {
struct stat st; struct stat st;
@ -2065,8 +2104,14 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
int ok_if_exists; int ok_if_exists;
patch->rejected = 1; /* we will drop this after we succeed */ 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) { if (old_name) {
int changed = 0;
int stat_ret = 0; int stat_ret = 0;
unsigned st_mode = 0; unsigned st_mode = 0;
@ -2096,15 +2141,12 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
lstat(old_name, &st)) lstat(old_name, &st))
return -1; return -1;
} }
if (!cached) if (!cached && verify_index_match(ce, &st))
changed = ce_match_stat(ce, &st, 1);
if (changed)
return error("%s: does not match index", return error("%s: does not match index",
old_name); old_name);
if (cached) if (cached)
st_mode = ntohl(ce->ce_mode); st_mode = ntohl(ce->ce_mode);
} } else if (stat_ret < 0)
else if (stat_ret < 0)
return error("%s: %s", old_name, strerror(errno)); return error("%s: %s", old_name, strerror(errno));
if (!cached) if (!cached)
@ -2354,7 +2396,11 @@ static void remove_file(struct patch *patch, int rmdir_empty)
cache_tree_invalidate_path(active_cache_tree, patch->old_name); cache_tree_invalidate_path(active_cache_tree, patch->old_name);
} }
if (!cached) { 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 *name = xstrdup(patch->old_name);
char *end = strrchr(name, '/'); char *end = strrchr(name, '/');
while (end) { while (end) {
@ -2382,13 +2428,21 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
memcpy(ce->name, path, namelen); memcpy(ce->name, path, namelen);
ce->ce_mode = create_ce_mode(mode); ce->ce_mode = create_ce_mode(mode);
ce->ce_flags = htons(namelen); ce->ce_flags = htons(namelen);
if (!cached) { if (S_ISGITLINK(mode)) {
if (lstat(path, &st) < 0) const char *s = buf;
die("unable to stat newly created file %s", path);
fill_stat_cache_info(ce, &st); 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) if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)
die("unable to add cache entry for %s", path); die("unable to add cache entry for %s", path);
} }
@ -2398,6 +2452,13 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
int fd; int fd;
char *nbuf; 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)) if (has_symlinks && S_ISLNK(mode))
/* Although buf:size is counted string, it also is NUL /* Although buf:size is counted string, it also is NUL
* terminated. * terminated.

View File

@ -175,4 +175,21 @@ test_expect_success 'checkout superproject with subproject already present' '
git-checkout master 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 test_done