Git development
 help / color / mirror / Atom feed
* [PATCH v5 02/10] clone: write detached HEAD in bare repositories
From: Nguyễn Thái Ngọc Duy @ 2012-01-16  9:46 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1326439322-15648-1-git-send-email-pclouds@gmail.com>

If we don't write, HEAD is still at refs/heads/master as initialized
by init-db, which may or may not match remote's HEAD.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c  |    9 +++------
 t/t5601-clone.sh |   25 ++++++++++++++++++++++++-
 2 files changed, 27 insertions(+), 7 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 9dcc5fe..91862f7 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -764,12 +764,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 		}
 	} else if (remote_head) {
 		/* Source had detached HEAD pointing somewhere. */
-		if (!option_bare) {
-			update_ref(reflog_msg.buf, "HEAD",
-				   remote_head->old_sha1,
-				   NULL, REF_NODEREF, DIE_ON_ERR);
-			our_head_points_at = remote_head;
-		}
+		update_ref(reflog_msg.buf, "HEAD", remote_head->old_sha1,
+			   NULL, REF_NODEREF, DIE_ON_ERR);
+		our_head_points_at = remote_head;
 	} else {
 		/* Nothing to checkout out */
 		if (!option_no_checkout)
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index 49821eb..e0b8db6 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -12,7 +12,10 @@ test_expect_success setup '
 		cd src &&
 		>file &&
 		git add file &&
-		git commit -m initial
+		git commit -m initial &&
+		echo 1 >file &&
+		git add file &&
+		git commit -m updated
 	)
 
 '
@@ -88,6 +91,26 @@ test_expect_success 'clone --mirror' '
 
 '
 
+test_expect_success 'clone --mirror with detached HEAD' '
+
+	( cd src && git checkout HEAD^ && git rev-parse HEAD >../expected ) &&
+	git clone --mirror src mirror.detached &&
+	( cd src && git checkout - ) &&
+	GIT_DIR=mirror.detached git rev-parse HEAD >actual &&
+	test_cmp expected actual
+
+'
+
+test_expect_success 'clone --bare with detached HEAD' '
+
+	( cd src && git checkout HEAD^ && git rev-parse HEAD >../expected ) &&
+	git clone --bare src bare.detached &&
+	( cd src && git checkout - ) &&
+	GIT_DIR=bare.detached git rev-parse HEAD >actual &&
+	test_cmp expected actual
+
+'
+
 test_expect_success 'clone --bare names the local repository <name>.git' '
 
 	git clone --bare src &&
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related

* [PATCH v5 03/10] clone: factor out checkout code
From: Nguyễn Thái Ngọc Duy @ 2012-01-16  9:46 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1326439322-15648-1-git-send-email-pclouds@gmail.com>

Read HEAD from disk instead of relying on local variable
our_head_points_at, so that if earlier code fails to make HEAD
properly, it'll be detected.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c |  101 +++++++++++++++++++++++++++++++-----------------------
 1 files changed, 58 insertions(+), 43 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 91862f7..98e3542 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -486,6 +486,63 @@ static void write_followtags(const struct ref *refs, const char *msg)
 	}
 }
 
+static int checkout(void)
+{
+	unsigned char sha1[20];
+	char *head;
+	struct lock_file *lock_file;
+	struct unpack_trees_options opts;
+	struct tree *tree;
+	struct tree_desc t;
+	int err = 0, fd;
+
+	if (option_no_checkout)
+		return 0;
+
+	head = resolve_refdup("HEAD", sha1, 1, NULL);
+	if (!head) {
+		warning(_("remote HEAD refers to nonexistent ref, "
+			  "unable to checkout.\n"));
+		return 0;
+	}
+	if (strcmp(head, "HEAD")) {
+		if (prefixcmp(head, "refs/heads/"))
+			die(_("HEAD not found below refs/heads!"));
+	}
+	free(head);
+
+	/* We need to be in the new work tree for the checkout */
+	setup_work_tree();
+
+	lock_file = xcalloc(1, sizeof(struct lock_file));
+	fd = hold_locked_index(lock_file, 1);
+
+	memset(&opts, 0, sizeof opts);
+	opts.update = 1;
+	opts.merge = 1;
+	opts.fn = oneway_merge;
+	opts.verbose_update = (option_verbosity > 0);
+	opts.src_index = &the_index;
+	opts.dst_index = &the_index;
+
+	tree = parse_tree_indirect(sha1);
+	parse_tree(tree);
+	init_tree_desc(&t, tree->buffer, tree->size);
+	unpack_trees(1, &t, &opts);
+
+	if (write_cache(fd, active_cache, active_nr) ||
+	    commit_locked_index(lock_file))
+		die(_("unable to write new index file"));
+
+	err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1),
+			sha1_to_hex(sha1), "1", NULL);
+
+	if (!err && option_recursive)
+		err = run_command_v_opt(argv_submodule, RUN_GIT_CMD);
+
+	return err;
+}
+
 static int write_one_config(const char *key, const char *value, void *data)
 {
 	return git_config_set_multivar(key, value ? value : "true", "^$", 0);
@@ -766,13 +823,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 		/* Source had detached HEAD pointing somewhere. */
 		update_ref(reflog_msg.buf, "HEAD", remote_head->old_sha1,
 			   NULL, REF_NODEREF, DIE_ON_ERR);
-		our_head_points_at = remote_head;
-	} else {
-		/* Nothing to checkout out */
-		if (!option_no_checkout)
-			warning(_("remote HEAD refers to nonexistent ref, "
-				"unable to checkout.\n"));
-		option_no_checkout = 1;
 	}
 
 	if (transport) {
@@ -780,42 +830,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 		transport_disconnect(transport);
 	}
 
-	if (!option_no_checkout) {
-		struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
-		struct unpack_trees_options opts;
-		struct tree *tree;
-		struct tree_desc t;
-		int fd;
-
-		/* We need to be in the new work tree for the checkout */
-		setup_work_tree();
-
-		fd = hold_locked_index(lock_file, 1);
-
-		memset(&opts, 0, sizeof opts);
-		opts.update = 1;
-		opts.merge = 1;
-		opts.fn = oneway_merge;
-		opts.verbose_update = (option_verbosity > 0);
-		opts.src_index = &the_index;
-		opts.dst_index = &the_index;
-
-		tree = parse_tree_indirect(our_head_points_at->old_sha1);
-		parse_tree(tree);
-		init_tree_desc(&t, tree->buffer, tree->size);
-		unpack_trees(1, &t, &opts);
-
-		if (write_cache(fd, active_cache, active_nr) ||
-		    commit_locked_index(lock_file))
-			die(_("unable to write new index file"));
-
-		err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1),
-				sha1_to_hex(our_head_points_at->old_sha1), "1",
-				NULL);
-
-		if (!err && option_recursive)
-			err = run_command_v_opt(argv_submodule, RUN_GIT_CMD);
-	}
+	err = checkout();
 
 	strbuf_release(&reflog_msg);
 	strbuf_release(&branch_top);
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related

* [PATCH v5 04/10] clone: factor out HEAD update code
From: Nguyễn Thái Ngọc Duy @ 2012-01-16  9:46 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1326439322-15648-1-git-send-email-pclouds@gmail.com>

While at it, update the comment at "if (remote_head)"

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c |   41 ++++++++++++++++++++++++-----------------
 1 files changed, 24 insertions(+), 17 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 98e3542..3b68014 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -486,6 +486,29 @@ static void write_followtags(const struct ref *refs, const char *msg)
 	}
 }
 
+static void update_head(const struct ref *our, const struct ref *remote,
+			const char *msg)
+{
+	if (our) {
+		/* Local default branch link */
+		create_symref("HEAD", our->name, NULL);
+		if (!option_bare) {
+			const char *head = skip_prefix(our->name, "refs/heads/");
+			update_ref(msg, "HEAD", our->old_sha1, NULL, 0, DIE_ON_ERR);
+			install_branch_config(0, head, option_origin, our->name);
+		}
+	} else if (remote) {
+		/*
+		 * We know remote HEAD points to a non-branch, or
+		 * HEAD points to a branch but we don't know which one, or
+		 * we asked for a specific branch but it did not exist.
+		 * Detach HEAD in all these cases.
+		 */
+		update_ref(msg, "HEAD", remote->old_sha1,
+			   NULL, REF_NODEREF, DIE_ON_ERR);
+	}
+}
+
 static int checkout(void)
 {
 	unsigned char sha1[20];
@@ -807,23 +830,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 			      reflog_msg.buf);
 	}
 
