Skip to content

Commit 99b43a6

Browse files
jacob-kellergitster
authored andcommitted
allow do_submodule_path to work even if submodule isn't checked out
Currently, do_submodule_path will attempt locating the .git directory by using read_gitfile on <path>/.git. If this fails it just assumes the <path>/.git is actually a git directory. This is good because it allows for handling submodules which were cloned in a regular manner first before being added to the superproject. Unfortunately this fails if the <path> is not actually checked out any longer, such as by removing the directory. Fix this by checking if the directory we found is actually a gitdir. In the case it is not, attempt to lookup the submodule configuration and find the name of where it is stored in the .git/modules/ directory of the superproject. If we can't locate the submodule configuration, this might occur because for example a submodule gitlink was added but the corresponding .gitmodules file was not properly updated. A die() here would not be pleasant to the users of submodule diff formats, so instead, modify do_submodule_path() to return an error code: - git_pathdup_submodule() returns NULL when we fail to find a path. - strbuf_git_path_submodule() propagates the error code to the caller. Modify the callers of these functions to check the error code and fail properly. This ensures we don't attempt to use a bad path that doesn't match the corresponding submodule. Because this change fixes add_submodule_odb() to work even if the submodule is not checked out, update the wording of the submodule log diff format to correctly display that the submodule is "not initialized" instead of "not checked out" Add tests to ensure this change works as expected. Signed-off-by: Jacob Keller <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 61cfbc0 commit 99b43a6

File tree

5 files changed

+173
-11
lines changed

5 files changed

+173
-11
lines changed

cache.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -819,8 +819,8 @@ extern void strbuf_git_common_path(struct strbuf *sb, const char *fmt, ...)
819819
__attribute__((format (printf, 2, 3)));
820820
extern char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
821821
__attribute__((format (printf, 2, 3)));
822-
extern void strbuf_git_path_submodule(struct strbuf *sb, const char *path,
823-
const char *fmt, ...)
822+
extern int strbuf_git_path_submodule(struct strbuf *sb, const char *path,
823+
const char *fmt, ...)
824824
__attribute__((format (printf, 3, 4)));
825825
extern char *git_pathdup(const char *fmt, ...)
826826
__attribute__((format (printf, 1, 2)));

path.c

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "string-list.h"
77
#include "dir.h"
88
#include "worktree.h"
9+
#include "submodule-config.h"
910

1011
static int get_st_mode_bits(const char *path, int *mode)
1112
{
@@ -466,12 +467,16 @@ const char *worktree_git_path(const struct worktree *wt, const char *fmt, ...)
466467
return pathname->buf;
467468
}
468469

