From: Junio C Hamano <gitster@pobox.com>
Date: Tue, 14 Mar 2017 22:23:18 +0000 (-0700)
Subject: Merge branch 'jk/interpret-branch-name'
X-Git-Tag: v2.13.0-rc0~121
X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/c809496c97122b7d297e7216c3bfff7262b212e0?hp=-c

Merge branch 'jk/interpret-branch-name'

"git branch @" created refs/heads/@ as a branch, and in general the
code that handled @{-1} and @{upstream} was a bit too loose in
disambiguating.

* jk/interpret-branch-name:
checkout: restrict @-expansions when finding branch
strbuf_check_ref_format(): expand only local branches
branch: restrict @-expansions when deleting
t3204: test git-branch @-expansion corner cases
interpret_branch_name: allow callers to restrict expansions
strbuf_branchname: add docstring
strbuf_branchname: drop return value
interpret_branch_name: move docstring to header file
interpret_branch_name(): handle auto-namelen for @{-1}
---

c809496c97122b7d297e7216c3bfff7262b212e0
diff --combined builtin/branch.c
index 94f7de7fa5,0c924612eb..291fe90de3
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@@ -28,7 -28,6 +28,7 @@@ static const char * const builtin_branc
  	N_("git branch [<options>] [-r] (-d | -D) <branch-name>..."),
  	N_("git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"),
  	N_("git branch [<options>] [-r | -a] [--points-at]"),
 +	N_("git branch [<options>] [-r | -a] [--format]"),
  	NULL
  };
  
