Skip to content

Commit

Permalink
mv: update the path entry in .gitmodules for moved submodules
Browse files Browse the repository at this point in the history
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.<submodule name>.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 <Jens.Lehmann@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
Jens Lehmann authored and Junio C Hamano committed Aug 6, 2013
1 parent 5fee995 commit 0656781
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 1 deletion.
2 changes: 2 additions & 0 deletions Documentation/git-mv.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ SUBMODULES
Moving a submodule using a gitfile (which means they were cloned
with a Git version 1.7.8 or newer) will update the gitfile and
core.worktree setting to make the submodule work in the new location.
It also will attempt to update the submodule.<name>.path setting in
the linkgit:gitmodules[5] file and stage that file (unless -n is used).

GIT
---
Expand Down
10 changes: 9 additions & 1 deletion builtin/mv.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ static struct lock_file lock_file;

int cmd_mv(int argc, const char **argv, const char *prefix)
{
int i, newfd;
int i, newfd, gitmodules_modified = 0;
int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
struct option builtin_mv_options[] = {
OPT__VERBOSE(&verbose, N_("be verbose")),
Expand All @@ -72,6 +72,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
struct stat st;
struct string_list src_for_dst = STRING_LIST_INIT_NODUP;

gitmodules_config();
git_config(git_default_config, NULL);

argc = parse_options(argc, argv, prefix, builtin_mv_options,
Expand Down Expand Up @@ -125,6 +126,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
struct strbuf submodule_dotgit = STRBUF_INIT;
if (!S_ISGITLINK(active_cache[first]->ce_mode))
die (_("Huh? Directory %s is in index and no submodule?"), src);
if (!is_staging_gitmodules_ok())
die (_("Please, stage your changes to .gitmodules or stash them to proceed"));
strbuf_addf(&submodule_dotgit, "%s/.git", src);
submodule_gitfile[i] = read_gitfile(submodule_dotgit.buf);
if (submodule_gitfile[i])
Expand Down Expand Up @@ -229,6 +232,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
die_errno (_("renaming '%s' failed"), src);
if (submodule_gitfile[i])
connect_work_tree_and_git_dir(dst, submodule_gitfile[i]);
if (!update_path_in_gitmodules(src, dst))
gitmodules_modified = 1;
}

if (mode == WORKING_DIRECTORY)
Expand All @@ -240,6 +245,9 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
rename_cache_entry_at(pos, dst);
}

if (gitmodules_modified)
stage_updated_gitmodules();

if (active_cache_changed) {
if (write_cache(newfd, active_cache, active_nr) ||
commit_locked_index(&lock_file))
Expand Down
34 changes: 34 additions & 0 deletions submodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,40 @@ int is_staging_gitmodules_ok(void)
return !gitmodules_is_modified;
}

/*
* Try to update the "path" entry in the "submodule.<name>" section of the
* .gitmodules file. Return 0 only if a .gitmodules file was found, a section
* with the correct path=<oldpath> 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;
Expand Down
1 change: 1 addition & 0 deletions submodule.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ enum {
};

int is_staging_gitmodules_ok(void);
int update_path_in_gitmodules(const char *oldpath, const char *newpath);
void stage_updated_gitmodules(void);
void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
const char *path);
Expand Down
75 changes: 75 additions & 0 deletions t/t7001-mv.sh
Original file line number Diff line number Diff line change
Expand Up @@ -308,8 +308,83 @@ test_expect_success 'git mv moves a submodule with gitfile' '
cd mod/sub &&
git status
) &&
echo mod/sub >expected &&
git config -f .gitmodules submodule.sub.path >actual &&
test_cmp expected actual &&
git update-index --refresh &&
git diff-files --quiet
'

test_expect_success 'mv does not complain when no .gitmodules file is found' '
rm -rf mod/sub &&
git reset --hard &&
git submodule update &&
git rm .gitmodules &&
entry="$(git ls-files --stage sub | cut -f 1)" &&
git mv sub mod/sub 2>actual.err &&
! test -s actual.err &&
! test -e sub &&
[ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
(
cd mod/sub &&
git status
) &&
git update-index --refresh &&
git diff-files --quiet
'

test_expect_success 'mv will error out on a modified .gitmodules file unless staged' '
rm -rf mod/sub &&
git reset --hard &&
git submodule update &&
git config -f .gitmodules foo.bar true &&
entry="$(git ls-files --stage sub | cut -f 1)" &&
test_must_fail git mv sub mod/sub 2>actual.err &&
test -s actual.err &&
test -e sub &&
git diff-files --quiet -- sub &&
git add .gitmodules &&
git mv sub mod/sub 2>actual.err &&
! test -s actual.err &&
! test -e sub &&
[ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
(
cd mod/sub &&
git status
) &&
git update-index --refresh &&
git diff-files --quiet
'

test_expect_success 'mv issues a warning when section is not found in .gitmodules' '
rm -rf mod/sub &&
git reset --hard &&
git submodule update &&
git config -f .gitmodules --remove-section submodule.sub &&
git add .gitmodules &&
entry="$(git ls-files --stage sub | cut -f 1)" &&
echo "warning: Could not find section in .gitmodules where path=sub" >expect.err &&
git mv sub mod/sub 2>actual.err &&
test_i18ncmp expect.err actual.err &&
! test -e sub &&
[ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
(
cd mod/sub &&
git status
) &&
git update-index --refresh &&
git diff-files --quiet
'

test_expect_success 'mv --dry-run does not touch the submodule or .gitmodules' '
rm -rf mod/sub &&
git reset --hard &&
git submodule update &&
git mv -n sub mod/sub 2>actual.err &&
test -f sub/.git &&
git diff-index --exit-code HEAD &&
git update-index --refresh &&
git diff-files --quiet -- sub .gitmodules
'

test_done

0 comments on commit 0656781

Please sign in to comment.