469-
static void do_submodule_path(struct strbuf *buf, const char *path,
470-
const char *fmt, va_list args)
470+
/* Returns 0 on success, negative on failure. */
471+
#define SUBMODULE_PATH_ERR_NOT_CONFIGURED -1
472+
static int do_submodule_path(struct strbuf *buf, const char *path,
473+
const char *fmt, va_list args)
471474
{
472475
const char *git_dir;
473476
struct strbuf git_submodule_common_dir = STRBUF_INIT;
474477
struct strbuf git_submodule_dir = STRBUF_INIT;
478+
const struct submodule *sub;
479+
int err = 0;
475480

476481
strbuf_addstr(buf, path);
477482
strbuf_complete(buf, '/');
@@ -482,6 +487,17 @@ static void do_submodule_path(struct strbuf *buf, const char *path,
482487
strbuf_reset(buf);
483488
strbuf_addstr(buf, git_dir);
484489
}
490+
if (!is_git_directory(buf->buf)) {
491+
gitmodules_config();
492+
sub = submodule_from_path(null_sha1, path);
493+
if (!sub) {
494+
err = SUBMODULE_PATH_ERR_NOT_CONFIGURED;
495+
goto cleanup;
496+
}
497+
strbuf_reset(buf);
498+
strbuf_git_path(buf, "%s/%s", "modules", sub->name);
499+
}
500+
485501
strbuf_addch(buf, '/');
486502
strbuf_addbuf(&git_submodule_dir, buf);
487503

@@ -492,27 +508,38 @@ static void do_submodule_path(struct strbuf *buf, const char *path,
492508

493509
strbuf_cleanup_path(buf);
494510

511+
cleanup:
495512
strbuf_release(&git_submodule_dir);
496513
strbuf_release(&git_submodule_common_dir);
514+
515+
return err;
497516
}
498517

499518
char *git_pathdup_submodule(const char *path, const char *fmt, ...)
500519
{
520+
int err;
501521
va_list args;
502522
struct strbuf buf = STRBUF_INIT;
503523
va_start(args, fmt);
504-
do_submodule_path(&buf, path, fmt, args);
524+
err = do_submodule_path(&buf, path, fmt, args);
505525
va_end(args);
526+
if (err) {
527+
strbuf_release(&buf);
528+
return NULL;
529+
}
506530
return strbuf_detach(&buf, NULL);
507531
}
508532

509-
void strbuf_git_path_submodule(struct strbuf *buf, const char *path,
510-
const char *fmt, ...)
533+
int strbuf_git_path_submodule(struct strbuf *buf, const char *path,
534+
const char *fmt, ...)
511535
{
536+
int err;
512537
va_list args;
513538
va_start(args, fmt);
514-
do_submodule_path(buf, path, fmt, args);
539+
err = do_submodule_path(buf, path, fmt, args);
515540
va_end(args);
541+
542+
return err;
516543
}
517544

518545
static void do_git_common_path(struct strbuf *buf,

refs/files-backend.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1225,13 +1225,19 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
12251225
struct strbuf refname;
12261226
struct strbuf path = STRBUF_INIT;
12271227
size_t path_baselen;
1228+
int err = 0;
12281229

12291230
if (*refs->name)
1230-
strbuf_git_path_submodule(&path, refs->name, "%s", dirname);
1231+
err = strbuf_git_path_submodule(&path, refs->name, "%s", dirname);
12311232
else
12321233
strbuf_git_path(&path, "%s", dirname);
12331234
path_baselen = path.len;
12341235

1236+
if (err) {
1237+
strbuf_release(&path);
1238+
return;
1239+
}
1240+
12351241
d = opendir(path.buf);
12361242
if (!d) {
12371243
strbuf_release(&path);

submodule.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,9 @@ static int add_submodule_odb(const char *path)
127127
int ret = 0;
128128
size_t alloc;
129129

130-
strbuf_git_path_submodule(&objects_directory, path, "objects/");
130+
ret = strbuf_git_path_submodule(&objects_directory, path, "objects/");
131+
if (ret)
132+
goto done;
131133
if (!is_directory(objects_directory.buf)) {
132134
ret = -1;
133135
goto done;
@@ -348,7 +350,7 @@ void show_submodule_summary(FILE *f, const char *path,
348350
if (is_null_sha1(two))
349351
message = "(submodule deleted)";
350352
else if (add_submodule_odb(path))
351-
message = "(not checked out)";
353+
message = "(not initialized)";
352354
else if (is_null_sha1(one))
353355
message = "(new submodule)";
354356
else if (!(left = lookup_commit_reference(one)) ||
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#!/bin/sh
2+
#
3+
# Copyright (c) 2016 Jacob Keller, based on t4041 by Jens Lehmann
4+
#
5+
6+
test_description='Test for submodule diff on non-checked out submodule
7+
8+
This test tries to verify that add_submodule_odb works when the submodule was
9+
initialized previously but the checkout has since been removed.
10+
'
11+
12+
. ./test-lib.sh
13+
14+
# Tested non-UTF-8 encoding
15+
test_encoding="ISO8859-1"
16+
17+
# String "added" in German (translated with Google Translate), encoded in UTF-8,
18+
# used in sample commit log messages in add_file() function below.
19+
added=$(printf "hinzugef\303\274gt")
20+
21+
add_file () {
22+
(
23+
cd "$1" &&
24+
shift &&
25+
for name
26+
do
27+
echo "$name" >"$name" &&
28+
git add "$name" &&
29+
test_tick &&
30+
# "git commit -m" would break MinGW, as Windows refuse to pass
31+
# $test_encoding encoded parameter to git.
32+
echo "Add $name ($added $name)" | iconv -f utf-8 -t $test_encoding |
33+
git -c "i18n.commitEncoding=$test_encoding" commit -F -
34+
done >/dev/null &&
35+
git rev-parse --short --verify HEAD
36+
)
37+
}
38+
39+
commit_file () {
40+
test_tick &&
41+
git commit "$@" -m "Commit $*" >/dev/null
42+
}
43+
44+
test_expect_success 'setup - submodules' '
45+
test_create_repo sm2 &&
46+
add_file . foo &&
47+
add_file sm2 foo1 foo2 &&
48+
smhead1=$(git -C sm2 rev-parse --short --verify HEAD)
49+
'
50+
51+
test_expect_success 'setup - git submodule add' '
52+
git submodule add ./sm2 sm1 &&
53+
commit_file sm1 .gitmodules &&
54+
git diff-tree -p --no-commit-id --submodule=log HEAD -- sm1 >actual &&
55+
cat >expected <<-EOF &&
56+
Submodule sm1 0000000...$smhead1 (new submodule)
57+
EOF
58+
test_cmp expected actual
59+
'
60+
61+
test_expect_success 'submodule directory removed' '
62+
rm -rf sm1 &&
63+
git diff-tree -p --no-commit-id --submodule=log HEAD -- sm1 >actual &&
64+
cat >expected <<-EOF &&
65+
Submodule sm1 0000000...$smhead1 (new submodule)
66+
EOF
67+
test_cmp expected actual
68+
'
69+
70+
test_expect_success 'setup - submodule multiple commits' '
71+
git submodule update --checkout sm1 &&
72+
smhead2=$(add_file sm1 foo3 foo4) &&
73+
commit_file sm1 &&
74+
git diff-tree -p --no-commit-id --submodule=log HEAD >actual &&
75+
cat >expected <<-EOF &&
76+
Submodule sm1 $smhead1..$smhead2:
77+
> Add foo4 ($added foo4)
78+
> Add foo3 ($added foo3)
79+
EOF
80+
test_cmp expected actual
81+
'
82+
83+
test_expect_success 'submodule removed multiple commits' '
84+
rm -rf sm1 &&
85+
git diff-tree -p --no-commit-id --submodule=log HEAD >actual &&
86+
cat >expected <<-EOF &&
87+
Submodule sm1 $smhead1..$smhead2:
88+
> Add foo4 ($added foo4)
89+
> Add foo3 ($added foo3)
90+
EOF
91+
test_cmp expected actual
92+
'
93+
94+
test_expect_success 'submodule not initialized in new clone' '
95+
git clone . sm3 &&
96+
git -C sm3 diff-tree -p --no-commit-id --submodule=log HEAD >actual &&
97+
cat >expected <<-EOF &&
98+
Submodule sm1 $smhead1...$smhead2 (not initialized)
99+
EOF
100+
test_cmp expected actual
101+
'
102+
103+
test_expect_success 'setup submodule moved' '
104+
git submodule update --checkout sm1 &&
105+
git mv sm1 sm4 &&
106+
commit_file sm4 &&
107+
git diff-tree -p --no-commit-id --submodule=log HEAD >actual &&
108+
cat >expected <<-EOF &&
109+
Submodule sm4 0000000...$smhead2 (new submodule)
110+
EOF
111+
test_cmp expected actual
112+
'
113+
114+
test_expect_success 'submodule moved then removed' '
115+
smhead3=$(add_file sm4 foo6 foo7) &&
116+
commit_file sm4 &&
117+
rm -rf sm4 &&
118+
git diff-tree -p --no-commit-id --submodule=log HEAD >actual &&
119+
cat >expected <<-EOF &&
120+
Submodule sm4 $smhead2..$smhead3:
121+
> Add foo7 ($added foo7)
122+
> Add foo6 ($added foo6)
123+
EOF
124+
test_cmp expected actual
125+
'
126+
127+
test_done

0 commit comments

Comments
 (0)