Skip to content

Commit 1ca7fa9

Browse files
committed
git_path_join_unrooted: return base len
The documentation for `git_path_join_unrooted` states that the base length will be returned, so that consumers like checkout know where to start creating directories instead of always creating directories at the directory root.
1 parent bd0e881 commit 1ca7fa9

File tree

4 files changed

+81
-15
lines changed

4 files changed

+81
-15
lines changed

src/checkout.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1102,7 +1102,7 @@ static int checkout_conflicts_mark_directoryfile(
11021102
goto done;
11031103
}
11041104

1105-
prefixed = git_path_equal_or_prefixed(path, entry->path);
1105+
prefixed = git_path_equal_or_prefixed(path, entry->path, NULL);
11061106

11071107
if (prefixed == GIT_PATH_EQUAL)
11081108
continue;

src/path.c

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -263,26 +263,31 @@ int git_path_root(const char *path)
263263
int git_path_join_unrooted(
264264
git_buf *path_out, const char *path, const char *base, ssize_t *root_at)
265265
{
266-
int error, root;
266+
ssize_t root;
267267

268268
assert(path && path_out);
269269

270-
root = git_path_root(path);
270+
root = (ssize_t)git_path_root(path);
271271

272272
if (base != NULL && root < 0) {
273-
error = git_buf_joinpath(path_out, base, path);
273+
if (git_buf_joinpath(path_out, base, path) < 0)
274+
return -1;
274275

275-
if (root_at)
276-
*root_at = (ssize_t)strlen(base);
277-
}
278-
else {
279-
error = git_buf_sets(path_out, path);
276+
root = (ssize_t)strlen(base);
277+
} else {
278+
if (git_buf_sets(path_out, path) < 0)
279+
return -1;
280280

281-
if (root_at)
282-
*root_at = (root < 0) ? 0 : (ssize_t)root;
281+
if (root < 0)
282+
root = 0;
283+
else if (base)
284+
git_path_equal_or_prefixed(base, path, &root);
283285
}
284286

285-
return error;
287+
if (root_at)
288+
*root_at = root;
289+
290+
return 0;
286291
}
287292

288293
int git_path_prettify(git_buf *path_out, const char *path, const char *base)

src/path.h

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -396,21 +396,35 @@ enum { GIT_PATH_NOTEQUAL = 0, GIT_PATH_EQUAL = 1, GIT_PATH_PREFIX = 2 };
396396
*/
397397
GIT_INLINE(int) git_path_equal_or_prefixed(
398398
const char *parent,
399-
const char *child)
399+
const char *child,
400+
ssize_t *prefixlen)
400401
{
401402
const char *p = parent, *c = child;
403+
int lastslash = 0;
402404

403405
while (*p && *c) {
406+
lastslash = (*p == '/');
407+
404408
if (*p++ != *c++)
405409
return GIT_PATH_NOTEQUAL;
406410
}
407411

408412
if (*p != '\0')
409413
return GIT_PATH_NOTEQUAL;
410-
if (*c == '\0')
414+
415+
if (*c == '\0') {
416+
if (prefixlen)
417+
*prefixlen = p - parent;
418+
411419
return GIT_PATH_EQUAL;
412-
if (*c == '/')
420+
}
421+
422+
if (*c == '/' || lastslash) {
423+
if (prefixlen)
424+
*prefixlen = (p - parent) - lastslash;
425+
413426
return GIT_PATH_PREFIX;
427+
}
414428

415429
return GIT_PATH_NOTEQUAL;
416430
}

tests/path/core.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,3 +305,50 @@ void test_path_core__isvalid_dotgit_with_hfs_ignorables(void)
305305
cl_assert_equal_b(true, git_path_isvalid(NULL, ".git\xe2\x80\xbf", GIT_PATH_REJECT_DOT_GIT_HFS));
306306
cl_assert_equal_b(true, git_path_isvalid(NULL, ".git\xe2\xab\x81", GIT_PATH_REJECT_DOT_GIT_HFS));
307307
}
308+
309+
static void test_join_unrooted(
310+
const char *expected_result,
311+
ssize_t expected_rootlen,
312+
const char *path,
313+
const char *base)
314+
{
315+
git_buf result = GIT_BUF_INIT;
316+
ssize_t root_at;
317+
318+
cl_git_pass(git_path_join_unrooted(&result, path, base, &root_at));
319+
cl_assert_equal_s(expected_result, result.ptr);
320+
cl_assert_equal_i(expected_rootlen, root_at);
321+
322+
git_buf_free(&result);
323+
}
324+
325+
void test_path_core__join_unrooted(void)
326+
{
327+
git_buf out = GIT_BUF_INIT;
328+
329+
test_join_unrooted("foo", 0, "foo", NULL);
330+
test_join_unrooted("foo/bar", 0, "foo/bar", NULL);
331+
332+
/* Relative paths have base prepended */
333+
test_join_unrooted("/foo/bar", 4, "bar", "/foo");
334+
test_join_unrooted("/foo/bar/foobar", 4, "bar/foobar", "/foo");
335+
test_join_unrooted("c:/foo/bar/foobar", 6, "bar/foobar", "c:/foo");
336+
test_join_unrooted("c:/foo/bar/foobar", 10, "foobar", "c:/foo/bar");
337+
338+
/* Absolute paths are not prepended with base */
339+
test_join_unrooted("/foo", 0, "/foo", "/asdf");
340+
test_join_unrooted("/foo/bar", 0, "/foo/bar", "/asdf");
341+
342+
/* Drive letter is given as root length on Windows */
343+
test_join_unrooted("c:/foo", 2, "c:/foo", "c:/asdf");
344+
test_join_unrooted("c:/foo/bar", 2, "c:/foo/bar", "c:/asdf");
345+
346+
/* Base is returned when it's provided and is the prefix */
347+
test_join_unrooted("c:/foo/bar/foobar", 6, "c:/foo/bar/foobar", "c:/foo");
348+
test_join_unrooted("c:/foo/bar/foobar", 10, "c:/foo/bar/foobar", "c:/foo/bar");
349+
350+
/* Trailing slash in the base is ignored */
351+
test_join_unrooted("c:/foo/bar/foobar", 6, "c:/foo/bar/foobar", "c:/foo/");
352+
353+
git_buf_free(&out);
354+
}

0 commit comments

Comments
 (0)