Refactor "tracking statistics" code used by "git checkout"
People seem to like "Your branch is ahead by N commit" report made by "git checkout", but the interface into the statistics function was a bit clunky. This splits the function into three parts: * The core "commit counting" function that takes "struct branch" and returns number of commits to show if we are ahead, behind or forked; * Convenience "stat formating" function that takes "struct branch" and formats the report into a given strbuf, using the above function; * "checkout" specific function that takes "branch_info" (type that is internal to checkout implementation), calls the above function and print the formatted result. in the hope that the former two can be more easily reusable. Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
							parent
							
								
									6cbf8b00fb
								
							
						
					
					
						commit
						6d21bf96b5
					
				|  | @ -305,97 +305,15 @@ static int merge_working_tree(struct checkout_opts *opts, | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void report_tracking(struct branch_info *new, struct checkout_opts *opts) | static void report_tracking(struct branch_info *new) | ||||||
| { | { | ||||||
| 	/* | 	struct strbuf sb = STRBUF_INIT; | ||||||
| 	 * We have switched to a new branch; is it building on |  | ||||||
| 	 * top of another branch, and if so does that other branch |  | ||||||
| 	 * have changes we do not have yet? |  | ||||||
| 	 */ |  | ||||||
| 	char *base; |  | ||||||
| 	unsigned char sha1[20]; |  | ||||||
| 	struct commit *ours, *theirs; |  | ||||||
| 	char symmetric[84]; |  | ||||||
| 	struct rev_info revs; |  | ||||||
| 	const char *rev_argv[10]; |  | ||||||
| 	int rev_argc; |  | ||||||
| 	int num_ours, num_theirs; |  | ||||||
| 	const char *remote_msg; |  | ||||||
| 	struct branch *branch = branch_get(new->name); | 	struct branch *branch = branch_get(new->name); | ||||||
|  |  | ||||||
| 	/* | 	if (!format_tracking_info(branch, &sb)) | ||||||
| 	 * Nothing to report unless we are marked to build on top of |  | ||||||
| 	 * somebody else. |  | ||||||
| 	 */ |  | ||||||
| 	if (!branch || !branch->merge || !branch->merge[0] || !branch->merge[0]->dst) |  | ||||||
| 		return; | 		return; | ||||||
|  | 	fputs(sb.buf, stdout); | ||||||
| 	/* | 	strbuf_release(&sb); | ||||||
| 	 * If what we used to build on no longer exists, there is |  | ||||||
| 	 * nothing to report. |  | ||||||
| 	 */ |  | ||||||
| 	base = branch->merge[0]->dst; |  | ||||||
| 	if (!resolve_ref(base, sha1, 1, NULL)) |  | ||||||
| 		return; |  | ||||||
|  |  | ||||||
| 	theirs = lookup_commit(sha1); |  | ||||||
| 	ours = new->commit; |  | ||||||
| 	if (!hashcmp(sha1, ours->object.sha1)) |  | ||||||
| 		return; /* we are the same */ |  | ||||||
|  |  | ||||||
| 	/* Run "rev-list --left-right ours...theirs" internally... */ |  | ||||||
| 	rev_argc = 0; |  | ||||||
| 	rev_argv[rev_argc++] = NULL; |  | ||||||
| 	rev_argv[rev_argc++] = "--left-right"; |  | ||||||
| 	rev_argv[rev_argc++] = symmetric; |  | ||||||
| 	rev_argv[rev_argc++] = "--"; |  | ||||||
| 	rev_argv[rev_argc] = NULL; |  | ||||||
|  |  | ||||||
| 	strcpy(symmetric, sha1_to_hex(ours->object.sha1)); |  | ||||||
| 	strcpy(symmetric + 40, "..."); |  | ||||||
| 	strcpy(symmetric + 43, sha1_to_hex(theirs->object.sha1)); |  | ||||||
|  |  | ||||||
| 	init_revisions(&revs, NULL); |  | ||||||
| 	setup_revisions(rev_argc, rev_argv, &revs, NULL); |  | ||||||
| 	prepare_revision_walk(&revs); |  | ||||||
|  |  | ||||||
| 	/* ... and count the commits on each side. */ |  | ||||||
| 	num_ours = 0; |  | ||||||
| 	num_theirs = 0; |  | ||||||
| 	while (1) { |  | ||||||
| 		struct commit *c = get_revision(&revs); |  | ||||||
| 		if (!c) |  | ||||||
| 			break; |  | ||||||
| 		if (c->object.flags & SYMMETRIC_LEFT) |  | ||||||
| 			num_ours++; |  | ||||||
| 		else |  | ||||||
| 			num_theirs++; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (!prefixcmp(base, "refs/remotes/")) { |  | ||||||
| 		remote_msg = " remote"; |  | ||||||
| 		base += strlen("refs/remotes/"); |  | ||||||
| 	} else { |  | ||||||
| 		remote_msg = ""; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (!num_theirs) |  | ||||||
| 		printf("Your branch is ahead of the tracked%s branch '%s' " |  | ||||||
| 		       "by %d commit%s.\n", |  | ||||||
| 		       remote_msg, base, |  | ||||||
| 		       num_ours, (num_ours == 1) ? "" : "s"); |  | ||||||
| 	else if (!num_ours) |  | ||||||
| 		printf("Your branch is behind the tracked%s branch '%s' " |  | ||||||
| 		       "by %d commit%s,\n" |  | ||||||
| 		       "and can be fast-forwarded.\n", |  | ||||||
| 		       remote_msg, base, |  | ||||||
| 		       num_theirs, (num_theirs == 1) ? "" : "s"); |  | ||||||
| 	else |  | ||||||
| 		printf("Your branch and the tracked%s branch '%s' " |  | ||||||
| 		       "have diverged,\nand respectively " |  | ||||||
| 		       "have %d and %d different commit(s) each.\n", |  | ||||||
| 		       remote_msg, base, |  | ||||||
| 		       num_ours, num_theirs); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static void update_refs_for_switch(struct checkout_opts *opts, | static void update_refs_for_switch(struct checkout_opts *opts, | ||||||
|  | @ -441,7 +359,7 @@ static void update_refs_for_switch(struct checkout_opts *opts, | ||||||
| 	remove_branch_state(); | 	remove_branch_state(); | ||||||
| 	strbuf_release(&msg); | 	strbuf_release(&msg); | ||||||
| 	if (!opts->quiet && (new->path || !strcmp(new->name, "HEAD"))) | 	if (!opts->quiet && (new->path || !strcmp(new->name, "HEAD"))) | ||||||
| 		report_tracking(new, opts); | 		report_tracking(new); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int switch_branches(struct checkout_opts *opts, struct branch_info *new) | static int switch_branches(struct checkout_opts *opts, struct branch_info *new) | ||||||
|  |  | ||||||
							
								
								
									
										113
									
								
								remote.c
								
								
								
								
							
							
						
						
									
										113
									
								
								remote.c
								
								
								
								
							|  | @ -1,6 +1,9 @@ | ||||||
| #include "cache.h" | #include "cache.h" | ||||||
| #include "remote.h" | #include "remote.h" | ||||||
| #include "refs.h" | #include "refs.h" | ||||||
|  | #include "commit.h" | ||||||
|  | #include "diff.h" | ||||||
|  | #include "revision.h" | ||||||
|  |  | ||||||
| static struct refspec s_tag_refspec = { | static struct refspec s_tag_refspec = { | ||||||
| 	0, | 	0, | ||||||
|  | @ -1222,3 +1225,113 @@ int resolve_remote_symref(struct ref *ref, struct ref *list) | ||||||
| 		} | 		} | ||||||
| 	return 1; | 	return 1; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Return true if there is anything to report, otherwise false. | ||||||
|  |  */ | ||||||
|  | int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs) | ||||||
|  | { | ||||||
|  | 	unsigned char sha1[20]; | ||||||
|  | 	struct commit *ours, *theirs; | ||||||
|  | 	char symmetric[84]; | ||||||
|  | 	struct rev_info revs; | ||||||
|  | 	const char *rev_argv[10], *base; | ||||||
|  | 	int rev_argc; | ||||||
|  |  | ||||||
|  | 	/* | ||||||
|  | 	 * Nothing to report unless we are marked to build on top of | ||||||
|  | 	 * somebody else. | ||||||
|  | 	 */ | ||||||
|  | 	if (!branch || | ||||||
|  | 	    !branch->merge || !branch->merge[0] || !branch->merge[0]->dst) | ||||||
|  | 		return 0; | ||||||
|  |  | ||||||
|  | 	/* | ||||||
|  | 	 * If what we used to build on no longer exists, there is | ||||||
|  | 	 * nothing to report. | ||||||
|  | 	 */ | ||||||
|  | 	base = branch->merge[0]->dst; | ||||||
|  | 	if (!resolve_ref(base, sha1, 1, NULL)) | ||||||
|  | 		return 0; | ||||||
|  | 	theirs = lookup_commit(sha1); | ||||||
|  | 	if (!theirs) | ||||||
|  | 		return 0; | ||||||
|  |  | ||||||
|  | 	if (!resolve_ref(branch->refname, sha1, 1, NULL)) | ||||||
|  | 		return 0; | ||||||
|  | 	ours = lookup_commit(sha1); | ||||||
|  | 	if (!ours) | ||||||
|  | 		return 0; | ||||||
|  |  | ||||||
|  | 	/* are we the same? */ | ||||||
|  | 	if (theirs == ours) | ||||||
|  | 		return 0; | ||||||
|  |  | ||||||
|  | 	/* Run "rev-list --left-right ours...theirs" internally... */ | ||||||
|  | 	rev_argc = 0; | ||||||
|  | 	rev_argv[rev_argc++] = NULL; | ||||||
|  | 	rev_argv[rev_argc++] = "--left-right"; | ||||||
|  | 	rev_argv[rev_argc++] = symmetric; | ||||||
|  | 	rev_argv[rev_argc++] = "--"; | ||||||
|  | 	rev_argv[rev_argc] = NULL; | ||||||
|  |  | ||||||
|  | 	strcpy(symmetric, sha1_to_hex(ours->object.sha1)); | ||||||
|  | 	strcpy(symmetric + 40, "..."); | ||||||
|  | 	strcpy(symmetric + 43, sha1_to_hex(theirs->object.sha1)); | ||||||
|  |  | ||||||
|  | 	init_revisions(&revs, NULL); | ||||||
|  | 	setup_revisions(rev_argc, rev_argv, &revs, NULL); | ||||||
|  | 	prepare_revision_walk(&revs); | ||||||
|  |  | ||||||
|  | 	/* ... and count the commits on each side. */ | ||||||
|  | 	*num_ours = 0; | ||||||
|  | 	*num_theirs = 0; | ||||||
|  | 	while (1) { | ||||||
|  | 		struct commit *c = get_revision(&revs); | ||||||
|  | 		if (!c) | ||||||
|  | 			break; | ||||||
|  | 		if (c->object.flags & SYMMETRIC_LEFT) | ||||||
|  | 			(*num_ours)++; | ||||||
|  | 		else | ||||||
|  | 			(*num_theirs)++; | ||||||
|  | 	} | ||||||
|  | 	return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Return true when there is anything to report, otherwise false. | ||||||
|  |  */ | ||||||
|  | int format_tracking_info(struct branch *branch, struct strbuf *sb) | ||||||
|  | { | ||||||
|  | 	int num_ours, num_theirs; | ||||||
|  | 	const char *base, *remote_msg; | ||||||
|  |  | ||||||
|  | 	if (!stat_tracking_info(branch, &num_ours, &num_theirs)) | ||||||
|  | 		return 0; | ||||||
|  |  | ||||||
|  | 	base = branch->merge[0]->dst; | ||||||
|  | 	if (!prefixcmp(base, "refs/remotes/")) { | ||||||
|  | 		remote_msg = " remote"; | ||||||
|  | 		base += strlen("refs/remotes/"); | ||||||
|  | 	} else { | ||||||
|  | 		remote_msg = ""; | ||||||
|  | 	} | ||||||
|  | 	if (!num_theirs) | ||||||
|  | 		strbuf_addf(sb, "Your branch is ahead of the tracked%s branch '%s' " | ||||||
|  | 			    "by %d commit%s.\n", | ||||||
|  | 			    remote_msg, base, | ||||||
|  | 			    num_ours, (num_ours == 1) ? "" : "s"); | ||||||
|  | 	else if (!num_ours) | ||||||
|  | 		strbuf_addf(sb, "Your branch is behind the tracked%s branch '%s' " | ||||||
|  | 			    "by %d commit%s,\n" | ||||||
|  | 			    "and can be fast-forwarded.\n", | ||||||
|  | 			    remote_msg, base, | ||||||
|  | 			    num_theirs, (num_theirs == 1) ? "" : "s"); | ||||||
|  | 	else | ||||||
|  | 		strbuf_addf(sb, "Your branch and the tracked%s branch '%s' " | ||||||
|  | 			    "have diverged,\nand respectively " | ||||||
|  | 			    "have %d and %d different commit(s) each.\n", | ||||||
|  | 			    remote_msg, base, | ||||||
|  | 			    num_ours, num_theirs); | ||||||
|  | 	return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										4
									
								
								remote.h
								
								
								
								
							
							
						
						
									
										4
									
								
								remote.h
								
								
								
								
							|  | @ -129,4 +129,8 @@ enum match_refs_flags { | ||||||
| 	MATCH_REFS_MIRROR	= (1 << 1), | 	MATCH_REFS_MIRROR	= (1 << 1), | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | /* Reporting of tracking info */ | ||||||
|  | int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs); | ||||||
|  | int format_tracking_info(struct branch *branch, struct strbuf *sb); | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Junio C Hamano
						Junio C Hamano