Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.kernel.org/pub/scm/git/git.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2022-06-04 00:30:34 +0300
committerJunio C Hamano <gitster@pobox.com>2022-06-04 00:30:34 +0300
commitb3b2ddced295c26134568cf879488a921a352865 (patch)
tree2933bae2dd17545280f0bdcf38eca1e03d93d3bf
parent83937e9592832408670da38bfe6e96c90ad63521 (diff)
parent89c6e450fe4a919ecb6fa698005a935531c732cf (diff)
Merge branch 'ds/bundle-uri'
Preliminary code refactoring around transport and bundle code. * ds/bundle-uri: bundle.h: make "fd" version of read_bundle_header() public remote: allow relative_url() to return an absolute url remote: move relative_url() http: make http_get_file() external fetch-pack: move --keep=* option filling to a function fetch-pack: add a deref_without_lazy_fetch_extended() dir API: add a generalized path_match_flags() function connect.c: refactor sending of agent & object-format
-rw-r--r--builtin/submodule--helper.c141
-rw-r--r--bundle.c8
-rw-r--r--bundle.h2
-rw-r--r--compat/mingw.c2
-rw-r--r--compat/win32/path-utils.h6
-rw-r--r--connect.c33
-rw-r--r--dir.c29
-rw-r--r--dir.h63
-rw-r--r--fetch-pack.c45
-rw-r--r--fsck.c23
-rw-r--r--git-compat-util.h8
-rw-r--r--http.c4
-rw-r--r--http.h9
-rw-r--r--path.c2
-rw-r--r--remote.c99
-rw-r--r--remote.h32
-rw-r--r--submodule-config.c6
17 files changed, 321 insertions, 191 deletions
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 1a8e5d0621..3aed3a4e48 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -72,135 +72,6 @@ static char *get_default_remote(void)
return repo_get_default_remote(the_repository);
}
-static int starts_with_dot_slash(const char *str)
-{
- return str[0] == '.' && is_dir_sep(str[1]);
-}
-
-static int starts_with_dot_dot_slash(const char *str)
-{
- return str[0] == '.' && str[1] == '.' && is_dir_sep(str[2]);
-}
-
-/*
- * Returns 1 if it was the last chop before ':'.
- */
-static int chop_last_dir(char **remoteurl, int is_relative)
-{
- char *rfind = find_last_dir_sep(*remoteurl);
- if (rfind) {
- *rfind = '\0';
- return 0;
- }
-
- rfind = strrchr(*remoteurl, ':');
- if (rfind) {
- *rfind = '\0';
- return 1;
- }
-
- if (is_relative || !strcmp(".", *remoteurl))
- die(_("cannot strip one component off url '%s'"),
- *remoteurl);
-
- free(*remoteurl);
- *remoteurl = xstrdup(".");
- return 0;
-}
-
-/*
- * The `url` argument is the URL that navigates to the submodule origin
- * repo. When relative, this URL is relative to the superproject origin
- * URL repo. The `up_path` argument, if specified, is the relative
- * path that navigates from the submodule working tree to the superproject
- * working tree. Returns the origin URL of the submodule.
- *
- * Return either an absolute URL or filesystem path (if the superproject
- * origin URL is an absolute URL or filesystem path, respectively) or a
- * relative file system path (if the superproject origin URL is a relative
- * file system path).
- *
- * When the output is a relative file system path, the path is either
- * relative to the submodule working tree, if up_path is specified, or to
- * the superproject working tree otherwise.
- *
- * NEEDSWORK: This works incorrectly on the domain and protocol part.
- * remote_url url outcome expectation
- * http://a.com/b ../c http://a.com/c as is
- * http://a.com/b/ ../c http://a.com/c same as previous line, but
- * ignore trailing slash in url
- * http://a.com/b ../../c http://c error out
- * http://a.com/b ../../../c http:/c error out
- * http://a.com/b ../../../../c http:c error out
- * http://a.com/b ../../../../../c .:c error out
- * NEEDSWORK: Given how chop_last_dir() works, this function is broken
- * when a local part has a colon in its path component, too.
- */
-static char *relative_url(const char *remote_url,
- const char *url,
- const char *up_path)
-{
- int is_relative = 0;
- int colonsep = 0;
- char *out;
- char *remoteurl = xstrdup(remote_url);
- struct strbuf sb = STRBUF_INIT;
- size_t len = strlen(remoteurl);
-
- if (is_dir_sep(remoteurl[len-1]))
- remoteurl[len-1] = '\0';
-
- if (!url_is_local_not_ssh(remoteurl) || is_absolute_path(remoteurl))
- is_relative = 0;
- else {
- is_relative = 1;
- /*
- * Prepend a './' to ensure all relative
- * remoteurls start with './' or '../'
- */
- if (!starts_with_dot_slash(remoteurl) &&
- !starts_with_dot_dot_slash(remoteurl)) {
- strbuf_reset(&sb);
- strbuf_addf(&sb, "./%s", remoteurl);
- free(remoteurl);
- remoteurl = strbuf_detach(&sb, NULL);
- }
- }
- /*
- * When the url starts with '../', remove that and the
- * last directory in remoteurl.
- */
- while (url) {
- if (starts_with_dot_dot_slash(url)) {
- url += 3;
- colonsep |= chop_last_dir(&remoteurl, is_relative);
- } else if (starts_with_dot_slash(url))
- url += 2;
- else
- break;
- }
- strbuf_reset(&sb);
- strbuf_addf(&sb, "%s%s%s", remoteurl, colonsep ? ":" : "/", url);
- if (ends_with(url, "/"))
- strbuf_setlen(&sb, sb.len - 1);
- free(remoteurl);
-
- if (starts_with_dot_slash(sb.buf))
- out = xstrdup(sb.buf + 2);
- else
- out = xstrdup(sb.buf);
-
- if (!up_path || !is_relative) {
- strbuf_release(&sb);
- return out;
- }
-
- strbuf_reset(&sb);
- strbuf_addf(&sb, "%s%s", up_path, out);
- free(out);
- return strbuf_detach(&sb, NULL);
-}
-
static char *resolve_relative_url(const char *rel_url, const char *up_path, int quiet)
{
char *remoteurl, *resolved_url;
@@ -592,6 +463,18 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
return 0;
}
+static int starts_with_dot_slash(const char *const path)
+{
+ return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_SLASH |
+ PATH_MATCH_XPLATFORM);
+}
+
+static int starts_with_dot_dot_slash(const char *const path)
+{
+ return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH |
+ PATH_MATCH_XPLATFORM);
+}
+
struct init_cb {
const char *prefix;
const char *superprefix;
diff --git a/bundle.c b/bundle.c
index d50cfb5aa7..5fa41a52f1 100644
--- a/bundle.c
+++ b/bundle.c
@@ -66,8 +66,8 @@ static int parse_bundle_signature(struct bundle_header *header, const char *line
return -1;
}
-static int parse_bundle_header(int fd, struct bundle_header *header,
- const char *report_path)
+int read_bundle_header_fd(int fd, struct bundle_header *header,
+ const char *report_path)
{
struct strbuf buf = STRBUF_INIT;
int status = 0;
@@ -143,7 +143,7 @@ int read_bundle_header(const char *path, struct bundle_header *header)
if (fd < 0)
return error(_("could not open '%s'"), path);
- return parse_bundle_header(fd, header, path);
+ return read_bundle_header_fd(fd, header, path);
}
int is_bundle(const char *path, int quiet)
@@ -153,7 +153,7 @@ int is_bundle(const char *path, int quiet)
if (fd < 0)
return 0;
- fd = parse_bundle_header(fd, &header, quiet ? NULL : path);
+ fd = read_bundle_header_fd(fd, &header, quiet ? NULL : path);
if (fd >= 0)
close(fd);
bundle_header_release(&header);
diff --git a/bundle.h b/bundle.h
index 7fef2108f4..0c052f5496 100644
--- a/bundle.h
+++ b/bundle.h
@@ -24,6 +24,8 @@ void bundle_header_release(struct bundle_header *header);
int is_bundle(const char *path, int quiet);
int read_bundle_header(const char *path, struct bundle_header *header);
+int read_bundle_header_fd(int fd, struct bundle_header *header,
+ const char *report_path);
int create_bundle(struct repository *r, const char *path,
int argc, const char **argv, struct strvec *pack_options,
int version);
diff --git a/compat/mingw.c b/compat/mingw.c
index 5772692a0a..2607de93af 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -2830,7 +2830,7 @@ not_a_reserved_name:
}
c = path[i];
- if (c && c != '.' && c != ':' && c != '/' && c != '\\')
+ if (c && c != '.' && c != ':' && !is_xplatform_dir_sep(c))
goto not_a_reserved_name;
/* contains reserved name */
diff --git a/compat/win32/path-utils.h b/compat/win32/path-utils.h
index bba2b64408..65fa3b9263 100644
--- a/compat/win32/path-utils.h
+++ b/compat/win32/path-utils.h
@@ -6,11 +6,7 @@ int win32_has_dos_drive_prefix(const char *path);
int win32_skip_dos_drive_prefix(char **path);
#define skip_dos_drive_prefix win32_skip_dos_drive_prefix
-static inline int win32_is_dir_sep(int c)
-{
- return c == '/' || c == '\\';
-}
-#define is_dir_sep win32_is_dir_sep
+#define is_dir_sep is_xplatform_dir_sep
static inline char *win32_find_last_dir_sep(const char *path)
{
char *ret = NULL;
diff --git a/connect.c b/connect.c
index afc79a6236..e6d0b1d34b 100644
--- a/connect.c
+++ b/connect.c
@@ -473,6 +473,24 @@ void check_stateless_delimiter(int stateless_rpc,
die("%s", error);
}
+static void send_capabilities(int fd_out, struct packet_reader *reader)
+{
+ const char *hash_name;
+
+ if (server_supports_v2("agent", 0))
+ packet_write_fmt(fd_out, "agent=%s", git_user_agent_sanitized());
+
+ if (server_feature_v2("object-format", &hash_name)) {
+ int hash_algo = hash_algo_by_name(hash_name);
+ if (hash_algo == GIT_HASH_UNKNOWN)
+ die(_("unknown object format '%s' specified by server"), hash_name);
+ reader->hash_algo = &hash_algos[hash_algo];
+ packet_write_fmt(fd_out, "object-format=%s", reader->hash_algo->name);
+ } else {
+ reader->hash_algo = &hash_algos[GIT_HASH_SHA1];
+ }
+}
+
struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
struct ref **list, int for_push,
struct transport_ls_refs_options *transport_options,
@@ -480,7 +498,6 @@ struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
int stateless_rpc)
{
int i;
- const char *hash_name;
struct strvec *ref_prefixes = transport_options ?
&transport_options->ref_prefixes : NULL;
const char **unborn_head_target = transport_options ?
@@ -490,18 +507,8 @@ struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
if (server_supports_v2("ls-refs", 1))
packet_write_fmt(fd_out, "command=ls-refs\n");
- if (server_supports_v2("agent", 0))
- packet_write_fmt(fd_out, "agent=%s", git_user_agent_sanitized());
-
- if (server_feature_v2("object-format", &hash_name)) {
- int hash_algo = hash_algo_by_name(hash_name);
- if (hash_algo == GIT_HASH_UNKNOWN)
- die(_("unknown object format '%s' specified by server"), hash_name);
- reader->hash_algo = &hash_algos[hash_algo];
- packet_write_fmt(fd_out, "object-format=%s", reader->hash_algo->name);
- } else {
- reader->hash_algo = &hash_algos[GIT_HASH_SHA1];
- }
+ /* Send capabilities */
+ send_capabilities(fd_out, reader);
if (server_options && server_options->nr &&
server_supports_v2("server-option", 1))
diff --git a/dir.c b/dir.c
index 3633c0852e..6ca2ef5f04 100644
--- a/dir.c
+++ b/dir.c
@@ -3955,3 +3955,32 @@ void relocate_gitdir(const char *path, const char *old_git_dir, const char *new_
connect_work_tree_and_git_dir(path, new_git_dir, 0);
}
+
+int path_match_flags(const char *const str, const enum path_match_flags flags)
+{
+ const char *p = str;
+
+ if (flags & PATH_MATCH_NATIVE &&
+ flags & PATH_MATCH_XPLATFORM)
+ BUG("path_match_flags() must get one match kind, not multiple!");
+ else if (!(flags & PATH_MATCH_KINDS_MASK))
+ BUG("path_match_flags() must get at least one match kind!");
+
+ if (flags & PATH_MATCH_STARTS_WITH_DOT_SLASH &&
+ flags & PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH)
+ BUG("path_match_flags() must get one platform kind, not multiple!");
+ else if (!(flags & PATH_MATCH_PLATFORM_MASK))
+ BUG("path_match_flags() must get at least one platform kind!");
+
+ if (*p++ != '.')
+ return 0;
+ if (flags & PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH &&
+ *p++ != '.')
+ return 0;
+
+ if (flags & PATH_MATCH_NATIVE)
+ return is_dir_sep(*p);
+ else if (flags & PATH_MATCH_XPLATFORM)
+ return is_xplatform_dir_sep(*p);
+ BUG("unreachable");
+}
diff --git a/dir.h b/dir.h
index 8e02dfb505..7bc862030c 100644
--- a/dir.h
+++ b/dir.h
@@ -578,4 +578,67 @@ void connect_work_tree_and_git_dir(const char *work_tree,
void relocate_gitdir(const char *path,
const char *old_git_dir,
const char *new_git_dir);
+
+/**
+ * The "enum path_matches_kind" determines how path_match_flags() will
+ * behave. The flags come in sets, and one (and only one) must be
+ * provided out of each "set":
+ *
+ * PATH_MATCH_NATIVE:
+ * Path separator is is_dir_sep()
+ * PATH_MATCH_XPLATFORM:
+ * Path separator is is_xplatform_dir_sep()
+ *
+ * Do we use is_dir_sep() to check for a directory separator
+ * (*_NATIVE), or do we always check for '/' or '\' (*_XPLATFORM). The
+ * "*_NATIVE" version on Windows is the same as "*_XPLATFORM",
+ * everywhere else "*_NATIVE" means "only /".
+ *
+ * PATH_MATCH_STARTS_WITH_DOT_SLASH:
+ * Match a path starting with "./"
+ * PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH:
+ * Match a path starting with "../"
+ *
+ * The "/" in the above is adjusted based on the "*_NATIVE" and
+ * "*_XPLATFORM" flags.
+ */
+enum path_match_flags {
+ PATH_MATCH_NATIVE = 1 << 0,
+ PATH_MATCH_XPLATFORM = 1 << 1,
+ PATH_MATCH_STARTS_WITH_DOT_SLASH = 1 << 2,
+ PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH = 1 << 3,
+};
+#define PATH_MATCH_KINDS_MASK (PATH_MATCH_STARTS_WITH_DOT_SLASH | \
+ PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH)
+#define PATH_MATCH_PLATFORM_MASK (PATH_MATCH_NATIVE | PATH_MATCH_XPLATFORM)
+
+/**
+ * path_match_flags() checks if a given "path" matches a given "enum
+ * path_match_flags" criteria.
+ */
+int path_match_flags(const char *const path, const enum path_match_flags f);
+
+/**
+ * starts_with_dot_slash_native(): convenience wrapper for
+ * path_match_flags() with PATH_MATCH_STARTS_WITH_DOT_SLASH and
+ * PATH_MATCH_NATIVE.
+ */
+static inline int starts_with_dot_slash_native(const char *const path)
+{
+ const enum path_match_flags what = PATH_MATCH_STARTS_WITH_DOT_SLASH;
+
+ return path_match_flags(path, what | PATH_MATCH_NATIVE);
+}
+
+/**
+ * starts_with_dot_slash_native(): convenience wrapper for
+ * path_match_flags() with PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH and
+ * PATH_MATCH_NATIVE.
+ */
+static inline int starts_with_dot_dot_slash_native(const char *const path)
+{
+ const enum path_match_flags what = PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH;
+
+ return path_match_flags(path, what | PATH_MATCH_NATIVE);
+}
#endif
diff --git a/fetch-pack.c b/fetch-pack.c
index 6d0d271259..cb6647d657 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -115,11 +115,12 @@ static void for_each_cached_alternate(struct fetch_negotiator *negotiator,
cb(negotiator, cache.items[i]);
}
-static struct commit *deref_without_lazy_fetch(const struct object_id *oid,
- int mark_tags_complete)
+static struct commit *deref_without_lazy_fetch_extended(const struct object_id *oid,
+ int mark_tags_complete,
+ enum object_type *type,
+ unsigned int oi_flags)
{
- enum object_type type;
- struct object_info info = { .typep = &type };
+ struct object_info info = { .typep = type };
struct commit *commit;
commit = lookup_commit_in_graph(the_repository, oid);
@@ -128,9 +129,9 @@ static struct commit *deref_without_lazy_fetch(const struct object_id *oid,
while (1) {
if (oid_object_info_extended(the_repository, oid, &info,
- OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK))
+ oi_flags))
return NULL;
- if (type == OBJ_TAG) {
+ if (*type == OBJ_TAG) {
struct tag *tag = (struct tag *)
parse_object(the_repository, oid);
@@ -144,7 +145,7 @@ static struct commit *deref_without_lazy_fetch(const struct object_id *oid,
}
}
- if (type == OBJ_COMMIT) {
+ if (*type == OBJ_COMMIT) {
struct commit *commit = lookup_commit(the_repository, oid);
if (!commit || repo_parse_commit(the_repository, commit))
return NULL;
@@ -154,6 +155,16 @@ static struct commit *deref_without_lazy_fetch(const struct object_id *oid,
return NULL;
}
+
+static struct commit *deref_without_lazy_fetch(const struct object_id *oid,
+ int mark_tags_complete)
+{
+ enum object_type type;
+ unsigned flags = OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK;
+ return deref_without_lazy_fetch_extended(oid, mark_tags_complete,
+ &type, flags);
+}
+
static int rev_list_insert_ref(struct fetch_negotiator *negotiator,
const struct object_id *oid)
{
@@ -836,6 +847,16 @@ static void parse_gitmodules_oids(int fd, struct oidset *gitmodules_oids)
} while (1);
}
+static void add_index_pack_keep_option(struct strvec *args)
+{
+ char hostname[HOST_NAME_MAX + 1];
+
+ if (xgethostname(hostname, sizeof(hostname)))
+ xsnprintf(hostname, sizeof(hostname), "localhost");
+ strvec_pushf(args, "--keep=fetch-pack %"PRIuMAX " on %s",
+ (uintmax_t)getpid(), hostname);
+}
+
/*
* If packfile URIs were provided, pass a non-NULL pointer to index_pack_args.
* The strings to pass as the --index-pack-arg arguments to http-fetch will be
@@ -905,14 +926,8 @@ static int get_pack(struct fetch_pack_args *args,
strvec_push(&cmd.args, "-v");
if (args->use_thin_pack)
strvec_push(&cmd.args, "--fix-thin");
- if ((do_keep || index_pack_args) && (args->lock_pack || unpack_limit)) {
- char hostname[HOST_NAME_MAX + 1];
- if (xgethostname(hostname, sizeof(hostname)))
- xsnprintf(hostname, sizeof(hostname), "localhost");
- strvec_pushf(&cmd.args,
- "--keep=fetch-pack %"PRIuMAX " on %s",
- (uintmax_t)getpid(), hostname);
- }
+ if ((do_keep || index_pack_args) && (args->lock_pack || unpack_limit))
+ add_index_pack_keep_option(&cmd.args);
if (!index_pack_args && args->check_self_contained_and_connected)
strvec_push(&cmd.args, "--check-self-contained-and-connected");
else
diff --git a/fsck.c b/fsck.c
index 3ec500d707..dd4822ba1b 100644
--- a/fsck.c
+++ b/fsck.c
@@ -975,27 +975,16 @@ done:
return ret;
}
-/*
- * Like builtin/submodule--helper.c's starts_with_dot_slash, but without
- * relying on the platform-dependent is_dir_sep helper.
- *
- * This is for use in checking whether a submodule URL is interpreted as
- * relative to the current directory on any platform, since \ is a
- * directory separator on Windows but not on other platforms.
- */
-static int starts_with_dot_slash(const char *str)
+static int starts_with_dot_slash(const char *const path)
{
- return str[0] == '.' && (str[1] == '/' || str[1] == '\\');
+ return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_SLASH |
+ PATH_MATCH_XPLATFORM);
}
-/*
- * Like starts_with_dot_slash, this is a variant of submodule--helper's
- * helper of the same name with the twist that it accepts backslash as a
- * directory separator even on non-Windows platforms.
- */
-static int starts_with_dot_dot_slash(const char *str)
+static int starts_with_dot_dot_slash(const char *const path)
{
- return str[0] == '.' && starts_with_dot_slash(str + 1);
+ return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH |
+ PATH_MATCH_XPLATFORM);
}
static int submodule_url_is_relative(const char *url)
diff --git a/git-compat-util.h b/git-compat-util.h
index ddc3fa2a4e..d9457b9aae 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -236,6 +236,12 @@
#include <sys/sysctl.h>
#endif
+/* Used by compat/win32/path-utils.h, and more */
+static inline int is_xplatform_dir_sep(int c)
+{
+ return c == '/' || c == '\\';
+}
+
#if defined(__CYGWIN__)
#include "compat/win32/path-utils.h"
#endif
@@ -416,11 +422,11 @@ static inline int git_skip_dos_drive_prefix(char **path)
#define skip_dos_drive_prefix git_skip_dos_drive_prefix
#endif
-#ifndef is_dir_sep
static inline int git_is_dir_sep(int c)
{
return c == '/';
}
+#ifndef is_dir_sep
#define is_dir_sep git_is_dir_sep
#endif
diff --git a/http.c b/http.c
index 68573b6da5..11c6f69fac 100644
--- a/http.c
+++ b/http.c
@@ -1989,8 +1989,8 @@ int http_get_strbuf(const char *url,
* If a previous interrupted download is detected (i.e. a previous temporary
* file is still around) the download is resumed.
*/
-static int http_get_file(const char *url, const char *filename,
- struct http_get_options *options)
+int http_get_file(const char *url, const char *filename,
+ struct http_get_options *options)
{
int ret;
struct strbuf tmpfile = STRBUF_INIT;
diff --git a/http.h b/http.h
index df1590e53a..ba303cfb37 100644
--- a/http.h
+++ b/http.h
@@ -163,6 +163,15 @@ struct http_get_options {
*/
int http_get_strbuf(const char *url, struct strbuf *result, struct http_get_options *options);
+/*
+ * Downloads a URL and stores the result in the given file.
+ *
+ * If a previous interrupted download is detected (i.e. a previous temporary
+ * file is still around) the download is resumed.
+ */
+int http_get_file(const char *url, const char *filename,
+ struct http_get_options *options);
+
int http_fetch_ref(const char *base, struct ref *ref);
/* Helpers for fetching packs */
diff --git a/path.c b/path.c
index 3236122aad..a3cfcd8a6e 100644
--- a/path.c
+++ b/path.c
@@ -1413,7 +1413,7 @@ int is_ntfs_dotgit(const char *name)
for (;;) {
c = *(name++);
- if (!c || c == '\\' || c == '/' || c == ':')
+ if (!c || is_xplatform_dir_sep(c) || c == ':')
return 1;
if (c != '.' && c != ' ')
return 0;
diff --git a/remote.c b/remote.c
index 930fdc9c2f..e98148ac22 100644
--- a/remote.c
+++ b/remote.c
@@ -14,6 +14,7 @@
#include "strvec.h"
#include "commit-reach.h"
#include "advice.h"
+#include "connect.h"
enum map_direction { FROM_SRC, FROM_DST };
@@ -2729,3 +2730,101 @@ void remote_state_clear(struct remote_state *remote_state)
hashmap_clear_and_free(&remote_state->remotes_hash, struct remote, ent);
hashmap_clear_and_free(&remote_state->branches_hash, struct remote, ent);
}
+
+/*
+ * Returns 1 if it was the last chop before ':'.
+ */
+static int chop_last_dir(char **remoteurl, int is_relative)
+{
+ char *rfind = find_last_dir_sep(*remoteurl);
+ if (rfind) {
+ *rfind = '\0';
+ return 0;
+ }
+
+ rfind = strrchr(*remoteurl, ':');
+ if (rfind) {
+ *rfind = '\0';
+ return 1;
+ }
+
+ if (is_relative || !strcmp(".", *remoteurl))
+ die(_("cannot strip one component off url '%s'"),
+ *remoteurl);
+
+ free(*remoteurl);
+ *remoteurl = xstrdup(".");
+ return 0;
+}
+
+char *relative_url(const char *remote_url, const char *url,
+ const char *up_path)
+{
+ int is_relative = 0;
+ int colonsep = 0;
+ char *out;
+ char *remoteurl;
+ struct strbuf sb = STRBUF_INIT;
+ size_t len;
+
+ if (!url_is_local_not_ssh(url) || is_absolute_path(url))
+ return xstrdup(url);
+
+ len = strlen(remote_url);
+ if (!len)
+ BUG("invalid empty remote_url");
+
+ remoteurl = xstrdup(remote_url);
+ if (is_dir_sep(remoteurl[len-1]))
+ remoteurl[len-1] = '\0';
+
+ if (!url_is_local_not_ssh(remoteurl) || is_absolute_path(remoteurl))
+ is_relative = 0;
+ else {
+ is_relative = 1;
+ /*
+ * Prepend a './' to ensure all relative
+ * remoteurls start with './' or '../'
+ */
+ if (!starts_with_dot_slash_native(remoteurl) &&
+ !starts_with_dot_dot_slash_native(remoteurl)) {
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "./%s", remoteurl);
+ free(remoteurl);
+ remoteurl = strbuf_detach(&sb, NULL);
+ }
+ }
+ /*
+ * When the url starts with '../', remove that and the
+ * last directory in remoteurl.
+ */
+ while (url) {
+ if (starts_with_dot_dot_slash_native(url)) {
+ url += 3;
+ colonsep |= chop_last_dir(&remoteurl, is_relative);
+ } else if (starts_with_dot_slash_native(url))
+ url += 2;
+ else
+ break;
+ }
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "%s%s%s", remoteurl, colonsep ? ":" : "/", url);
+ if (ends_with(url, "/"))
+ strbuf_setlen(&sb, sb.len - 1);
+ free(remoteurl);
+
+ if (starts_with_dot_slash_native(sb.buf))
+ out = xstrdup(sb.buf + 2);
+ else
+ out = xstrdup(sb.buf);
+
+ if (!up_path || !is_relative) {
+ strbuf_release(&sb);
+ return out;
+ }
+
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "%s%s", up_path, out);
+ free(out);
+ return strbuf_detach(&sb, NULL);
+}
diff --git a/remote.h b/remote.h
index 4a1209ae2c..dd4402436f 100644
--- a/remote.h
+++ b/remote.h
@@ -409,4 +409,36 @@ int parseopt_push_cas_option(const struct option *, const char *arg, int unset);
int is_empty_cas(const struct push_cas_option *);
void apply_push_cas(struct push_cas_option *, struct remote *, struct ref *);
+/*
+ * The `url` argument is the URL that navigates to the submodule origin
+ * repo. When relative, this URL is relative to the superproject origin
+ * URL repo. The `up_path` argument, if specified, is the relative
+ * path that navigates from the submodule working tree to the superproject
+ * working tree. Returns the origin URL of the submodule.
+ *
+ * Return either an absolute URL or filesystem path (if the superproject
+ * origin URL is an absolute URL or filesystem path, respectively) or a
+ * relative file system path (if the superproject origin URL is a relative
+ * file system path).
+ *
+ * When the output is a relative file system path, the path is either
+ * relative to the submodule working tree, if up_path is specified, or to
+ * the superproject working tree otherwise.
+ *
+ * NEEDSWORK: This works incorrectly on the domain and protocol part.
+ * remote_url url outcome expectation
+ * http://a.com/b ../c http://a.com/c as is
+ * http://a.com/b/ ../c http://a.com/c same as previous line, but
+ * ignore trailing slash in url
+ * http://a.com/b ../../c http://c error out
+ * http://a.com/b ../../../c http:/c error out
+ * http://a.com/b ../../../../c http:c error out
+ * http://a.com/b ../../../../../c .:c error out
+ * http://a.com/b http://d.org/e http://d.org/e as is
+ * NEEDSWORK: Given how chop_last_dir() works, this function is broken
+ * when a local part has a colon in its path component, too.
+ */
+char *relative_url(const char *remote_url, const char *url,
+ const char *up_path);
+
#endif
diff --git a/submodule-config.c b/submodule-config.c
index 29668b0620..ce3beaf5d4 100644
--- a/submodule-config.c
+++ b/submodule-config.c
@@ -204,17 +204,17 @@ int check_submodule_name(const char *name)
return -1;
/*
- * Look for '..' as a path component. Check both '/' and '\\' as
+ * Look for '..' as a path component. Check is_xplatform_dir_sep() as
* separators rather than is_dir_sep(), because we want the name rules
* to be consistent across platforms.
*/
goto in_component; /* always start inside component */
while (*name) {
char c = *name++;
- if (c == '/' || c == '\\') {
+ if (is_xplatform_dir_sep(c)) {
in_component:
if (name[0] == '.' && name[1] == '.' &&
- (!name[2] || name[2] == '/' || name[2] == '\\'))
+ (!name[2] || is_xplatform_dir_sep(name[2])))
return -1;
}
}