@@@ -38,11 -37,11 +38,11 @@@ static unsigned char head_sha1[20]
  static int branch_use_color = -1;
  static char branch_colors[][COLOR_MAXLEN] = {
  	GIT_COLOR_RESET,
 -	GIT_COLOR_NORMAL,	/* PLAIN */
 -	GIT_COLOR_RED,		/* REMOTE */
 -	GIT_COLOR_NORMAL,	/* LOCAL */
 -	GIT_COLOR_GREEN,	/* CURRENT */
 -	GIT_COLOR_BLUE,		/* UPSTREAM */
 +	GIT_COLOR_NORMAL,       /* PLAIN */
 +	GIT_COLOR_RED,          /* REMOTE */
 +	GIT_COLOR_NORMAL,       /* LOCAL */
 +	GIT_COLOR_GREEN,        /* CURRENT */
 +	GIT_COLOR_BLUE,         /* UPSTREAM */
  };
  enum color_branch {
  	BRANCH_COLOR_RESET = 0,
@@@ -191,17 -190,20 +191,20 @@@ static int delete_branches(int argc, co
  	int ret = 0;
  	int remote_branch = 0;
  	struct strbuf bname = STRBUF_INIT;
+ 	unsigned allowed_interpret;
  
  	switch (kinds) {
  	case FILTER_REFS_REMOTES:
  		fmt = "refs/remotes/%s";
  		/* For subsequent UI messages */
  		remote_branch = 1;
+ 		allowed_interpret = INTERPRET_BRANCH_REMOTE;
  
  		force = 1;
  		break;
  	case FILTER_REFS_BRANCHES:
  		fmt = "refs/heads/%s";
+ 		allowed_interpret = INTERPRET_BRANCH_LOCAL;
  		break;
  	default:
  		die(_("cannot use -a with -d"));
@@@ -216,7 -218,7 +219,7 @@@
  		char *target = NULL;
  		int flags = 0;
  
- 		strbuf_branchname(&bname, argv[i]);
+ 		strbuf_branchname(&bname, argv[i], allowed_interpret);
  		free(name);
  		name = mkpathdup(fmt, bname.buf);
  
@@@ -252,7 -254,7 +255,7 @@@
  			goto next;
  		}
  
 -		if (delete_ref(name, is_null_sha1(sha1) ? NULL : sha1,
 +		if (delete_ref(NULL, name, is_null_sha1(sha1) ? NULL : sha1,
  			       REF_NODEREF)) {
  			error(remote_branch
  			      ? _("Error deleting remote-tracking branch '%s'")
@@@ -281,98 -283,221 +284,98 @@@
  	return(ret);
  }
  
 -static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
 -		int show_upstream_ref)
 +static int calc_maxwidth(struct ref_array *refs, int remote_bonus)
  {
 -	int ours, theirs;
 -	char *ref = NULL;
 -	struct branch *branch = branch_get(branch_name);
 -	const char *upstream;
 -	struct strbuf fancy = STRBUF_INIT;
 -	int upstream_is_gone = 0;
 -	int added_decoration = 1;
 -
 -	if (stat_tracking_info(branch, &ours, &theirs, &upstream) < 0) {
 -		if (!upstream)
 -			return;
 -		upstream_is_gone = 1;
 -	}
 -
 -	if (show_upstream_ref) {
 -		ref = shorten_unambiguous_ref(upstream, 0);
 -		if (want_color(branch_use_color))
 -			strbuf_addf(&fancy, "%s%s%s",
 -					branch_get_color(BRANCH_COLOR_UPSTREAM),
 -					ref, branch_get_color(BRANCH_COLOR_RESET));
 -		else
 -			strbuf_addstr(&fancy, ref);
 -	}
 +	int i, max = 0;
 +	for (i = 0; i < refs->nr; i++) {
 +		struct ref_array_item *it = refs->items[i];
 +		const char *desc = it->refname;
 +		int w;
  
 -	if (upstream_is_gone) {
 -		if (show_upstream_ref)
 -			strbuf_addf(stat, _("[%s: gone]"), fancy.buf);
 -		else
 -			added_decoration = 0;
 -	} else if (!ours && !theirs) {
 -		if (show_upstream_ref)
 -			strbuf_addf(stat, _("[%s]"), fancy.buf);
 -		else
 -			added_decoration = 0;
 -	} else if (!ours) {
 -		if (show_upstream_ref)
 -			strbuf_addf(stat, _("[%s: behind %d]"), fancy.buf, theirs);
 -		else
 -			strbuf_addf(stat, _("[behind %d]"), theirs);
 +		skip_prefix(it->refname, "refs/heads/", &desc);
 +		skip_prefix(it->refname, "refs/remotes/", &desc);
 +		if (it->kind == FILTER_REFS_DETACHED_HEAD) {
 +			char *head_desc = get_head_description();
 +			w = utf8_strwidth(head_desc);
 +			free(head_desc);
 +		} else
 +			w = utf8_strwidth(desc);
  
 -	} else if (!theirs) {
 -		if (show_upstream_ref)
 -			strbuf_addf(stat, _("[%s: ahead %d]"), fancy.buf, ours);
 -		else
 -			strbuf_addf(stat, _("[ahead %d]"), ours);
 -	} else {
 -		if (show_upstream_ref)
 -			strbuf_addf(stat, _("[%s: ahead %d, behind %d]"),
 -				    fancy.buf, ours, theirs);
 -		else
 -			strbuf_addf(stat, _("[ahead %d, behind %d]"),
 -				    ours, theirs);
 +		if (it->kind == FILTER_REFS_REMOTES)
 +			w += remote_bonus;
 +		if (w > max)
 +			max = w;
  	}
 -	strbuf_release(&fancy);
 -	if (added_decoration)
 -		strbuf_addch(stat, ' ');
 -	free(ref);
 +	return max;
  }
  
 -static void add_verbose_info(struct strbuf *out, struct ref_array_item *item,
 -			     struct ref_filter *filter, const char *refname)
 +static const char *quote_literal_for_format(const char *s)
  {
 -	struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT;
 -	const char *sub = _(" **** invalid ref ****");
 -	struct commit *commit = item->commit;
 +	static struct strbuf buf = STRBUF_INIT;
  
 -	if (!parse_commit(commit)) {
 -		pp_commit_easy(CMIT_FMT_ONELINE, commit, &subject);
 -		sub = subject.buf;
 +	strbuf_reset(&buf);
 +	while (*s) {
 +		const char *ep = strchrnul(s, '%');
 +		if (s < ep)
 +			strbuf_add(&buf, s, ep - s);
 +		if (*ep == '%') {
 +			strbuf_addstr(&buf, "%%");
 +			s = ep + 1;
 +		} else {
 +			s = ep;
 +		}
  	}
 -
 -	if (item->kind == FILTER_REFS_BRANCHES)
 -		fill_tracking_info(&stat, refname, filter->verbose > 1);
 -
 -	strbuf_addf(out, " %s %s%s",
 -		find_unique_abbrev(item->commit->object.oid.hash, filter->abbrev),
 -		stat.buf, sub);
 -	strbuf_release(&stat);
 -	strbuf_release(&subject);
 +	return buf.buf;
  }
  
 -static char *get_head_description(void)
 +static char *build_format(struct ref_filter *filter, int maxwidth, const char *remote_prefix)
  {
 -	struct strbuf desc = STRBUF_INIT;
 -	struct wt_status_state state;
 -	memset(&state, 0, sizeof(state));
 -	wt_status_get_state(&state, 1);
 -	if (state.rebase_in_progress ||
 -	    state.rebase_interactive_in_progress)
 -		strbuf_addf(&desc, _("(no branch, rebasing %s)"),
 -			    state.branch);
 -	else if (state.bisect_in_progress)
 -		strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
 -			    state.branch);
 -	else if (state.detached_from) {
 -		if (state.detached_at)
 -			/* TRANSLATORS: make sure this matches
 -			   "HEAD detached at " in wt-status.c */
 -			strbuf_addf(&desc, _("(HEAD detached at %s)"),
 -				state.detached_from);
 -		else
 -			/* TRANSLATORS: make sure this matches
 -			   "HEAD detached from " in wt-status.c */
 -			strbuf_addf(&desc, _("(HEAD detached from %s)"),
 -				state.detached_from);
 -	}
 -	else
 -		strbuf_addstr(&desc, _("(no branch)"));
 -	free(state.branch);
 -	free(state.onto);
 -	free(state.detached_from);
 -	return strbuf_detach(&desc, NULL);
 -}
 +	struct strbuf fmt = STRBUF_INIT;
 +	struct strbuf local = STRBUF_INIT;
 +	struct strbuf remote = STRBUF_INIT;
  
 -static void format_and_print_ref_item(struct ref_array_item *item, int maxwidth,
 -				      struct ref_filter *filter, const char *remote_prefix)
 -{
 -	char c;
 -	int current = 0;
 -	int color;
 -	struct strbuf out = STRBUF_INIT, name = STRBUF_INIT;
 -	const char *prefix_to_show = "";
 -	const char *prefix_to_skip = NULL;
 -	const char *desc = item->refname;
 -	char *to_free = NULL;
 +	strbuf_addf(&fmt, "%%(if)%%(HEAD)%%(then)* %s%%(else)  %%(end)",
 +		    branch_get_color(BRANCH_COLOR_CURRENT));
  
 -	switch (item->kind) {
 -	case FILTER_REFS_BRANCHES:
 -		prefix_to_skip = "refs/heads/";
 -		skip_prefix(desc, prefix_to_skip, &desc);
 -		if (!filter->detached && !strcmp(desc, head))
 -			current = 1;
 +	if (filter->verbose) {
 +		strbuf_addf(&local, "%%(align:%d,left)%%(refname:lstrip=2)%%(end)", maxwidth);
 +		strbuf_addf(&local, "%s", branch_get_color(BRANCH_COLOR_RESET));
 +		strbuf_addf(&local, " %%(objectname:short=7) ");
 +
 +		if (filter->verbose > 1)
 +			strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)"
 +				    "%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)",
 +				    branch_get_color(BRANCH_COLOR_UPSTREAM), branch_get_color(BRANCH_COLOR_RESET));
  		else
 -			color = BRANCH_COLOR_LOCAL;
 -		break;
 -	case FILTER_REFS_REMOTES:
 -		prefix_to_skip = "refs/remotes/";
 -		skip_prefix(desc, prefix_to_skip, &desc);
 -		color = BRANCH_COLOR_REMOTE;
 -		prefix_to_show = remote_prefix;
 -		break;
 -	case FILTER_REFS_DETACHED_HEAD:
 -		desc = to_free = get_head_description();
 -		current = 1;
 -		break;
 -	default:
 -		color = BRANCH_COLOR_PLAIN;
 -		break;
 -	}
 -
 -	c = ' ';
 -	if (current) {
 -		c = '*';
 -		color = BRANCH_COLOR_CURRENT;
 -	}
 +			strbuf_addf(&local, "%%(if)%%(upstream:track)%%(then)%%(upstream:track) %%(end)%%(contents:subject)");
  
 -	strbuf_addf(&name, "%s%s", prefix_to_show, desc);
 -	if (filter->verbose) {
 -		int utf8_compensation = strlen(name.buf) - utf8_strwidth(name.buf);
 -		strbuf_addf(&out, "%c %s%-*s%s", c, branch_get_color(color),
 -			    maxwidth + utf8_compensation, name.buf,
 +		strbuf_addf(&remote, "%s%%(align:%d,left)%s%%(refname:lstrip=2)%%(end)%s%%(if)%%(symref)%%(then) -> %%(symref:short)"
 +			    "%%(else) %%(objectname:short=7) %%(contents:subject)%%(end)",
 +			    branch_get_color(BRANCH_COLOR_REMOTE), maxwidth, quote_literal_for_format(remote_prefix),
  			    branch_get_color(BRANCH_COLOR_RESET));
 -	} else
 -		strbuf_addf(&out, "%c %s%s%s", c, branch_get_color(color),
 -			    name.buf, branch_get_color(BRANCH_COLOR_RESET));
 -
 -	if (item->symref) {
 -		const char *symref = item->symref;
 -		if (prefix_to_skip)
 -			skip_prefix(symref, prefix_to_skip, &symref);
 -		strbuf_addf(&out, " -> %s", symref);
 -	}
 -	else if (filter->verbose)
 -		/* " f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h" */
 -		add_verbose_info(&out, item, filter, desc);
 -	if (column_active(colopts)) {
 -		assert(!filter->verbose && "--column and --verbose are incompatible");
 -		string_list_append(&output, out.buf);
  	} else {
 -		printf("%s\n", out.buf);
 +		strbuf_addf(&local, "%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)",
 +			    branch_get_color(BRANCH_COLOR_RESET));
 +		strbuf_addf(&remote, "%s%s%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)",
 +			    branch_get_color(BRANCH_COLOR_REMOTE), quote_literal_for_format(remote_prefix),
 +			    branch_get_color(BRANCH_COLOR_RESET));
  	}
 -	strbuf_release(&name);
 -	strbuf_release(&out);
 -	free(to_free);
 -}
  
 -static int calc_maxwidth(struct ref_array *refs, int remote_bonus)
 -{
 -	int i, max = 0;
 -	for (i = 0; i < refs->nr; i++) {
 -		struct ref_array_item *it = refs->items[i];
 -		const char *desc = it->refname;
 -		int w;
 -
 -		skip_prefix(it->refname, "refs/heads/", &desc);
 -		skip_prefix(it->refname, "refs/remotes/", &desc);
 -		w = utf8_strwidth(desc);
 +	strbuf_addf(&fmt, "%%(if:notequals=refs/remotes)%%(refname:rstrip=-2)%%(then)%s%%(else)%s%%(end)", local.buf, remote.buf);
  
 -		if (it->kind == FILTER_REFS_REMOTES)
 -			w += remote_bonus;
 -		if (w > max)
 -			max = w;
 -	}
 -	return max;
 +	strbuf_release(&local);
 +	strbuf_release(&remote);
 +	return strbuf_detach(&fmt, NULL);
  }
  
 -static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting)
 +static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting, const char *format)
  {
  	int i;
  	struct ref_array array;
  	int maxwidth = 0;
  	const char *remote_prefix = "";
 +	struct strbuf out = STRBUF_INIT;
 +	char *to_free = NULL;
  
  	/*
  	 * If we are listing more than just remote branches,
@@@ -384,32 -509,27 +387,32 @@@
  
  	memset(&array, 0, sizeof(array));
  
 -	verify_ref_format("%(refname)%(symref)");
  	filter_refs(&array, filter, filter->kind | FILTER_REFS_INCLUDE_BROKEN);
  
  	if (filter->verbose)
  		maxwidth = calc_maxwidth(&array, strlen(remote_prefix));
  
 -	/*
 -	 * If no sorting parameter is given then we default to sorting
 -	 * by 'refname'. This would give us an alphabetically sorted
 -	 * array with the 'HEAD' ref at the beginning followed by
 -	 * local branches 'refs/heads/...' and finally remote-tacking
 -	 * branches 'refs/remotes/...'.
 -	 */
 -	if (!sorting)
 -		sorting = ref_default_sorting();
 +	if (!format)
 +		format = to_free = build_format(filter, maxwidth, remote_prefix);
 +	verify_ref_format(format);
 +
  	ref_array_sort(sorting, &array);
  
 -	for (i = 0; i < array.nr; i++)
 -		format_and_print_ref_item(array.items[i], maxwidth, filter, remote_prefix);
 +	for (i = 0; i < array.nr; i++) {
 +		format_ref_array_item(array.items[i], format, 0, &out);
 +		if (column_active(colopts)) {
 +			assert(!filter->verbose && "--column and --verbose are incompatible");
 +			 /* format to a string_list to let print_columns() do its job */
 +			string_list_append(&output, out.buf);
 +		} else {
 +			fwrite(out.buf, 1, out.len, stdout);
 +			putchar('\n');
 +		}
 +		strbuf_release(&out);
 +	}
  
  	ref_array_clear(&array);
 +	free(to_free);
  }
  
  static void reject_rebase_or_bisect_branch(const char *target)
@@@ -471,15 -591,14 +474,15 @@@ static void rename_branch(const char *o
  
  	if (rename_ref(oldref.buf, newref.buf, logmsg.buf))
  		die(_("Branch rename failed"));
 -	strbuf_release(&logmsg);
  
  	if (recovery)
  		warning(_("Renamed a misnamed branch '%s' away"), oldref.buf + 11);
  
 -	if (replace_each_worktree_head_symref(oldref.buf, newref.buf))
 +	if (replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf))
  		die(_("Branch renamed to %s, but HEAD is not updated!"), newname);
  
 +	strbuf_release(&logmsg);
 +
  	strbuf_addf(&oldsection, "branch.%s", oldref.buf + 11);
  	strbuf_release(&oldref);
  	strbuf_addf(&newsection, "branch.%s", newref.buf + 11);
@@@ -529,9 -648,7 +532,9 @@@ int cmd_branch(int argc, const char **a
  	const char *new_upstream = NULL;
  	enum branch_track track;
  	struct ref_filter filter;
 +	int icase = 0;
  	static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
 +	const char *format = NULL;
  
  	struct option options[] = {
  		OPT_GROUP(N_("Generic options")),
@@@ -572,13 -689,9 +575,13 @@@
  			OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
  			N_("print only branches of the object"), 0, parse_opt_object_name
  		},
 +		OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
 +		OPT_STRING(  0 , "format", &format, N_("format"), N_("format to use for the output")),
  		OPT_END(),
  	};
  
 +	setup_ref_filter_porcelain_msg();
 +
  	memset(&filter, 0, sizeof(filter));
  	filter.kind = FILTER_REFS_BRANCHES;
  	filter.abbrev = -1;
@@@ -613,8 -726,6 +616,8 @@@
  
  	if (filter.abbrev == -1)
  		filter.abbrev = DEFAULT_ABBREV;
 +	filter.ignore_case = icase;
 +
  	finalize_colopts(&colopts, -1);
  	if (filter.verbose) {
  		if (explicitly_enable_column(colopts))
@@@ -636,17 -747,7 +639,17 @@@
  		if ((filter.kind & FILTER_REFS_BRANCHES) && filter.detached)
  			filter.kind |= FILTER_REFS_DETACHED_HEAD;
  		filter.name_patterns = argv;
 -		print_ref_list(&filter, sorting);
 +		/*
 +		 * If no sorting parameter is given then we default to sorting
 +		 * by 'refname'. This would give us an alphabetically sorted
 +		 * array with the 'HEAD' ref at the beginning followed by
 +		 * local branches 'refs/heads/...' and finally remote-tacking
 +		 * branches 'refs/remotes/...'.
 +		 */
 +		if (!sorting)
 +			sorting = ref_default_sorting();
 +		sorting->ignore_case = icase;
 +		print_ref_list(&filter, sorting, format);
  		print_columns(&output, colopts, NULL);
  		string_list_clear(&output, 0);
  		return 0;
diff --combined builtin/checkout.c
index f174f50303,767ca9e18c..81f07c3ef2
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@@ -56,8 -56,8 +56,8 @@@ static int post_checkout_hook(struct co
  			      int changed)
  {
  	return run_hook_le(NULL, "post-checkout",
 -			   sha1_to_hex(old ? old->object.oid.hash : null_sha1),
 -			   sha1_to_hex(new ? new->object.oid.hash : null_sha1),
 +			   oid_to_hex(old ? &old->object.oid : &null_oid),
 +			   oid_to_hex(new ? &new->object.oid : &null_oid),
  			   changed ? "1" : "0", NULL);
  	/* "new" can be NULL when checking out from the index before
  	   a commit exists. */
@@@ -274,7 -274,7 +274,7 @@@ static int checkout_paths(const struct 
  
  	lock_file = xcalloc(1, sizeof(struct lock_file));
  
 -	hold_locked_index(lock_file, 1);
 +	hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
  	if (read_cache_preload(&opts->pathspec) < 0)
  		return error(_("index file corrupt"));
  
@@@ -452,7 -452,7 +452,7 @@@ static void setup_branch_path(struct br
  {
  	struct strbuf buf = STRBUF_INIT;
  
- 	strbuf_branchname(&buf, branch->name);
+ 	strbuf_branchname(&buf, branch->name, INTERPRET_BRANCH_LOCAL);
  	if (strcmp(buf.buf, branch->name))
  		branch->name = xstrdup(buf.buf);
  	strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
@@@ -467,7 -467,7 +467,7 @@@ static int merge_working_tree(const str
  	int ret;
  	struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
  
 -	hold_locked_index(lock_file, 1);
 +	hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
  	if (read_cache_preload(NULL) < 0)
  		return error(_("index file corrupt"));
  
@@@ -612,25 -612,22 +612,25 @@@ static void update_refs_for_switch(cons
  	const char *old_desc, *reflog_msg;
  	if (opts->new_branch) {
  		if (opts->new_orphan_branch) {
 -			if (opts->new_branch_log && !log_all_ref_updates) {
 +			char *refname;
 +
 +			refname = mkpathdup("refs/heads/%s", opts->new_orphan_branch);
 +			if (opts->new_branch_log &&
 +			    !should_autocreate_reflog(refname)) {
  				int ret;
 -				char *refname;
  				struct strbuf err = STRBUF_INIT;
  
 -				refname = mkpathdup("refs/heads/%s", opts->new_orphan_branch);
  				ret = safe_create_reflog(refname, 1, &err);
 -				free(refname);
  				if (ret) {
  					fprintf(stderr, _("Can not do reflog for '%s': %s\n"),
  						opts->new_orphan_branch, err.buf);
  					strbuf_release(&err);
 +					free(refname);
  					return;
  				}
  				strbuf_release(&err);
  			}
 +			free(refname);
  		}
  		else
  			create_branch(opts->new_branch, new->name,
diff --combined builtin/merge.c
index a96d4fb501,84375db714..848a298556
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@@ -46,7 -46,6 +46,7 @@@ static const char * const builtin_merge
  	N_("git merge [<options>] [<commit>...]"),
  	N_("git merge [<options>] <msg> HEAD <commit>"),
  	N_("git merge --abort"),
 +	N_("git merge --continue"),
  	NULL
  };
  
@@@ -66,7 -65,6 +66,7 @@@ static int option_renormalize
  static int verbosity;
  static int allow_rerere_auto;
  static int abort_current_merge;
 +static int continue_current_merge;
  static int allow_unrelated_histories;
  static int show_progress = -1;
  static int default_to_upstream = 1;
@@@ -225,8 -223,6 +225,8 @@@ static struct option builtin_merge_opti
  	OPT__VERBOSITY(&verbosity),
  	OPT_BOOL(0, "abort", &abort_current_merge,
  		N_("abort the current in-progress merge")),
 +	OPT_BOOL(0, "continue", &continue_current_merge,
 +		N_("continue the current in-progress merge")),
  	OPT_BOOL(0, "allow-unrelated-histories", &allow_unrelated_histories,
  		 N_("allow merging unrelated histories")),
  	OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
@@@ -438,7 -434,7 +438,7 @@@ static void merge_name(const char *remo
  	char *found_ref;
  	int len, early;
  
- 	strbuf_branchname(&bname, remote);
+ 	strbuf_branchname(&bname, remote, 0);
  	remote = bname.buf;
  
  	memset(branch_head, 0, sizeof(branch_head));
@@@ -638,7 -634,7 +638,7 @@@ static int try_merge_strategy(const cha
  {
  	static struct lock_file lock;
  
 -	hold_locked_index(&lock, 1);
 +	hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
  	refresh_cache(REFRESH_QUIET);
  	if (active_cache_changed &&
  	    write_locked_index(&the_index, &lock, COMMIT_LOCK))
@@@ -675,7 -671,7 +675,7 @@@
  		for (j = common; j; j = j->next)
  			commit_list_insert(j->item, &reversed);
  
 -		hold_locked_index(&lock, 1);
 +		hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
  		clean = merge_recursive(&o, head,
  				remoteheads->item, reversed, &result);
  		if (clean < 0)
@@@ -785,7 -781,7 +785,7 @@@ static int merge_trivial(struct commit 
  	struct commit_list *parents, **pptr = &parents;
  	static struct lock_file lock;
  
 -	hold_locked_index(&lock, 1);
 +	hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
  	refresh_cache(REFRESH_QUIET);
  	if (active_cache_changed &&
  	    write_locked_index(&the_index, &lock, COMMIT_LOCK))
@@@ -1129,7 -1125,6 +1129,7 @@@ int cmd_merge(int argc, const char **ar
  	const char *best_strategy = NULL, *wt_strategy = NULL;
  	struct commit_list *remoteheads, *p;
  	void *branch_to_free;
 +	int orig_argc = argc;
  
  	if (argc == 2 && !strcmp(argv[1], "-h"))
  		usage_with_options(builtin_merge_usage, builtin_merge_options);
@@@ -1163,10 -1158,6 +1163,10 @@@
  		int nargc = 2;
  		const char *nargv[] = {"reset", "--merge", NULL};
  
 +		if (orig_argc != 2)
 +			usage_msg_opt(_("--abort expects no arguments"),
 +			      builtin_merge_usage, builtin_merge_options);
 +
  		if (!file_exists(git_path_merge_head()))
  			die(_("There is no merge to abort (MERGE_HEAD missing)."));
  
@@@ -1175,22 -1166,6 +1175,22 @@@
  		goto done;
  	}
  
 +	if (continue_current_merge) {
 +		int nargc = 1;
 +		const char *nargv[] = {"commit", NULL};
 +
 +		if (orig_argc != 2)
 +			usage_msg_opt(_("--continue expects no arguments"),
 +			      builtin_merge_usage, builtin_merge_options);
 +
 +		if (!file_exists(git_path_merge_head()))
 +			die(_("There is no merge in progress (MERGE_HEAD missing)."));
 +
 +		/* Invoke 'git commit' */
 +		ret = cmd_commit(nargc, nargv, prefix);
 +		goto done;
 +	}
 +
  	if (read_cache_unmerged())
  		die_resolve_conflict("merge");
  
diff --combined cache.h
index 8c0e644207,7aea88534d..c958269715
--- a/cache.h
+++ b/cache.h
@@@ -507,16 -507,14 +507,16 @@@ extern int is_nonbare_repository_dir(st
  #define READ_GITFILE_ERR_NO_PATH 6
  #define READ_GITFILE_ERR_NOT_A_REPO 7
  #define READ_GITFILE_ERR_TOO_LARGE 8
 +extern void read_gitfile_error_die(int error_code, const char *path, const char *dir);
  extern const char *read_gitfile_gently(const char *path, int *return_error_code);
  #define read_gitfile(path) read_gitfile_gently((path), NULL)
 -extern const char *resolve_gitdir(const char *suspect);
 +extern const char *resolve_gitdir_gently(const char *suspect, int *return_error_code);
 +#define resolve_gitdir(path) resolve_gitdir_gently((path), NULL)
 +
  extern void set_git_work_tree(const char *tree);
  
  #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
  
 -extern const char **get_pathspec(const char *prefix, const char **pathspec);
  extern void setup_work_tree(void);
  extern const char *setup_git_directory_gently(int *);
  extern const char *setup_git_directory(void);
@@@ -633,7 -631,7 +633,7 @@@ extern int chmod_index_entry(struct ind
  extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
  extern void set_object_name_for_intent_to_add_entry(struct cache_entry *ce);
  extern int index_name_is_other(const struct index_state *, const char *, int);
 -extern void *read_blob_data_from_index(struct index_state *, const char *, unsigned long *);
 +extern void *read_blob_data_from_index(const struct index_state *, const char *, unsigned long *);
  
  /* do stat comparison even if CE_VALID is true */
  #define CE_MATCH_IGNORE_VALID		01
@@@ -695,6 -693,7 +695,6 @@@ extern int minimum_abbrev, default_abbr
  extern int ignore_case;
  extern int assume_unchanged;
  extern int prefer_symlink_refs;
 -extern int log_all_ref_updates;
  extern int warn_ambiguous_refs;
  extern int warn_on_object_refname_ambiguity;
  extern const char *apply_default_whitespace;
@@@ -762,14 -761,6 +762,14 @@@ enum hide_dotfiles_type 
  };
  extern enum hide_dotfiles_type hide_dotfiles;
  
 +enum log_refs_config {
 +	LOG_REFS_UNSET = -1,
 +	LOG_REFS_NONE = 0,
 +	LOG_REFS_NORMAL,
 +	LOG_REFS_ALWAYS
 +};
 +extern enum log_refs_config log_all_ref_updates;
 +
  enum branch_track {
  	BRANCH_TRACK_UNSPECIFIED = -1,
  	BRANCH_TRACK_NEVER = 0,
@@@ -1045,6 -1036,9 +1045,6 @@@ static inline int is_empty_tree_oid(con
  	return !hashcmp(oid->hash, EMPTY_TREE_SHA1_BIN);
  }
  
 -
 -int git_mkstemp(char *path, size_t n, const char *template);
 -
  /* set default permissions by passing mode arguments to open(2) */
  int git_mkstemps_mode(char *pattern, int suffix_len, int mode);
  int git_mkstemp_mode(char *pattern, int mode);
@@@ -1069,9 -1063,8 +1069,9 @@@ int adjust_shared_perm(const char *path
  
  /*
   * Create the directory containing the named path, using care to be
 - * somewhat safe against races.  Return one of the scld_error values
 - * to indicate success/failure.
 + * somewhat safe against races. Return one of the scld_error values to
 + * indicate success/failure. On error, set errno to describe the
 + * problem.
   *
   * SCLD_VANISHED indicates that one of the ancestor directories of the
   * path existed at one point during the function call and then
@@@ -1095,49 -1088,6 +1095,49 @@@ enum scld_error 
  enum scld_error safe_create_leading_directories(char *path);
  enum scld_error safe_create_leading_directories_const(const char *path);
  
 +/*
 + * Callback function for raceproof_create_file(). This function is
 + * expected to do something that makes dirname(path) permanent despite
 + * the fact that other processes might be cleaning up empty
 + * directories at the same time. Usually it will create a file named
 + * path, but alternatively it could create another file in that
 + * directory, or even chdir() into that directory. The function should
 + * return 0 if the action was completed successfully. On error, it
 + * should return a nonzero result and set errno.
 + * raceproof_create_file() treats two errno values specially:
 + *
 + * - ENOENT -- dirname(path) does not exist. In this case,
 + *             raceproof_create_file() tries creating dirname(path)
 + *             (and any parent directories, if necessary) and calls
 + *             the function again.
 + *
 + * - EISDIR -- the file already exists and is a directory. In this
 + *             case, raceproof_create_file() removes the directory if
 + *             it is empty (and recursively any empty directories that
 + *             it contains) and calls the function again.
 + *
 + * Any other errno causes raceproof_create_file() to fail with the
 + * callback's return value and errno.
 + *
 + * Obviously, this function should be OK with being called again if it
 + * fails with ENOENT or EISDIR. In other scenarios it will not be
 + * called again.
 + */
 +typedef int create_file_fn(const char *path, void *cb);
 +
 +/*
 + * Create a file in dirname(path) by calling fn, creating leading
 + * directories if necessary. Retry a few times in case we are racing
 + * with another process that is trying to clean up the directory that
 + * contains path. See the documentation for create_file_fn for more
 + * details.
 + *
 + * Return the value and set the errno that resulted from the most
 + * recent call of fn. fn is always called at least once, and will be
 + * called more than once if it returns ENOENT or EISDIR.
 + */
 +int raceproof_create_file(const char *path, create_file_fn fn, void *cb);
 +
  int mkdir_in_gitdir(const char *path);
  extern char *expand_user_path(const char *path);
  const char *enter_repo(const char *path, int strict);
@@@ -1146,13 -1096,9 +1146,13 @@@ static inline int is_absolute_path(cons
  	return is_dir_sep(path[0]) || has_dos_drive_prefix(path);
  }
  int is_directory(const char *);
 +char *strbuf_realpath(struct strbuf *resolved, const char *path,
 +		      int die_on_error);
  const char *real_path(const char *path);
  const char *real_path_if_valid(const char *path);
 +char *real_pathdup(const char *path, int die_on_error);
  const char *absolute_path(const char *path);
 +char *absolute_pathdup(const char *path);
  const char *remove_leading_path(const char *in, const char *prefix);
  const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
  int normalize_path_copy_len(char *dst, const char *src, int *prefix_len);
@@@ -1211,8 -1157,7 +1211,8 @@@ extern int write_sha1_file(const void *
  extern int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type, unsigned char *sha1, unsigned flags);
  extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
  extern int force_object_loose(const unsigned char *sha1, time_t mtime);
 -extern int git_open(const char *name);
 +extern int git_open_cloexec(const char *name, int flags);
 +#define git_open(name) git_open_cloexec(name, O_RDONLY)
  extern void *map_sha1_file(const unsigned char *sha1, unsigned long *size);
  extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
  extern int parse_sha1_header(const char *hdr, unsigned long *sizep);
@@@ -1226,19 -1171,6 +1226,19 @@@ extern int finalize_object_file(const c
  
  extern int has_sha1_pack(const unsigned char *sha1);
  
 +/*
 + * Open the loose object at path, check its sha1, and return the contents,
 + * type, and size. If the object is a blob, then "contents" may return NULL,
 + * to allow streaming of large blobs.
 + *
 + * Returns 0 on success, negative on error (details may be written to stderr).
 + */
 +int read_loose_object(const char *path,
 +		      const unsigned char *expected_sha1,
 +		      enum object_type *type,
 +		      unsigned long *size,
 +		      void **contents);
 +
  /*
   * Return true iff we have an object named sha1, whether local or in
   * an alternate object database, and whether packed or loose.  This
@@@ -1360,7 -1292,37 +1360,37 @@@ extern char *oid_to_hex_r(char *out, co
  extern char *sha1_to_hex(const unsigned char *sha1);	/* static buffer result! */
  extern char *oid_to_hex(const struct object_id *oid);	/* same static buffer as sha1_to_hex */
  
- extern int interpret_branch_name(const char *str, int len, struct strbuf *);
+ /*
+  * This reads short-hand syntax that not only evaluates to a commit
+  * object name, but also can act as if the end user spelled the name
+  * of the branch from the command line.
+  *
+  * - "@{-N}" finds the name of the Nth previous branch we were on, and
+  *   places the name of the branch in the given buf and returns the
+  *   number of characters parsed if successful.
+  *
+  * - "<branch>@{upstream}" finds the name of the other ref that
+  *   <branch> is configured to merge with (missing <branch> defaults
+  *   to the current branch), and places the name of the branch in the
+  *   given buf and returns the number of characters parsed if
+  *   successful.
+  *
+  * If the input is not of the accepted format, it returns a negative
+  * number to signal an error.
+  *
+  * If the input was ok but there are not N branch switches in the
+  * reflog, it returns 0.
+  *
+  * If "allowed" is non-zero, it is a treated as a bitfield of allowable
+  * expansions: local branches ("refs/heads/"), remote branches
+  * ("refs/remotes/"), or "HEAD". If no "allowed" bits are set, any expansion is
+  * allowed, even ones to refs outside of those namespaces.
+  */
+ #define INTERPRET_BRANCH_LOCAL (1<<0)
+ #define INTERPRET_BRANCH_REMOTE (1<<1)
+ #define INTERPRET_BRANCH_HEAD (1<<2)
+ extern int interpret_branch_name(const char *str, int len, struct strbuf *,
+ 				 unsigned allowed);
  extern int get_oid_mb(const char *str, struct object_id *oid);
  
  extern int validate_headref(const char *ref);
@@@ -1790,8 -1752,6 +1820,8 @@@ extern int git_default_config(const cha
  extern int git_config_from_file(config_fn_t fn, const char *, void *);
  extern int git_config_from_mem(config_fn_t fn, const enum config_origin_type,
  					const char *name, const char *buf, size_t len, void *data);
 +extern int git_config_from_blob_sha1(config_fn_t fn, const char *name,
 +				     const unsigned char *sha1, void *data);
  extern void git_config_push_parameter(const char *text);
  extern int git_config_from_parameters(config_fn_t fn, void *data);
  extern void git_config(config_fn_t fn, void *);
@@@ -1860,11 -1820,8 +1890,11 @@@ extern int git_config_include(const cha
   *
   * (i.e., what gets handed to a config_fn_t). The caller provides the section;
   * we return -1 if it does not match, 0 otherwise. The subsection and key
 - * out-parameters are filled by the function (and subsection is NULL if it is
 + * out-parameters are filled by the function (and *subsection is NULL if it is
   * missing).
 + *
 + * If the subsection pointer-to-pointer passed in is NULL, returns 0 only if
 + * there is no subsection at all.
   */
  extern int parse_config_key(const char *var,
  			    const char *section,
diff --combined refs.c
index 4d6bf9237b,da847f6901..b0d58948a5
--- a/refs.c
+++ b/refs.c
@@@ -3,7 -3,6 +3,7 @@@
   */
  
  #include "cache.h"
 +#include "hashmap.h"
  #include "lockfile.h"
  #include "refs.h"
  #include "refs/refs-internal.h"
@@@ -405,7 -404,7 +405,7 @@@ int refname_match(const char *abbrev_na
  static char *substitute_branch_name(const char **string, int *len)
  {
  	struct strbuf buf = STRBUF_INIT;
- 	int ret = interpret_branch_name(*string, *len, &buf);
+ 	int ret = interpret_branch_name(*string, *len, &buf, 0);
  
  	if (ret == *len) {
  		size_t size;
@@@ -592,8 -591,8 +592,8 @@@ static int delete_pseudoref(const char 
  	return 0;
  }
  
 -int delete_ref(const char *refname, const unsigned char *old_sha1,
 -	       unsigned int flags)
 +int delete_ref(const char *msg, const char *refname,
 +	       const unsigned char *old_sha1, unsigned int flags)
  {
  	struct ref_transaction *transaction;
  	struct strbuf err = STRBUF_INIT;
@@@ -604,7 -603,7 +604,7 @@@
  	transaction = ref_transaction_begin(&err);
  	if (!transaction ||
  	    ref_transaction_delete(transaction, refname, old_sha1,
 -				   flags, NULL, &err) ||
 +				   flags, msg, &err) ||
  	    ref_transaction_commit(transaction, &err)) {
  		error("%s", err.buf);
  		ref_transaction_free(transaction);
@@@ -639,17 -638,12 +639,17 @@@ int copy_reflog_msg(char *buf, const ch
  
  int should_autocreate_reflog(const char *refname)
  {
 -	if (!log_all_ref_updates)
 +	switch (log_all_ref_updates) {
 +	case LOG_REFS_ALWAYS:
 +		return 1;
 +	case LOG_REFS_NORMAL:
 +		return starts_with(refname, "refs/heads/") ||
 +			starts_with(refname, "refs/remotes/") ||
 +			starts_with(refname, "refs/notes/") ||
 +			!strcmp(refname, "HEAD");
 +	default:
  		return 0;
 -	return starts_with(refname, "refs/heads/") ||
 -		starts_with(refname, "refs/remotes/") ||
 -		starts_with(refname, "refs/notes/") ||
 -		!strcmp(refname, "HEAD");
 +	}
  }
  
  int is_branch(const char *refname)
@@@ -1035,10 -1029,10 +1035,10 @@@ static struct string_list *hide_refs
  
  int parse_hide_refs_config(const char *var, const char *value, const char *section)
  {
 +	const char *key;
  	if (!strcmp("transfer.hiderefs", var) ||
 -	    /* NEEDSWORK: use parse_config_key() once both are merged */
 -	    (starts_with(var, section) && var[strlen(section)] == '.' &&
 -	     !strcmp(var + strlen(section), ".hiderefs"))) {
 +	    (!parse_config_key(var, section, NULL, NULL, &key) &&
 +	     !strcmp(key, "hiderefs"))) {
  		char *ref;
  		int len;
  
@@@ -1235,10 -1229,10 +1235,10 @@@ int for_each_rawref(each_ref_fn fn, voi
  }
  
  /* This function needs to return a meaningful errno on failure */
 -static const char *resolve_ref_recursively(struct ref_store *refs,
 -					   const char *refname,
 -					   int resolve_flags,
 -					   unsigned char *sha1, int *flags)
 +const char *resolve_ref_recursively(struct ref_store *refs,
 +				    const char *refname,
 +				    int resolve_flags,
 +				    unsigned char *sha1, int *flags)
  {
  	static struct strbuf sb_refname = STRBUF_INIT;
  	int unused_flags;
@@@ -1358,102 -1352,62 +1358,102 @@@ int resolve_gitlink_ref(const char *sub
  	return 0;
  }
  
 +struct submodule_hash_entry
 +{
 +	struct hashmap_entry ent; /* must be the first member! */
 +
 +	struct ref_store *refs;
 +
 +	/* NUL-terminated name of submodule: */
 +	char submodule[FLEX_ARRAY];
 +};
 +
 +static int submodule_hash_cmp(const void *entry, const void *entry_or_key,
 +			      const void *keydata)
 +{
 +	const struct submodule_hash_entry *e1 = entry, *e2 = entry_or_key;
 +	const char *submodule = keydata ? keydata : e2->submodule;
 +
 +	return strcmp(e1->submodule, submodule);
 +}
 +
 +static struct submodule_hash_entry *alloc_submodule_hash_entry(
 +		const char *submodule, struct ref_store *refs)
 +{
 +	struct submodule_hash_entry *entry;
 +
 +	FLEX_ALLOC_STR(entry, submodule, submodule);
 +	hashmap_entry_init(entry, strhash(submodule));
 +	entry->refs = refs;
 +	return entry;
 +}
 +
  /* A pointer to the ref_store for the main repository: */
  static struct ref_store *main_ref_store;
  
 -/* A linked list of ref_stores for submodules: */
 -static struct ref_store *submodule_ref_stores;
 +/* A hashmap of ref_stores, stored by submodule name: */
 +static struct hashmap submodule_ref_stores;
  
 -void base_ref_store_init(struct ref_store *refs,
 -			 const struct ref_storage_be *be,
 -			 const char *submodule)
 +/*
 + * Return the ref_store instance for the specified submodule (or the
 + * main repository if submodule is NULL). If that ref_store hasn't
 + * been initialized yet, return NULL.
 + */
 +static struct ref_store *lookup_ref_store(const char *submodule)
 +{
 +	struct submodule_hash_entry *entry;
 +
 +	if (!submodule)
 +		return main_ref_store;
 +
 +	if (!submodule_ref_stores.tablesize)
 +		/* It's initialized on demand in register_ref_store(). */
 +		return NULL;
 +
 +	entry = hashmap_get_from_hash(&submodule_ref_stores,
 +				      strhash(submodule), submodule);
 +	return entry ? entry->refs : NULL;
 +}
 +
 +/*
 + * Register the specified ref_store to be the one that should be used
 + * for submodule (or the main repository if submodule is NULL). It is
 + * a fatal error to call this function twice for the same submodule.
 + */
 +static void register_ref_store(struct ref_store *refs, const char *submodule)
  {
 -	refs->be = be;
  	if (!submodule) {
  		if (main_ref_store)
  			die("BUG: main_ref_store initialized twice");
  
 -		refs->submodule = "";
 -		refs->next = NULL;
  		main_ref_store = refs;
  	} else {
 -		if (lookup_ref_store(submodule))
 +		if (!submodule_ref_stores.tablesize)
 +			hashmap_init(&submodule_ref_stores, submodule_hash_cmp, 0);
 +
 +		if (hashmap_put(&submodule_ref_stores,
 +				alloc_submodule_hash_entry(submodule, refs)))
  			die("BUG: ref_store for submodule '%s' initialized twice",
  			    submodule);
 -
 -		refs->submodule = xstrdup(submodule);
 -		refs->next = submodule_ref_stores;
 -		submodule_ref_stores = refs;
  	}
  }
  
 -struct ref_store *ref_store_init(const char *submodule)
 +/*
 + * Create, record, and return a ref_store instance for the specified
 + * submodule (or the main repository if submodule is NULL).
 + */
 +static struct ref_store *ref_store_init(const char *submodule)
  {
  	const char *be_name = "files";
  	struct ref_storage_be *be = find_ref_storage_backend(be_name);
 +	struct ref_store *refs;
  
  	if (!be)
  		die("BUG: reference backend %s is unknown", be_name);
  
 -	if (!submodule || !*submodule)
 -		return be->init(NULL);
 -	else
 -		return be->init(submodule);
 -}
 -
 -struct ref_store *lookup_ref_store(const char *submodule)
 -{
 -	struct ref_store *refs;
 -
 -	if (!submodule || !*submodule)
 -		return main_ref_store;
 -
 -	for (refs = submodule_ref_stores; refs; refs = refs->next) {
 -		if (!strcmp(submodule, refs->submodule))
 -			return refs;
 -	}
 -
 -	return NULL;
 +	refs = be->init(submodule);
 +	register_ref_store(refs, submodule);
 +	return refs;
  }
  
  struct ref_store *get_ref_store(const char *submodule)
@@@ -1481,10 -1435,10 +1481,10 @@@
  	return refs;
  }
  
 -void assert_main_repository(struct ref_store *refs, const char *caller)
 +void base_ref_store_init(struct ref_store *refs,
 +			 const struct ref_storage_be *be)
  {
 -	if (*refs->submodule)
 -		die("BUG: %s called for a submodule", caller);
 +	refs->be = be;
  }
  
  /* backend functions */
diff --combined strbuf.h
index cf8e4bf532,4d225ffe51..80047b1bb7
--- a/strbuf.h
+++ b/strbuf.h
@@@ -109,7 -109,9 +109,7 @@@ extern void strbuf_attach(struct strbu
   */
  static inline void strbuf_swap(struct strbuf *a, struct strbuf *b)
  {
 -	struct strbuf tmp = *a;
 -	*a = *b;
 -	*b = tmp;
 +	SWAP(*a, *b);
  }
  
  
@@@ -441,20 -443,6 +441,20 @@@ extern int strbuf_getcwd(struct strbuf 
   */
  extern void strbuf_add_absolute_path(struct strbuf *sb, const char *path);
  
 +/**
 + * Canonize `path` (make it absolute, resolve symlinks, remove extra
 + * slashes) and append it to `sb`.  Die with an informative error
 + * message if there is a problem.
 + *
 + * The directory part of `path` (i.e., everything up to the last
 + * dir_sep) must denote a valid, existing directory, but the last
 + * component need not exist.
 + *
 + * Callers that don't mind links should use the more lightweight
 + * strbuf_add_absolute_path() instead.
 + */
 +extern void strbuf_add_real_path(struct strbuf *sb, const char *path);
 +
  
  /**
   * Normalize in-place the path contained in the strbuf. See
@@@ -574,7 -562,26 +574,26 @@@ static inline void strbuf_complete_line
  	strbuf_complete(sb, '\n');
  }
  
- extern int strbuf_branchname(struct strbuf *sb, const char *name);
+ /*
+  * Copy "name" to "sb", expanding any special @-marks as handled by
+  * interpret_branch_name(). The result is a non-qualified branch name
+  * (so "foo" or "origin/master" instead of "refs/heads/foo" or
+  * "refs/remotes/origin/master").
+  *
+  * Note that the resulting name may not be a syntactically valid refname.
+  *
+  * If "allowed" is non-zero, restrict the set of allowed expansions. See
+  * interpret_branch_name() for details.
+  */
+ extern void strbuf_branchname(struct strbuf *sb, const char *name,
+ 			      unsigned allowed);
+ 
+ /*
+  * Like strbuf_branchname() above, but confirm that the result is
+  * syntactically valid to be used as a local branch name in refs/heads/.
+  *
+  * The return value is "0" if the result is valid, and "-1" otherwise.
+  */
  extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name);
  
  extern void strbuf_addstr_urlencode(struct strbuf *, const char *,