-	if (our_head_points_at) {
-		/* Local default branch link */
-		create_symref("HEAD", our_head_points_at->name, NULL);
-		if (!option_bare) {
-			const char *head = skip_prefix(our_head_points_at->name,
-						       "refs/heads/");
-			update_ref(reflog_msg.buf, "HEAD",
-				   our_head_points_at->old_sha1,
-				   NULL, 0, DIE_ON_ERR);
-			install_branch_config(0, head, option_origin,
-					      our_head_points_at->name);
-		}
-	} else if (remote_head) {
-		/* Source had detached HEAD pointing somewhere. */
-		update_ref(reflog_msg.buf, "HEAD", remote_head->old_sha1,
-			   NULL, REF_NODEREF, DIE_ON_ERR);
-	}
+	update_head(our_head_points_at, remote_head, reflog_msg.buf);
 
 	if (transport) {
 		transport_unlock_pack(transport);
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related

* [PATCH v5 05/10] clone: factor out remote ref writing
From: Nguyễn Thái Ngọc Duy @ 2012-01-16  9:46 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1326439322-15648-1-git-send-email-pclouds@gmail.com>

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c |   39 +++++++++++++++++++++++++--------------
 1 files changed, 25 insertions(+), 14 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 3b68014..2733fa4 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -486,6 +486,29 @@ static void write_followtags(const struct ref *refs, const char *msg)
 	}
 }
 
+static void update_remote_refs(const struct ref *refs,
+			       const struct ref *mapped_refs,
+			       const struct ref *remote_head_points_at,
+			       const char *branch_top,
+			       const char *msg)
+{
+	if (refs) {
+		clear_extra_refs();
+		write_remote_refs(mapped_refs);
+		if (option_single_branch)
+			write_followtags(refs, msg);
+	}
+
+	if (remote_head_points_at && !option_bare) {
+		struct strbuf head_ref = STRBUF_INIT;
+		strbuf_addstr(&head_ref, branch_top);
+		strbuf_addstr(&head_ref, "HEAD");
+		create_symref(head_ref.buf,
+			      remote_head_points_at->peer_ref->name,
+			      msg);
+	}
+}
+
 static void update_head(const struct ref *our, const struct ref *remote,
 			const char *msg)
 {
@@ -782,12 +805,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 	}
 
 	if (refs) {
-		clear_extra_refs();
-
-		write_remote_refs(mapped_refs);
-		if (option_single_branch)
-			write_followtags(refs, reflog_msg.buf);
-
 		remote_head = find_ref_by_name(refs, "HEAD");
 		remote_head_points_at =
 			guess_remote_head(remote_head, mapped_refs, 0);
@@ -821,14 +838,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 					      "refs/heads/master");
 	}
 
-	if (remote_head_points_at && !option_bare) {
-		struct strbuf head_ref = STRBUF_INIT;
-		strbuf_addstr(&head_ref, branch_top.buf);
-		strbuf_addstr(&head_ref, "HEAD");
-		create_symref(head_ref.buf,
-			      remote_head_points_at->peer_ref->name,
-			      reflog_msg.buf);
-	}
+	update_remote_refs(refs, mapped_refs, remote_head_points_at,
+			   branch_top.buf, reflog_msg.buf);
 
 	update_head(our_head_points_at, remote_head, reflog_msg.buf);
 
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related

* [PATCH v5 06/10] clone: delay cloning until after remote HEAD checking
From: Nguyễn Thái Ngọc Duy @ 2012-01-16  9:46 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1326439322-15648-1-git-send-email-pclouds@gmail.com>

This gives us an opportunity to abort the command during remote HEAD
check without wasting much bandwidth.

Cloning with remote-helper remains before the check because the remote
helper updates mapped_refs, which is necessary for remote ref checks.
foreign_vcs field is used to indicate the transport is handled by
remote helper.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c |   54 +++++++++++++++++++++++++++---------------------------
 transport.c     |    5 ++++-
 2 files changed, 31 insertions(+), 28 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 2733fa4..a1fbb3b 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -364,13 +364,8 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
 	closedir(dir);
 }
 
-static const struct ref *clone_local(const char *src_repo,
-				     const char *dest_repo)
+static void clone_local(const char *src_repo, const char *dest_repo)
 {
-	const struct ref *ret;
-	struct remote *remote;
-	struct transport *transport;
-
 	if (option_shared) {
 		struct strbuf alt = STRBUF_INIT;
 		strbuf_addf(&alt, "%s/objects", src_repo);
@@ -386,13 +381,8 @@ static const struct ref *clone_local(const char *src_repo,
 		strbuf_release(&dest);
 	}
 
-	remote = remote_get(src_repo);
-	transport = transport_get(remote, src_repo);
-	ret = transport_get_remote_refs(transport);
-	transport_disconnect(transport);
 	if (0 <= option_verbosity)
 		printf(_("done.\n"));
-	return ret;
 }
 
 static const char *junk_work_tree;
@@ -619,6 +609,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 	struct strbuf key = STRBUF_INIT, value = STRBUF_INIT;
 	struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
 	struct transport *transport = NULL;
+	struct remote *remote;
 	int err = 0;
 
 	struct refspec *refspec;
@@ -773,13 +764,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
 	strbuf_reset(&value);
 
-	if (is_local) {
-		refs = clone_local(path, git_dir);
-		mapped_refs = wanted_peer_refs(refs, refspec);
-	} else {
-		struct remote *remote = remote_get(option_origin);
-		transport = transport_get(remote, remote->url[0]);
+	remote = remote_get(option_origin);
+	transport = transport_get(remote, remote->url[0]);
 
+	if (!is_local) {
 		if (!transport->get_refs_list || !transport->fetch)
 			die(_("Don't know how to clone %s"), transport->url);
 
@@ -796,14 +784,23 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 		if (option_upload_pack)
 			transport_set_option(transport, TRANS_OPT_UPLOADPACK,
 					     option_upload_pack);
-
-		refs = transport_get_remote_refs(transport);
-		if (refs) {
-			mapped_refs = wanted_peer_refs(refs, refspec);
-			transport_fetch_refs(transport, mapped_refs);
-		}
 	}
 
+	refs = transport_get_remote_refs(transport);
+	mapped_refs = refs ? wanted_peer_refs(refs, refspec) : NULL;
+
+	/*
+	 * mapped_refs may be updated if transport-helper is used so
+	 * we need fetch it early because remote_head code below
+	 * relies on it.
+	 *
+	 * for normal clones, transport_get_remote_refs() should
+	 * return reliable ref set, we can delay cloning until after
+	 * remote HEAD check.
+	 */
+	if (!is_local && remote->foreign_vcs && refs)
+		transport_fetch_refs(transport, mapped_refs);
+
 	if (refs) {
 		remote_head = find_ref_by_name(refs, "HEAD");
 		remote_head_points_at =
@@ -838,15 +835,18 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 					      "refs/heads/master");
 	}
 
+	if (is_local)
+		clone_local(path, git_dir);
+	else if (refs && !remote->foreign_vcs)
+		transport_fetch_refs(transport, mapped_refs);
+
 	update_remote_refs(refs, mapped_refs, remote_head_points_at,
 			   branch_top.buf, reflog_msg.buf);
 
 	update_head(our_head_points_at, remote_head, reflog_msg.buf);
 
-	if (transport) {
-		transport_unlock_pack(transport);
-		transport_disconnect(transport);
-	}
+	transport_unlock_pack(transport);
+	transport_disconnect(transport);
 
 	err = checkout();
 
diff --git a/transport.c b/transport.c
index a99b7c9..4366639 100644
--- a/transport.c
+++ b/transport.c
@@ -895,8 +895,10 @@ struct transport *transport_get(struct remote *remote, const char *url)
 
 		while (is_urlschemechar(p == url, *p))
 			p++;
-		if (!prefixcmp(p, "::"))
+		if (!prefixcmp(p, "::")) {
 			helper = xstrndup(url, p - url);
+			remote->foreign_vcs = helper;
+		}
 	}
 
 	if (helper) {
@@ -938,6 +940,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
 		char *handler = xmalloc(len + 1);
 		handler[len] = 0;
 		strncpy(handler, url, len);
+		remote->foreign_vcs = handler;
 		transport_helper_init(ret, handler);
 	}
 
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related

* [PATCH v5 07/10] clone: --branch=<branch> always means refs/heads/<branch>
From: Nguyễn Thái Ngọc Duy @ 2012-01-16  9:46 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1326439322-15648-1-git-send-email-pclouds@gmail.com>

It does not make sense to look outside refs/heads for HEAD's target
(src_ref_prefix can be set to "refs/" if --mirror is used) because ref
code only allows symref in form refs/heads/...

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c |   30 ++++++++++++++++--------------
 1 files changed, 16 insertions(+), 14 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index a1fbb3b..253a794 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -48,7 +48,6 @@ static int option_verbosity;
 static int option_progress;
 static struct string_list option_config;
 static struct string_list option_reference;
-static const char *src_ref_prefix = "refs/heads/";
 
 static int opt_parse_reference(const struct option *opt, const char *arg, int unset)
 {
@@ -413,6 +412,17 @@ static void remove_junk_on_signal(int signo)
 	raise(signo);
 }
 
+static struct ref *find_remote_branch(const struct ref *refs, const char *branch)
+{
+	struct ref *ref;
+	struct strbuf head = STRBUF_INIT;
+	strbuf_addstr(&head, "refs/heads/");
+	strbuf_addstr(&head, branch);
+	ref = find_ref_by_name(refs, head.buf);
+	strbuf_release(&head);
+	return ref;
+}
+
 static struct ref *wanted_peer_refs(const struct ref *refs,
 		struct refspec *refspec)
 {
@@ -425,13 +435,8 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
 
 		if (!option_branch)
 			remote_head = guess_remote_head(head, refs, 0);
-		else {
-			struct strbuf sb = STRBUF_INIT;
-			strbuf_addstr(&sb, src_ref_prefix);
-			strbuf_addstr(&sb, option_branch);
-			remote_head = find_ref_by_name(refs, sb.buf);
-			strbuf_release(&sb);
-		}
+		else
+			remote_head = find_remote_branch(refs, option_branch);
 
 		if (!remote_head && option_branch)
 			warning(_("Could not find remote branch %s to clone."),
@@ -502,7 +507,7 @@ static void update_remote_refs(const struct ref *refs,
 static void update_head(const struct ref *our, const struct ref *remote,
 			const char *msg)
 {
-	if (our) {
+	if (our && !prefixcmp(our->name, "refs/heads/")) {
 		/* Local default branch link */
 		create_symref("HEAD", our->name, NULL);
 		if (!option_bare) {
@@ -609,6 +614,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 	struct strbuf key = STRBUF_INIT, value = STRBUF_INIT;
 	struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
 	struct transport *transport = NULL;
+	const char *src_ref_prefix = "refs/heads/";
 	struct remote *remote;
 	int err = 0;
 
@@ -807,12 +813,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 			guess_remote_head(remote_head, mapped_refs, 0);
 
 		if (option_branch) {
-			struct strbuf head = STRBUF_INIT;
-			strbuf_addstr(&head, src_ref_prefix);
-			strbuf_addstr(&head, option_branch);
 			our_head_points_at =
-				find_ref_by_name(mapped_refs, head.buf);
-			strbuf_release(&head);
+				find_remote_branch(mapped_refs, option_branch);
 
 			if (!our_head_points_at) {
 				warning(_("Remote branch %s not found in "
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related

* [PATCH v5 08/10] clone: refuse to clone if --branch points to bogus ref
From: Nguyễn Thái Ngọc Duy @ 2012-01-16  9:46 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1326439322-15648-1-git-send-email-pclouds@gmail.com>

It's possible that users make a typo in the branch name. Stop and let
users recheck. Falling back to remote's HEAD is not documented any
way.

Except when using remote helper, the pack has not been transferred at
this stage yet so we don't waste much bandwidth.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 builtin/clone.c         |   12 ++++--------
 t/t5500-fetch-pack.sh   |    7 -------
 t/t5706-clone-branch.sh |    8 ++------
 3 files changed, 6 insertions(+), 21 deletions(-)

diff --git a/builtin/clone.c b/builtin/clone.c
index 253a794..3cfedb3 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -518,8 +518,7 @@ static void update_head(const struct ref *our, const struct ref *remote,
 	} else if (remote) {
 		/*
 		 * We know remote HEAD points to a non-branch, or
-		 * HEAD points to a branch but we don't know which one, or
-		 * we asked for a specific branch but it did not exist.
+		 * HEAD points to a branch but we don't know which one.
 		 * Detach HEAD in all these cases.
 		 */
 		update_ref(msg, "HEAD", remote->old_sha1,
@@ -816,12 +815,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 			our_head_points_at =
 				find_remote_branch(mapped_refs, option_branch);
 
-			if (!our_head_points_at) {
-				warning(_("Remote branch %s not found in "
-					"upstream %s, using HEAD instead"),
-					option_branch, option_origin);
-				our_head_points_at = remote_head_points_at;
-			}
+			if (!our_head_points_at)
+				die(_("Remote branch %s not found in upstream %s"),
+				    option_branch, option_origin);
 		}
 		else
 			our_head_points_at = remote_head_points_at;
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index 7e85c71..5237066 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -282,13 +282,6 @@ test_expect_success 'clone shallow object count' '
 	test_cmp count3.expected count3.actual
 '
 
-test_expect_success 'clone shallow with nonexistent --branch' '
-	git clone --depth 1 --branch Z "file://$(pwd)/." shallow4 &&
-	GIT_DIR=shallow4/.git git rev-parse HEAD >actual &&
-	git rev-parse HEAD >expected &&
-	test_cmp expected actual
-'
-
 test_expect_success 'clone shallow with detached HEAD' '
 	git checkout HEAD^ &&
 	git clone --depth 1 "file://$(pwd)/." shallow5 &&
diff --git a/t/t5706-clone-branch.sh b/t/t5706-clone-branch.sh
index f3f9a76..56be67e 100755
--- a/t/t5706-clone-branch.sh
+++ b/t/t5706-clone-branch.sh
@@ -57,12 +57,8 @@ test_expect_success 'clone -b does not munge remotes/origin/HEAD' '
 	)
 '
 
-test_expect_success 'clone -b with bogus branch chooses HEAD' '
-	git clone -b bogus parent clone-bogus &&
-	(cd clone-bogus &&
-	 check_HEAD master &&
-	 check_file one
-	)
+test_expect_success 'clone -b with bogus branch' '
+	test_must_fail git clone -b bogus parent clone-bogus
 '
 
 test_done
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related

* [PATCH v5 09/10] clone: allow --branch to take a tag
From: Nguyễn Thái Ngọc Duy @ 2012-01-16  9:46 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1326439322-15648-1-git-send-email-pclouds@gmail.com>

Because a tag ref cannot be put to HEAD, HEAD will become detached.
This is consistent with "git checkout <tag>".

This is mostly useful in shallow clone, where it allows you to clone a
tag in addtion to branches.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-clone.txt |    5 +++--
 builtin/clone.c             |   20 +++++++++++++++++++-
 t/t5500-fetch-pack.sh       |   15 +++++++++++++++
 t/t5601-clone.sh            |    9 +++++++++
 4 files changed, 46 insertions(+), 3 deletions(-)

diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index 0931a3e..6e22522 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -147,8 +147,9 @@ objects from the source repository into a pack in the cloned repository.
 -b <name>::
 	Instead of pointing the newly created HEAD to the branch pointed
 	to by the cloned repository's HEAD, point to `<name>` branch
-	instead. In a non-bare repository, this is the branch that will
-	be checked out.
+	instead. `--branch` can also take tags and treat them like
+	detached HEAD. In a non-bare repository, this is the branch
+	that will be checked out.
 
 --upload-pack <upload-pack>::
 -u <upload-pack>::
diff --git a/builtin/clone.c b/builtin/clone.c
index 3cfedb3..651b4cc 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -420,6 +420,15 @@ static struct ref *find_remote_branch(const struct ref *refs, const char *branch
 	strbuf_addstr(&head, branch);
 	ref = find_ref_by_name(refs, head.buf);
 	strbuf_release(&head);
+
+	if (ref)
+		return ref;
+
+	strbuf_addstr(&head, "refs/tags/");
+	strbuf_addstr(&head, branch);
+	ref = find_ref_by_name(refs, head.buf);
+	strbuf_release(&head);
+
 	return ref;
 }
 
@@ -441,8 +450,12 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
 		if (!remote_head && option_branch)
 			warning(_("Could not find remote branch %s to clone."),
 				option_branch);
-		else
+		else {
 			get_fetch_map(remote_head, refspec, &tail, 0);
+
+			/* if --branch=tag, pull the requested tag explicitly */
+			get_fetch_map(remote_head, tag_refspec, &tail, 0);
+		}
 	} else
 		get_fetch_map(refs, refspec, &tail, 0);
 
@@ -515,6 +528,11 @@ static void update_head(const struct ref *our, const struct ref *remote,
 			update_ref(msg, "HEAD", our->old_sha1, NULL, 0, DIE_ON_ERR);
 			install_branch_config(0, head, option_origin, our->name);
 		}
+	} else if (our) {
+		struct commit *c = lookup_commit_reference(our->old_sha1);
+		/* --branch specifies a non-branch (i.e. tags), detach HEAD */
+		update_ref(msg, "HEAD", c->object.sha1,
+			   NULL, REF_NODEREF, DIE_ON_ERR);
 	} else if (remote) {
 		/*
 		 * We know remote HEAD points to a non-branch, or
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index 5237066..ce51692 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -311,4 +311,19 @@ EOF
 	test_cmp count6.expected count6.actual
 '
 
+test_expect_success 'shallow cloning single tag' '
+	git clone --depth 1 --branch=TAGB1 "file://$(pwd)/." shallow7 &&
+	cat >taglist.expected <<\EOF &&
+TAGB1
+TAGB2
+EOF
+	GIT_DIR=shallow7/.git git tag -l >taglist.actual &&
+	test_cmp taglist.expected taglist.actual &&
+
+	echo "in-pack: 7" > count7.expected &&
+	GIT_DIR=shallow7/.git git count-objects -v |
+		grep "^in-pack" > count7.actual &&
+	test_cmp count7.expected count7.actual
+'
+
 test_done
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index e0b8db6..67869b4 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -271,4 +271,13 @@ test_expect_success 'clone from original with relative alternate' '
 	grep /src/\\.git/objects target-10/objects/info/alternates
 '
 
+test_expect_success 'clone checking out a tag' '
+	git clone --branch=some-tag src dst.tag &&
+	GIT_DIR=src/.git git rev-parse some-tag >expected &&
+	test_cmp expected dst.tag/.git/HEAD &&
+	GIT_DIR=dst.tag/.git git config remote.origin.fetch >fetch.actual &&
+	echo "+refs/heads/*:refs/remotes/origin/*" >fetch.expected &&
+	test_cmp fetch.expected fetch.actual
+'
+
 test_done
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related

* [PATCH v5 10/10] clone: print advice on checking out detached HEAD
From: Nguyễn Thái Ngọc Duy @ 2012-01-16  9:46 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Nguyễn Thái Ngọc Duy
In-Reply-To: <1326439322-15648-1-git-send-email-pclouds@gmail.com>

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 advice.c           |   14 ++++++++++++++
 advice.h           |    1 +
 builtin/checkout.c |   16 +---------------
 builtin/clone.c    |    5 ++++-
 4 files changed, 20 insertions(+), 16 deletions(-)

diff --git a/advice.c b/advice.c
index e02e632..3e1a145 100644
--- a/advice.c
+++ b/advice.c
@@ -64,3 +64,17 @@ void NORETURN die_resolve_conflict(const char *me)
 	error_resolve_conflict(me);
 	die("Exiting because of an unresolved conflict.");
 }
+
+void detach_advice(const char *new_name)
+{
+	const char fmt[] =
+	"Note: checking out '%s'.\n\n"
+	"You are in 'detached HEAD' state. You can look around, make experimental\n"
+	"changes and commit them, and you can discard any commits you make in this\n"
+	"state without impacting any branches by performing another checkout.\n\n"
+	"If you want to create a new branch to retain commits you create, you may\n"
+	"do so (now or later) by using -b with the checkout command again. Example:\n\n"
+	"  git checkout -b new_branch_name\n\n";
+
+	fprintf(stderr, fmt, new_name);
+}
diff --git a/advice.h b/advice.h
index e5d0af7..7bda45b 100644
--- a/advice.h
+++ b/advice.h
@@ -14,5 +14,6 @@ int git_default_advice_config(const char *var, const char *value);
 void advise(const char *advice, ...);
 int error_resolve_conflict(const char *me);
 extern void NORETURN die_resolve_conflict(const char *me);
+void detach_advice(const char *new_name);
 
 #endif /* ADVICE_H */
diff --git a/builtin/checkout.c b/builtin/checkout.c
index f1984d9..5bf96ba 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -514,20 +514,6 @@ static void report_tracking(struct branch_info *new)
 	strbuf_release(&sb);
 }
 
-static void detach_advice(const char *old_path, const char *new_name)
-{
-	const char fmt[] =
-	"Note: checking out '%s'.\n\n"
-	"You are in 'detached HEAD' state. You can look around, make experimental\n"
-	"changes and commit them, and you can discard any commits you make in this\n"
-	"state without impacting any branches by performing another checkout.\n\n"
-	"If you want to create a new branch to retain commits you create, you may\n"
-	"do so (now or later) by using -b with the checkout command again. Example:\n\n"
-	"  git checkout -b new_branch_name\n\n";
-
-	fprintf(stderr, fmt, new_name);
-}
-
 static void update_refs_for_switch(struct checkout_opts *opts,
 				   struct branch_info *old,
 				   struct branch_info *new)
@@ -575,7 +561,7 @@ static void update_refs_for_switch(struct checkout_opts *opts,
 			   REF_NODEREF, DIE_ON_ERR);
 		if (!opts->quiet) {
 			if (old->path && advice_detached_head)
-				detach_advice(old->path, new->name);
+				detach_advice(new->name);
 			describe_detached_head(_("HEAD is now at"), new->commit);
 		}
 	} else if (new->path) {	/* Switch branches. */
diff --git a/builtin/clone.c b/builtin/clone.c
index 651b4cc..72eebca 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -563,7 +563,10 @@ static int checkout(void)
 			  "unable to checkout.\n"));
 		return 0;
 	}
-	if (strcmp(head, "HEAD")) {
+	if (!strcmp(head, "HEAD")) {
+		if (advice_detached_head)
+			detach_advice(sha1_to_hex(sha1));
+	} else {
 		if (prefixcmp(head, "refs/heads/"))
 			die(_("HEAD not found below refs/heads!"));
 	}
-- 
1.7.3.1.256.g2539c.dirty

^ permalink raw reply related

* [PATCH v2 1/2] am: learn passing -b to mailinfo
From: Thomas Rast @ 2012-01-16 10:53 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano
In-Reply-To: <8762ghxpxw.fsf@thomas.inf.ethz.ch>

git-am could pass -k to mailinfo, but not -b.  Introduce an option
that does so.  We change the meaning of the 'keep' state file, but are
careful not to cause a problem unless you downgrade in the middle of
an 'am' run.

This uncovers a bug in mailinfo -b, hence the failing test.

Signed-off-by: Thomas Rast <trast@student.ethz.ch>
---

This fixes the broken 'if', and the use of 'echo' with an argument
that starts with '-'.

 Documentation/git-am.txt |    3 +++
 git-am.sh                |   12 ++++++++----
 t/t4150-am.sh            |   14 ++++++++++++--
 3 files changed, 23 insertions(+), 6 deletions(-)

diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 887466d..ee6cca2 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -40,6 +40,9 @@ OPTIONS
 --keep::
 	Pass `-k` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]).
 
+--keep-non-patch::
+	Pass `-b` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]).
+
 --keep-cr::
 --no-keep-cr::
 	With `--keep-cr`, call 'git mailsplit' (see linkgit:git-mailsplit[1])
diff --git a/git-am.sh b/git-am.sh
index 1c13b13..b8adde7 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -15,6 +15,7 @@ q,quiet         be quiet
 s,signoff       add a Signed-off-by line to the commit message
 u,utf8          recode into utf8 (default)
 k,keep          pass -k flag to git-mailinfo
+keep-non-patch  pass -b flag to git-mailinfo
 keep-cr         pass --keep-cr flag to git-mailsplit for mbox format
 no-keep-cr      do not pass --keep-cr flag to git-mailsplit independent of am.keepcr
 c,scissors      strip everything before a scissors line
@@ -386,7 +387,9 @@ do
 	--no-utf8)
 		utf8= ;;
 	-k|--keep)
-		keep=t ;;
+		keep=-k ;;
+	--keep-non-patch)
+		keep=-b ;;
 	-c|--scissors)
 		scissors=t ;;
 	--no-scissors)
@@ -398,7 +401,7 @@ do
 	--abort)
 		abort=t ;;
 	--rebasing)
-		rebasing=t threeway=t keep=t scissors=f no_inbody_headers=t ;;
+		rebasing=t threeway=t keep=-k scissors=f no_inbody_headers=t ;;
 	-d|--dotest)
 		die "$(gettext "-d option is no longer supported.  Do not use.")"
 		;;
@@ -529,7 +532,7 @@ else
 	echo "$threeway" >"$dotest/threeway"
 	echo "$sign" >"$dotest/sign"
 	echo "$utf8" >"$dotest/utf8"
-	echo "$keep" >"$dotest/keep"
+	printf "%s" "$keep" >"$dotest/keep"
 	echo "$scissors" >"$dotest/scissors"
 	echo "$no_inbody_headers" >"$dotest/no_inbody_headers"
 	echo "$GIT_QUIET" >"$dotest/quiet"
@@ -571,7 +574,8 @@ then
 else
 	utf8=-n
 fi
-if test "$(cat "$dotest/keep")" = t
+keep=$(cat "$dotest/keep")
+if test "$keep" = t
 then
 	keep=-k
 fi
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index d7d9ccc..7e7c83c 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -237,7 +237,7 @@ test_expect_success 'am stays in branch' '
 
 test_expect_success 'am --signoff does not add Signed-off-by: line if already there' '
 	git format-patch --stdout HEAD^ >patch3 &&
