From b92c5f228a9c07fe339c8fd5406069602b6452f6 Mon Sep 17 00:00:00 2001 From: Finn Arne Gangstad Date: Fri, 3 Apr 2009 11:02:37 +0200 Subject: [PATCH 1/6] builtin-remote.c: Split out prune_remote as a separate function. prune_remote will be used in update(), so this function was split out to avoid code duplication. Signed-off-by: Finn Arne Gangstad Signed-off-by: Junio C Hamano --- builtin-remote.c | 56 ++++++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/builtin-remote.c b/builtin-remote.c index 9ef846f6a4..c53966fd8d 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -26,6 +26,7 @@ static const char * const builtin_remote_usage[] = { static int verbose; static int show_all(void); +static int prune_remote(const char *remote, int dry_run); static inline int postfixcmp(const char *string, const char *postfix) { @@ -1128,46 +1129,49 @@ static int prune(int argc, const char **argv) OPT__DRY_RUN(&dry_run), OPT_END() }; - struct ref_states states; - const char *dangling_msg; argc = parse_options(argc, argv, options, builtin_remote_usage, 0); if (argc < 1) usage_with_options(builtin_remote_usage, options); - dangling_msg = (dry_run - ? " %s will become dangling!\n" - : " %s has become dangling!\n"); + for (; argc; argc--, argv++) + result |= prune_remote(*argv, dry_run); - memset(&states, 0, sizeof(states)); - for (; argc; argc--, argv++) { - int i; - - get_remote_ref_states(*argv, &states, GET_REF_STATES); + return result; +} - if (states.stale.nr) { - printf("Pruning %s\n", *argv); - printf("URL: %s\n", - states.remote->url_nr - ? states.remote->url[0] - : "(no URL)"); - } +static int prune_remote(const char *remote, int dry_run) +{ + int result = 0, i; + struct ref_states states; + const char *dangling_msg = dry_run + ? " %s will become dangling!\n" + : " %s has become dangling!\n"; - for (i = 0; i < states.stale.nr; i++) { - const char *refname = states.stale.items[i].util; + memset(&states, 0, sizeof(states)); + get_remote_ref_states(remote, &states, GET_REF_STATES); + + if (states.stale.nr) { + printf("Pruning %s\n", remote); + printf("URL: %s\n", + states.remote->url_nr + ? states.remote->url[0] + : "(no URL)"); + } - if (!dry_run) - result |= delete_ref(refname, NULL, 0); + for (i = 0; i < states.stale.nr; i++) { + const char *refname = states.stale.items[i].util; - printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned", - abbrev_ref(refname, "refs/remotes/")); - warn_dangling_symref(dangling_msg, refname); - } + if (!dry_run) + result |= delete_ref(refname, NULL, 0); - free_remote_ref_states(&states); + printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned", + abbrev_ref(refname, "refs/remotes/")); + warn_dangling_symref(dangling_msg, refname); } + free_remote_ref_states(&states); return result; } From efa54803cb1dc15923799f94abf82cb0433c2b9b Mon Sep 17 00:00:00 2001 From: Finn Arne Gangstad Date: Fri, 3 Apr 2009 11:03:44 +0200 Subject: [PATCH 2/6] git remote update: New option --prune With the --prune (or -p) option, git remote update will also prune all the remotes that it fetches. Previously, you had to do a manual git remote prune for each of the remotes you wanted to prune, and this could be tedious with many remotes. A single command will now update a set of remotes, and remove all stale branches: git remote update -p [group] Signed-off-by: Finn Arne Gangstad Signed-off-by: Junio C Hamano --- Documentation/git-remote.txt | 4 +++- builtin-remote.c | 20 ++++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt index c9c0e6f932..0b6e67dbca 100644 --- a/Documentation/git-remote.txt +++ b/Documentation/git-remote.txt @@ -16,7 +16,7 @@ SYNOPSIS 'git remote set-head' [-a | -d | ] 'git remote show' [-n] 'git remote prune' [-n | --dry-run] -'git remote update' [group] +'git remote update' [-p | --prune] [group] DESCRIPTION ----------- @@ -125,6 +125,8 @@ the configuration parameter remotes.default will get used; if remotes.default is not defined, all remotes which do not have the configuration parameter remote..skipDefaultUpdate set to true will be updated. (See linkgit:git-config[1]). ++ +With `--prune` option, prune all the remotes that are updated. DISCUSSION diff --git a/builtin-remote.c b/builtin-remote.c index c53966fd8d..3146eb467d 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -15,7 +15,7 @@ static const char * const builtin_remote_usage[] = { "git remote set-head [-a | -d | ]", "git remote show [-n] ", "git remote prune [-n | --dry-run] ", - "git remote [-v | --verbose] update [group]", + "git remote [-v | --verbose] update [-p | --prune] [group]", NULL }; @@ -1208,10 +1208,18 @@ static int get_remote_group(const char *key, const char *value, void *cb) static int update(int argc, const char **argv) { - int i, result = 0; + int i, result = 0, prune = 0; struct string_list list = { NULL, 0, 0, 0 }; static const char *default_argv[] = { NULL, "default", NULL }; + struct option options[] = { + OPT_GROUP("update specific options"), + OPT_BOOLEAN('p', "prune", &prune, + "prune remotes after fecthing"), + OPT_END() + }; + argc = parse_options(argc, argv, options, builtin_remote_usage, + PARSE_OPT_KEEP_ARGV0); if (argc < 2) { argc = 2; argv = default_argv; @@ -1226,8 +1234,12 @@ static int update(int argc, const char **argv) if (!result && !list.nr && argc == 2 && !strcmp(argv[1], "default")) result = for_each_remote(get_one_remote_for_update, &list); - for (i = 0; i < list.nr; i++) - result |= fetch_remote(list.items[i].string); + for (i = 0; i < list.nr; i++) { + int err = fetch_remote(list.items[i].string); + result |= err; + if (!err && prune) + result |= prune_remote(list.items[i].string, 0); + } /* all names were strdup()ed or strndup()ed */ list.strdup_strings = 1; From bed5d42163ec2e2ddde3b1d78d303a4fb39bc0d0 Mon Sep 17 00:00:00 2001 From: Finn Arne Gangstad Date: Mon, 6 Apr 2009 15:41:00 +0200 Subject: [PATCH 3/6] git remote update: Report error for non-existing groups Previosly, git remote update would just silently fail and do nothing. Now it will report an error saying that the group does not exist. Signed-off-by: Finn Arne Gangstad Signed-off-by: Junio C Hamano --- builtin-remote.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/builtin-remote.c b/builtin-remote.c index 3146eb467d..51df99ba93 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -1188,16 +1188,18 @@ struct remote_group { struct string_list *list; } remote_group; -static int get_remote_group(const char *key, const char *value, void *cb) +static int get_remote_group(const char *key, const char *value, void *num_hits) { if (!prefixcmp(key, "remotes.") && !strcmp(key + 8, remote_group.name)) { /* split list by white space */ int space = strcspn(value, " \t\n"); while (*value) { - if (space > 1) + if (space > 1) { string_list_append(xstrndup(value, space), remote_group.list); + ++*((int *)num_hits); + } value += space + (value[space] != '\0'); space = strcspn(value, " \t\n"); } @@ -1227,8 +1229,11 @@ static int update(int argc, const char **argv) remote_group.list = &list; for (i = 1; i < argc; i++) { + int groups_found = 0; remote_group.name = argv[i]; - result = git_config(get_remote_group, NULL); + result = git_config(get_remote_group, &groups_found); + if (!groups_found && (i != 1 || strcmp(argv[1], "default"))) + die("No such remote group: '%s'", argv[i]); } if (!result && !list.nr && argc == 2 && !strcmp(argv[1], "default")) From 9a23ba3375e2afa8045a433a3debce99c373beb2 Mon Sep 17 00:00:00 2001 From: Finn Arne Gangstad Date: Mon, 6 Apr 2009 15:41:01 +0200 Subject: [PATCH 4/6] remote: New function remote_is_configured() Previously, there was no easy way to check for the existence of a configured remote. remote_get for example would always create the remote "on demand". This new function returns 1 if the remote is configured, 0 otherwise. Signed-off-by: Finn Arne Gangstad Signed-off-by: Junio C Hamano --- remote.c | 11 +++++++++++ remote.h | 1 + 2 files changed, 12 insertions(+) diff --git a/remote.c b/remote.c index 2b037f11b2..b36fd70978 100644 --- a/remote.c +++ b/remote.c @@ -667,6 +667,17 @@ struct remote *remote_get(const char *name) return ret; } +int remote_is_configured(const char *name) +{ + int i; + read_config(); + + for (i = 0; i < remotes_nr; i++) + if (!strcmp(name, remotes[i]->name)) + return 1; + return 0; +} + int for_each_remote(each_remote_fn fn, void *priv) { int i, result = 0; diff --git a/remote.h b/remote.h index de3d21b662..99706a89bc 100644 --- a/remote.h +++ b/remote.h @@ -45,6 +45,7 @@ struct remote { }; struct remote *remote_get(const char *name); +int remote_is_configured(const char *name); typedef int each_remote_fn(struct remote *remote, void *priv); int for_each_remote(each_remote_fn fn, void *priv); From b344e1614b15dfde0ab4dfc175bed1aac39bc264 Mon Sep 17 00:00:00 2001 From: Finn Arne Gangstad Date: Mon, 6 Apr 2009 15:41:02 +0200 Subject: [PATCH 5/6] git remote update: Fallback to remote if group does not exist Previously, git remote update would fail unless there was a remote group configured with the same name as the remote. git remote update will now fall back to using the remote if no matching group can be found. This enables "git remote update -p ..." to fetch and prune one or more remotes, for example. Signed-off-by: Finn Arne Gangstad Signed-off-by: Junio C Hamano --- Documentation/git-remote.txt | 2 +- builtin-remote.c | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt index 0b6e67dbca..9e2b4eaa38 100644 --- a/Documentation/git-remote.txt +++ b/Documentation/git-remote.txt @@ -16,7 +16,7 @@ SYNOPSIS 'git remote set-head' [-a | -d | ] 'git remote show' [-n] 'git remote prune' [-n | --dry-run] -'git remote update' [-p | --prune] [group] +'git remote update' [-p | --prune] [group | remote]... DESCRIPTION ----------- diff --git a/builtin-remote.c b/builtin-remote.c index 51df99ba93..ca7c639ad3 100644 --- a/builtin-remote.c +++ b/builtin-remote.c @@ -1232,8 +1232,14 @@ static int update(int argc, const char **argv) int groups_found = 0; remote_group.name = argv[i]; result = git_config(get_remote_group, &groups_found); - if (!groups_found && (i != 1 || strcmp(argv[1], "default"))) - die("No such remote group: '%s'", argv[i]); + if (!groups_found && (i != 1 || strcmp(argv[1], "default"))) { + struct remote *remote; + if (!remote_is_configured(argv[i])) + die("No such remote or remote group: %s", + argv[i]); + remote = remote_get(argv[i]); + string_list_append(remote->name, remote_group.list); + } } if (!result && !list.nr && argc == 2 && !strcmp(argv[1], "default")) From 27845e9548b7b5b316d89f64546466f2004ee414 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Mon, 6 Apr 2009 16:18:23 -0400 Subject: [PATCH 6/6] add tests for remote groups This tries to systematically cover existing behavior, and also mark some expect_failure cases for desired behavior. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- t/t5506-remote-groups.sh | 81 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100755 t/t5506-remote-groups.sh diff --git a/t/t5506-remote-groups.sh b/t/t5506-remote-groups.sh new file mode 100755 index 0000000000..2a1806b0b4 --- /dev/null +++ b/t/t5506-remote-groups.sh @@ -0,0 +1,81 @@ +#!/bin/sh + +test_description='git remote group handling' +. ./test-lib.sh + +mark() { + echo "$1" >mark +} + +update_repo() { + (cd $1 && + echo content >>file && + git add file && + git commit -F ../mark) +} + +update_repos() { + update_repo one $1 && + update_repo two $1 +} + +repo_fetched() { + if test "`git log -1 --pretty=format:%s $1 --`" = "`cat mark`"; then + echo >&2 "repo was fetched: $1" + return 0 + fi + echo >&2 "repo was not fetched: $1" + return 1 +} + +test_expect_success 'setup' ' + mkdir one && (cd one && git init) && + mkdir two && (cd two && git init) && + git remote add -m master one one && + git remote add -m master two two +' + +test_expect_success 'no group updates all' ' + mark update-all && + update_repos && + git remote update && + repo_fetched one && + repo_fetched two +' + +test_expect_success 'nonexistant group produces error' ' + mark nonexistant && + update_repos && + test_must_fail git remote update nonexistant && + ! repo_fetched one && + ! repo_fetched two +' + +test_expect_success 'updating group updates all members' ' + mark group-all && + update_repos && + git config --add remotes.all one && + git config --add remotes.all two && + git remote update all && + repo_fetched one && + repo_fetched two +' + +test_expect_success 'updating group does not update non-members' ' + mark group-some && + update_repos && + git config --add remotes.some one && + git remote update some && + repo_fetched one && + ! repo_fetched two +' + +test_expect_success 'updating remote name updates that remote' ' + mark remote-name && + update_repos && + git remote update one && + repo_fetched one && + ! repo_fetched two +' + +test_done