DWIM "git checkout frotz" to "git checkout -b frotz origin/frotz"
When 'frotz' is not a valid object name and not a tracked filename, we used to complain and failed this command. When there is only one remote that has 'frotz' as one of its tracking branches, we can DWIM it as a request to create a local branch 'frotz' forking from the matching remote tracking branch. Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
							parent
							
								
									c6e8c8005a
								
							
						
					
					
						commit
						70c9ac2f19
					
				|  | @ -572,6 +572,40 @@ static int interactive_checkout(const char *revision, const char **pathspec, | ||||||
| 	return run_add_interactive(revision, "--patch=checkout", pathspec); | 	return run_add_interactive(revision, "--patch=checkout", pathspec); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | struct tracking_name_data { | ||||||
|  | 	const char *name; | ||||||
|  | 	char *remote; | ||||||
|  | 	int unique; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static int check_tracking_name(const char *refname, const unsigned char *sha1, | ||||||
|  | 			       int flags, void *cb_data) | ||||||
|  | { | ||||||
|  | 	struct tracking_name_data *cb = cb_data; | ||||||
|  | 	const char *slash; | ||||||
|  |  | ||||||
|  | 	if (prefixcmp(refname, "refs/remotes/")) | ||||||
|  | 		return 0; | ||||||
|  | 	slash = strchr(refname + 13, '/'); | ||||||
|  | 	if (!slash || strcmp(slash + 1, cb->name)) | ||||||
|  | 		return 0; | ||||||
|  | 	if (cb->remote) { | ||||||
|  | 		cb->unique = 0; | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | 	cb->remote = xstrdup(refname); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static const char *unique_tracking_name(const char *name) | ||||||
|  | { | ||||||
|  | 	struct tracking_name_data cb_data = { name, NULL, 1 }; | ||||||
|  | 	for_each_ref(check_tracking_name, &cb_data); | ||||||
|  | 	if (cb_data.unique) | ||||||
|  | 		return cb_data.remote; | ||||||
|  | 	free(cb_data.remote); | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
| int cmd_checkout(int argc, const char **argv, const char *prefix) | int cmd_checkout(int argc, const char **argv, const char *prefix) | ||||||
| { | { | ||||||
|  | @ -630,8 +664,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) | ||||||
| 		opts.new_branch = argv0 + 1; | 		opts.new_branch = argv0 + 1; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if (opts.track == BRANCH_TRACK_UNSPECIFIED) |  | ||||||
| 		opts.track = git_branch_track; |  | ||||||
| 	if (conflict_style) { | 	if (conflict_style) { | ||||||
| 		opts.merge = 1; /* implied */ | 		opts.merge = 1; /* implied */ | ||||||
| 		git_xmerge_config("merge.conflictstyle", conflict_style, NULL); | 		git_xmerge_config("merge.conflictstyle", conflict_style, NULL); | ||||||
|  | @ -655,6 +687,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) | ||||||
| 	 *   With no paths, if <something> is a commit, that is to | 	 *   With no paths, if <something> is a commit, that is to | ||||||
| 	 *   switch to the branch or detach HEAD at it. | 	 *   switch to the branch or detach HEAD at it. | ||||||
| 	 * | 	 * | ||||||
|  | 	 *   With no paths, if <something> is _not_ a commit, no -t nor -b | ||||||
|  | 	 *   was given, and there is a tracking branch whose name is | ||||||
|  | 	 *   <something> in one and only one remote, then this is a short-hand | ||||||
|  | 	 *   to fork local <something> from that remote tracking branch. | ||||||
|  | 	 * | ||||||
| 	 *   Otherwise <something> shall not be ambiguous. | 	 *   Otherwise <something> shall not be ambiguous. | ||||||
| 	 *   - If it's *only* a reference, treat it like case (1). | 	 *   - If it's *only* a reference, treat it like case (1). | ||||||
| 	 *   - If it's only a path, treat it like case (2). | 	 *   - If it's only a path, treat it like case (2). | ||||||
|  | @ -677,7 +714,20 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) | ||||||
| 		if (get_sha1(arg, rev)) { | 		if (get_sha1(arg, rev)) { | ||||||
| 			if (has_dash_dash)          /* case (1) */ | 			if (has_dash_dash)          /* case (1) */ | ||||||
| 				die("invalid reference: %s", arg); | 				die("invalid reference: %s", arg); | ||||||
| 			goto no_reference;          /* case (3 -> 2) */ | 			if (!patch_mode && | ||||||
|  | 			    opts.track == BRANCH_TRACK_UNSPECIFIED && | ||||||
|  | 			    !opts.new_branch && | ||||||
|  | 			    !check_filename(NULL, arg) && | ||||||
|  | 			    argc == 1) { | ||||||
|  | 				const char *remote = unique_tracking_name(arg); | ||||||
|  | 				if (!remote || get_sha1(remote, rev)) | ||||||
|  | 					goto no_reference; | ||||||
|  | 				opts.new_branch = arg; | ||||||
|  | 				arg = remote; | ||||||
|  | 				/* DWIMmed to create local branch */ | ||||||
|  | 			} | ||||||
|  | 			else | ||||||
|  | 				goto no_reference; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		/* we can't end up being in (2) anymore, eat the argument */ | 		/* we can't end up being in (2) anymore, eat the argument */ | ||||||
|  | @ -715,6 +765,10 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| no_reference: | no_reference: | ||||||
|  |  | ||||||
|  | 	if (opts.track == BRANCH_TRACK_UNSPECIFIED) | ||||||
|  | 		opts.track = git_branch_track; | ||||||
|  |  | ||||||
| 	if (argc) { | 	if (argc) { | ||||||
| 		const char **pathspec = get_pathspec(prefix, argv); | 		const char **pathspec = get_pathspec(prefix, argv); | ||||||
|  |  | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Junio C Hamano
						Junio C Hamano