1. Stefan Saasen
  2. git

Commits

Junio C Hamano  committed c276857 Merge

Merge branch 'fg/remote-prune'

* fg/remote-prune:
add tests for remote groups
git remote update: Fallback to remote if group does not exist
remote: New function remote_is_configured()
git remote update: Report error for non-existing groups
git remote update: New option --prune
builtin-remote.c: Split out prune_remote as a separate function.

  • Participants
  • Parent commits 07fb030, 27845e9
  • Branches master

Comments (0)

Files changed (5)

File Documentation/git-remote.txt

View file
  • Ignore whitespace
 'git remote set-head' <name> [-a | -d | <branch>]
 'git remote show' [-n] <name>
 'git remote prune' [-n | --dry-run] <name>
-'git remote update' [group]
+'git remote update' [-p | --prune] [group | remote]...
 
 DESCRIPTION
 -----------
 remotes.default is not defined, all remotes which do not have the
 configuration parameter remote.<name>.skipDefaultUpdate set to true will
 be updated.  (See linkgit:git-config[1]).
++
+With `--prune` option, prune all the remotes that are updated.
 
 
 DISCUSSION

File builtin-remote.c

View file
  • Ignore whitespace
 	"git remote set-head <name> [-a | -d | <branch>]",
 	"git remote show [-n] <name>",
 	"git remote prune [-n | --dry-run] <name>",
-	"git remote [-v | --verbose] update [group]",
+	"git remote [-v | --verbose] update [-p | --prune] [group]",
 	NULL
 };
 
 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)
 {
 		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");
-
-	memset(&states, 0, sizeof(states));
-	for (; argc; argc--, argv++) {
-		int i;
+	for (; argc; argc--, argv++)
+		result |= prune_remote(*argv, dry_run);
 
-		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;
 }
 
 	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");
 		}
 
 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;
 
 	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"))) {
+			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"))
 		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;

File remote.c

View file
  • Ignore whitespace
 	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;

File remote.h

View file
  • Ignore whitespace
 };
 
 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);

File t/t5506-remote-groups.sh

View file
  • Ignore whitespace
+#!/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