Skip to content

Commit a88c915

Browse files
jlehmanngitster
authored andcommitted
mv: move submodules using a gitfile
When moving a submodule which uses a gitfile to point to the git directory stored in .git/modules/<name> 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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 1150246 commit a88c915

File tree

5 files changed

+73
-5
lines changed

5 files changed

+73
-5
lines changed

Documentation/git-mv.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ SYNOPSIS
1313

1414
DESCRIPTION
1515
-----------
16-
This script is used to move or rename a file, directory or symlink.
16+
Move or rename a file, directory or symlink.
1717

1818
git mv [-v] [-f] [-n] [-k] <source> <destination>
1919
git mv [-v] [-f] [-n] [-k] <source> ... <destination directory>
@@ -44,6 +44,12 @@ OPTIONS
4444
--verbose::
4545
Report the names of files as they are moved.
4646

47+
SUBMODULES
48+
----------
49+
Moving a submodule using a gitfile (which means they were cloned
50+
with a Git version 1.7.8 or newer) will update the gitfile and
51+
core.worktree setting to make the submodule work in the new location.
52+
4753
GIT
4854
---
4955
Part of the linkgit:git[1] suite

builtin/mv.c

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "cache-tree.h"
1010
#include "string-list.h"
1111
#include "parse-options.h"
12+
#include "submodule.h"
1213

1314
static const char * const builtin_mv_usage[] = {
1415
N_("git mv [options] <source>... <destination>"),
@@ -66,7 +67,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
6667
OPT_BOOLEAN('k', NULL, &ignore_errors, N_("skip move/rename errors")),
6768
OPT_END(),
6869
};
69-
const char **source, **destination, **dest_path;
70+
const char **source, **destination, **dest_path, **submodule_gitfile;
7071
enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes;
7172
struct stat st;
7273
struct string_list src_for_dst = STRING_LIST_INIT_NODUP;
@@ -85,6 +86,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
8586
source = internal_copy_pathspec(prefix, argv, argc, 0);
8687
modes = xcalloc(argc, sizeof(enum update_mode));
8788
dest_path = internal_copy_pathspec(prefix, argv + argc, 1, 0);
89+
submodule_gitfile = xcalloc(argc, sizeof(char *));
8890

8991
if (dest_path[0][0] == '\0')
9092
/* special case: "." was normalized to "" */
@@ -120,8 +122,14 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
120122
else if (src_is_dir) {
121123
int first = cache_name_pos(src, length);
122124
if (first >= 0) {
125+
struct strbuf submodule_dotgit = STRBUF_INIT;
123126
if (!S_ISGITLINK(active_cache[first]->ce_mode))
124127
die (_("Huh? Directory %s is in index and no submodule?"), src);
128+
strbuf_addf(&submodule_dotgit, "%s/.git", src);
129+
submodule_gitfile[i] = read_gitfile(submodule_dotgit.buf);
130+
if (submodule_gitfile[i])
131+
submodule_gitfile[i] = xstrdup(submodule_gitfile[i]);
132+
strbuf_release(&submodule_dotgit);
125133
} else {
126134
const char *src_w_slash = add_slash(src);
127135
int last, len_w_slash = length + 1;
@@ -216,9 +224,12 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
216224
int pos;
217225
if (show_only || verbose)
218226
printf(_("Renaming %s to %s\n"), src, dst);
219-
if (!show_only && mode != INDEX &&
220-
rename(src, dst) < 0 && !ignore_errors)
221-
die_errno (_("renaming '%s' failed"), src);
227+
if (!show_only && mode != INDEX) {
228+
if (rename(src, dst) < 0 && !ignore_errors)
229+
die_errno (_("renaming '%s' failed"), src);
230+
if (submodule_gitfile[i])
231+
connect_work_tree_and_git_dir(dst, submodule_gitfile[i]);
232+
}
222233

223234
if (mode == WORKING_DIRECTORY)
224235
continue;

submodule.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,3 +1004,34 @@ int merge_submodule(unsigned char result[20], const char *path,
10041004
free(merges.objects);
10051005
return 0;
10061006
}
1007+
1008+
/* Update gitfile and core.worktree setting to connect work tree and git dir */
1009+
void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir)
1010+
{
1011+
struct strbuf file_name = STRBUF_INIT;
1012+
struct strbuf rel_path = STRBUF_INIT;
1013+
const char *real_work_tree = xstrdup(real_path(work_tree));
1014+
FILE *fp;
1015+
1016+
/* Update gitfile */
1017+
strbuf_addf(&file_name, "%s/.git", work_tree);
1018+
fp = fopen(file_name.buf, "w");
1019+
if (!fp)
1020+
die(_("Could not create git link %s"), file_name.buf);
1021+
fprintf(fp, "gitdir: %s\n", relative_path(git_dir, real_work_tree,
1022+
&rel_path));
1023+
fclose(fp);
1024+
1025+
/* Update core.worktree setting */
1026+
strbuf_reset(&file_name);
1027+
strbuf_addf(&file_name, "%s/config", git_dir);
1028+
if (git_config_set_in_file(file_name.buf, "core.worktree",
1029+
relative_path(real_work_tree, git_dir,
1030+
&rel_path)))
1031+
die(_("Could not set core.worktree in %s"),
1032+
file_name.buf);
1033+
1034+
strbuf_release(&file_name);
1035+
strbuf_release(&rel_path);
1036+
free((void *)real_work_tree);
1037+
}

submodule.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,6 @@ int merge_submodule(unsigned char result[20], const char *path, const unsigned c
3636
int find_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name,
3737
struct string_list *needs_pushing);
3838
int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name);
39+
void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);
3940

4041
#endif

t/t7001-mv.sh

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,4 +293,23 @@ test_expect_success 'git mv moves a submodule with a .git directory and no .gitm
293293
git diff-files --quiet
294294
'
295295

296+
test_expect_success 'git mv moves a submodule with gitfile' '
297+
rm -rf mod/sub &&
298+
git reset --hard &&
299+
git submodule update &&
300+
entry="$(git ls-files --stage sub | cut -f 1)" &&
301+
(
302+
cd mod &&
303+
git mv ../sub/ .
304+
) &&
305+
! test -e sub &&
306+
[ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
307+
(
308+
cd mod/sub &&
309+
git status
310+
) &&
311+
git update-index --refresh &&
312+
git diff-files --quiet
313+
'
314+
296315
test_done

0 commit comments

Comments
 (0)