int cmd_upload_archive(int argc, const char **argv, const char *prefix, struct repository *repo);
int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix, struct repository *repo);
int cmd_upload_pack(int argc, const char **argv, const char *prefix, struct repository *repo);
+int cmd_url_parse(int argc, const char **argv, const char *prefix, struct repository *repo);
int cmd_var(int argc, const char **argv, const char *prefix, struct repository *repo);
int cmd_verify_commit(int argc, const char **argv, const char *prefix, struct repository *repo);
int cmd_verify_tag(int argc, const char **argv, const char *prefix, struct repository *repo);
--- /dev/null
+#include "builtin.h"
+#include "gettext.h"
+#include "parse-options.h"
+#include "url.h"
+#include "urlmatch.h"
+
+static const char * const builtin_url_parse_usage[] = {
+ N_("git url-parse [-c <component>] [--] <url>..."),
+ NULL
+};
+
+static char *component_arg;
+
+static struct option builtin_url_parse_options[] = {
+ OPT_STRING('c', "component", &component_arg, N_("component"),
+ N_("which URL component to extract")),
+ OPT_END(),
+};
+
+enum url_component {
+ URL_NONE = 0,
+ URL_SCHEME,
+ URL_USER,
+ URL_PASSWORD,
+ URL_HOST,
+ URL_PORT,
+ URL_PATH,
+};
+
+static void parse_or_die(const char *url, struct url_info *info)
+{
+ if (url_is_local_not_ssh(url)) {
+ if (*url == '/')
+ die("'%s' is not a URL; if you meant a local "
+ "repository, use 'file://%s'", url, url);
+ if (has_dos_drive_prefix(url))
+ die("'%s' is not a URL; if you meant a local "
+ "repository, use 'file:///%s'", url, url);
+ die("'%s' is not a URL; if you meant a local repository, "
+ "use a 'file://' URL with an absolute path", url);
+ }
+ if (!url_parse(url, info))
+ die("invalid git URL '%s': %s", url, info->err);
+}
+
+static enum url_component get_component_or_die(const char *arg)
+{
+ if (!strcmp("path", arg))
+ return URL_PATH;
+ if (!strcmp("host", arg))
+ return URL_HOST;
+ if (!strcmp("scheme", arg))
+ return URL_SCHEME;
+ if (!strcmp("user", arg))
+ return URL_USER;
+ if (!strcmp("password", arg))
+ return URL_PASSWORD;
+ if (!strcmp("port", arg))
+ return URL_PORT;
+ die("invalid git URL component '%s'", arg);
+}
+
+static char *extract_component(enum url_component component,
+ struct url_info *info)
+{
+ size_t offset, length;
+
+ switch (component) {
+ case URL_SCHEME:
+ offset = 0;
+ length = info->scheme_len;
+ break;
+ case URL_USER:
+ offset = info->user_off;
+ length = info->user_len;
+ break;
+ case URL_PASSWORD:
+ offset = info->passwd_off;
+ length = info->passwd_len;
+ break;
+ case URL_HOST:
+ offset = info->host_off;
+ length = info->host_len;
+ break;
+ case URL_PORT:
+ offset = info->port_off;
+ length = info->port_len;
+ break;
+ case URL_PATH:
+ offset = info->path_off;
+ length = info->path_len;
+ break;
+ case URL_NONE:
+ return NULL;
+ }
+
+ return xstrndup(info->url + offset, length);
+}
+
+int cmd_url_parse(int argc,
+ const char **argv,
+ const char *prefix,
+ struct repository *repo UNUSED)
+{
+ struct url_info info;
+ enum url_component selected = URL_NONE;
+ char *extracted;
+ int i;
+
+ argc = parse_options(argc, argv, prefix, builtin_url_parse_options,
+ builtin_url_parse_usage, 0);
+
+ if (argc == 0)
+ usage_with_options(builtin_url_parse_usage,
+ builtin_url_parse_options);
+
+ if (component_arg)
+ selected = get_component_or_die(component_arg);
+
+ for (i = 0; i < argc; i++) {
+ parse_or_die(argv[i], &info);
+
+ if (selected != URL_NONE) {
+ extracted = extract_component(selected, &info);
+ if (extracted) {
+ puts(extracted);
+ free(extracted);
+ }
+ }
+
+ free(info.url);
+ }
+
+ return 0;
+}
{ "upload-archive", cmd_upload_archive, NO_PARSEOPT },
{ "upload-archive--writer", cmd_upload_archive_writer, NO_PARSEOPT },
{ "upload-pack", cmd_upload_pack },
+ { "url-parse", cmd_url_parse },
{ "var", cmd_var, RUN_SETUP_GENTLY | NO_PARSEOPT },
{ "verify-commit", cmd_verify_commit, RUN_SETUP },
{ "verify-pack", cmd_verify_pack },