Add git remote set-branches

Add ‘git remote set-branches’ for changing the list of tracked refs
for a remote repository with one "porcelain-level" command.  This
complements the longstanding ‘git remote add --track’ option.

The interface is based on the ‘git remote set-url’ subcommand.

   git remote set-branches base --add C
   git remote set-branches base A B D
   git remote set-branches base --delete D; # not implemented

Suggested-by: martin f. krafft <madduck@debian.org>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Jonathan Nieder 2010-05-19 13:38:50 -05:00 committed by Junio C Hamano
parent 636e87d705
commit 3d8b69495f
3 changed files with 192 additions and 11 deletions

View File

@ -14,6 +14,7 @@ SYNOPSIS
'git remote rename' <old> <new>
'git remote rm' <name>
'git remote set-head' <name> (-a | -d | <branch>)
'git remote set-branches' <name> [--add] <branch>...
'git remote set-url' [--push] <name> <newurl> [<oldurl>]
'git remote set-url --add' [--push] <name> <newurl>
'git remote set-url --delete' [--push] <name> <url>
@ -104,6 +105,18 @@ remote set-head origin master" will set `$GIT_DIR/refs/remotes/origin/HEAD` to
`refs/remotes/origin/master` already exists; if not it must be fetched first.
+
'set-branches'::
Changes the list of branches tracked by the named remote.
This can be used to track a subset of the available remote branches
after the initial setup for a remote.
+
The named branches will be interpreted as if specified with the
`-t` option on the 'git remote add' command line.
+
With `--add`, instead of replacing the list of currently tracked
branches, adds to that list.
'set-url'::
Changes URL remote points to. Sets first URL remote points to matching

View File

