Code

Merge branch 'jn/remote-set-branches'
authorJunio C Hamano <gitster@pobox.com>
Fri, 18 Jun 2010 18:16:55 +0000 (11:16 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 18 Jun 2010 18:16:55 +0000 (11:16 -0700)
* jn/remote-set-branches:
  Add git remote set-branches

Conflicts:
builtin/remote.c

1  2 
Documentation/git-remote.txt
builtin/remote.c
t/t5505-remote.sh

index ebaaadc1786b3581f70764aff5d0af4038c86644,de4386bf7eca2d236a320d83f514cd5010461a1c..aa021b0cb803a9cd9f08566f6a6fe407eb025e4b
@@@ -10,10 -10,11 +10,11 @@@ SYNOPSI
  --------
  [verse]
  'git remote' [-v | --verbose]
 -'git remote add' [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>
 +'git remote add' [-t <branch>] [-m <master>] [-f] [--tags|--no-tags] [--mirror] <name> <url>
  '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>
@@@ -51,12 -52,6 +52,12 @@@ update remote-tracking branches <name>/
  With `-f` option, `git fetch <name>` is run immediately after
  the remote information is set up.
  +
 +With `--tags` option, `git fetch <name>` imports every tag from the
 +remote repository.
 ++
 +With `--no-tags` option, `git fetch <name>` does not import tags from
 +the remote repository.
 ++
  With `-t <branch>` option, instead of the default glob
  refspec for the remote to track all branches under
  `$GIT_DIR/remotes/<name>/`, a refspec to track only `<branch>`
@@@ -110,6 -105,18 +111,18 @@@ remote set-head origin master" will se
  `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
diff --combined builtin/remote.c
index 0e99a9957dad39ded17ce98de49b164fdb70eacd,c4d17b52bc7725c1e1c77e1ce3e2379729a924c4..4745957b9602ed9fe5c983de35ad74cc85e9b537
@@@ -16,6 -16,7 +16,7 @@@ static const char * const builtin_remot
        "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 +43,12 @@@ static const char * const builtin_remot
        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,15 -111,23 +111,29 @@@ static int fetch_remote(const char *nam
        return 0;
  }
  
 +enum {
 +      TAGS_UNSET = 0,
 +      TAGS_DEFAULT = 1,
 +      TAGS_SET = 2
 +};
 +
+ 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;
 +      int fetch = 0, mirror = 0, fetch_tags = TAGS_DEFAULT;
        struct string_list track = { NULL, 0, 0 };
        const char *master = NULL;
        struct remote *remote;
  
        struct option options[] = {
                OPT_BOOLEAN('f', "fetch", &fetch, "fetch the remote branches"),
 +              OPT_SET_INT(0, "tags", &fetch_tags,
 +                          "import all tags and associated objects when fetching",
 +                          TAGS_SET),
 +              OPT_SET_INT(0, NULL, &fetch_tags,
 +                          "or do not fetch any tag at all (--no-tags)", TAGS_UNSET),
                OPT_CALLBACK('t', "track", &track, "branch",
                        "branch(es) to track", opt_parse_track),
                OPT_STRING('m', "master", &master, "branch", "master branch"),
        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;
        }
  
                        return 1;
        }
  
 +      if (fetch_tags != TAGS_DEFAULT) {
 +              strbuf_reset(&buf);
 +              strbuf_addf(&buf, "remote.%s.tagopt", name);
 +              if (git_config_set(buf.buf,
 +                      fetch_tags == TAGS_SET ? "--tags" : "--no-tags"))
 +                      return 1;
 +      }
 +
        if (fetch && fetch_remote(name))
                return 1;
  
@@@ -1284,6 -1277,72 +1296,72 @@@ static int update(int argc, const char 
        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;
@@@ -1449,6 -1508,8 +1527,8 @@@ int cmd_remote(int argc, const char **a
                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"))
diff --combined t/t5505-remote.sh
index 41f17e76939ff0470878018cfbc11b45a8e92137,8aaf53ed818126291cc644e02d6dcd569edbbff6..4c498b1902e4b0c74ea8d6bfc039d583bcc4364e
@@@ -320,69 -320,6 +320,69 @@@ test_expect_success 'add alt && prune' 
         git rev-parse --verify refs/remotes/origin/side2)
  '
  
 +cat >test/expect <<\EOF
 +some-tag
 +EOF
 +
 +test_expect_success 'add with reachable tags (default)' '
 +      (cd one &&
 +       >foobar &&
 +       git add foobar &&
 +       git commit -m "Foobar" &&
 +       git tag -a -m "Foobar tag" foobar-tag &&
 +       git reset --hard HEAD~1 &&
 +       git tag -a -m "Some tag" some-tag) &&
 +      (mkdir add-tags &&
 +       cd add-tags &&
 +       git init &&
 +       git remote add -f origin ../one &&
 +       git tag -l some-tag >../test/output &&
 +       git tag -l foobar-tag >>../test/output &&
 +       test_must_fail git config remote.origin.tagopt) &&
 +      test_cmp test/expect test/output
 +'
 +
 +cat >test/expect <<\EOF
 +some-tag
 +foobar-tag
 +--tags
 +EOF
 +
 +test_expect_success 'add --tags' '
 +      (rm -rf add-tags &&
 +       mkdir add-tags &&
 +       cd add-tags &&
 +       git init &&
 +       git remote add -f --tags origin ../one &&
 +       git tag -l some-tag >../test/output &&
 +       git tag -l foobar-tag >>../test/output &&
 +       git config remote.origin.tagopt >>../test/output) &&
 +      test_cmp test/expect test/output
 +'
 +
 +cat >test/expect <<\EOF
 +--no-tags
 +EOF
 +
 +test_expect_success 'add --no-tags' '
 +      (rm -rf add-tags &&
 +       mkdir add-no-tags &&
 +       cd add-no-tags &&
 +       git init &&
 +       git remote add -f --no-tags origin ../one &&
 +       git tag -l some-tag >../test/output &&
 +       git tag -l foobar-tag >../test/output &&
 +       git config remote.origin.tagopt >>../test/output) &&
 +      (cd one &&
 +       git tag -d some-tag foobar-tag) &&
 +      test_cmp test/expect test/output
 +'
 +
 +test_expect_success 'reject --no-no-tags' '
 +      (cd add-no-tags &&
 +       test_must_fail git remote add -f --no-no-tags neworigin ../one)
 +'
 +
  cat > one/expect << EOF
    apis/master
    apis/side
@@@ -597,6 -534,94 +597,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 &&