Git development
 help / color / mirror / Atom feed
* Re: [PATCH v2 4/4] Add explicit Cygwin check to guard WIN32 header inclusion
From: Ramsay Jones @ 2009-11-09 19:26 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Ramsay Jones, Marius Storm-Olsen, Johannes Sixt, GIT Mailing-list
In-Reply-To: <4AF5D6F8.40608@ramsay1.demon.co.uk>

Ramsay Jones wrote:
> diff --git a/git-compat-util.h b/git-compat-util.h
> index ef60803..c4b9e5a 100644
> --- a/git-compat-util.h
> +++ b/git-compat-util.h
> @@ -65,10 +65,10 @@
>  #define _NETBSD_SOURCE 1
>  #define _SGI_SOURCE 1
>  
> -#ifdef WIN32 /* Both MinGW and MSVC */
> -#define WIN32_LEAN_AND_MEAN  /* stops windows.h including winsock.h */
> -#include <winsock2.h>
> -#include <windows.h>
> +#if defined(_WIN32) && !defined(__CYGWIN__) /* Both MinGW and MSVC */
> +# define WIN32_LEAN_AND_MEAN  /* stops windows.h including winsock.h */
> +# include <winsock2.h>
> +# include <windows.h>

Arrgghhh.

Sorry, Junio, I meant to change this before sending it out. :(

I had intended to remove the added indentation (which I don't even remember
doing!), since it obscures the "real change", which simply consists of the
new expression in the #if preprocessor line.

I'll send a new version of this patch.

ATB,
Ramsay Jones

^ permalink raw reply

* [PATCH v3 4/4] Add explicit Cygwin check to guard WIN32 header inclusion
From: Ramsay Jones @ 2009-11-09 19:31 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Marius Storm-Olsen, Johannes Sixt, GIT Mailing-list

Since commit 435bdf8c ("Make usage of windows.h lean and mean",
16-9-2009), the amount of code potentially including the WIN32
API header files has greatly increased. In particular, the Cygwin
build is at greater risk of inadvertently including WIN32 code
within preprocessor sections protected by the WIN32 or _WIN32
macros.

The previous commit message, along with comments elsewhere, assert
that the WIN32 macro is not defined on Cygwin. Currently, this is
true for the cygwin build. However, the cygwin platform can be
used to develop WIN32 GUI, WIN32 console, and POSIX applications.
Indeed it is possible to create applications which use a mix of
the WIN32 API and POSIX code (eg git!).

Unlike native WIN32 compilers, gcc on cygwin does not automatically
define the _WIN32 macro. However, as soon as you include the
<windows.h> header file, the _WIN32 and WIN32 macros are defined.

In order to reduce the risk of problems in the future, we protect
the inclusion of the windows header with an explicit check for
__CYGWIN__. Also, we move the other use of the <windows.h> header
from compat/win32.h to compat/cygwin.c

Signed-off-by: Ramsay Jones <ramsay@ramsay1.demon.co.uk>
---

Changes since v2:
    - removed indentation from the #if-#endif block

I hope you don't mind, but I've only sent this patch, rather than
re-send the whole series. If you would like a complete re-send, just
let me know.

ATB,
Ramsay Jones

 compat/cygwin.c   |    1 +
 compat/win32.h    |    3 ---
 git-compat-util.h |    2 +-
 3 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/compat/cygwin.c b/compat/cygwin.c
index b4a51b9..6695515 100644
--- a/compat/cygwin.c
+++ b/compat/cygwin.c
@@ -1,5 +1,6 @@
 #define WIN32_LEAN_AND_MEAN
 #include "../git-compat-util.h"
+#include <windows.h>
 #include "win32.h"
 #include "../cache.h" /* to read configuration */
 
diff --git a/compat/win32.h b/compat/win32.h
index 8ce9104..a7ed72b 100644
--- a/compat/win32.h
+++ b/compat/win32.h
@@ -2,9 +2,6 @@
 #define WIN32_H
 
 /* common Win32 functions for MinGW and Cygwin */
-#ifndef WIN32         /* Not defined by Cygwin */
-#include <windows.h>
-#endif
 
 static inline int file_attr_to_st_mode (DWORD attr)
 {
diff --git a/git-compat-util.h b/git-compat-util.h
index ef60803..0cd2693 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -65,7 +65,7 @@
 #define _NETBSD_SOURCE 1
 #define _SGI_SOURCE 1
 
-#ifdef WIN32 /* Both MinGW and MSVC */
+#if defined(_WIN32) && !defined(__CYGWIN__) /* Both MinGW and MSVC */
 #define WIN32_LEAN_AND_MEAN  /* stops windows.h including winsock.h */
 #include <winsock2.h>
 #include <windows.h>
-- 
1.6.5

^ permalink raw reply related

* [PATCH 0/5] Re-implement 'git remote update' using 'git fetch'
From: Björn Gustavsson @ 2009-11-09 20:08 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

Here is the re-roll of my patch series for extending
the capabilities of 'git fetch' so that it can be used
for re-implementing 'git remote update'.

Since Jay Soffian is working on implementing 'git fetch
--prune', I have not attempted to implement it myself.
Therefore, 'git fetch update' in the final patch still
silently ignores the --prune option.

I have made the following changes compared to the previous
version of the series:

* Updated the usage strings in builtin_fetch.c.

* Added test cases for testing the new functionality in
  'git fetch'.

* Updated the documentation.

* Added a new test case for 'git remote update --prune'.
  (Since all tests case succeeded even though 'git
  remote update' didn't handle --prune correctly, there
  was clearly a missing test case.) In the final commit,
  I marked that new test case as a known breakage, so
  that the test suite will still pass.

* Some corrections and improvements of the commit
  messages.


Björn Gustavsson (5):
  Teach the --all option to 'git fetch'
  Teach the --multiple option to 'git fetch'
  Add the configuration option skipFetchAll
  Add missing test for 'git remote update --prune'
  Re-implement 'git remote update' using 'git fetch'

 Documentation/config.txt           |    8 ++-
 Documentation/fetch-options.txt    |    9 ++
 Documentation/git-fetch.txt        |   14 +++-
 Documentation/pull-fetch-param.txt |    7 ++
 builtin-fetch.c                    |  160 ++++++++++++++++++++++++++++++++----
 builtin-remote.c                   |   86 ++++++-------------
 remote.c                           |    3 +-
 t/t5505-remote.sh                  |   11 +++
 t/t5506-remote-groups.sh           |   21 ++++-
 t/t5514-fetch-multiple.sh          |  154 ++++++++++++++++++++++++++++++++++
 10 files changed, 389 insertions(+), 84 deletions(-)
 create mode 100755 t/t5514-fetch-multiple.sh

^ permalink raw reply

* [PATCH 1/5] Teach the --all option to 'git fetch'
From: Björn Gustavsson @ 2009-11-09 20:09 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

'git remote' is meant for managing remotes and 'git fetch' is meant
for actually fetching data from remote repositories. Therefore, it is
not logical that you must use 'git remote update' to fetch from
more than one repository at once.

Add the --all option to 'git fetch', to tell it to attempt to fetch
from all remotes. Also, if --all is not given, the <repository>
argument is allowed to be the name of a group, to allow fetching
from all repositories in the group.

Other options except -v and -q are silently ignored.

Signed-off-by: Björn Gustavsson <bgustavsson@gmail.com>
---
 Documentation/fetch-options.txt    |    3 +
 Documentation/git-fetch.txt        |   12 +++-
 Documentation/pull-fetch-param.txt |    7 ++
 builtin-fetch.c                    |  150 +++++++++++++++++++++++++++++++----
 t/t5506-remote-groups.sh           |   21 +++++-
 t/t5514-fetch-multiple.sh          |   76 ++++++++++++++++++
 6 files changed, 247 insertions(+), 22 deletions(-)
 create mode 100755 t/t5514-fetch-multiple.sh

diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt
index 2886874..93d73c3 100644
--- a/Documentation/fetch-options.txt
+++ b/Documentation/fetch-options.txt
@@ -1,3 +1,6 @@
+--all::
+	Fetch all remotes.
+
 -a::
 --append::
 	Append ref names and object names of fetched refs to the
diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt
index f2483d6..3616466 100644
--- a/Documentation/git-fetch.txt
+++ b/Documentation/git-fetch.txt
@@ -10,11 +10,15 @@ SYNOPSIS
 --------
 'git fetch' <options> <repository> <refspec>...
 
+'git fetch' <options> <group>
+
+'git fetch' --all <options>
+
 
 DESCRIPTION
 -----------
-Fetches named heads or tags from another repository, along with
-the objects necessary to complete them.
+Fetches named heads or tags from one or more other repositories,
+along with the objects necessary to complete them.
 
 The ref names and their object names of fetched refs are stored
 in `.git/FETCH_HEAD`.  This information is left for a later merge
@@ -28,6 +32,10 @@ pointed by remote tags that it does not yet have, then fetch
 those missing tags.  If the other end has tags that point at
 branches you are not interested in, you will not get them.
 
+'git fetch' can fetch from either a single named repository, or
+or from several repositories at once if <group> is given and
+there is a remotes.<group> entry in the configuration file.
+(See linkgit:git-config[1]).
 
 OPTIONS
 -------
diff --git a/Documentation/pull-fetch-param.txt b/Documentation/pull-fetch-param.txt
index f9811f2..712b91a 100644
--- a/Documentation/pull-fetch-param.txt
+++ b/Documentation/pull-fetch-param.txt
@@ -4,6 +4,13 @@
 	(see the section <<URLS,GIT URLS>> below) or the name
 	of a remote (see the section <<REMOTES,REMOTES>> below).
 
+ifndef::git-pull[]
+<group>::
+	A name referring to a list of repositories as the value
+	of remotes.<group> in the configuration file.
+	(See linkgit:git-config[1]).
+endif::git-pull[]
+
 <refspec>::
 	The format of a <refspec> parameter is an optional plus
 	`{plus}`, followed by the source ref <src>, followed
diff --git a/builtin-fetch.c b/builtin-fetch.c
index a35a6f8..2bf51cc 100644
--- a/builtin-fetch.c
+++ b/builtin-fetch.c
@@ -14,6 +14,8 @@
 
 static const char * const builtin_fetch_usage[] = {
 	"git fetch [options] [<repository> <refspec>...]",
+	"git fetch [options] <group>",
+	"git fetch --all [options]",
 	NULL
 };
 
@@ -23,7 +25,7 @@ enum {
 	TAGS_SET = 2
 };
 
-static int append, force, keep, update_head_ok, verbosity;
+static int all, append, force, keep, update_head_ok, verbosity;
 static int tags = TAGS_DEFAULT;
 static const char *depth;
 static const char *upload_pack;
@@ -32,6 +34,8 @@ static struct transport *transport;
 
 static struct option builtin_fetch_options[] = {
 	OPT__VERBOSITY(&verbosity),
+	OPT_BOOLEAN(0, "all", &all,
+		    "fetch from all remotes"),
 	OPT_BOOLEAN('a', "append", &append,
 		    "append to .git/FETCH_HEAD instead of overwriting"),
 	OPT_STRING(0, "upload-pack", &upload_pack, "PATH",
@@ -680,27 +684,89 @@ static void set_option(const char *name, const char *value)
 			name, transport->url);
 }
 
-int cmd_fetch(int argc, const char **argv, const char *prefix)
+static int get_one_remote_for_fetch(struct remote *remote, void *priv)
+{
+	struct string_list *list = priv;
+	string_list_append(remote->name, list);
+	return 0;
+}
+
+struct remote_group_data {
+	const char *name;
+	struct string_list *list;
+};
+
+static int get_remote_group(const char *key, const char *value, void *priv)
+{
+	struct remote_group_data *g = priv;
+
+	if (!prefixcmp(key, "remotes.") &&
+			!strcmp(key + 8, g->name)) {
+		/* split list by white space */
+		int space = strcspn(value, " \t\n");
+		while (*value) {
+			if (space > 1) {
+				string_list_append(xstrndup(value, space),
+						   g->list);
+			}
+			value += space + (value[space] != '\0');
+			space = strcspn(value, " \t\n");
+		}
+	}
+
+	return 0;
+}
+
+static int add_remote_or_group(const char *name, struct string_list *list)
+{
+	int prev_nr = list->nr;
+	struct remote_group_data g = { name, list };
+
+	git_config(get_remote_group, &g);
+	if (list->nr == prev_nr) {
+		struct remote *remote;
+		if (!remote_is_configured(name))
+			return 0;
+		remote = remote_get(name);
+		string_list_append(remote->name, list);
+	}
+	return 1;
+}
+
+static int fetch_multiple(struct string_list *list)
+{
+	int i, result = 0;
+	const char *argv[] = { "fetch", NULL, NULL, NULL, NULL };
+	int argc = 1;
+
+	if (verbosity >= 2)
+		argv[argc++] = "-v";
+	if (verbosity >= 1)
+		argv[argc++] = "-v";
+	else if (verbosity < 0)
+		argv[argc++] = "-q";
+
+	for (i = 0; i < list->nr; i++) {
+		const char *name = list->items[i].string;
+		argv[argc] = name;
+		if (verbosity >= 0)
+			printf("Fetching %s\n", name);
+		if (run_command_v_opt(argv, RUN_GIT_CMD)) {
+			error("Could not fetch %s", name);
+			result = 1;
+		}
+	}
+
+	return result;
+}
+
+static int fetch_one(struct remote *remote, int argc, const char **argv)
 {
-	struct remote *remote;
 	int i;
 	static const char **refs = NULL;
 	int ref_nr = 0;
 	int exit_code;
 
-	/* Record the command line for the reflog */
-	strbuf_addstr(&default_rla, "fetch");
-	for (i = 1; i < argc; i++)
-		strbuf_addf(&default_rla, " %s", argv[i]);
-
-	argc = parse_options(argc, argv, prefix,
-			     builtin_fetch_options, builtin_fetch_usage, 0);
-
-	if (argc == 0)
-		remote = remote_get(NULL);
-	else
-		remote = remote_get(argv[0]);
-
 	if (!remote)
 		die("Where do you want to fetch from today?");
 
@@ -716,10 +782,10 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
 	if (depth)
 		set_option(TRANS_OPT_DEPTH, depth);
 
-	if (argc > 1) {
+	if (argc > 0) {
 		int j = 0;
 		refs = xcalloc(argc + 1, sizeof(const char *));
-		for (i = 1; i < argc; i++) {
+		for (i = 0; i < argc; i++) {
 			if (!strcmp(argv[i], "tag")) {
 				char *ref;
 				i++;
@@ -746,3 +812,51 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
 	transport = NULL;
 	return exit_code;
 }
+
+int cmd_fetch(int argc, const char **argv, const char *prefix)
+{
+	int i;
+	struct string_list list = { NULL, 0, 0, 0 };
+	struct remote *remote;
+	int result = 0;
+
+	/* Record the command line for the reflog */
+	strbuf_addstr(&default_rla, "fetch");
+	for (i = 1; i < argc; i++)
+		strbuf_addf(&default_rla, " %s", argv[i]);
+
+	argc = parse_options(argc, argv, prefix,
+			     builtin_fetch_options, builtin_fetch_usage, 0);
+
+	if (all) {
+		if (argc == 1)
+			die("fetch --all does not take a repository argument");
+		else if (argc > 1)
+			die("fetch --all does not make sense with refspecs");
+		(void) for_each_remote(get_one_remote_for_fetch, &list);
+		result = fetch_multiple(&list);
+	} else if (argc == 0) {
+		/* No arguments -- use default remote */
+		remote = remote_get(NULL);
+		result = fetch_one(remote, argc, argv);
+	} else {
+		/* Single remote or group */
+		(void) add_remote_or_group(argv[0], &list);
+		if (list.nr > 1) {
+			/* More than one remote */
+			if (argc > 1)
+				die("Fetching a group and specifying refspecs does not make sense");
+			result = fetch_multiple(&list);
+		} else {
+			/* Zero or one remotes */
+			remote = remote_get(argv[0]);
+			result = fetch_one(remote, argc-1, argv+1);
+		}
+	}
+
+	/* All names were strdup()ed or strndup()ed */
+	list.strdup_strings = 1;
+	string_list_clear(&list, 0);
+
+	return result;
+}
diff --git a/t/t5506-remote-groups.sh b/t/t5506-remote-groups.sh
index 2a1806b..b7b7dda 100755
--- a/t/t5506-remote-groups.sh
+++ b/t/t5506-remote-groups.sh
@@ -51,7 +51,7 @@ test_expect_success 'nonexistant group produces error' '
 	! repo_fetched two
 '
 
-test_expect_success 'updating group updates all members' '
+test_expect_success 'updating group updates all members (remote update)' '
 	mark group-all &&
 	update_repos &&
 	git config --add remotes.all one &&
@@ -61,7 +61,15 @@ test_expect_success 'updating group updates all members' '
 	repo_fetched two
 '
 
-test_expect_success 'updating group does not update non-members' '
+test_expect_success 'updating group updates all members (fetch)' '
+	mark fetch-group-all &&
+	update_repos &&
+	git fetch all &&
+	repo_fetched one &&
+	repo_fetched two
+'
+
+test_expect_success 'updating group does not update non-members (remote update)' '
 	mark group-some &&
 	update_repos &&
 	git config --add remotes.some one &&
@@ -70,6 +78,15 @@ test_expect_success 'updating group does not update non-members' '
 	! repo_fetched two
 '
 
+test_expect_success 'updating group does not update non-members (fetch)' '
+	mark fetch-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 &&
diff --git a/t/t5514-fetch-multiple.sh b/t/t5514-fetch-multiple.sh
new file mode 100755
index 0000000..25244bf
--- /dev/null
+++ b/t/t5514-fetch-multiple.sh
@@ -0,0 +1,76 @@
+#!/bin/sh
+
+test_description='fetch --all works correctly'
+
+. ./test-lib.sh
+
+setup_repository () {
+	mkdir "$1" && (
+	cd "$1" &&
+	git init &&
+	>file &&
+	git add file &&
+	test_tick &&
+	git commit -m "Initial" &&
+	git checkout -b side &&
+	>elif &&
+	git add elif &&
+	test_tick &&
+	git commit -m "Second" &&
+	git checkout master
+	)
+}
+
+test_expect_success setup '
+	setup_repository one &&
+	setup_repository two &&
+	(
+		cd two && git branch another
+	) &&
+	git clone --mirror two three
+	git clone one test
+'
+
+cat > test/expect << EOF
+  one/master
+  one/side
+  origin/HEAD -> origin/master
+  origin/master
+  origin/side
+  three/another
+  three/master
+  three/side
+  two/another
+  two/master
+  two/side
+EOF
+
+test_expect_success 'git fetch --all' '
+	(cd test &&
+	 git remote add one ../one &&
+	 git remote add two ../two &&
+	 git remote add three ../three &&
+	 git fetch --all &&
+	 git branch -r > output &&
+	 test_cmp expect output)
+'
+
+test_expect_success 'git fetch --all should continue if a remote has errors' '
+	(git clone one test2 &&
+	 cd test2 &&
+	 git remote add bad ../non-existing &&
+	 git remote add one ../one &&
+	 git remote add two ../two &&
+	 git remote add three ../three &&
+	 test_must_fail git fetch --all &&
+	 git branch -r > output &&
+	 test_cmp ../test/expect output)
+'
+
+test_expect_success 'git fetch --all does not allow non-option arguments' '
+	(cd test &&
+	 test_must_fail git fetch --all origin &&
+	 test_must_fail git fetch --all origin master)
+'
+
+test_done
-- 
1.6.5.1.69.g36942

^ permalink raw reply related

* [PATCH 2/5] Teach the --multiple option to 'git fetch'
From: Björn Gustavsson @ 2009-11-09 20:10 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

Add the --multiple option to specify that all arguments are either
groups or remotes. The primary reason for adding this option is
to allow us to re-implement 'git remote update' using fetch.

It would have been nice if this option was not needed, but since
the colon in a refspec is optional, it is in general not possible
to know whether a single, colon-less argument is a remote or a
refspec.

Signed-off-by: Björn Gustavsson <bgustavsson@gmail.com>
---
 Documentation/fetch-options.txt |    6 +++++
 Documentation/git-fetch.txt     |    2 +
 builtin-fetch.c                 |   11 ++++++++-
 t/t5514-fetch-multiple.sh       |   44 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 62 insertions(+), 1 deletions(-)

diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt
index 93d73c3..8b0cf58 100644
--- a/Documentation/fetch-options.txt
+++ b/Documentation/fetch-options.txt
@@ -24,6 +24,12 @@
 --keep::
 	Keep downloaded pack.
 
+ifndef::git-pull[]
+--multiple::
+	Allow several <repository> and <group> arguments to be
+	specified. No <refspec>s may be specified.
+endif::git-pull[]
+
 ifdef::git-pull[]
 --no-tags::
 endif::git-pull[]
diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt
index 3616466..9b9e568 100644
--- a/Documentation/git-fetch.txt
+++ b/Documentation/git-fetch.txt
@@ -12,6 +12,8 @@ SYNOPSIS
 
 'git fetch' <options> <group>
 
+'git fetch' --multiple <options> [<repository> | <group>]...
+
 'git fetch' --all <options>
 
 
diff --git a/builtin-fetch.c b/builtin-fetch.c
index 2bf51cc..a5a70a0 100644
--- a/builtin-fetch.c
+++ b/builtin-fetch.c
@@ -15,6 +15,7 @@
 static const char * const builtin_fetch_usage[] = {
 	"git fetch [options] [<repository> <refspec>...]",
 	"git fetch [options] <group>",
+	"git fetch --multiple [options] [<repository> | <group>]...",
 	"git fetch --all [options]",
 	NULL
 };
@@ -25,7 +26,7 @@ enum {
 	TAGS_SET = 2
 };
 
-static int all, append, force, keep, update_head_ok, verbosity;
+static int all, append, force, keep, multiple, update_head_ok, verbosity;
 static int tags = TAGS_DEFAULT;
 static const char *depth;
 static const char *upload_pack;
@@ -42,6 +43,8 @@ static struct option builtin_fetch_options[] = {
 		   "path to upload pack on remote end"),
 	OPT_BOOLEAN('f', "force", &force,
 		    "force overwrite of local branch"),
+	OPT_BOOLEAN('m', "multiple", &multiple,
+		    "fetch from multiple remotes"),
 	OPT_SET_INT('t', "tags", &tags,
 		    "fetch all tags and associated objects", TAGS_SET),
 	OPT_SET_INT('n', NULL, &tags,
@@ -839,6 +842,12 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
 		/* No arguments -- use default remote */
 		remote = remote_get(NULL);
 		result = fetch_one(remote, argc, argv);
+	} else if (multiple) {
+		/* All arguments are assumed to be remotes or groups */
+		for (i = 0; i < argc; i++)
+			if (!add_remote_or_group(argv[i], &list))
+				die("No such remote or remote group: %s", argv[i]);
+		result = fetch_multiple(&list);
 	} else {
 		/* Single remote or group */
 		(void) add_remote_or_group(argv[0], &list);
diff --git a/t/t5514-fetch-multiple.sh b/t/t5514-fetch-multiple.sh
index 25244bf..69c64ab 100755
--- a/t/t5514-fetch-multiple.sh
+++ b/t/t5514-fetch-multiple.sh
@@ -73,4 +73,48 @@ test_expect_success 'git fetch --all does not allow non-option arguments' '
 	 test_must_fail git fetch --all origin master)
 '
 
+cat > expect << EOF
+  origin/HEAD -> origin/master
+  origin/master
+  origin/side
+  three/another
+  three/master
+  three/side
+EOF
+
+test_expect_success 'git fetch --multiple (but only one remote)' '
+	(git clone one test3 &&
+	 cd test3 &&
+	 git remote add three ../three &&
+	 git fetch --multiple three &&
+	 git branch -r > output &&
+	 test_cmp ../expect output)
+'
+
+cat > expect << EOF
+  one/master
+  one/side
+  origin/HEAD -> origin/master
+  origin/master
+  origin/side
+  two/another
+  two/master
+  two/side
+EOF
+
+test_expect_success 'git fetch --multiple (two remotes)' '
+	(git clone one test4 &&
+	 cd test4 &&
+	 git remote add one ../one &&
+	 git remote add two ../two &&
+	 git fetch --multiple one two &&
+	 git branch -r > output &&
+	 test_cmp ../expect output)
+'
+
+test_expect_success 'git fetch --multiple (bad remote names)' '
+	(cd test4 &&
+	 test_must_fail git fetch --multiple four)
+'
+
 test_done
-- 
1.6.5.1.69.g36942

^ permalink raw reply related

* [PATCH 3/5] Add the configuration option skipFetchAll
From: Björn Gustavsson @ 2009-11-09 20:11 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

Implement the configuration skipFetchAll option to allow
certain remotes to be skipped when doing 'git fetch --all' and
'git remote update'. The existing skipDefaultUpdate variable
is still honored (by 'git fetch --all' and 'git remote update').
(If both are set in the configuration file with different values,
the value of the last occurrence will be used.)

Signed-off-by: Björn Gustavsson <bgustavsson@gmail.com>
---
 Documentation/config.txt  |    8 +++++++-
 builtin-fetch.c           |    3 ++-
 remote.c                  |    3 ++-
 t/t5514-fetch-multiple.sh |   40 +++++++++++++++++++++++++++++++++++++---
 4 files changed, 48 insertions(+), 6 deletions(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index d1e2120..348b367 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -1394,7 +1394,13 @@ remote.<name>.mirror::
 
 remote.<name>.skipDefaultUpdate::
 	If true, this remote will be skipped by default when updating
-	using the update subcommand of linkgit:git-remote[1].
+	using linkgit:git-fetch[1] or the `update` subcommand of
+	linkgit:git-remote[1].
+
+remote.<name>.skipFetchAll::
+	If true, this remote will be skipped by default when updating
+	using linkgit:git-fetch[1] or the `update` subcommand of
+	linkgit:git-remote[1].
 
 remote.<name>.receivepack::
 	The default program to execute on the remote side when pushing.  See
diff --git a/builtin-fetch.c b/builtin-fetch.c
index a5a70a0..945dfd8 100644
--- a/builtin-fetch.c
+++ b/builtin-fetch.c
@@ -690,7 +690,8 @@ static void set_option(const char *name, const char *value)
 static int get_one_remote_for_fetch(struct remote *remote, void *priv)
 {
 	struct string_list *list = priv;
-	string_list_append(remote->name, list);
+	if (!remote->skip_default_update)
+		string_list_append(remote->name, list);
 	return 0;
 }
 
diff --git a/remote.c b/remote.c
index 73d33f2..beaf9fb 100644
--- a/remote.c
+++ b/remote.c
@@ -396,7 +396,8 @@ static int handle_config(const char *key, const char *value, void *cb)
 		remote->mirror = git_config_bool(key, value);
 	else if (!strcmp(subkey, ".skipdefaultupdate"))
 		remote->skip_default_update = git_config_bool(key, value);
-
+	else if (!strcmp(subkey, ".skipfetchall"))
+		remote->skip_default_update = git_config_bool(key, value);
 	else if (!strcmp(subkey, ".url")) {
 		const char *v;
 		if (git_config_string(&v, key, value))
diff --git a/t/t5514-fetch-multiple.sh b/t/t5514-fetch-multiple.sh
index 69c64ab..b737332 100755
--- a/t/t5514-fetch-multiple.sh
+++ b/t/t5514-fetch-multiple.sh
@@ -94,9 +94,6 @@ test_expect_success 'git fetch --multiple (but only one remote)' '
 cat > expect << EOF
   one/master
   one/side
-  origin/HEAD -> origin/master
-  origin/master
-  origin/side
   two/another
   two/master
   two/side
@@ -105,6 +102,7 @@ EOF
 test_expect_success 'git fetch --multiple (two remotes)' '
 	(git clone one test4 &&
 	 cd test4 &&
+	 git remote rm origin &&
 	 git remote add one ../one &&
 	 git remote add two ../two &&
 	 git fetch --multiple one two &&
@@ -117,4 +115,40 @@ test_expect_success 'git fetch --multiple (bad remote names)' '
 	 test_must_fail git fetch --multiple four)
 '
 
+
+test_expect_success 'git fetch --all (skipFetchAll)' '
+	(cd test4 &&
+	 for b in $(git branch -r)
+	 do
+		git branch -r -d $b || break
+	 done &&
+	 git remote add three ../three &&
+	 git config remote.three.skipFetchAll true &&
+	 git fetch --all &&
+	 git branch -r > output &&
+	 test_cmp ../expect output)
+'
+
+cat > expect << EOF
+  one/master
+  one/side
+  three/another
+  three/master
+  three/side
+  two/another
+  two/master
+  two/side
+EOF
+
+test_expect_success 'git fetch --multiple (ignoring skipFetchAll)' '
+	(cd test4 &&
+	 for b in $(git branch -r)
+	 do
+		git branch -r -d $b || break
+	 done &&
+	 git fetch --multiple one two three &&
+	 git branch -r > output &&
+	 test_cmp ../expect output)
+'
+
 test_done
-- 
1.6.5.1.69.g36942

^ permalink raw reply related

* [PATCH 4/5] Add missing test for 'git remote update --prune'
From: Björn Gustavsson @ 2009-11-09 20:11 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano


Signed-off-by: Björn Gustavsson <bgustavsson@gmail.com>
---
 t/t5505-remote.sh |   11 +++++++++++
 1 files changed, 11 insertions(+), 0 deletions(-)

diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index 852ccb5..e931ce6 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -365,6 +365,17 @@ test_expect_success 'update with arguments' '
 
 '
 
+test_expect_success 'update --prune' '
+
+	(cd one &&
+	 git branch -m side2 side3) &&
+	(cd test &&
+	 git remote update --prune &&
+	 (cd ../one && git branch -m side3 side2)
+	 git rev-parse refs/remotes/origin/side3 &&
+	 test_must_fail git rev-parse refs/remotes/origin/side2)
+'
+
 cat > one/expect << EOF
   apis/master
   apis/side
-- 
1.6.5.1.69.g36942

^ permalink raw reply related

* [PATCH 5/5] Re-implement 'git remote update' using 'git fetch'
From: Björn Gustavsson @ 2009-11-09 20:12 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano

In order not to duplicate functionality, re-implement 'git remote
update' in terms of 'git fetch'.

There is currently one backward incompatibility: the --prune
option is silently ignored.

Signed-off-by: Björn Gustavsson <bgustavsson@gmail.com>
---
 builtin-remote.c  |   86 ++++++++++++++++-------------------------------------
 t/t5505-remote.sh |    2 +-
 2 files changed, 27 insertions(+), 61 deletions(-)

diff --git a/builtin-remote.c b/builtin-remote.c
index 0777dd7..fd7e0b2 100644
--- a/builtin-remote.c
+++ b/builtin-remote.c
@@ -1189,88 +1189,54 @@ static int prune_remote(const char *remote, int dry_run)
 	return result;
 }
 
-static int get_one_remote_for_update(struct remote *remote, void *priv)
+static int get_remote_default(const char *key, const char *value, void *priv)
 {
-	struct string_list *list = priv;
-	if (!remote->skip_default_update)
-		string_list_append(remote->name, list);
-	return 0;
-}
-
-static struct remote_group {
-	const char *name;
-	struct string_list *list;
-} remote_group;
-
-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) {
-				string_list_append(xstrndup(value, space),
-						remote_group.list);
-				++*((int *)num_hits);
-			}
-			value += space + (value[space] != '\0');
-			space = strcspn(value, " \t\n");
-		}
+	if (strcmp(key, "remotes.default") == 0) {
+		int *found = priv;
+		*found = 1;
 	}
-
 	return 0;
 }
 
 static int update(int argc, const char **argv)
 {
-	int i, result = 0, prune = 0;
-	struct string_list list = { NULL, 0, 0, 0 };
-	static const char *default_argv[] = { NULL, "default", NULL };
+	int i, prune = 0;
 	struct option options[] = {
 		OPT_GROUP("update specific options"),
 		OPT_BOOLEAN('p', "prune", &prune,
 			    "prune remotes after fetching"),
 		OPT_END()
 	};
+	const char **fetch_argv;
+	int fetch_argc = 0;
+	int default_defined = 0;
+
+	fetch_argv = xmalloc(sizeof(char *) * (argc+4));
 
 	argc = parse_options(argc, argv, NULL, 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, &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);
-		}
-	}
+	fetch_argv[fetch_argc++] = "fetch";
 
-	if (!result && !list.nr  && argc == 2 && !strcmp(argv[1], "default"))
-		result = for_each_remote(get_one_remote_for_update, &list);
+	if (verbose)
+		fetch_argv[fetch_argc++] = "-v";
+	if (argc < 2) {
+		fetch_argv[fetch_argc++] = "default";
+	} else {
+		fetch_argv[fetch_argc++] = "--multiple";
+		for (i = 1; i < argc; i++)
+			fetch_argv[fetch_argc++] = argv[i];
+	}
 
-	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);
+	if (strcmp(fetch_argv[fetch_argc-1], "default") == 0) {
+		git_config(get_remote_default, &default_defined);
+		if (!default_defined)
+			fetch_argv[fetch_argc-1] = "--all";
 	}
 
-	/* all names were strdup()ed or strndup()ed */
-	list.strdup_strings = 1;
-	string_list_clear(&list, 0);
+	fetch_argv[fetch_argc] = NULL;
 
-	return result;
+	return run_command_v_opt(fetch_argv, RUN_GIT_CMD);
 }
 
 static int get_one_entry(struct remote *remote, void *priv)
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index e931ce6..562cca2 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -365,7 +365,7 @@ test_expect_success 'update with arguments' '
 
 '
 
-test_expect_success 'update --prune' '
+test_expect_failure 'update --prune' '
 
 	(cd one &&
 	 git branch -m side2 side3) &&
-- 
1.6.5.1.69.g36942

^ permalink raw reply related

* Re: Problem signing a tag
From: Joshua J. Kugler @ 2009-11-09 20:15 UTC (permalink / raw)
  To: Michael J Gruber; +Cc: Junio C Hamano, Alex Riesen, git
In-Reply-To: <4AF3F23B.8080203@drmicha.warpmail.net>

On Friday 06 November 2009, Michael J Gruber said something like:
> Junio C Hamano venit, vidit, dixit 05.11.2009 21:09:
> > Michael J Gruber <git@drmicha.warpmail.net> writes:
> >> Dig dig dig... gpg exits with 2 in a lot of cases, one would need
> >> to parse fd-error to find out more. But it also looks as if gpg
> >> exits normally with a good passphrase. So I tried, and at least
> >> with gpg 1.4.9 and git 1.6.5.2 I can sign tags with "use-agent"
> >> and without a running agent: I get asked for the passphrase (after
> >> reporting the agent MIA), and everything's fine.
> >>
> >> My gpg returns 0 in this case; it returns 2 only if I don't enter
> >> the passphrase. So, this seems to depend on the version of gpg. Or
> >> on entering the correct passphrase ;)
> >
> > If the problematic gpg that gives 2 is older than yours, the
> > situation looks to me that "exiting 2 when failed to contact agent
> > but got a good passphrase some other way and successfully signed"
> > was diagnosed as a bug and then fixed in gpg.  If that is the case
> > can we find out which version that fix is in, and add an entry to
> > FAQ to help next person who will be hit by this when using "tag
> > -s"?
>
> Both of us seem to be using gpg 1.4.9, which is weird. I even checked
> Fedora's srpm, they don't apply any patches for this. For the record,
> I'm doing
>
> unset GPG_AGENT_INFO
> echo a |gpg -bsa
>
> with "use-agent" and a default key signing specified in my gpg conf.
> This returns "0" if I enter the correct passphrase (after being
> warned about the missing agent) and "2" if I enter a wrong one
> repeatedly. Joshua, your reports seem to confirm that you get 2 in
> both cases from your gpg 1.4.9, right?

I discovered that there must have been something locked up or wedged.  I 
rebooted (kernel update), and it worked. I looked at the warning again, 
and realized that the warning I got when the exit value was 2 was:

gpg: problem with the agent - disabling agent use

while the warning I got when the exit value was 0 was: 

gpg: gpg-agent is not available in this session

So, two different warnings.

Thanks again for all the troubleshooting help!

j

-- 
Joshua Kugler
Part-Time System Admin/Programmer
http://www.eeinternet.com
PGP Key: http://pgp.mit.edu/  ID 0x14EA086E

^ permalink raw reply

* Re: [PATCH v2 4/4] Add explicit Cygwin check to guard WIN32 header inclusion
From: Junio C Hamano @ 2009-11-09 20:30 UTC (permalink / raw)
  To: Ramsay Jones; +Cc: Marius Storm-Olsen, Johannes Sixt, GIT Mailing-list
In-Reply-To: <4AF86CDB.6030200@ramsay1.demon.co.uk>

Ramsay Jones <ramsay@ramsay1.demon.co.uk> writes:

> Ramsay Jones wrote:
>> diff --git a/git-compat-util.h b/git-compat-util.h
>>...
>
> Arrgghhh.
>
> Sorry, Junio, I meant to change this before sending it out. :(
>
> I had intended to remove the added indentation (which I don't even remember
> doing!), since it obscures the "real change", which simply consists of the
> new expression in the #if preprocessor line.
>
> I'll send a new version of this patch.

Thanks for a notice.

The only reason I queue patches to 'pu' is to give them (slightly) wider
exposure than merely being in the mailing list archive, and I do not
expect myself merging this series to 'next' before seeing Acks from
Windows stakeholders, as I do not build anything on that platform myself.

I saw a comment on substance not form of this patch from J6t, and I
suspect that it was a more important issue.  You can de-indent while
sending your updated patch to address that issue.

Thanks.

^ permalink raw reply

* Re: [PATCH v3 08/12] Allow helper to map private ref names into  normal names
From: Junio C Hamano @ 2009-11-09 20:32 UTC (permalink / raw)
  To: Sverre Rabbelier
  Cc: Junio C Hamano, Daniel Barkalow, Git List, Johannes Schindelin,
	Johan Herland
In-Reply-To: <fabb9a1e0911090910g4745325l842cdb91e4d9485c@mail.gmail.com>

Sverre Rabbelier <srabbelier@gmail.com> writes:

> Heya,
>
> On Mon, Nov 9, 2009 at 17:44, Junio C Hamano <gitster@pobox.com> wrote:
>> Sverre Rabbelier <srabbelier@gmail.com> writes:
>>> It's in next now, so please send follow-ups against sr/vcs-helper.
>>
>> Do you mean 'pu' or 'next'?
>
> Sorry, my hands did not type what my mind intended, I meant to say
> 'pu', apologies!

Fine---mistakes happen; it made me worried for a while.

So either follow-up, squash-in or replacement would be fine.
 

^ permalink raw reply

* Re: Git drawbacks?
From: Dmitry Potapov @ 2009-11-09 21:06 UTC (permalink / raw)
  To: Dmitry Smirnov; +Cc: B Smith-Mannschott, git
In-Reply-To: <28c656e20911091047r353e9451hd856b99541fbd5ff@mail.gmail.com>

On Mon, Nov 09, 2009 at 07:47:08PM +0100, B Smith-Mannschott wrote:
> 
> You don't have to wait to comitting to your own branches, but do make
> sure to run your usual builds and tests before pushing or asking
> another to pull changes from you.

Yes, it is one most useful feature of Git that you can commit (save)
your changes immediately but amend them later. This helps a lot to
make changes smaller, cleaner and easier to review.

With many other VCS, a typical policy is that you do not commit your
changes unless you have finished and tested them. But it means that
your changes are not committed and stored only in the work tree for
a long time. Moreover, when you eventually decide that they are good
enough to commit, you will produce a huge patch, which will be difficult
to review or to bisect history later.

With Git you do not have to worry about testing when you can commit your
changes. Typically I would commit some my changes as I progress to my
goal, but later I will review all commits. Probably, squash some changes
with fixes, clean up some other, add better explanations of what is done
and why, etc... But I do not have to worry about all those trifles as
I write code to see if some feature is worth or not, if this solution
works or I should try something else...

So, you can always commit your changes as your progress to your goal and
review amend them later before publishing. This means that you can have
as many work-in-progress branches as you wish, and you do not need a
separate work tree for each of them -- everything can be stored in the
repository, and you can go to another computer, issue 'git fetch' and
continue your work at the exact point where you left it. So, it is very
flexible.


Dmitry

^ permalink raw reply

* Re: pulling git -- version confusion
From: Junio C Hamano @ 2009-11-09 21:58 UTC (permalink / raw)
  To: Rustom Mody; +Cc: Sverre Rabbelier, git
In-Reply-To: <f46c52560911090524l51140858sdde29d76e2cfed49@mail.gmail.com>

Rustom Mody <rustompmody@gmail.com> writes:

> On Mon, Nov 9, 2009 at 1:21 PM, Sverre Rabbelier <srabbelier@gmail.com> wrote:
>> On Mon, Nov 9, 2009 at 08:45, Rustom Mody <rustompmody@gmail.com> wrote:
>>> Gives me
>>> fatal: ambiguous argument 'origin/master': unknown revision or path
>>> not in the working tree.
>>> Use '--' to separate paths from revisions
>>
>> Well, as what remote do you have upstream configured?
>>
>> What is the output of
>> $ git config -l
>
> gives me
>
> core.safecrlf=true
> user.email=<my email>
> user.name=<my name>
> core.repositoryformatversion=0
> core.filemode=true
> gui.geometry=885x450+0+27 207 192
>
> So theres no remote?
> But .git/remotes/origin has
>
> URL: git://git.kernel.org/pub/scm/git/git.git
> Pull: refs/heads/master:refs/heads/origin
> Pull: refs/heads/maint:refs/heads/maint
> Pull: refs/heads/next:refs/heads/next
> Pull: refs/heads/html:refs/heads/html
> Pull: refs/heads/pu:refs/heads/pu
> Pull: refs/heads/todo:refs/heads/todo
> Pull: refs/heads/man:refs/heads/man

That's a layout that was default before 1.5.0, I think.  3 years is an eon
in git timescale.

People giving advice based on modern git experience would say things like
"origin/master", but in your layout that ref that keeps track of where the
'master' branch at the remote repository is is not called 'origin/master',
but simply 'origin'.

So instead of

> Try either:
> $ git checkout master
> $ git reset --hard origin/master

that Sverre gave you, in your repository you would do:

    $ git checkout master
    $ git reset --hard origin

^ permalink raw reply

* Re: [PATCH v3 4/4] Add explicit Cygwin check to guard WIN32 header inclusion
From: Junio C Hamano @ 2009-11-09 22:25 UTC (permalink / raw)
  To: Ramsay Jones; +Cc: Marius Storm-Olsen, Johannes Sixt, GIT Mailing-list
In-Reply-To: <4AF86E0C.4080001@ramsay1.demon.co.uk>

Ramsay Jones <ramsay@ramsay1.demon.co.uk> writes:

> Changes since v2:
>     - removed indentation from the #if-#endif block
>
> I hope you don't mind, but I've only sent this patch, rather than
> re-send the whole series. If you would like a complete re-send, just
> let me know.

No, I don't mind "replace only this one out of the four-patch series" at
all as long as what's being replaced is marked clearly enough (which you
did in this case).

But I thought J6t had problems with what this particular patch does, not
how the patch was indented?  So I'd wait for more comments and hopefully
an Ack from him before touching this one.

^ permalink raw reply

* [RFC, PATCH] git send-email: Make --no-chain-reply-to the default
From: Ingo Molnar @ 2009-11-10  4:08 UTC (permalink / raw)
  To: Peter Zijlstra, git, Junio C Hamano
In-Reply-To: <1257789555.4108.348.camel@laptop>


(moved from lkml to the Git list)

* Peter Zijlstra <peterz@infradead.org> wrote:

> >                            Mailer: 
> > git-send-email 1.6.5.2
> 
> Please teach your git-send-email thing to use --no-chain-reply-to.

about half of every patch series that gets sent to me on lkml is 
unreadable in my email client due to the default threading that 
git-send-email does. It looks like this:

28685 r T Nov 05 Hitoshi Mitake  (  31) [PATCH v5 0/7] Adding general performance benchmarki
28686   T Nov 05 Hitoshi Mitake  (  31) +->[PATCH v5 1/7] Adding new directory and header fo
28687   T Nov 05 Hitoshi Mitake  ( 368) | +->[PATCH v5 2/7] sched-messaging.c: benchmark for
28688   T Nov 05 Hitoshi Mitake  ( 148) | | +->[PATCH v5 3/7] sched-pipe.c: benchmark for pi
28689   T Nov 05 Hitoshi Mitake  ( 149) | | | +->[PATCH v5 4/7] builtin-bench.c: General fra
28690   T Nov 05 Hitoshi Mitake  (  24) | | | | +->[PATCH v5 5/7] Modifying builtin.h for ne
28691   T Nov 05 Hitoshi Mitake  (  25) | | | | | +->[PATCH v5 6/7] Modyfing perf.c for subc
28692   T Nov 05 Hitoshi Mitake  (  30) | | | | | | +->[PATCH v5 7/7] Modyfing Makefile to b

and with 10 or more patches it's an absolute pain as threading depth 
increases. Furthermore, the subject lines are not aligned vertically, 
making it very hard to see the general shortlog-alike structure of the 
series, at a glance. Plus i dont even _see_ the title over a certain 
depth, as i run out of screen real estate.

So ... the question would be ... could git-send-email flip its default 
please, via the patch below? Am i missing something subtle about why 
this default was chosen?

	Ingo

Signed-off-by: Ingo Molnar <mingo@elte.hu>

diff --git a/git-send-email.perl b/git-send-email.perl
index a0279de..ff00940 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -188,7 +188,7 @@ my (@suppress_cc);
 
 my %config_bool_settings = (
     "thread" => [\$thread, 1],
-    "chainreplyto" => [\$chain_reply_to, 1],
+    "chainreplyto" => [\$chain_reply_to, 0],
     "suppressfrom" => [\$suppress_from, undef],
     "signedoffbycc" => [\$signed_off_by_cc, undef],
     "signedoffcc" => [\$signed_off_by_cc, undef],      # Deprecated

^ permalink raw reply related

* [PATCH 0/4] Teach fetch --prune and --dry run options
From: Jay Soffian @ 2009-11-10  4:58 UTC (permalink / raw)
  To: git; +Cc: Jay Soffian, Junio C Hamano, Björn Gustavsson

This series builds on bg/fetch-multi, by teaching fetch --prune, re-enabling
"remote update --prune" and ensuring its corresponding test now passes.

As a convenience to users used to "remote prune" supporting the "--dry-run"
option, we also teach this option to fetch since it's trivial to do so.

I've left out re-implementing "remote prune" as a synonym for "update --prune"
since it's not a 1:1 match. And as per Junio's comments, teaching fetch a 
"--prune-only" option would be a UI wart.

Jay Soffian (4):
  remote: refactor some logic into get_stale_heads()
  teach warn_dangling_symref to take a FILE argument
  builtin-fetch: add --prune option
  builtin-fetch: add --dry-run option

 Documentation/fetch-options.txt |    9 ++++++++
 builtin-fetch.c                 |   42 +++++++++++++++++++++++++++++++++++---
 builtin-remote.c                |   38 +++++++++++------------------------
 refs.c                          |    7 +++--
 refs.h                          |    2 +-
 remote.c                        |   40 +++++++++++++++++++++++++++++++++++++
 remote.h                        |    3 ++
 t/t5505-remote.sh               |    2 +-
 8 files changed, 108 insertions(+), 35 deletions(-)

^ permalink raw reply

* [PATCH 1/4] remote: refactor some logic into get_stale_heads()
From: Jay Soffian @ 2009-11-10  4:58 UTC (permalink / raw)
  To: git; +Cc: Jay Soffian, Junio C Hamano, Björn Gustavsson
In-Reply-To: <1257829113-52168-1-git-send-email-jaysoffian@gmail.com>

Move the logic in builtin-remote.c which determines which local heads are stale
to remote.c so it can be used by other builtins.
---
 builtin-remote.c |   32 ++++++++------------------------
 remote.c         |   40 ++++++++++++++++++++++++++++++++++++++++
 remote.h         |    3 +++
 3 files changed, 51 insertions(+), 24 deletions(-)

diff --git a/builtin-remote.c b/builtin-remote.c
index fd7e0b2..0ea4e60 100644
--- a/builtin-remote.c
+++ b/builtin-remote.c
@@ -227,32 +227,10 @@ struct ref_states {
 	int queried;
 };
 
-static int handle_one_branch(const char *refname,
-	const unsigned char *sha1, int flags, void *cb_data)
-{
-	struct ref_states *states = cb_data;
-	struct refspec refspec;
-
-	memset(&refspec, 0, sizeof(refspec));
-	refspec.dst = (char *)refname;
-	if (!remote_find_tracking(states->remote, &refspec)) {
-		struct string_list_item *item;
-		const char *name = abbrev_branch(refspec.src);
-		/* symbolic refs pointing nowhere were handled already */
-		if ((flags & REF_ISSYMREF) ||
-		    string_list_has_string(&states->tracked, name) ||
-		    string_list_has_string(&states->new, name))
-			return 0;
-		item = string_list_append(name, &states->stale);
-		item->util = xstrdup(refname);
-	}
-	return 0;
-}
-
 static int get_ref_states(const struct ref *remote_refs, struct ref_states *states)
 {
 	struct ref *fetch_map = NULL, **tail = &fetch_map;
-	struct ref *ref;
+	struct ref *ref, *stale_refs;
 	int i;
 
 	for (i = 0; i < states->remote->fetch_refspec_nr; i++)
@@ -268,11 +246,17 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
 		else
 			string_list_append(abbrev_branch(ref->name), &states->tracked);
 	}
+	stale_refs = get_stale_heads(states->remote, fetch_map);
+	for (ref = stale_refs; ref; ref = ref->next) {
+		struct string_list_item *item =
+			string_list_append(abbrev_branch(ref->name), &states->stale);
+		item->util = xstrdup(ref->name);
+	}
+	free_refs(stale_refs);
 	free_refs(fetch_map);
 
 	sort_string_list(&states->new);
 	sort_string_list(&states->tracked);
-	for_each_ref(handle_one_branch, states);
 	sort_string_list(&states->stale);
 
 	return 0;
diff --git a/remote.c b/remote.c
index beaf9fb..eae5866 100644
--- a/remote.c
+++ b/remote.c
@@ -6,6 +6,7 @@
 #include "revision.h"
 #include "dir.h"
 #include "tag.h"
+#include "string-list.h"
 
 static struct refspec s_tag_refspec = {
 	0,
@@ -1587,3 +1588,42 @@ struct ref *guess_remote_head(const struct ref *head,
 
 	return list;
 }
+
+struct stale_heads_info {
+	struct remote *remote;
+	struct string_list *ref_names;
+	struct ref **stale_refs_tail;
+};
+
+static int get_stale_heads_cb(const char *refname,
+	const unsigned char *sha1, int flags, void *cb_data)
+{
+	struct stale_heads_info *info = cb_data;
+	struct refspec refspec;
+	memset(&refspec, 0, sizeof(refspec));
+	refspec.dst = (char *)refname;
+	if (!remote_find_tracking(info->remote, &refspec)) {
+		if (!((flags & REF_ISSYMREF) ||
+		    string_list_has_string(info->ref_names, refspec.src))) {
+			struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail);
+			hashcpy(ref->new_sha1, sha1);
+		}
+	}
+	return 0;
+}
+
+struct ref *get_stale_heads(struct remote *remote, struct ref *fetch_map)
+{
+	struct ref *ref, *stale_refs = NULL;
+	struct string_list ref_names = { NULL, 0, 0, 0 };
+	struct stale_heads_info info;
+	info.remote = remote;
+	info.ref_names = &ref_names;
+	info.stale_refs_tail = &stale_refs;
+	for (ref = fetch_map; ref; ref = ref->next)
+		string_list_append(ref->name, &ref_names);
+	sort_string_list(&ref_names);
+	for_each_ref(get_stale_heads_cb, &info);
+	string_list_clear(&ref_names, 0);
+	return stale_refs;
+}
diff --git a/remote.h b/remote.h
index 5db8420..d0aba81 100644
--- a/remote.h
+++ b/remote.h
@@ -154,4 +154,7 @@ struct ref *guess_remote_head(const struct ref *head,
 			      const struct ref *refs,
 			      int all);
 
+/* Return refs which no longer exist on remote */
+struct ref *get_stale_heads(struct remote *remote, struct ref *fetch_map);
+
 #endif
-- 
1.6.4.2

^ permalink raw reply related

* [PATCH 3/4] builtin-fetch: add --prune option
From: Jay Soffian @ 2009-11-10  4:58 UTC (permalink / raw)
  To: git; +Cc: Jay Soffian, Junio C Hamano, Björn Gustavsson
In-Reply-To: <1257829113-52168-3-git-send-email-jaysoffian@gmail.com>

Teach fetch to cull stale remote tracking branches after fetching via --prune.

We can now enable the "--prune" option to "remote update", which was recently
re-implemented as an invocation of git fetch.
---
 Documentation/fetch-options.txt |    4 ++++
 builtin-fetch.c                 |   32 ++++++++++++++++++++++++++++++--
 builtin-remote.c                |    4 +++-
 t/t5505-remote.sh               |    2 +-
 4 files changed, 38 insertions(+), 4 deletions(-)

diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt
index 8b0cf58..500637a 100644
--- a/Documentation/fetch-options.txt
+++ b/Documentation/fetch-options.txt
@@ -28,6 +28,10 @@ ifndef::git-pull[]
 --multiple::
 	Allow several <repository> and <group> arguments to be
 	specified. No <refspec>s may be specified.
+
+--prune::
+	After fetching, remove any remote tracking branches which
+	no longer exist	on the remote.
 endif::git-pull[]
 
 ifdef::git-pull[]
diff --git a/builtin-fetch.c b/builtin-fetch.c
index 945dfd8..fd31072 100644
--- a/builtin-fetch.c
+++ b/builtin-fetch.c
@@ -26,7 +26,7 @@ enum {
 	TAGS_SET = 2
 };
 
-static int all, append, force, keep, multiple, update_head_ok, verbosity;
+static int all, append, force, keep, multiple, prune, update_head_ok, verbosity;
 static int tags = TAGS_DEFAULT;
 static const char *depth;
 static const char *upload_pack;
@@ -49,6 +49,8 @@ static struct option builtin_fetch_options[] = {
 		    "fetch all tags and associated objects", TAGS_SET),
 	OPT_SET_INT('n', NULL, &tags,
 		    "do not fetch all tags (--no-tags)", TAGS_UNSET),
+	OPT_BOOLEAN('p', "prune", &prune,
+		    "prune tracking branches no longer on remote"),
 	OPT_BOOLEAN('k', "keep", &keep, "keep downloaded pack"),
 	OPT_BOOLEAN('u', "update-head-ok", &update_head_ok,
 		    "allow updating of HEAD ref"),
@@ -492,6 +494,28 @@ static int fetch_refs(struct transport *transport, struct ref *ref_map)
 	return ret;
 }
 
+static int prune_refs(struct transport *transport, struct ref *ref_map)
+{
+	int result = 0;
+	struct ref *ref, *stale_refs = get_stale_heads(transport->remote, ref_map);
+	const char *dangling_msg = dry_run
+		? "   (%s will become dangling)\n"
+		: "   (%s has become dangling)\n";
+
+	for (ref = stale_refs; ref; ref = ref->next) {
+		if (!dry_run)
+			result |= delete_ref(ref->name, NULL, 0);
+		if (verbosity >= 0) {
+			fprintf(stderr, " x %-*s %-*s -> %s\n",
+				SUMMARY_WIDTH, "[deleted]",
+				REFCOL_WIDTH, "(none)", prettify_refname(ref->name));
+			warn_dangling_symref(stderr, dangling_msg, ref->name);
+		}
+	}
+	free_refs(stale_refs);
+	return result;
+}
+
 static int add_existing(const char *refname, const unsigned char *sha1,
 			int flag, void *cbdata)
 {
@@ -657,6 +681,8 @@ static int do_fetch(struct transport *transport,
 		free_refs(ref_map);
 		return 1;
 	}
+	if (prune)
+		prune_refs(transport, ref_map);
 	free_refs(ref_map);
 
 	/* if neither --no-tags nor --tags was specified, do automated tag
@@ -740,9 +766,11 @@ static int add_remote_or_group(const char *name, struct string_list *list)
 static int fetch_multiple(struct string_list *list)
 {
 	int i, result = 0;
-	const char *argv[] = { "fetch", NULL, NULL, NULL, NULL };
+	const char *argv[] = { "fetch", NULL, NULL, NULL, NULL, NULL };
 	int argc = 1;
 
+	if (prune)
+		argv[argc++] = "--prune";
 	if (verbosity >= 2)
 		argv[argc++] = "-v";
 	if (verbosity >= 1)
diff --git a/builtin-remote.c b/builtin-remote.c
index e67b89f..fb0d66d 100644
--- a/builtin-remote.c
+++ b/builtin-remote.c
@@ -1195,13 +1195,15 @@ static int update(int argc, const char **argv)
 	int fetch_argc = 0;
 	int default_defined = 0;
 
-	fetch_argv = xmalloc(sizeof(char *) * (argc+4));
+	fetch_argv = xmalloc(sizeof(char *) * (argc+5));
 
 	argc = parse_options(argc, argv, NULL, options, builtin_remote_usage,
 			     PARSE_OPT_KEEP_ARGV0);
 
 	fetch_argv[fetch_argc++] = "fetch";
 
+	if (prune)
+		fetch_argv[fetch_argc++] = "--prune";
 	if (verbose)
 		fetch_argv[fetch_argc++] = "-v";
 	if (argc < 2) {
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index 562cca2..e931ce6 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -365,7 +365,7 @@ test_expect_success 'update with arguments' '
 
 '
 
-test_expect_failure 'update --prune' '
+test_expect_success 'update --prune' '
 
 	(cd one &&
 	 git branch -m side2 side3) &&
-- 
1.6.4.2

^ permalink raw reply related

* [PATCH 2/4] teach warn_dangling_symref to take a FILE argument
From: Jay Soffian @ 2009-11-10  4:58 UTC (permalink / raw)
  To: git; +Cc: Jay Soffian, Junio C Hamano, Björn Gustavsson
In-Reply-To: <1257829113-52168-2-git-send-email-jaysoffian@gmail.com>

Different callers of warn_dangling_symref() may want to control whether its
output goes to stdout or stderr so let it take a FILE argument.
---
 builtin-remote.c |    2 +-
 refs.c           |    7 ++++---
 refs.h           |    2 +-
 3 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/builtin-remote.c b/builtin-remote.c
index 0ea4e60..e67b89f 100644
--- a/builtin-remote.c
+++ b/builtin-remote.c
@@ -1166,7 +1166,7 @@ static int prune_remote(const char *remote, int dry_run)
 
 		printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned",
 		       abbrev_ref(refname, "refs/remotes/"));
-		warn_dangling_symref(dangling_msg, refname);
+		warn_dangling_symref(stdout, dangling_msg, refname);
 	}
 
 	free_remote_ref_states(&states);
diff --git a/refs.c b/refs.c
index 808f56b..3e73a0a 100644
--- a/refs.c
+++ b/refs.c
@@ -286,6 +286,7 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
 }
 
 struct warn_if_dangling_data {
+	FILE *fp;
 	const char *refname;
 	const char *msg_fmt;
 };
@@ -304,13 +305,13 @@ static int warn_if_dangling_symref(const char *refname, const unsigned char *sha
 	if (!resolves_to || strcmp(resolves_to, d->refname))
 		return 0;
 
-	printf(d->msg_fmt, refname);
+	fprintf(d->fp, d->msg_fmt, refname);
 	return 0;
 }
 
-void warn_dangling_symref(const char *msg_fmt, const char *refname)
+void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
 {
-	struct warn_if_dangling_data data = { refname, msg_fmt };
+	struct warn_if_dangling_data data = { fp, refname, msg_fmt };
 	for_each_rawref(warn_if_dangling_symref, &data);
 }
 
diff --git a/refs.h b/refs.h
index 777b5b7..e141991 100644
--- a/refs.h
+++ b/refs.h
@@ -29,7 +29,7 @@ extern int for_each_replace_ref(each_ref_fn, void *);
 /* can be used to learn about broken ref and symref */
 extern int for_each_rawref(each_ref_fn, void *);
 
-extern void warn_dangling_symref(const char *msg_fmt, const char *refname);
+extern void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname);
 
 /*
  * Extra refs will be listed by for_each_ref() before any actual refs
-- 
1.6.4.2

^ permalink raw reply related

* [PATCH 4/4] builtin-fetch: add --dry-run option
From: Jay Soffian @ 2009-11-10  4:58 UTC (permalink / raw)
  To: git; +Cc: Jay Soffian, Junio C Hamano, Björn Gustavsson
In-Reply-To: <1257829113-52168-4-git-send-email-jaysoffian@gmail.com>

Teach fetch --dry-run as users of "git remote prune" switching to "git fetch
--prune" may expect it. Unfortunately OPT__DRY_RUN() cannot be used as fetch
already uses "-n" for something else.
---
 Documentation/fetch-options.txt |    5 +++++
 builtin-fetch.c                 |   14 ++++++++++----
 2 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt
index 500637a..ab6419f 100644
--- a/Documentation/fetch-options.txt
+++ b/Documentation/fetch-options.txt
@@ -12,6 +12,11 @@
 	`git clone` with `--depth=<depth>` option (see linkgit:git-clone[1])
 	by the specified number of commits.
 
+ifndef::git-pull[]
+--dry-run::
+	Show what would be done, without making any changes.
+endif::git-pull[]
+
 -f::
 --force::
 	When 'git-fetch' is used with `<rbranch>:<lbranch>`
diff --git a/builtin-fetch.c b/builtin-fetch.c
index fd31072..8082d36 100644
--- a/builtin-fetch.c
+++ b/builtin-fetch.c
@@ -26,7 +26,7 @@ enum {
 	TAGS_SET = 2
 };
 
-static int all, append, force, keep, multiple, prune, update_head_ok, verbosity;
+static int all, append, dry_run, force, keep, multiple, prune, update_head_ok, verbosity;
 static int tags = TAGS_DEFAULT;
 static const char *depth;
 static const char *upload_pack;
@@ -51,6 +51,8 @@ static struct option builtin_fetch_options[] = {
 		    "do not fetch all tags (--no-tags)", TAGS_UNSET),
 	OPT_BOOLEAN('p', "prune", &prune,
 		    "prune tracking branches no longer on remote"),
+	OPT_BOOLEAN(0, "dry-run", &dry_run,
+		    "dry run"),
 	OPT_BOOLEAN('k', "keep", &keep, "keep downloaded pack"),
 	OPT_BOOLEAN('u', "update-head-ok", &update_head_ok,
 		    "allow updating of HEAD ref"),
@@ -187,6 +189,8 @@ static int s_update_ref(const char *action,
 	char *rla = getenv("GIT_REFLOG_ACTION");
 	static struct ref_lock *lock;
 
+	if (dry_run)
+		return 0;
 	if (!rla)
 		rla = default_rla.buf;
 	snprintf(msg, sizeof(msg), "%s: %s", rla, action);
@@ -312,7 +316,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
 	char note[1024];
 	const char *what, *kind;
 	struct ref *rm;
-	char *url, *filename = git_path("FETCH_HEAD");
+	char *url, *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD");
 
 	fp = fopen(filename, "a");
 	if (!fp)
@@ -658,7 +662,7 @@ static int do_fetch(struct transport *transport,
 		die("Don't know how to fetch from %s", transport->url);
 
 	/* if not appending, truncate FETCH_HEAD */
-	if (!append) {
+	if (!append && !dry_run) {
 		char *filename = git_path("FETCH_HEAD");
 		FILE *fp = fopen(filename, "w");
 		if (!fp)
@@ -766,9 +770,11 @@ static int add_remote_or_group(const char *name, struct string_list *list)
 static int fetch_multiple(struct string_list *list)
 {
 	int i, result = 0;
-	const char *argv[] = { "fetch", NULL, NULL, NULL, NULL, NULL };
+	const char *argv[] = { "fetch", NULL, NULL, NULL, NULL, NULL, NULL };
 	int argc = 1;
 
+	if (dry_run)
+		argv[argc++] = "--dry-run";
 	if (prune)
 		argv[argc++] = "--prune";
 	if (verbosity >= 2)
-- 
1.6.4.2

^ permalink raw reply related

* [PATCH 0/4] Teach fetch --prune and --dry run options
From: Jay Soffian @ 2009-11-10  5:03 UTC (permalink / raw)
  To: git; +Cc: Jay Soffian, Junio C Hamano, Björn Gustavsson

(Darn it, I left the SOB off. This is a resend of the series with SOB. Sorry
for the noise...)

This series builds on bg/fetch-multi, by teaching fetch --prune, re-enabling
"remote update --prune" and ensuring its corresponding test now passes.

As a convenience to users used to "remote prune" supporting the "--dry-run"
option, we also teach this option to fetch since it's trivial to do so.

I've left out re-implementing "remote prune" as a synonym for "update --prune"
since it's not a 1:1 match. And as per Junio's comments, teaching fetch a 
"--prune-only" option would be a UI wart.

Jay Soffian (4):
  remote: refactor some logic into get_stale_heads()
  teach warn_dangling_symref to take a FILE argument
  builtin-fetch: add --prune option
  builtin-fetch: add --dry-run option

 Documentation/fetch-options.txt |    9 ++++++++
 builtin-fetch.c                 |   42 +++++++++++++++++++++++++++++++++++---
 builtin-remote.c                |   38 +++++++++++------------------------
 refs.c                          |    7 +++--
 refs.h                          |    2 +-
 remote.c                        |   40 +++++++++++++++++++++++++++++++++++++
 remote.h                        |    3 ++
 t/t5505-remote.sh               |    2 +-
 8 files changed, 108 insertions(+), 35 deletions(-)

^ permalink raw reply

* [PATCH 1/4] remote: refactor some logic into get_stale_heads()
From: Jay Soffian @ 2009-11-10  5:03 UTC (permalink / raw)
  To: git; +Cc: Jay Soffian, Junio C Hamano, Björn Gustavsson
In-Reply-To: <1257829414-52316-1-git-send-email-jaysoffian@gmail.com>

Move the logic in builtin-remote.c which determines which local heads are stale
to remote.c so it can be used by other builtins.

Signed-off-by: Jay Soffian <jaysoffian@gmail.com>
---
 builtin-remote.c |   32 ++++++++------------------------
 remote.c         |   40 ++++++++++++++++++++++++++++++++++++++++
 remote.h         |    3 +++
 3 files changed, 51 insertions(+), 24 deletions(-)

diff --git a/builtin-remote.c b/builtin-remote.c
index fd7e0b2..0ea4e60 100644
--- a/builtin-remote.c
+++ b/builtin-remote.c
@@ -227,32 +227,10 @@ struct ref_states {
 	int queried;
 };
 
-static int handle_one_branch(const char *refname,
-	const unsigned char *sha1, int flags, void *cb_data)
-{
-	struct ref_states *states = cb_data;
-	struct refspec refspec;
-
-	memset(&refspec, 0, sizeof(refspec));
-	refspec.dst = (char *)refname;
-	if (!remote_find_tracking(states->remote, &refspec)) {
-		struct string_list_item *item;
-		const char *name = abbrev_branch(refspec.src);
-		/* symbolic refs pointing nowhere were handled already */
-		if ((flags & REF_ISSYMREF) ||
-		    string_list_has_string(&states->tracked, name) ||
-		    string_list_has_string(&states->new, name))
-			return 0;
-		item = string_list_append(name, &states->stale);
-		item->util = xstrdup(refname);
-	}
-	return 0;
-}
-
 static int get_ref_states(const struct ref *remote_refs, struct ref_states *states)
 {
 	struct ref *fetch_map = NULL, **tail = &fetch_map;
-	struct ref *ref;
+	struct ref *ref, *stale_refs;
 	int i;
 
 	for (i = 0; i < states->remote->fetch_refspec_nr; i++)
@@ -268,11 +246,17 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
 		else
 			string_list_append(abbrev_branch(ref->name), &states->tracked);
 	}
+	stale_refs = get_stale_heads(states->remote, fetch_map);
+	for (ref = stale_refs; ref; ref = ref->next) {
+		struct string_list_item *item =
+			string_list_append(abbrev_branch(ref->name), &states->stale);
+		item->util = xstrdup(ref->name);
+	}
+	free_refs(stale_refs);
 	free_refs(fetch_map);
 
 	sort_string_list(&states->new);
 	sort_string_list(&states->tracked);
-	for_each_ref(handle_one_branch, states);
 	sort_string_list(&states->stale);
 
 	return 0;
diff --git a/remote.c b/remote.c
index beaf9fb..eae5866 100644
--- a/remote.c
+++ b/remote.c
@@ -6,6 +6,7 @@
 #include "revision.h"
 #include "dir.h"
 #include "tag.h"
+#include "string-list.h"
 
 static struct refspec s_tag_refspec = {
 	0,
@@ -1587,3 +1588,42 @@ struct ref *guess_remote_head(const struct ref *head,
 
 	return list;
 }
+
+struct stale_heads_info {
+	struct remote *remote;
+	struct string_list *ref_names;
+	struct ref **stale_refs_tail;
+};
+
+static int get_stale_heads_cb(const char *refname,
+	const unsigned char *sha1, int flags, void *cb_data)
+{
+	struct stale_heads_info *info = cb_data;
+	struct refspec refspec;
+	memset(&refspec, 0, sizeof(refspec));
+	refspec.dst = (char *)refname;
+	if (!remote_find_tracking(info->remote, &refspec)) {
+		if (!((flags & REF_ISSYMREF) ||
+		    string_list_has_string(info->ref_names, refspec.src))) {
+			struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail);
+			hashcpy(ref->new_sha1, sha1);
+		}
+	}
+	return 0;
+}
+
+struct ref *get_stale_heads(struct remote *remote, struct ref *fetch_map)
+{
+	struct ref *ref, *stale_refs = NULL;
+	struct string_list ref_names = { NULL, 0, 0, 0 };
+	struct stale_heads_info info;
+	info.remote = remote;
+	info.ref_names = &ref_names;
+	info.stale_refs_tail = &stale_refs;
+	for (ref = fetch_map; ref; ref = ref->next)
+		string_list_append(ref->name, &ref_names);
+	sort_string_list(&ref_names);
+	for_each_ref(get_stale_heads_cb, &info);
+	string_list_clear(&ref_names, 0);
+	return stale_refs;
+}
diff --git a/remote.h b/remote.h
index 5db8420..d0aba81 100644
--- a/remote.h
+++ b/remote.h
@@ -154,4 +154,7 @@ struct ref *guess_remote_head(const struct ref *head,
 			      const struct ref *refs,
 			      int all);
 
+/* Return refs which no longer exist on remote */
+struct ref *get_stale_heads(struct remote *remote, struct ref *fetch_map);
+
 #endif
-- 
1.6.4.2

^ permalink raw reply related

* [PATCH 2/4] teach warn_dangling_symref to take a FILE argument
From: Jay Soffian @ 2009-11-10  5:03 UTC (permalink / raw)
  To: git; +Cc: Jay Soffian, Junio C Hamano, Björn Gustavsson
In-Reply-To: <1257829414-52316-2-git-send-email-jaysoffian@gmail.com>

Different callers of warn_dangling_symref() may want to control whether its
output goes to stdout or stderr so let it take a FILE argument.

Signed-off-by: Jay Soffian <jaysoffian@gmail.com>
---
 builtin-remote.c |    2 +-
 refs.c           |    7 ++++---
 refs.h           |    2 +-
 3 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/builtin-remote.c b/builtin-remote.c
index 0ea4e60..e67b89f 100644
--- a/builtin-remote.c
+++ b/builtin-remote.c
@@ -1166,7 +1166,7 @@ static int prune_remote(const char *remote, int dry_run)
 
 		printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned",
 		       abbrev_ref(refname, "refs/remotes/"));
-		warn_dangling_symref(dangling_msg, refname);
+		warn_dangling_symref(stdout, dangling_msg, refname);
 	}
 
 	free_remote_ref_states(&states);
diff --git a/refs.c b/refs.c
index 808f56b..3e73a0a 100644
--- a/refs.c
+++ b/refs.c
@@ -286,6 +286,7 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
 }
 
 struct warn_if_dangling_data {
+	FILE *fp;
 	const char *refname;
 	const char *msg_fmt;
 };
@@ -304,13 +305,13 @@ static int warn_if_dangling_symref(const char *refname, const unsigned char *sha
 	if (!resolves_to || strcmp(resolves_to, d->refname))
 		return 0;
 
-	printf(d->msg_fmt, refname);
+	fprintf(d->fp, d->msg_fmt, refname);
 	return 0;
 }
 
-void warn_dangling_symref(const char *msg_fmt, const char *refname)
+void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
 {
-	struct warn_if_dangling_data data = { refname, msg_fmt };
+	struct warn_if_dangling_data data = { fp, refname, msg_fmt };
 	for_each_rawref(warn_if_dangling_symref, &data);
 }
 
diff --git a/refs.h b/refs.h
index 777b5b7..e141991 100644
--- a/refs.h
+++ b/refs.h
@@ -29,7 +29,7 @@ extern int for_each_replace_ref(each_ref_fn, void *);
 /* can be used to learn about broken ref and symref */
 extern int for_each_rawref(each_ref_fn, void *);
 
-extern void warn_dangling_symref(const char *msg_fmt, const char *refname);
+extern void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname);
 
 /*
  * Extra refs will be listed by for_each_ref() before any actual refs
-- 
1.6.4.2

^ permalink raw reply related

* [PATCH 3/4] builtin-fetch: add --prune option
From: Jay Soffian @ 2009-11-10  5:03 UTC (permalink / raw)
  To: git; +Cc: Jay Soffian, Junio C Hamano, Björn Gustavsson
In-Reply-To: <1257829414-52316-3-git-send-email-jaysoffian@gmail.com>

Teach fetch to cull stale remote tracking branches after fetching via --prune.

We can now enable the "--prune" option to "remote update", which was recently
re-implemented as an invocation of git fetch.

Signed-off-by: Jay Soffian <jaysoffian@gmail.com>
---
 Documentation/fetch-options.txt |    4 ++++
 builtin-fetch.c                 |   32 ++++++++++++++++++++++++++++++--
 builtin-remote.c                |    4 +++-
 t/t5505-remote.sh               |    2 +-
 4 files changed, 38 insertions(+), 4 deletions(-)

diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt
index 8b0cf58..500637a 100644
--- a/Documentation/fetch-options.txt
+++ b/Documentation/fetch-options.txt
@@ -28,6 +28,10 @@ ifndef::git-pull[]
 --multiple::
 	Allow several <repository> and <group> arguments to be
 	specified. No <refspec>s may be specified.
+
+--prune::
+	After fetching, remove any remote tracking branches which
+	no longer exist	on the remote.
 endif::git-pull[]
 
 ifdef::git-pull[]
diff --git a/builtin-fetch.c b/builtin-fetch.c
index 945dfd8..fd31072 100644
--- a/builtin-fetch.c
+++ b/builtin-fetch.c
@@ -26,7 +26,7 @@ enum {
 	TAGS_SET = 2
 };
 
-static int all, append, force, keep, multiple, update_head_ok, verbosity;
+static int all, append, force, keep, multiple, prune, update_head_ok, verbosity;
 static int tags = TAGS_DEFAULT;
 static const char *depth;
 static const char *upload_pack;
@@ -49,6 +49,8 @@ static struct option builtin_fetch_options[] = {
 		    "fetch all tags and associated objects", TAGS_SET),
 	OPT_SET_INT('n', NULL, &tags,
 		    "do not fetch all tags (--no-tags)", TAGS_UNSET),
+	OPT_BOOLEAN('p', "prune", &prune,
+		    "prune tracking branches no longer on remote"),
 	OPT_BOOLEAN('k', "keep", &keep, "keep downloaded pack"),
 	OPT_BOOLEAN('u', "update-head-ok", &update_head_ok,
 		    "allow updating of HEAD ref"),
@@ -492,6 +494,28 @@ static int fetch_refs(struct transport *transport, struct ref *ref_map)
 	return ret;
 }
 
+static int prune_refs(struct transport *transport, struct ref *ref_map)
+{
+	int result = 0;
+	struct ref *ref, *stale_refs = get_stale_heads(transport->remote, ref_map);
+	const char *dangling_msg = dry_run
+		? "   (%s will become dangling)\n"
+		: "   (%s has become dangling)\n";
+
+	for (ref = stale_refs; ref; ref = ref->next) {
+		if (!dry_run)
+			result |= delete_ref(ref->name, NULL, 0);
+		if (verbosity >= 0) {
+			fprintf(stderr, " x %-*s %-*s -> %s\n",
+				SUMMARY_WIDTH, "[deleted]",
+				REFCOL_WIDTH, "(none)", prettify_refname(ref->name));
+			warn_dangling_symref(stderr, dangling_msg, ref->name);
+		}
+	}
+	free_refs(stale_refs);
+	return result;
+}
+
 static int add_existing(const char *refname, const unsigned char *sha1,
 			int flag, void *cbdata)
 {
@@ -657,6 +681,8 @@ static int do_fetch(struct transport *transport,
 		free_refs(ref_map);
 		return 1;
 	}
+	if (prune)
+		prune_refs(transport, ref_map);
 	free_refs(ref_map);
 
 	/* if neither --no-tags nor --tags was specified, do automated tag
@@ -740,9 +766,11 @@ static int add_remote_or_group(const char *name, struct string_list *list)
 static int fetch_multiple(struct string_list *list)
 {
 	int i, result = 0;
-	const char *argv[] = { "fetch", NULL, NULL, NULL, NULL };
+	const char *argv[] = { "fetch", NULL, NULL, NULL, NULL, NULL };
 	int argc = 1;
 
+	if (prune)
+		argv[argc++] = "--prune";
 	if (verbosity >= 2)
 		argv[argc++] = "-v";
 	if (verbosity >= 1)
diff --git a/builtin-remote.c b/builtin-remote.c
index e67b89f..fb0d66d 100644
--- a/builtin-remote.c
+++ b/builtin-remote.c
@@ -1195,13 +1195,15 @@ static int update(int argc, const char **argv)
 	int fetch_argc = 0;
 	int default_defined = 0;
 
-	fetch_argv = xmalloc(sizeof(char *) * (argc+4));
+	fetch_argv = xmalloc(sizeof(char *) * (argc+5));
 
 	argc = parse_options(argc, argv, NULL, options, builtin_remote_usage,
 			     PARSE_OPT_KEEP_ARGV0);
 
 	fetch_argv[fetch_argc++] = "fetch";
 
+	if (prune)
+		fetch_argv[fetch_argc++] = "--prune";
 	if (verbose)
 		fetch_argv[fetch_argc++] = "-v";
 	if (argc < 2) {
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index 562cca2..e931ce6 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -365,7 +365,7 @@ test_expect_success 'update with arguments' '
 
 '
 
-test_expect_failure 'update --prune' '
+test_expect_success 'update --prune' '
 
 	(cd one &&
 	 git branch -m side2 side3) &&
-- 
1.6.4.2

^ permalink raw reply related

* [PATCH 4/4] builtin-fetch: add --dry-run option
From: Jay Soffian @ 2009-11-10  5:03 UTC (permalink / raw)
  To: git; +Cc: Jay Soffian, Junio C Hamano, Björn Gustavsson
In-Reply-To: <1257829414-52316-4-git-send-email-jaysoffian@gmail.com>

Teach fetch --dry-run as users of "git remote prune" switching to "git fetch
--prune" may expect it. Unfortunately OPT__DRY_RUN() cannot be used as fetch
already uses "-n" for something else.

Signed-off-by: Jay Soffian <jaysoffian@gmail.com>
---
 Documentation/fetch-options.txt |    5 +++++
 builtin-fetch.c                 |   14 ++++++++++----
 2 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt
index 500637a..ab6419f 100644
--- a/Documentation/fetch-options.txt
+++ b/Documentation/fetch-options.txt
@@ -12,6 +12,11 @@
 	`git clone` with `--depth=<depth>` option (see linkgit:git-clone[1])
 	by the specified number of commits.
 
+ifndef::git-pull[]
+--dry-run::
+	Show what would be done, without making any changes.
+endif::git-pull[]
+
 -f::
 --force::
 	When 'git-fetch' is used with `<rbranch>:<lbranch>`
diff --git a/builtin-fetch.c b/builtin-fetch.c
index fd31072..8082d36 100644
--- a/builtin-fetch.c
+++ b/builtin-fetch.c
@@ -26,7 +26,7 @@ enum {
 	TAGS_SET = 2
 };
 
-static int all, append, force, keep, multiple, prune, update_head_ok, verbosity;
+static int all, append, dry_run, force, keep, multiple, prune, update_head_ok, verbosity;
 static int tags = TAGS_DEFAULT;
 static const char *depth;
 static const char *upload_pack;
@@ -51,6 +51,8 @@ static struct option builtin_fetch_options[] = {
 		    "do not fetch all tags (--no-tags)", TAGS_UNSET),
 	OPT_BOOLEAN('p', "prune", &prune,
 		    "prune tracking branches no longer on remote"),
+	OPT_BOOLEAN(0, "dry-run", &dry_run,
+		    "dry run"),
 	OPT_BOOLEAN('k', "keep", &keep, "keep downloaded pack"),
 	OPT_BOOLEAN('u', "update-head-ok", &update_head_ok,
 		    "allow updating of HEAD ref"),
@@ -187,6 +189,8 @@ static int s_update_ref(const char *action,
 	char *rla = getenv("GIT_REFLOG_ACTION");
 	static struct ref_lock *lock;
 
+	if (dry_run)
+		return 0;
 	if (!rla)
 		rla = default_rla.buf;
 	snprintf(msg, sizeof(msg), "%s: %s", rla, action);
@@ -312,7 +316,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
 	char note[1024];
 	const char *what, *kind;
 	struct ref *rm;
-	char *url, *filename = git_path("FETCH_HEAD");
+	char *url, *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD");
 
 	fp = fopen(filename, "a");
 	if (!fp)
@@ -658,7 +662,7 @@ static int do_fetch(struct transport *transport,
 		die("Don't know how to fetch from %s", transport->url);
 
 	/* if not appending, truncate FETCH_HEAD */
-	if (!append) {
+	if (!append && !dry_run) {
 		char *filename = git_path("FETCH_HEAD");
 		FILE *fp = fopen(filename, "w");
 		if (!fp)
@@ -766,9 +770,11 @@ static int add_remote_or_group(const char *name, struct string_list *list)
 static int fetch_multiple(struct string_list *list)
 {
 	int i, result = 0;
-	const char *argv[] = { "fetch", NULL, NULL, NULL, NULL, NULL };
+	const char *argv[] = { "fetch", NULL, NULL, NULL, NULL, NULL, NULL };
 	int argc = 1;
 
+	if (dry_run)
+		argv[argc++] = "--dry-run";
 	if (prune)
 		argv[argc++] = "--prune";
 	if (verbosity >= 2)
-- 
1.6.4.2

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox