Merge branch 'sb/clone-origin'
"git clone" learned clone.defaultremotename configuration variable to customize what nickname to use to call the remote the repository was cloned from. * sb/clone-origin: clone: allow configurable default for `-o`/`--origin` clone: read new remote name from remote_name instead of option_origin clone: validate --origin option before use refs: consolidate remote name validation remote: add tests for add and rename with invalid names clone: use more conventional config/option layering clone: add tests for --template and some disallowed option pairs
This commit is contained in:
commit
40696c6727
@ -334,6 +334,8 @@ include::config/checkout.txt[]
|
||||
|
||||
include::config/clean.txt[]
|
||||
|
||||
include::config/clone.txt[]
|
||||
|
||||
include::config/color.txt[]
|
||||
|
||||
include::config/column.txt[]
|
||||
|
4
Documentation/config/clone.txt
Normal file
4
Documentation/config/clone.txt
Normal file
@ -0,0 +1,4 @@
|
||||
clone.defaultRemoteName::
|
||||
The name of the remote to create when cloning a repository. Defaults to
|
||||
`origin`, and can be overridden by passing the `--origin` command-line
|
||||
option to linkgit:git-clone[1].
|
@ -183,8 +183,9 @@ objects from the source repository into a pack in the cloned repository.
|
||||
|
||||
-o <name>::
|
||||
--origin <name>::
|
||||
Instead of using the remote name `origin` to keep track
|
||||
of the upstream repository, use `<name>`.
|
||||
Instead of using the remote name `origin` to keep track of the upstream
|
||||
repository, use `<name>`. Overrides `clone.defaultRemoteName` from the
|
||||
config.
|
||||
|
||||
-b <name>::
|
||||
--branch <name>::
|
||||
|
@ -53,6 +53,7 @@ static int option_shallow_submodules;
|
||||
static int deepen;
|
||||
static char *option_template, *option_depth, *option_since;
|
||||
static char *option_origin = NULL;
|
||||
static char *remote_name = NULL;
|
||||
static char *option_branch = NULL;
|
||||
static struct string_list option_not = STRING_LIST_INIT_NODUP;
|
||||
static const char *real_git_dir;
|
||||
@ -721,7 +722,7 @@ static void update_head(const struct ref *our, const struct ref *remote,
|
||||
if (!option_bare) {
|
||||
update_ref(msg, "HEAD", &our->old_oid, NULL, 0,
|
||||
UPDATE_REFS_DIE_ON_ERR);
|
||||
install_branch_config(0, head, option_origin, our->name);
|
||||
install_branch_config(0, head, remote_name, our->name);
|
||||
}
|
||||
} else if (our) {
|
||||
struct commit *c = lookup_commit_reference(the_repository,
|
||||
@ -851,8 +852,26 @@ static int checkout(int submodule_progress)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int git_clone_config(const char *k, const char *v, void *cb)
|
||||
{
|
||||
if (!strcmp(k, "clone.defaultremotename")) {
|
||||
free(remote_name);
|
||||
remote_name = xstrdup(v);
|
||||
}
|
||||
return git_default_config(k, v, cb);
|
||||
}
|
||||
|
||||
static int write_one_config(const char *key, const char *value, void *data)
|
||||
{
|
||||
/*
|
||||
* give git_clone_config a chance to write config values back to the
|
||||
* environment, since git_config_set_multivar_gently only deals with
|
||||
* config-file writes
|
||||
*/
|
||||
int apply_failed = git_clone_config(key, value, data);
|
||||
if (apply_failed)
|
||||
return apply_failed;
|
||||
|
||||
return git_config_set_multivar_gently(key,
|
||||
value ? value : "true",
|
||||
CONFIG_REGEX_NONE, 0);
|
||||
@ -905,12 +924,12 @@ static void write_refspec_config(const char *src_ref_prefix,
|
||||
}
|
||||
/* Configure the remote */
|
||||
if (value.len) {
|
||||
strbuf_addf(&key, "remote.%s.fetch", option_origin);
|
||||
strbuf_addf(&key, "remote.%s.fetch", remote_name);
|
||||
git_config_set_multivar(key.buf, value.buf, "^$", 0);
|
||||
strbuf_reset(&key);
|
||||
|
||||
if (option_mirror) {
|
||||
strbuf_addf(&key, "remote.%s.mirror", option_origin);
|
||||
strbuf_addf(&key, "remote.%s.mirror", remote_name);
|
||||
git_config_set(key.buf, "true");
|
||||
strbuf_reset(&key);
|
||||
}
|
||||
@ -963,6 +982,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
|
||||
struct strvec ref_prefixes = STRVEC_INIT;
|
||||
|
||||
packet_trace_identity("clone");
|
||||
|
||||
git_config(git_clone_config, NULL);
|
||||
|
||||
argc = parse_options(argc, argv, prefix, builtin_clone_options,
|
||||
builtin_clone_usage, 0);
|
||||
|
||||
@ -991,9 +1013,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
|
||||
option_no_checkout = 1;
|
||||
}
|
||||
|
||||
if (!option_origin)
|
||||
option_origin = "origin";
|
||||
|
||||
repo_name = argv[0];
|
||||
|
||||
path = get_repo_path(repo_name, &is_bundle);
|
||||
@ -1124,9 +1143,30 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
|
||||
if (real_git_dir)
|
||||
git_dir = real_git_dir;
|
||||
|
||||
/*
|
||||
* additional config can be injected with -c, make sure it's included
|
||||
* after init_db, which clears the entire config environment.
|
||||
*/
|
||||
write_config(&option_config);
|
||||
|
||||
git_config(git_default_config, NULL);
|
||||
/*
|
||||
* re-read config after init_db and write_config to pick up any config
|
||||
* injected by --template and --config, respectively.
|
||||
*/
|
||||
git_config(git_clone_config, NULL);
|
||||
|
||||
/*
|
||||
* apply the remote name provided by --origin only after this second
|
||||
* call to git_config, to ensure it overrides all config-based values.
|
||||
*/
|
||||
if (option_origin != NULL)
|
||||
remote_name = xstrdup(option_origin);
|
||||
|
||||
if (remote_name == NULL)
|
||||
remote_name = xstrdup("origin");
|
||||
|
||||
if (!valid_remote_name(remote_name))
|
||||
die(_("'%s' is not a valid remote name"), remote_name);
|
||||
|
||||
if (option_bare) {
|
||||
if (option_mirror)
|
||||
@ -1135,15 +1175,15 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
|
||||
|
||||
git_config_set("core.bare", "true");
|
||||
} else {
|
||||
strbuf_addf(&branch_top, "refs/remotes/%s/", option_origin);
|
||||
strbuf_addf(&branch_top, "refs/remotes/%s/", remote_name);
|
||||
}
|
||||
|
||||
strbuf_addf(&key, "remote.%s.url", option_origin);
|
||||
strbuf_addf(&key, "remote.%s.url", remote_name);
|
||||
git_config_set(key.buf, repo);
|
||||
strbuf_reset(&key);
|
||||
|
||||
if (option_no_tags) {
|
||||
strbuf_addf(&key, "remote.%s.tagOpt", option_origin);
|
||||
strbuf_addf(&key, "remote.%s.tagOpt", remote_name);
|
||||
git_config_set(key.buf, "--no-tags");
|
||||
strbuf_reset(&key);
|
||||
}
|
||||
@ -1154,7 +1194,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
|
||||
if (option_sparse_checkout && git_sparse_checkout_init(dir))
|
||||
return 1;
|
||||
|
||||
remote = remote_get(option_origin);
|
||||
remote = remote_get(remote_name);
|
||||
|
||||
refspec_appendf(&remote->fetch, "+%s*:%s*", src_ref_prefix,
|
||||
branch_top.buf);
|
||||
@ -1266,7 +1306,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
|
||||
|
||||
if (!our_head_points_at)
|
||||
die(_("Remote branch %s not found in upstream %s"),
|
||||
option_branch, option_origin);
|
||||
option_branch, remote_name);
|
||||
}
|
||||
else
|
||||
our_head_points_at = remote_head_points_at;
|
||||
@ -1274,7 +1314,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
|
||||
else {
|
||||
if (option_branch)
|
||||
die(_("Remote branch %s not found in upstream %s"),
|
||||
option_branch, option_origin);
|
||||
option_branch, remote_name);
|
||||
|
||||
warning(_("You appear to have cloned an empty repository."));
|
||||
mapped_refs = NULL;
|
||||
@ -1286,7 +1326,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
|
||||
const char *branch = git_default_branch_name();
|
||||
char *ref = xstrfmt("refs/heads/%s", branch);
|
||||
|
||||
install_branch_config(0, branch, option_origin, ref);
|
||||
install_branch_config(0, branch, remote_name, ref);
|
||||
free(ref);
|
||||
}
|
||||
}
|
||||
@ -1295,7 +1335,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
|
||||
remote_head_points_at, &branch_top);
|
||||
|
||||
if (filter_options.choice)
|
||||
partial_clone_register(option_origin, &filter_options);
|
||||
partial_clone_register(remote_name, &filter_options);
|
||||
|
||||
if (is_local)
|
||||
clone_local(path, git_dir);
|
||||
@ -1327,6 +1367,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
|
||||
junk_mode = JUNK_LEAVE_REPO;
|
||||
err = checkout(submodule_progress);
|
||||
|
||||
free(remote_name);
|
||||
strbuf_release(&reflog_msg);
|
||||
strbuf_release(&branch_top);
|
||||
strbuf_release(&key);
|
||||
|
@ -194,8 +194,7 @@ static int add(int argc, const char **argv)
|
||||
if (remote_is_configured(remote, 1))
|
||||
die(_("remote %s already exists."), name);
|
||||
|
||||
strbuf_addf(&buf2, "refs/heads/test:refs/remotes/%s/test", name);
|
||||
if (!valid_fetch_refspec(buf2.buf))
|
||||
if (!valid_remote_name(name))
|
||||
die(_("'%s' is not a valid remote name"), name);
|
||||
|
||||
strbuf_addf(&buf, "remote.%s.url", name);
|
||||
@ -696,11 +695,9 @@ static int mv(int argc, const char **argv)
|
||||
if (remote_is_configured(newremote, 1))
|
||||
die(_("remote %s already exists."), rename.new_name);
|
||||
|
||||
strbuf_addf(&buf, "refs/heads/test:refs/remotes/%s/test", rename.new_name);
|
||||
if (!valid_fetch_refspec(buf.buf))
|
||||
if (!valid_remote_name(rename.new_name))
|
||||
die(_("'%s' is not a valid remote name"), rename.new_name);
|
||||
|
||||
strbuf_reset(&buf);
|
||||
strbuf_addf(&buf, "remote.%s", rename.old_name);
|
||||
strbuf_addf(&buf2, "remote.%s", rename.new_name);
|
||||
if (git_config_rename_section(buf.buf, buf2.buf) < 1)
|
||||
|
10
refspec.c
10
refspec.c
@ -245,6 +245,16 @@ int valid_fetch_refspec(const char *fetch_refspec_str)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int valid_remote_name(const char *name)
|
||||
{
|
||||
int result;
|
||||
struct strbuf refspec = STRBUF_INIT;
|
||||
strbuf_addf(&refspec, "refs/heads/test:refs/remotes/%s/test", name);
|
||||
result = valid_fetch_refspec(refspec.buf);
|
||||
strbuf_release(&refspec);
|
||||
return result;
|
||||
}
|
||||
|
||||
void refspec_ref_prefixes(const struct refspec *rs,
|
||||
struct strvec *ref_prefixes)
|
||||
{
|
||||
|
@ -64,6 +64,7 @@ void refspec_appendn(struct refspec *rs, const char **refspecs, int nr);
|
||||
void refspec_clear(struct refspec *rs);
|
||||
|
||||
int valid_fetch_refspec(const char *refspec);
|
||||
int valid_remote_name(const char *name);
|
||||
|
||||
struct strvec;
|
||||
/*
|
||||
|
@ -179,6 +179,13 @@ test_expect_success 'rename errors out early when deleting non-existent branch'
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'rename errors out early when when new name is invalid' '
|
||||
test_config remote.foo.vcs bar &&
|
||||
echo "fatal: '\''invalid...name'\'' is not a valid remote name" >expect &&
|
||||
test_must_fail git remote rename foo invalid...name 2>actual &&
|
||||
test_i18ncmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'add existing foreign_vcs remote' '
|
||||
test_config remote.foo.vcs bar &&
|
||||
echo "fatal: remote foo already exists." >expect &&
|
||||
@ -194,6 +201,12 @@ test_expect_success 'add existing foreign_vcs remote' '
|
||||
test_i18ncmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'add invalid foreign_vcs remote' '
|
||||
echo "fatal: '\''invalid...name'\'' is not a valid remote name" >expect &&
|
||||
test_must_fail git remote add invalid...name bar 2>actual &&
|
||||
test_i18ncmp expect actual
|
||||
'
|
||||
|
||||
cat >test/expect <<EOF
|
||||
* remote origin
|
||||
Fetch URL: $(pwd)/one
|
||||
|
@ -15,7 +15,73 @@ test_expect_success 'setup' '
|
||||
test_expect_success 'clone -o' '
|
||||
|
||||
git clone -o foo parent clone-o &&
|
||||
(cd clone-o && git rev-parse --verify refs/remotes/foo/master)
|
||||
git -C clone-o rev-parse --verify refs/remotes/foo/master
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'rejects invalid -o/--origin' '
|
||||
|
||||
test_must_fail git clone -o "bad...name" parent clone-bad-name 2>err &&
|
||||
test_i18ngrep "'\''bad...name'\'' is not a valid remote name" err
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'disallows --bare with --origin' '
|
||||
|
||||
test_must_fail git clone -o foo --bare parent clone-bare-o 2>err &&
|
||||
test_debug "cat err" &&
|
||||
test_i18ngrep -e "--bare and --origin foo options are incompatible" err
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'disallows --bare with --separate-git-dir' '
|
||||
|
||||
test_must_fail git clone --bare --separate-git-dir dot-git-destiation parent clone-bare-sgd 2>err &&
|
||||
test_debug "cat err" &&
|
||||
test_i18ngrep -e "--bare and --separate-git-dir are incompatible" err
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'uses "origin" for default remote name' '
|
||||
|
||||
git clone parent clone-default-origin &&
|
||||
git -C clone-default-origin rev-parse --verify refs/remotes/origin/master
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'prefers --template config over normal config' '
|
||||
|
||||
template="$TRASH_DIRECTORY/template-with-config" &&
|
||||
mkdir "$template" &&
|
||||
git config --file "$template/config" foo.bar from_template &&
|
||||
test_config_global foo.bar from_global &&
|
||||
git clone "--template=$template" parent clone-template-config &&
|
||||
test "$(git -C clone-template-config config --local foo.bar)" = "from_template"
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'prefers -c config over --template config' '
|
||||
|
||||
template="$TRASH_DIRECTORY/template-with-ignored-config" &&
|
||||
mkdir "$template" &&
|
||||
git config --file "$template/config" foo.bar from_template &&
|
||||
git clone "--template=$template" -c foo.bar=inline parent clone-template-inline-config &&
|
||||
test "$(git -C clone-template-inline-config config --local foo.bar)" = "inline"
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'prefers config "clone.defaultRemoteName" over default' '
|
||||
|
||||
test_config_global clone.defaultRemoteName from_config &&
|
||||
git clone parent clone-config-origin &&
|
||||
git -C clone-config-origin rev-parse --verify refs/remotes/from_config/master
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'prefers --origin over -c config' '
|
||||
|
||||
git clone -c clone.defaultRemoteName=inline --origin from_option parent clone-o-and-inline-config &&
|
||||
git -C clone-o-and-inline-config rev-parse --verify refs/remotes/from_option/master
|
||||
|
||||
'
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user