@ -16,6 +16,7 @@ static const char * const builtin_remote_usage[] = {
"git remote [-v | --verbose] show [-n] <name>",
"git remote prune [-n | --dry-run] <name>",
"git remote [-v | --verbose] update [-p | --prune] [group | remote]",
"git remote set-branches <name> [--add] <branch>...",
"git remote set-url <name> <newurl> [<oldurl>]",
"git remote set-url --add <name> <newurl>",
"git remote set-url --delete <name> <url>",
@ -42,6 +43,12 @@ static const char * const builtin_remote_sethead_usage[] = {
NULL
};
static const char * const builtin_remote_setbranches_usage[] = {
"git remote set-branches <name> <branch>...",
"git remote set-branches --add <name> <branch>...",
NULL
};
static const char * const builtin_remote_show_usage[] = {
"git remote show [<options>] <name>",
NULL
@ -104,6 +111,20 @@ static int fetch_remote(const char *name)
return 0;
}
static int add_branch(const char *key, const char *branchname,
const char *remotename, int mirror, struct strbuf *tmp)
{
strbuf_reset(tmp);
strbuf_addch(tmp, '+');
if (mirror)
strbuf_addf(tmp, "refs/%s:refs/%s",
branchname, branchname);
else
strbuf_addf(tmp, "refs/heads/%s:refs/remotes/%s/%s",
branchname, remotename, branchname);
return git_config_set_multivar(key, tmp->buf, "^$", 0);
}
static int add(int argc, const char **argv)
{
int fetch = 0, mirror = 0;
@ -151,17 +172,8 @@ static int add(int argc, const char **argv)
if (track.nr == 0)
string_list_append("*", &track);
for (i = 0; i < track.nr; i++) {
struct string_list_item *item = track.items + i;
strbuf_reset(&buf2);
strbuf_addch(&buf2, '+');
if (mirror)
strbuf_addf(&buf2, "refs/%s:refs/%s",
item->string, item->string);
else
strbuf_addf(&buf2, "refs/heads/%s:refs/remotes/%s/%s",
item->string, name, item->string);
if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0))
if (add_branch(buf.buf, track.items[i].string,
name, mirror, &buf2))
return 1;
}
@ -1265,6 +1277,72 @@ static int update(int argc, const char **argv)
return run_command_v_opt(fetch_argv, RUN_GIT_CMD);
}
static int remove_all_fetch_refspecs(const char *remote, const char *key)
{
return git_config_set_multivar(key, NULL, NULL, 1);
}
static int add_branches(struct remote *remote, const char **branches,
const char *key)
{
const char *remotename = remote->name;
int mirror = remote->mirror;
struct strbuf refspec = STRBUF_INIT;
for (; *branches; branches++)
if (add_branch(key, *branches, remotename, mirror, &refspec)) {
strbuf_release(&refspec);
return 1;
}
strbuf_release(&refspec);
return 0;
}
static int set_remote_branches(const char *remotename, const char **branches,
int add_mode)
{
struct strbuf key = STRBUF_INIT;
struct remote *remote;
strbuf_addf(&key, "remote.%s.fetch", remotename);
if (!remote_is_configured(remotename))
die("No such remote '%s'", remotename);
remote = remote_get(remotename);
if (!add_mode && remove_all_fetch_refspecs(remotename, key.buf)) {
strbuf_release(&key);
return 1;
}
if (add_branches(remote, branches, key.buf)) {
strbuf_release(&key);
return 1;
}
strbuf_release(&key);
return 0;
}
static int set_branches(int argc, const char **argv)
{
int add_mode = 0;
struct option options[] = {
OPT_BOOLEAN('\0', "add", &add_mode, "add branch"),
OPT_END()
};
argc = parse_options(argc, argv, NULL, options,
builtin_remote_setbranches_usage, 0);
if (argc == 0) {
error("no remote specified");
usage_with_options(builtin_remote_seturl_usage, options);
}
argv[argc] = NULL;
return set_remote_branches(argv[0], argv + 1, add_mode);
}
static int set_url(int argc, const char **argv)
{
int i, push_mode = 0, add_mode = 0, delete_mode = 0;
@ -1430,6 +1508,8 @@ int cmd_remote(int argc, const char **argv, const char *prefix)
result = rm(argc, argv);
else if (!strcmp(argv[0], "set-head"))
result = set_head(argc, argv);
else if (!strcmp(argv[0], "set-branches"))
result = set_branches(argc, argv);
else if (!strcmp(argv[0], "set-url"))
result = set_url(argc, argv);
else if (!strcmp(argv[0], "show"))

View File

@ -534,6 +534,94 @@ test_expect_success 'show empty remote' '
)
'
test_expect_success 'remote set-branches requires a remote' '
test_must_fail git remote set-branches &&
test_must_fail git remote set-branches --add
'
test_expect_success 'remote set-branches' '
echo "+refs/heads/*:refs/remotes/scratch/*" >expect.initial &&
sort <<-\EOF >expect.add &&
+refs/heads/*:refs/remotes/scratch/*
+refs/heads/other:refs/remotes/scratch/other
EOF
sort <<-\EOF >expect.replace &&
+refs/heads/maint:refs/remotes/scratch/maint
+refs/heads/master:refs/remotes/scratch/master
+refs/heads/next:refs/remotes/scratch/next
EOF
sort <<-\EOF >expect.add-two &&
+refs/heads/maint:refs/remotes/scratch/maint
+refs/heads/master:refs/remotes/scratch/master
+refs/heads/next:refs/remotes/scratch/next
+refs/heads/pu:refs/remotes/scratch/pu
+refs/heads/t/topic:refs/remotes/scratch/t/topic
EOF
sort <<-\EOF >expect.setup-ffonly &&
refs/heads/master:refs/remotes/scratch/master
+refs/heads/next:refs/remotes/scratch/next
EOF
sort <<-\EOF >expect.respect-ffonly &&
refs/heads/master:refs/remotes/scratch/master
+refs/heads/next:refs/remotes/scratch/next
+refs/heads/pu:refs/remotes/scratch/pu
EOF
git clone .git/ setbranches &&
(
cd setbranches &&
git remote rename origin scratch &&
git config --get-all remote.scratch.fetch >config-result &&
sort <config-result >../actual.initial &&
git remote set-branches scratch --add other &&
git config --get-all remote.scratch.fetch >config-result &&
sort <config-result >../actual.add &&
git remote set-branches scratch maint master next &&
git config --get-all remote.scratch.fetch >config-result &&
sort <config-result >../actual.replace &&
git remote set-branches --add scratch pu t/topic &&
git config --get-all remote.scratch.fetch >config-result &&
sort <config-result >../actual.add-two &&
git config --unset-all remote.scratch.fetch &&
git config remote.scratch.fetch \
refs/heads/master:refs/remotes/scratch/master &&
git config --add remote.scratch.fetch \
+refs/heads/next:refs/remotes/scratch/next &&
git config --get-all remote.scratch.fetch >config-result &&
sort <config-result >../actual.setup-ffonly &&
git remote set-branches --add scratch pu &&
git config --get-all remote.scratch.fetch >config-result &&
sort <config-result >../actual.respect-ffonly
) &&
test_cmp expect.initial actual.initial &&
test_cmp expect.add actual.add &&
test_cmp expect.replace actual.replace &&
test_cmp expect.add-two actual.add-two &&
test_cmp expect.setup-ffonly actual.setup-ffonly &&
test_cmp expect.respect-ffonly actual.respect-ffonly
'
test_expect_success 'remote set-branches with --mirror' '
echo "+refs/*:refs/*" >expect.initial &&
echo "+refs/heads/master:refs/heads/master" >expect.replace &&
git clone --mirror .git/ setbranches-mirror &&
(
cd setbranches-mirror &&
git remote rename origin scratch &&
git config --get-all remote.scratch.fetch >../actual.initial &&
git remote set-branches scratch heads/master &&
git config --get-all remote.scratch.fetch >../actual.replace
) &&
test_cmp expect.initial actual.initial &&
test_cmp expect.replace actual.replace
'
test_expect_success 'new remote' '
git remote add someremote foo &&
echo foo >expect &&