-	sed -e "/^Subject/ s,\[PATCH,Re: Re: Re: & 1/5 v2," patch3 >patch4 &&
+	sed -e "/^Subject/ s,\[PATCH,Re: Re: Re: & 1/5 v2] [foo," patch3 >patch4 &&
 	rm -fr .git/rebase-apply &&
 	git reset --hard &&
 	git checkout HEAD^ &&
@@ -259,7 +259,17 @@ test_expect_success 'am --keep really keeps the subject' '
 	git am --keep patch4 &&
 	! test -d .git/rebase-apply &&
 	git cat-file commit HEAD >actual &&
-	grep "Re: Re: Re: \[PATCH 1/5 v2\] third" actual
+	grep "Re: Re: Re: \[PATCH 1/5 v2\] \[foo\] third" actual
+'
+
+test_expect_failure 'am --keep-non-patch really keeps the non-patch part' '
+	rm -fr .git/rebase-apply &&
+	git reset --hard &&
+	git checkout HEAD^ &&
+	git am --keep-non-patch patch4 &&
+	! test -d .git/rebase-apply &&
+	git cat-file commit HEAD >actual &&
+	grep "^\[foo\] third" actual
 '
 
 test_expect_success 'am -3 falls back to 3-way merge' '
-- 
1.7.9.rc0.168.g3847c

^ permalink raw reply related

* [PATCH v2 2/2] mailinfo: with -b, keep space after [foo]
From: Thomas Rast @ 2012-01-16 10:53 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano
In-Reply-To: <a804650f805fd8c89a843302cb92bbbdf36b8c0b.1326710194.git.trast@student.ethz.ch>

The logic for the -b mode, where [PATCH] is dropped but [foo] is not,
silently ate all spaces after the ].

Fix this by keeping the next isspace() character, if there is any.
Being more thorough is pointless, as the later cleanup_space() call
will normalize any sequence of whitespace to a single ' '.

Signed-off-by: Thomas Rast <trast@student.ethz.ch>
---

Unchanged from last time.

 builtin/mailinfo.c |   11 ++++++++++-
 t/t4150-am.sh      |    2 +-
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/builtin/mailinfo.c b/builtin/mailinfo.c
index bfb32b7..eaf9e15 100644
--- a/builtin/mailinfo.c
+++ b/builtin/mailinfo.c
@@ -250,8 +250,17 @@ static void cleanup_subject(struct strbuf *subject)
 			    (7 <= remove &&
 			     memmem(subject->buf + at, remove, "PATCH", 5)))
 				strbuf_remove(subject, at, remove);
-			else
+			else {
 				at += remove;
+				/*
+				 * If the input had a space after the ], keep
+				 * it.  We don't bother with finding the end of
+				 * the space, since we later normalize it
+				 * anyway.
+				 */
+				if (isspace(subject->buf[at]))
+					at += 1;
+			}
 			continue;
 		}
 		break;
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index 7e7c83c..8807b60 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -262,7 +262,7 @@ test_expect_success 'am --keep really keeps the subject' '
 	grep "Re: Re: Re: \[PATCH 1/5 v2\] \[foo\] third" actual
 '
 
-test_expect_failure 'am --keep-non-patch really keeps the non-patch part' '
+test_expect_success 'am --keep-non-patch really keeps the non-patch part' '
 	rm -fr .git/rebase-apply &&
 	git reset --hard &&
 	git checkout HEAD^ &&
-- 
1.7.9.rc0.168.g3847c

^ permalink raw reply related

* Re: Bug? Git checkout fails with a wrong error message
From: Holger Hellmuth @ 2012-01-16 11:07 UTC (permalink / raw)
  To: Yves Goergen; +Cc: git, Jeff King, Carlos Martín Nieto
In-Reply-To: <4F128AD0.5020101@unclassified.de>

On 15.01.2012 09:14, Yves Goergen wrote:
> On 13.01.2012 20:28 CE(S)T, Holger Hellmuth wrote:
>> Is it possible that Visual Studio changes them while you are comitting?
>
> No. Those files may only be modified while open.
>
>>> I renamed the file and created a new one with the same name. Is it so
>>> simple to crash the Git repository?
>>
>> Who said anything about crash? git simply doesn't care whether a change
>> is because of a rename. It isn't special or different to any change you
>> can make to a file
>
> Well, there is a tracked file about which Git says it's untracked. How
> would you describe such internal inconsistency? Maybe corruption would
> fit better.

The original point I was trying to make was that git rename is made out 
of the rather simple operations git add <newname> and git rm <oldname>. 
Not a seldom used function but the basic operations of the vcs. It must 
be one heck of a corner case or a bit flip in the hardware.

The most likely place where the corruption could be is the index. This 
is actually a simple file located in .git\ that can be recreated by 
deleting that file and doing "git reset". I would shut down tortoise-git 
(i.e. the explorer) before doing this and use the command line.

^ permalink raw reply

* Re: [PATCH] bash-completion: don't add quoted space for ZSH (fix regression)
From: Matthieu Moy @ 2012-01-16 11:49 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7vsjjhwvdy.fsf@alter.siamese.dyndns.org>

Junio C Hamano <gitster@pobox.com> writes:

> but is that the right thing to do if suffix came from "$4"?
>
> As far as I can see, "$4" is used to append "." in very limited cases, and
> nobody explicitly passes SP as "$4" when calling this, so it may be easier
> to read if you moved this before that "if we have 3 or more args, use the
> fourth one as the suffix" block, i.e. something like this?

Why not, but in case someone explicitely passes " " as $4 in the future,
it's likely to be better to strip it for the same reason we strip it here.

I don't care much either way in this case.

> +	# Because we use '-o nospace' under bash, we need to compensate
> +	# for it by appending SP after completed word ourselves.
> +	local suffix="${BASH_VERSION+ }"

Not sure why you reworded the comment, but I don't think it's a good
idea to remove the "ZSH would quote the trailing space added with -S"
that I had added, because this is really the reason we do a special case
here. Your version is misleading, because we use -o nospace for ZSH too.

So, overall, I prefer my version ;-).

-- 
Matthieu Moy
http://www-verimag.imag.fr/~moy/

^ permalink raw reply

* [PATCH] test_interactive: interactive debugging in test scripts
From: Pete Wyckoff @ 2012-01-16 15:49 UTC (permalink / raw)
  To: Jeff King; +Cc: Jens Lehmann, Git Mailing List, Junio C Hamano
In-Reply-To: <20120115232413.GA14724@sigill.intra.peff.net>

For test debugging, pause test execution and spawn a shell to
allow examination of internal test state.  The shell has access
to all exported variables from the test.  Exit the shell to
continue the test.  Calls to this function should never be
included in committed code.  The "--verbose" option is required.

Signed-off-by: Pete Wyckoff <pw@padd.com>
---
peff@peff.net wrote on Sun, 15 Jan 2012 18:24 -0500:
> On Sun, Jan 15, 2012 at 09:00:41PM +0100, Jens Lehmann wrote:
> > As that invocation is not that easy to remember add the test_bash
> > convenience function. This function also checks if the -v flag is given
> > and will complain if that is not the case instead of letting the test
> > hang until ^D is pressed.
> 
> Nice. Many times I have added such a "bash" or "gdb" invocation then
> forgotten "-v", only to scratch my head at why the test seemed to be
> hanging.

