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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
diff --git a/src/checkout.c b/src/checkout.c
index 1074fe6..aad17f8 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -1102,7 +1102,7 @@ static int checkout_conflicts_mark_directoryfile(
goto done;
}
- prefixed = git_path_equal_or_prefixed(path, entry->path);
+ prefixed = git_path_equal_or_prefixed(path, entry->path, NULL);
if (prefixed == GIT_PATH_EQUAL)
continue;
diff --git a/src/path.c b/src/path.c
index 0bad962..58d7192 100644
--- a/src/path.c
+++ b/src/path.c
@@ -263,26 +263,31 @@ int git_path_root(const char *path)
int git_path_join_unrooted(
git_buf *path_out, const char *path, const char *base, ssize_t *root_at)
{
- int error, root;
+ ssize_t root;
assert(path && path_out);
- root = git_path_root(path);
+ root = (ssize_t)git_path_root(path);
if (base != NULL && root < 0) {
- error = git_buf_joinpath(path_out, base, path);
+ if (git_buf_joinpath(path_out, base, path) < 0)
+ return -1;
- if (root_at)
- *root_at = (ssize_t)strlen(base);
- }
- else {
- error = git_buf_sets(path_out, path);
+ root = (ssize_t)strlen(base);
+ } else {
+ if (git_buf_sets(path_out, path) < 0)
+ return -1;
- if (root_at)
- *root_at = (root < 0) ? 0 : (ssize_t)root;
+ if (root < 0)
+ root = 0;
+ else if (base)
+ git_path_equal_or_prefixed(base, path, &root);
}
- return error;
+ if (root_at)
+ *root_at = root;
+
+ return 0;
}
int git_path_prettify(git_buf *path_out, const char *path, const char *base)
diff --git a/src/path.h b/src/path.h
index b753140..440b542 100644
--- a/src/path.h
+++ b/src/path.h
@@ -396,21 +396,35 @@ enum { GIT_PATH_NOTEQUAL = 0, GIT_PATH_EQUAL = 1, GIT_PATH_PREFIX = 2 };
*/
GIT_INLINE(int) git_path_equal_or_prefixed(
const char *parent,
- const char *child)
+ const char *child,
+ ssize_t *prefixlen)
{
const char *p = parent, *c = child;
+ int lastslash = 0;
while (*p && *c) {
+ lastslash = (*p == '/');
+
if (*p++ != *c++)
return GIT_PATH_NOTEQUAL;
}
if (*p != '\0')
return GIT_PATH_NOTEQUAL;
- if (*c == '\0')
+
+ if (*c == '\0') {
+ if (prefixlen)
+ *prefixlen = p - parent;
+
return GIT_PATH_EQUAL;
- if (*c == '/')
+ }
+
+ if (*c == '/' || lastslash) {
+ if (prefixlen)
+ *prefixlen = (p - parent) - lastslash;
+
return GIT_PATH_PREFIX;
+ }
return GIT_PATH_NOTEQUAL;
}
diff --git a/tests/path/core.c b/tests/path/core.c
index 5b110f6..064f149 100644
--- a/tests/path/core.c
+++ b/tests/path/core.c
@@ -305,3 +305,50 @@ void test_path_core__isvalid_dotgit_with_hfs_ignorables(void)
cl_assert_equal_b(true, git_path_isvalid(NULL, ".git\xe2\x80\xbf", GIT_PATH_REJECT_DOT_GIT_HFS));
cl_assert_equal_b(true, git_path_isvalid(NULL, ".git\xe2\xab\x81", GIT_PATH_REJECT_DOT_GIT_HFS));
}
+
+static void test_join_unrooted(
+ const char *expected_result,
+ ssize_t expected_rootlen,
+ const char *path,
+ const char *base)
+{
+ git_buf result = GIT_BUF_INIT;
+ ssize_t root_at;
+
+ cl_git_pass(git_path_join_unrooted(&result, path, base, &root_at));
+ cl_assert_equal_s(expected_result, result.ptr);
+ cl_assert_equal_i(expected_rootlen, root_at);
+
+ git_buf_free(&result);
+}
+
+void test_path_core__join_unrooted(void)
+{
+ git_buf out = GIT_BUF_INIT;
+
+ test_join_unrooted("foo", 0, "foo", NULL);
+ test_join_unrooted("foo/bar", 0, "foo/bar", NULL);
+
+ /* Relative paths have base prepended */
+ test_join_unrooted("/foo/bar", 4, "bar", "/foo");
+ test_join_unrooted("/foo/bar/foobar", 4, "bar/foobar", "/foo");
+ test_join_unrooted("c:/foo/bar/foobar", 6, "bar/foobar", "c:/foo");
+ test_join_unrooted("c:/foo/bar/foobar", 10, "foobar", "c:/foo/bar");
+
+ /* Absolute paths are not prepended with base */
+ test_join_unrooted("/foo", 0, "/foo", "/asdf");
+ test_join_unrooted("/foo/bar", 0, "/foo/bar", "/asdf");
+
+ /* Drive letter is given as root length on Windows */
+ test_join_unrooted("c:/foo", 2, "c:/foo", "c:/asdf");
+ test_join_unrooted("c:/foo/bar", 2, "c:/foo/bar", "c:/asdf");
+
+ /* Base is returned when it's provided and is the prefix */
+ test_join_unrooted("c:/foo/bar/foobar", 6, "c:/foo/bar/foobar", "c:/foo");
+ test_join_unrooted("c:/foo/bar/foobar", 10, "c:/foo/bar/foobar", "c:/foo/bar");
+
+ /* Trailing slash in the base is ignored */
+ test_join_unrooted("c:/foo/bar/foobar", 6, "c:/foo/bar/foobar", "c:/foo/");
+
+ git_buf_free(&out);
+}