From a88c915de9886fe17005a5daff4900ced0ea76ad Mon Sep 17 00:00:00 2001 From: Jens Lehmann Date: Tue, 30 Jul 2013 21:50:03 +0200 Subject: mv: move submodules using a gitfile When moving a submodule which uses a gitfile to point to the git directory stored in .git/modules/ of the superproject two changes must be made to make the submodule work: the .git file and the core.worktree setting must be adjusted to point from work tree to git directory and back. Achieve that by remembering which submodule uses a gitfile by storing the result of read_gitfile() of each submodule. If that is not NULL the new function connect_work_tree_and_git_dir() is called after renaming the submodule's work tree which updates the two settings to the new values. Extend the man page to inform the user about that feature (and while at it change the description to not talk about a script anymore, as mv is a builtin for quite some time now). Signed-off-by: Jens Lehmann Signed-off-by: Junio C Hamano --- submodule.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'submodule.c') diff --git a/submodule.c b/submodule.c index 85415d0057..392e83e0bf 100644 --- a/submodule.c +++ b/submodule.c @@ -1004,3 +1004,34 @@ int merge_submodule(unsigned char result[20], const char *path, free(merges.objects); return 0; } + +/* Update gitfile and core.worktree setting to connect work tree and git dir */ +void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir) +{ + struct strbuf file_name = STRBUF_INIT; + struct strbuf rel_path = STRBUF_INIT; + const char *real_work_tree = xstrdup(real_path(work_tree)); + FILE *fp; + + /* Update gitfile */ + strbuf_addf(&file_name, "%s/.git", work_tree); + fp = fopen(file_name.buf, "w"); + if (!fp) + die(_("Could not create git link %s"), file_name.buf); + fprintf(fp, "gitdir: %s\n", relative_path(git_dir, real_work_tree, + &rel_path)); + fclose(fp); + + /* Update core.worktree setting */ + strbuf_reset(&file_name); + strbuf_addf(&file_name, "%s/config", git_dir); + if (git_config_set_in_file(file_name.buf, "core.worktree", + relative_path(real_work_tree, git_dir, + &rel_path))) + die(_("Could not set core.worktree in %s"), + file_name.buf); + + strbuf_release(&file_name); + strbuf_release(&rel_path); + free((void *)real_work_tree); +} -- cgit v1.2.1 From 5fee995244e13fd59500425d49038b00499396f8 Mon Sep 17 00:00:00 2001 From: Jens Lehmann Date: Tue, 30 Jul 2013 21:50:34 +0200 Subject: submodule.c: add .gitmodules staging helper functions Add the new is_staging_gitmodules_ok() and stage_updated_gitmodules() functions to submodule.c. The first makes it possible for call sites to see if the .gitmodules file did contain any unstaged modifications they would accidentally stage in addition to those they intend to stage themselves. The second function stages all modifications to the .gitmodules file, both will be used by subsequent patches for the mv and rm commands. Signed-off-by: Jens Lehmann Signed-off-by: Junio C Hamano --- submodule.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) (limited to 'submodule.c') diff --git a/submodule.c b/submodule.c index 392e83e0bf..fb5134a43b 100644 --- a/submodule.c +++ b/submodule.c @@ -10,6 +10,7 @@ #include "string-list.h" #include "sha1-array.h" #include "argv-array.h" +#include "blob.h" static struct string_list config_name_for_path; static struct string_list config_fetch_recurse_submodules_for_name; @@ -30,6 +31,51 @@ static struct sha1_array ref_tips_after_fetch; */ static int gitmodules_is_unmerged; +/* + * This flag is set if the .gitmodules file had unstaged modifications on + * startup. This must be checked before allowing modifications to the + * .gitmodules file with the intention to stage them later, because when + * continuing we would stage the modifications the user didn't stage herself + * too. That might change in a future version when we learn to stage the + * changes we do ourselves without staging any previous modifications. + */ +static int gitmodules_is_modified; + + +int is_staging_gitmodules_ok(void) +{ + return !gitmodules_is_modified; +} + +void stage_updated_gitmodules(void) +{ + struct strbuf buf = STRBUF_INIT; + struct stat st; + int pos; + struct cache_entry *ce; + int namelen = strlen(".gitmodules"); + + pos = cache_name_pos(".gitmodules", namelen); + if (pos < 0) { + warning(_("could not find .gitmodules in index")); + return; + } + ce = active_cache[pos]; + ce->ce_flags = namelen; + if (strbuf_read_file(&buf, ".gitmodules", 0) < 0) + die(_("reading updated .gitmodules failed")); + if (lstat(".gitmodules", &st) < 0) + die_errno(_("unable to stat updated .gitmodules")); + fill_stat_cache_info(ce, &st); + ce->ce_mode = ce_mode_from_stat(ce, st.st_mode); + if (remove_cache_entry_at(pos) < 0) + die(_("unable to remove .gitmodules from index")); + if (write_sha1_file(buf.buf, buf.len, blob_type, ce->sha1)) + die(_("adding updated .gitmodules failed")); + if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE)) + die(_("staging updated .gitmodules failed")); +} + static int add_submodule_odb(const char *path) { struct strbuf objects_directory = STRBUF_INIT; @@ -116,6 +162,11 @@ void gitmodules_config(void) !memcmp(ce->name, ".gitmodules", 11)) gitmodules_is_unmerged = 1; } + } else if (pos < active_nr) { + struct stat st; + if (lstat(".gitmodules", &st) == 0 && + ce_match_stat(active_cache[pos], &st, 0) & DATA_CHANGED) + gitmodules_is_modified = 1; } if (!gitmodules_is_unmerged) -- cgit v1.2.1 From 0656781fadca17cc51fb1c30f265c251ebe12819 Mon Sep 17 00:00:00 2001 From: Jens Lehmann Date: Tue, 6 Aug 2013 21:15:11 +0200 Subject: mv: update the path entry in .gitmodules for moved submodules Currently using "git mv" on a submodule moves the submodule's work tree in that of the superproject. But the submodule's path setting in .gitmodules is left untouched, which is now inconsistent with the work tree and makes git commands that rely on the proper path -> name mapping (like status and diff) behave strangely. Let "git mv" help here by not only moving the submodule's work tree but also updating the "submodule..path" setting from the .gitmodules file and stage both. This doesn't happen when no .gitmodules file is found and only issues a warning when it doesn't have a section for this submodule. This is because the user might just use plain gitlinks without the .gitmodules file or has already updated the path setting by hand before issuing the "git mv" command (in which case the warning reminds him that mv would have done that for him). Only when .gitmodules is found and contains merge conflicts the mv command will fail and tell the user to resolve the conflict before trying again. Also extend the man page to inform the user about this new feature. Signed-off-by: Jens Lehmann Signed-off-by: Junio C Hamano --- submodule.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'submodule.c') diff --git a/submodule.c b/submodule.c index fb5134a43b..d4e8276f17 100644 --- a/submodule.c +++ b/submodule.c @@ -47,6 +47,40 @@ int is_staging_gitmodules_ok(void) return !gitmodules_is_modified; } +/* + * Try to update the "path" entry in the "submodule." section of the + * .gitmodules file. Return 0 only if a .gitmodules file was found, a section + * with the correct path= setting was found and we could update it. + */ +int update_path_in_gitmodules(const char *oldpath, const char *newpath) +{ + struct strbuf entry = STRBUF_INIT; + struct string_list_item *path_option; + + if (!file_exists(".gitmodules")) /* Do nothing without .gitmodules */ + return -1; + + if (gitmodules_is_unmerged) + die(_("Cannot change unmerged .gitmodules, resolve merge conflicts first")); + + path_option = unsorted_string_list_lookup(&config_name_for_path, oldpath); + if (!path_option) { + warning(_("Could not find section in .gitmodules where path=%s"), oldpath); + return -1; + } + strbuf_addstr(&entry, "submodule."); + strbuf_addstr(&entry, path_option->util); + strbuf_addstr(&entry, ".path"); + if (git_config_set_in_file(".gitmodules", entry.buf, newpath) < 0) { + /* Maybe the user already did that, don't error out here */ + warning(_("Could not update .gitmodules entry %s"), entry.buf); + strbuf_release(&entry); + return -1; + } + strbuf_release(&entry); + return 0; +} + void stage_updated_gitmodules(void) { struct strbuf buf = STRBUF_INIT; -- cgit v1.2.1 From 95c16418f0375e2fc325f32c3d7578fba9cfd7ef Mon Sep 17 00:00:00 2001 From: Jens Lehmann Date: Tue, 6 Aug 2013 21:15:25 +0200 Subject: rm: delete .gitmodules entry of submodules removed from the work tree Currently using "git rm" on a submodule removes the submodule's work tree from that of the superproject and the gitlink from the index. But the submodule's section in .gitmodules is left untouched, which is a leftover of the now removed submodule and might irritate users (as opposed to the setting in .git/config, this must stay as a reminder that the user showed interest in this submodule so it will be repopulated later when an older commit is checked out). Let "git rm" help the user by not only removing the submodule from the work tree but by also removing the "submodule." section from the .gitmodules file and stage both. This doesn't happen when the "--cached" option is used, as it would modify the work tree. This also silently does nothing when no .gitmodules file is found and only issues a warning when it doesn't have a section for this submodule. This is because the user might just use plain gitlinks without the .gitmodules file or has already removed the section by hand before issuing the "git rm" command (in which case the warning reminds him that rm would have done that for him). Only when .gitmodules is found and contains merge conflicts the rm command will fail and tell the user to resolve the conflict before trying again. Also extend the man page to inform the user about this new feature. While at it promote the submodule sub-section to a chapter as it made not much sense under "REMOVING FILES THAT HAVE DISAPPEARED FROM THE FILESYSTEM". In t7610 three uses of "git rm submod" had to be replaced with "git rm --cached submod" because that test expects .gitmodules and the work tree to stay untouched. Also in t7400 the tests for the remaining settings in the .gitmodules file had to be changed to assert that these settings are missing. Signed-off-by: Jens Lehmann Signed-off-by: Junio C Hamano --- submodule.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'submodule.c') diff --git a/submodule.c b/submodule.c index d4e8276f17..0494492bd0 100644 --- a/submodule.c +++ b/submodule.c @@ -81,6 +81,39 @@ int update_path_in_gitmodules(const char *oldpath, const char *newpath) return 0; } +/* + * Try to remove the "submodule." section from .gitmodules where the given + * path is configured. Return 0 only if a .gitmodules file was found, a section + * with the correct path= setting was found and we could remove it. + */ +int remove_path_from_gitmodules(const char *path) +{ + struct strbuf sect = STRBUF_INIT; + struct string_list_item *path_option; + + if (!file_exists(".gitmodules")) /* Do nothing without .gitmodules */ + return -1; + + if (gitmodules_is_unmerged) + die(_("Cannot change unmerged .gitmodules, resolve merge conflicts first")); + + path_option = unsorted_string_list_lookup(&config_name_for_path, path); + if (!path_option) { + warning(_("Could not find section in .gitmodules where path=%s"), path); + return -1; + } + strbuf_addstr(§, "submodule."); + strbuf_addstr(§, path_option->util); + if (git_config_rename_section_in_file(".gitmodules", sect.buf, NULL) < 0) { + /* Maybe the user already did that, don't error out here */ + warning(_("Could not remove .gitmodules entry for %s"), path); + strbuf_release(§); + return -1; + } + strbuf_release(§); + return 0; +} + void stage_updated_gitmodules(void) { struct strbuf buf = STRBUF_INIT; -- cgit v1.2.1 From 4b05440283350ec3b01afabe847d9ce5cf4cbdb7 Mon Sep 17 00:00:00 2001 From: Jharrod LaFon Date: Mon, 19 Aug 2013 09:26:56 -0700 Subject: avoid segfault on submodule.*.path set to an empty "true" Git fails due to a segmentation fault if a submodule path is empty. Here is an example .gitmodules that will cause a segmentation fault: [submodule "foo-module"] path url = http://host/repo.git $ git status Segmentation fault (core dumped) This is because the parsing of "submodule.*.path" is not prepared to see a value-less "true" and assumes that the value is always non-NULL (parsing of "ignore" has the same problem). Fix it by checking the NULL-ness of value and complain with config_error_nonbool(). Signed-off-by: Jharrod LaFon Signed-off-by: Junio C Hamano --- submodule.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'submodule.c') diff --git a/submodule.c b/submodule.c index 975bc87e48..8f7515ed66 100644 --- a/submodule.c +++ b/submodule.c @@ -134,6 +134,9 @@ int parse_submodule_config_option(const char *var, const char *value) return 0; if (!strcmp(key, "path")) { + if (!value) + return config_error_nonbool(var); + config = unsorted_string_list_lookup(&config_name_for_path, value); if (config) free(config->util); @@ -151,6 +154,9 @@ int parse_submodule_config_option(const char *var, const char *value) } else if (!strcmp(key, "ignore")) { char *name_cstr; + if (!value) + return config_error_nonbool(var); + if (strcmp(value, "untracked") && strcmp(value, "dirty") && strcmp(value, "all") && strcmp(value, "none")) { warning("Invalid parameter \"%s\" for config option \"submodule.%s.ignore\"", value, var); -- cgit v1.2.1 From 59556548230e617b837343c2c07e357e688e2ca4 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sat, 30 Nov 2013 21:55:40 +0100 Subject: replace {pre,suf}fixcmp() with {starts,ends}_with() Leaving only the function definitions and declarations so that any new topic in flight can still make use of the old functions, replace existing uses of the prefixcmp() and suffixcmp() with new API functions. The change can be recreated by mechanically applying this: $ git grep -l -e prefixcmp -e suffixcmp -- \*.c | grep -v strbuf\\.c | xargs perl -pi -e ' s|!prefixcmp\(|starts_with\(|g; s|prefixcmp\(|!starts_with\(|g; s|!suffixcmp\(|ends_with\(|g; s|suffixcmp\(|!ends_with\(|g; ' on the result of preparatory changes in this series. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- submodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'submodule.c') diff --git a/submodule.c b/submodule.c index 1905d75b2b..613857e400 100644 --- a/submodule.c +++ b/submodule.c @@ -201,7 +201,7 @@ void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt, int submodule_config(const char *var, const char *value, void *cb) { - if (!prefixcmp(var, "submodule.")) + if (starts_with(var, "submodule.")) return parse_submodule_config_option(var, value); else if (!strcmp(var, "fetch.recursesubmodules")) { config_fetch_recurse_submodules = parse_fetch_recurse_submodules_arg(var, value); -- cgit v1.2.1