Yes, good catch noticing the need for "-v".

Here's something similar that I've been playing around with
locally.  I export HOME and TERM in the debug shell to make
sure all the features around dotfiles and color/editing work
nicely.  Also just use SHELL directly, not SHELL_PATH or bash,
to cater to other shell users.

And it is necessary to export any test variables you want to use
in the debug shell.  I often cut-n-paste lines containing
TEST_DIRECTORY and TRASH_DIRECTORY; there could be others,
in test scripts and helper libraries too.

> 2. I do this not just with bash, but with "gdb". I wonder if it is worth
>    making this "test_foo bash", for some value of "foo" (the ones that
>    occur to me are "debug" and "run", but of course they are taken).
> 
>    Actually, I wonder if the existing test_debug could handle this
>    already (though you do have to remember to add "--debug" to your
>    command line, then).

While it would be nice to use:

    test_interactive gdb --args git ...

the path is setup to invoke the script in bin-wrappers/git,
requiring either --with-dashes or something like

    test_interactive gdb --args "$GIT_EXEC_PATH"/git ...

both of which I'm sure to forget.  I have a "test_gdb_git" that
can be used in place of "git" in a script for exactly this
purpose, but feel it's not general enough to warrant inclusion.
It's easy to start a shell then invoke gdb by hand.

Between mine and Jens' there is hopefully something widely useful
here.

		-- Pete

 t/README      |    9 +++++++++
 t/test-lib.sh |   34 ++++++++++++++++++++++++++++++++++
 2 files changed, 43 insertions(+), 0 deletions(-)

diff --git a/t/README b/t/README
index c85abaf..da4521f 100644
--- a/t/README
+++ b/t/README
@@ -548,6 +548,15 @@ library for your script to use.
 		...
 	'
 
+ - test_interactive
+
+   For test debugging, pause test execution and spawn a shell
+   to allow examination of internal test state.  The shell has
+   access to all exported variables from the test.  Exit the shell
+   to continue the test.  Calls to this function should never be
+   included in committed code.  This function requires the "-v"
+   ("--verbose") option.
+
 Prerequisites
 -------------
 
diff --git a/t/test-lib.sh b/t/test-lib.sh
index a65dfc7..a834602 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -32,7 +32,9 @@ done,*)
 esac
 
 # Keep the original TERM for say_color
+# and the original HOME for interactive debugging
 ORIGINAL_TERM=$TERM
+ORIGINAL_HOME="$HOME"
 
 # For repeatability, reset the environment to known value.
 LANG=C
@@ -473,6 +475,36 @@ test_debug () {
 	test "$debug" = "" || eval "$1"
 }
 
+#
+# Add to a test script to spawn a shell during execution.  This
+# spawns a shell allowing inspection of internal test state.  Exit
+# the shell to continue with the test.  Example:
+#
+# 	test_expect_success 'test' '
+# 		git do-something &&
+# 		test_interactive &&
+# 		test_cmp expected current
+#	'
+#
+# Be sure to remove the debug lines before submitting:  it only
+# works with "-v".
+#
+test_interactive () {
+	if test -z "$verbose" ; then
+		error >&5 "test_interactive requires --verbose"
+	fi
+	say >&3 "Interactive debugging"
+	say >&3 "Exit shell to continue test"
+	(
+		# restore important original environment variables
+		exec 0<&6 3>&5 2>&4
+		export HOME="$ORIGINAL_HOME"
+		export TERM="$ORIGINAL_TERM"
+		exec $SHELL
+	)
+	return 0
+}
+
 test_eval_ () {
 	# This is a separate function because some tests use
 	# "return" to end a test_expect_success block early.
@@ -901,6 +933,7 @@ then
 	# itself.
 	TEST_DIRECTORY=$(pwd)
 fi
+export TEST_DIRECTORY
 GIT_BUILD_DIR="$TEST_DIRECTORY"/..
 
 if test -n "$valgrind"
@@ -1038,6 +1071,7 @@ case "$test" in
 /*) TRASH_DIRECTORY="$test" ;;
  *) TRASH_DIRECTORY="$TEST_DIRECTORY/$test" ;;
 esac
+export TRASH_DIRECTORY
 test ! -z "$debug" || remove_trash=$TRASH_DIRECTORY
 rm -fr "$test" || {
 	GIT_EXIT_OK=t
-- 
1.7.9.rc0.47.gc9457

^ permalink raw reply related

* Re: [PATCH 2/2] commit, write-tree: allow to ignore CE_INTENT_TO_ADD while writing trees
From: Pete Wyckoff @ 2012-01-16 16:46 UTC (permalink / raw)
  To: Nguyễn Thái Ngọc Duy
  Cc: git, Jonathan Nieder, Junio C Hamano
In-Reply-To: <1326681407-6344-3-git-send-email-pclouds@gmail.com>

pclouds@gmail.com wrote on Mon, 16 Jan 2012 09:36 +0700:
> Normally cache-tree will not produce trees from an index that has
> CE_INTENT_TO_ADD entries. This is a safe measure to avoid
> mis-interpreting user's intention regarding this flag.
> 
> There are situations however where users want to create trees/commits
> regardless i-t-a entries. Allow such cases with commit.ignoreIntentToAdd
> for git-commit and --ignore-intent-to-add for git-write-tree.

Recently I tried to use "--intent-to-add" on a new file, but when
committing was annoyed by the confusing error, and that I was
forced to do something with that new file.

With commit.ignoreIntentToAdd I can happily commit while leaving
the new file for later.  It stays in the index and is easy to see
in "git status".

I don't understand the need for an option in write-tree; just the
configuration variable is required.

Here's some changes to the docs you might squash in.  It took me
a while to figure out what this variable was about, and I tried
to explain it more clearly for a non-developer audience.

		-- Pete

--------8<------------
From 2471de7083ca3198f59a4734c0d11e9446874de1 Mon Sep 17 00:00:00 2001
From: Pete Wyckoff <pw@padd.com>
Date: Mon, 16 Jan 2012 11:44:26 -0500
Subject: [PATCH] documentation for commit.ignoreIntentToAdd

---
 Documentation/config.txt  |   13 ++++++++-----
 Documentation/git-add.txt |   15 ++++++++-------
 2 files changed, 16 insertions(+), 12 deletions(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index 7ba8777..a2cbb50 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -821,6 +821,14 @@ color.ui::
 	`never` if you prefer git commands not to use color unless enabled
 	explicitly with some other configuration or the `--color` option.
 
+commit.ignoreIntentToAdd::
+	When 'git add' is invoked with `-N`, an "intent-to-add" entry is
+	made in the index.  At commit time, these entries must be removed
+	from the index ("git reset ...") or added ("git add ...").  This
+	boolean variable makes it possible to commit while leaving the
+	"intent-to-add" entries still in the index.  See the description
+	of the `-N` option in linkgit:git-add[1] for details.
+
 commit.status::
 	A boolean to enable/disable inclusion of status information in the
 	commit message template when using an editor to prepare the commit
@@ -831,11 +839,6 @@ commit.template::
 	"{tilde}/" is expanded to the value of `$HOME` and "{tilde}user/" to the
 	specified user's home directory.
 
-commit.ignoreIntentToAdd::
-	Allow to commit the index as-is even if there are
-	intent-to-add entries (see option `-N` in linkgit:git-add[1])
-	in index.
-
 credential.helper::
 	Specify an external helper to be called when a username or
 	password credential is needed; the helper may consult external
diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index ec548ea..1c2ac44 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -125,14 +125,15 @@ subdirectories.
 	useful for, among other things, showing the unstaged content of
 	such files with `git diff`.
 +
-Paths added with this option have intent-to-add flag in index. The
-flag is removed once real content is added or updated. By default you
-cannot commit the index as-is from until this flag is removed from all
-entries (i.e. all entries have real content). See commit.ignoreIntentToAdd
-regardless the flag.
+When committing, these paths must be either added to the index (without
+the `-N` flag) or removed (with "git reset").  However, a configuration
+variable `commit.ignoreIntentToAdd` can be set to allow commits to
+proceed, while the intent-to-add paths remain in the index.
 +
-Committing with `git commit -a` or with selected paths works
-regardless the config key and the flag.
+Regardless of the configuration variable, invoking `git commit -a` will
+commit all files including the ones marked with intent-to-add.
+Specifying a <filepattern> can be used to commit files other than the
+ones marked with intent-to-add.
 
 --refresh::
 	Don't add the file(s), but only refresh their stat()
-- 
1.7.9.rc0.18.gdae96

^ permalink raw reply related

* Re: The shared Git repo used by git-new-workdir
From: Holger Hellmuth @ 2012-01-16 18:09 UTC (permalink / raw)
  To: Hilco Wijbenga; +Cc: Git Users
In-Reply-To: <CAE1pOi3fBUBeNuhJqtJhxuMfz2G3iYOJy7U2HX6Nv4kqjcbnhw@mail.gmail.com>

On 14.01.2012 21:59, Hilco Wijbenga wrote:
> Hi all,
>
> First off, I use git-new-workdir a lot and it's working great. Kudos
> to its developers!
>
> I have been looking at the Git clone that is at the root of
> git-new-workdir (i.e. the repository that is reused by all my
> git-new-workdir created directories). This repo shows a lot of
> activity when I run "git status" there.
>
> So now I'm wondering. Should I simply ignore this completely? Or is
> there some "clean up" I can do so that "git status" shows nothing? Or
> would I destroy my git-new-workdir directories doing that? So far I've
> only used this repo to create branches (i.e. to run git-new-workdir).
>
> I would like to understand a bit better how I should treat this repo.
> Whether it's basically a "do-not-touch" environment or whether I can
> safely treat it as a normal Git repo.

Take a look at the rather simple script git-new-workdir (everything 
important happens in the last 20 lines). It just makes logical links to 
all files (mostly directories) under .git except three files that relate 
to the index (mainly the index file itself and HEAD)

That would suggest that normal git operations operating on files in 
those directories happen identical whether you are in the root repo or 
in any of the satellites. Only where the whole repo is acted upon (git 
clone, cp/rsync or deletion of the whole repo) the root repo would be 
"special".

^ permalink raw reply

* Simulating an empty git repository without having said repository on disk
From: Richard Hartmann @ 2012-01-16 18:34 UTC (permalink / raw)
  To: Git List

Hi all,

for vcsh[1], I need a rather hackish feature: List all files untracked
by vcsh. The plan to achieve this is:

Get lists of all files by all repos which' GIT_WORK_TREE is in one
directory ($HOME, by default), merge all lists into one and use that
as a .gitignore or exclude. Then run `git status` with $GIT_WORK_TREE
pointing to $HOME while using said ignore/exclude. That will give me a
list of all files & directories which are not tracked by any of the
git repos managed by vcsh.

I could create an empty git repo to run this operation in, but that
seems wasteful. Thus, I would prefer to run this command against a
non-existing, empty git repo. Problem is, I could not find a way to do
this.


I know the empty tree's SHA is hard-coded into git so I was hoping
there would be a way to trick git using this, but I couldn't find a
way.


Any and all help appreciated, even if it's just a "no, this is not possible"


Thanks,
Richard


[1] https://github.com/RichiH/vcsh

^ permalink raw reply

* Re: Bug? Git checkout fails with a wrong error message
From: Yves Goergen @ 2012-01-16 18:50 UTC (permalink / raw)
  To: Holger Hellmuth; +Cc: git, Jeff King, Carlos Martín Nieto
In-Reply-To: <4F1404E7.9040805@ira.uka.de>

It's getting more weird. I believe that (msys)Git doesn't really know
how the filesystem on its operating system works. I have made some more
changes now and want to commit them. TortoiseGit reports the files
Form1.Designer.cs and Form1.designer.cs (note the case difference) as
modified and ready to commit. How is that supposed to work? On Windows,
file names are case-insensitive (as on MacOS X) and both names refer to
the absolute same file. 'git status' has the very same listing with that
same file twice.

What else is now broken in my repository?

If the index is such a problem child, how can I safely delete it
completely and maybe have it regenerated if Git can't live without it?

-- 
Yves Goergen "LonelyPixel" <nospam.list@unclassified.de>
Visit my web laboratory at http://beta.unclassified.de

^ permalink raw reply

* Re: The shared Git repo used by git-new-workdir
From: Hilco Wijbenga @ 2012-01-16 18:57 UTC (permalink / raw)
  To: Holger Hellmuth; +Cc: Git Users
In-Reply-To: <4F1467C1.504@ira.uka.de>

On 16 January 2012 10:09, Holger Hellmuth <hellmuth@ira.uka.de> wrote:
> On 14.01.2012 21:59, Hilco Wijbenga wrote:
> Take a look at the rather simple script git-new-workdir (everything
> important happens in the last 20 lines). It just makes logical links to all
> files (mostly directories) under .git except three files that relate to the
> index (mainly the index file itself and HEAD)

Yes, I did that but I cannot figure out from that why I see lots of
files and such staged for commit on master when I almost never work on
master. I only use master to pull in upstream and to merge in one of
my branches and push. So unless I'm doing that, I would expect "git
status" to not output anything.

> That would suggest that normal git operations operating on files in those
> directories happen identical whether you are in the root repo or in any of
> the satellites. Only where the whole repo is acted upon (git clone, cp/rsync
> or deletion of the whole repo) the root repo would be "special".

That all makes sense to me and is what I was expecting. So why is "git
status" on master displaying anything?

Here is what I see:

In my working directory:
hilco@centaur /mnt/lacie/workspaces/my-project-master
my-project-master (master $ u=)$ git status
# On branch master
nothing to commit (working directory clean)

In the shared repo:
hilco@centaur ~/git-clones/my-project my-project (master +$ u=)$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       deleted:    .gitattributes
#       modified:   .gitignore
#       new file:   ...
... hundreds more ...

Is this related to my use of a symlink? I have a symlink
"~/workspaces" pointing to /mnt/lacie/workspaces/. Is that somehow
affecting things?

^ permalink raw reply

* Re: [PATCH 2/3] git-p4: Search for parent commit on branch creation
From: Pete Wyckoff @ 2012-01-16 18:57 UTC (permalink / raw)
  To: Vitor Antunes; +Cc: git
In-Reply-To: <1326674360-2771-3-git-send-email-vitor.hda@gmail.com>

vitor.hda@gmail.com wrote on Mon, 16 Jan 2012 00:39 +0000:
> To find out which is its parent the commit of the new branch is applied
> sequentially to each blob of the parent branch from the newest to the
> oldest. The first blob which results in a zero diff is considered the
> parent commit. If none is found, then the commit is applied to the top
> of the parent branch.
> 
> A fast-import "checkpoint" call is required for each comparison because
> diff-tree is only able to work with blobs on disk. But most of these
> commits will not be part of the final imported tree, making fast-import
> fail. To avoid this, the temporary branches are tracked and then removed
> at the end of the import process.

This looks much better without the need for "--force".  It'll be
great to fix this major branch detection problem.  Can you make a
couple of further minor changes?

> diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4
> @@ -2012,7 +2014,28 @@ class P4Sync(Command, P4UserMap):
> -                        self.commit(description, filesForCommit, branch, [branchPrefix], parent)
> +                        parentFound = 0
> +                        if len(parent) > 0:
> +                            self.checkpoint()
> +                            for blob in read_pipe_lines("git rev-list --reverse --no-merges %s" % parent):
> +                                blob = blob.strip()
> +                                tempBranch = self.tempBranchLocation + os.sep + "%d-%s" % (change, blob)
> +                                if self.verbose:
> +                                    print "Creating temporary branch: " + tempBranch
> +                                self.commit(description, filesForCommit, tempBranch, [branchPrefix], blob)
> +                                self.tempBranches.append(tempBranch)
> +                                self.checkpoint()
> +                                if len( read_pipe("git diff-tree %s %s" % (blob, tempBranch)) ) == 0:
> +                                    parentFound = 1
> +                                    if self.verbose:
> +                                        print "Found parent of %s in commit %s" % (branch, blob)
> +                                    break
> +                        if parentFound:
> +                            self.commit(description, filesForCommit, branch, [branchPrefix], blob)
> +                        else:
> +                            if self.verbose:
> +                                print "Parent of %s not found. Committing into head of %s" % (branch, parent)
> +                            self.commit(description, filesForCommit, branch, [branchPrefix], parent)

1.  Move the tempBranch commit outside of the "for blob" loop.
    It can have no parent, and the diff-tree will still tell you
    if you found the same contents.  Instead of a ref for
    each blob inspected for each change, you'll just have one ref
    per change.  Only one checkpoint() after the tempBranch
    commit should be needed.

2.  Nit.  parentFound is boolean, use True/False, not 1/0.

> @@ -2347,6 +2370,12 @@ class P4Sync(Command, P4UserMap):
> +        # Cleanup temporary branches created during import
> +        if self.tempBranches != []:
> +            for branch in self.tempBranches:
> +                os.remove(".git" + os.sep + branch)
> +            os.rmdir(".git" + os.sep + self.tempBranchLocation)
> +

3.  Deleting refs should probably use "git update-ref -d"
    just in case GIT_DIR is not ".git".  I think you could just
    leave the "git-p4-tmp" directory around, but use
    os.environ["GIT_DIR"] instead of ".git" if you want to
    delete it.

4.  Paths are best manipulated with os.path.join(dir, file), to handle
    weirdnesses like drive letters.

Eventually if the fast-import protocol learns to delete the refs
it creates, we can clean up a bit more nicely.  I think there was
agreement this was a good idea, just needs someone to do it
sometime.

		-- Pete

^ permalink raw reply

* Re: Bug? Git checkout fails with a wrong error message
From: Yves Goergen @ 2012-01-16 18:58 UTC (permalink / raw)
  To: Holger Hellmuth; +Cc: git, Jeff King, Carlos Martín Nieto
In-Reply-To: <4F1404E7.9040805@ira.uka.de>

Great, I have the same file with an equal name twice in my repository
(with 'git ls-files').

How stupid! Git, go learn file names.

I've read (and seen) bad things about Git and Windows, and I knew the
Great Failure Day would eventually come. And I've read that Mercurial
would be better suitable for Windows. You don't know anything about
that, do you?

-- 
Yves Goergen "LonelyPixel" <nospam.list@unclassified.de>
Visit my web laboratory at http://beta.unclassified.de

^ permalink raw reply

* Re: Cannot push a commit
From: Matthias Fechner @ 2012-01-16 18:59 UTC (permalink / raw)
  To: git
In-Reply-To: <4F1297E0.1060703@fechner.net>

Am 15.01.2012 10:09, schrieb Matthias Fechner:
> Dear List,
> 
> I have a big problem with a file which I cannot push to my central git
> repository.
> The file can be found here:
> http://dl.fechner.net/APP_UD.sch

where should I raise a bug report for this?

Bye
Matthias

^ permalink raw reply

* Re: Bug? Git checkout fails with a wrong error message
From: Jeff King @ 2012-01-16 19:09 UTC (permalink / raw)
  To: Yves Goergen; +Cc: Holger Hellmuth, git, Carlos Martín Nieto
In-Reply-To: <4F14718B.80209@unclassified.de>

On Mon, Jan 16, 2012 at 07:50:51PM +0100, Yves Goergen wrote:

> It's getting more weird. I believe that (msys)Git doesn't really know
> how the filesystem on its operating system works. I have made some more
> changes now and want to commit them. TortoiseGit reports the files
> Form1.Designer.cs and Form1.designer.cs (note the case difference) as
> modified and ready to commit. How is that supposed to work? On Windows,
> file names are case-insensitive (as on MacOS X) and both names refer to
> the absolute same file. 'git status' has the very same listing with that
> same file twice.

What is the output of "git config core.ignorecase" in your repository?

> If the index is such a problem child, how can I safely delete it
> completely and maybe have it regenerated if Git can't live without it?

If you delete your index, it will appear to git as if you have staged
all files for deletion (if you run "git status", for example). You can
then run "git reset" to regenerate it based on the last commit.

But I doubt that will help your problem. It seems unlikely to me that
the source of the problem is a corrupted index, but rather is some
corner case in case-insensitive comparisons between the index and the
working tree.

-Peff

^ permalink raw reply

* Re: [PATCH 3/3] git-p4: Add test case for complex branch import
From: Pete Wyckoff @ 2012-01-16 19:12 UTC (permalink / raw)
  To: Vitor Antunes; +Cc: git
In-Reply-To: <1326674360-2771-4-git-send-email-vitor.hda@gmail.com>

vitor.hda@gmail.com wrote on Mon, 16 Jan 2012 00:39 +0000:
> diff --git a/t/t9801-git-p4-branch.sh b/t/t9801-git-p4-branch.sh
> +test_expect_success 'git-p4 add complex branches' '
> +	test_when_finished cleanup_git &&
> +	test_create_repo "$git" &&
> +	(
> +		cd "$cli" &&
> +		changelist=$(p4 changes -m1 //depot/... | cut -d" " -f2) &&
> +		changelist=$((changelist - 5)) &&
> +		p4 integrate //depot/branch1/...@$changelist //depot/branch4/... &&
> +		p4 submit -d "branch4" &&
> +		changelist=$((changelist + 2)) &&
> +		p4 integrate //depot/branch1/...@$changelist //depot/branch5/... &&
> +		p4 submit -d "branch5" &&
> +		cd "$TRASH_DIRECTORY"
> +	)
> +'

Sorry: I think I wanted the "$"s removed from inside $((..)).
Turns out that some shells don't grok that.  The above should be:

	changelist=$(($changelist - 5)) &&

You can drop the last cd to $TRASH_DIRECTORY since you're inside
a subshell.  (Nice addition of the subshells.)

> +
> +# Configure branches through git-config and clone them. git-p4 will only be able
> +# to clone the original structure if it is able to detect the origin changelist
> +# of each branch.
> +test_expect_success 'git-p4 clone complex branches' '
> +	test_when_finished cleanup_git &&
> +	test_create_repo "$git" &&
> +	(
> +		test_when_finished cleanup_git &&
> +		test_create_repo "$git" &&

These two lines can go; you already did it outside the subshell.

> +		cd "$git" &&
> +		git config git-p4.branchList branch1:branch2 &&
> +		git config --add git-p4.branchList branch1:branch3 &&
> +		git config --add git-p4.branchList branch1:branch4 &&
> +		git config --add git-p4.branchList branch1:branch5 &&
> +		"$GITP4" clone --dest=. --detect-branches //depot@all &&
> +		git log --all --graph --decorate --stat &&
> +		git reset --hard p4/depot/branch1 &&
> +		test -f file1 &&
> +		test -f file2 &&
> +		test -f file3 &&

There are preferred functions for these tests, I learned recently:

	test_path_is_file file1 &&

> +		grep -q update file2 &&
> +		git reset --hard p4/depot/branch2 &&
> +		test -f file1 &&
> +		test -f file2 &&
> +		test ! -f file3 &&

Similarly

	test_path_is_missing file3 &&

> +		! grep -q update file2 &&
> +		git reset --hard p4/depot/branch3 &&
> +		test -f file1 &&
> +		test -f file2 &&
> +		test -f file3 &&
> +		grep -q update file2 &&
> +		git reset --hard p4/depot/branch4 &&
> +		test -f file1 &&
> +		test -f file2 &&
> +		test ! -f file3 &&
> +		! grep -q update file2 &&
> +		git reset --hard p4/depot/branch5 &&
> +		test -f file1 &&
> +		test -f file2 &&
> +		test -f file3 &&
> +		! grep -q update file2 &&
> +		test ! -d .git/git-p4-tmp
> +	)
> +'

^ permalink raw reply

* Re: The shared Git repo used by git-new-workdir
From: Holger Hellmuth @ 2012-01-16 19:15 UTC (permalink / raw)
  To: Hilco Wijbenga; +Cc: Git Users
In-Reply-To: <CAE1pOi3JocCoDGAmpCYdGdJN4E1nz8O4_i0MtLhwhP_axmH-uw@mail.gmail.com>

On 16.01.2012 19:57, Hilco Wijbenga wrote:
> In my working directory:
> hilco@centaur /mnt/lacie/workspaces/my-project-master
> my-project-master (master $ u=)$ git status
> # On branch master
> nothing to commit (working directory clean)
>
> In the shared repo:
> hilco@centaur ~/git-clones/my-project my-project (master +$ u=)$ git status
> # On branch master
> # Changes to be committed:
> #   (use "git reset HEAD<file>..." to unstage)
> #
> #       deleted:    .gitattributes
> #       modified:   .gitignore
> #       new file:   ...
> ... hundreds more ...

This is related to your using two repos with the same branch 
(irrespective of root repo or not).

There is nothing wrong with that per se, but if you add/commit/merge etc 
in one of those two, the working directory and index of the other repo 
doesn't get updated automatically. You would have to do "git reset 
--hard" in that repo to get it up-to-date

If you want to avoid this just don't check out the same branch in any 
two repos, root or not.

^ permalink raw reply


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