Re: [RFC] checkout to notice forks (Re: Minor annoyance with git push)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sat, Feb 16, 2008 at 05:08:22PM -0800, Junio C Hamano wrote:

> and you say "git checkout next", then after we switch the branch
> we check the upstream (in this case, refs/remotes/linus/next)
> and our branch, and:
> 
>     (1) if they match, nothing happens;
> 
>     (2) if you are ahead (i.e. the upstream is a strict ancestor
>         of you), one line message tells you so;
> 
>     (3) otherwise, you are either behind or you and the upstream
>         have forked.  One line message will tell you which and
>         then you will see a "log --pretty=oneline --left-right".

Overall I think this is a sensible idea. For (3), it probably makes
sense to limit the output in some cases. If I checkout a topic branch
that I haven't looked at in a few days or even weeks, I am going to get
spammed with hundreds of commits.

Most of the time what I really want to know is "I am not up to date and
should merge or rebase." Automatically showing _which_ commits diverge
is a convenience that makes sense if there are a handful of them. For
larger cases, the user can easily run "git log upstream...branch".

Of course this is speculation and gut feeling; I'll try running with
this for a few weeks and see if my opinion changes.

-Peff


> 
> We could enhance this with an option that tells the command to
> check if there is no local change, and automatically fast
> forward when you are truly behind.  But I ripped out that change
> because I was unsure what the right way should be to allow users
> to control it (issues include that checkout should not become
> automatically interactive).
> 
> This is hot off the press and I know it tends to be a bit too
> loud.  It is based on Daniel's "git checkout in C" with Dscho's
> lock_file fix.
> 
> ---
> 
>  builtin-checkout.c |  136 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 136 insertions(+), 0 deletions(-)
> 
> diff --git a/builtin-checkout.c b/builtin-checkout.c
> index 59a0ef4..9370ba0 100644
> --- a/builtin-checkout.c
> +++ b/builtin-checkout.c
> @@ -12,6 +12,7 @@
>  #include "branch.h"
>  #include "diff.h"
>  #include "revision.h"
> +#include "remote.h"
>  
>  static const char * const checkout_usage[] = {
>  	"git checkout [options] <branch>",
> @@ -290,6 +291,139 @@ static int merge_working_tree(struct checkout_opts *opts,
>  	return 0;
>  }
>  
> +/*
> + * We really should allow cb_data... Yuck
> + */
> +static const char *branch_name;
> +static int branch_name_len;
> +static char *found_remote;
> +static char *found_merge;
> +static int read_branch_config(const char *var, const char *value)
> +{
> +	const char *name;
> +	if (prefixcmp(var, "branch."))
> +		return 0; /* not ours */
> +	name = var + strlen("branch.");
> +	if (strncmp(name, branch_name, branch_name_len) ||
> +	    name[branch_name_len] != '.')
> +		return 0; /* not ours either */
> +	if (!strcmp(name + branch_name_len, ".remote")) {
> +		/*
> +		 * Yeah, I know Christian's clean-up should
> +		 * be used here, but the topic is based on an
> +		 * older fork point.
> +		 */
> +		if (!value)
> +			return error("'%s' not string", var);
> +		found_remote = xstrdup(value);
> +		return 0;
> +	}
> +	if (!strcmp(name + branch_name_len, ".merge")) {
> +		if (!value)
> +			return error("'%s' not string", var);
> +		found_merge = xstrdup(value);
> +		return 0;
> +	}
> +	return 0; /* not ours */
> +}
> +
> +static int find_build_base(const char *ours, char **base)
> +{
> +	struct remote *remote;
> +	struct refspec spec;
> +
> +	*base = NULL;
> +
> +	branch_name = ours + strlen("refs/heads/");
> +	branch_name_len = strlen(branch_name);
> +	found_remote = NULL;
> +	found_merge = NULL;
> +	git_config(read_branch_config);
> +
> +	if (!found_remote || !found_merge) {
> +	cleanup:
> +		free(found_remote);
> +		free(found_merge);
> +		return 0;
> +	}
> +
> +	remote = remote_get(found_remote);
> +	memset(&spec, 0, sizeof(spec));
> +	spec.src = found_merge;
> +	if (remote_find_tracking(remote, &spec))
> +		goto cleanup;
> +	*base = spec.dst;
> +	return 1;
> +}
> +
> +static void adjust_to_tracking(struct branch_info *new, struct checkout_opts *opts)
> +{
> +	/*
> +	 * 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;
> +	const char *msgfmt;
> +	char symmetric[84];
> +	int show_log;
> +
> +	if (!resolve_ref(new->path, sha1, 1, NULL))
> +		return;
> +	ours = lookup_commit(sha1);
> +
> +	if (!find_build_base(new->path, &base))
> +		return;
> +
> +	sprintf(symmetric, "%s", sha1_to_hex(sha1));
> +
> +	/*
> +	 * Ok, it is tracking base; is it ahead of us?
> +	 */
> +	if (!resolve_ref(base, sha1, 1, NULL))
> +		return;
> +	theirs = lookup_commit(sha1);
> +
> +	sprintf(symmetric + 40, "...%s", sha1_to_hex(sha1));
> +
> +	if (!hashcmp(sha1, ours->object.sha1))
> +		return; /* we are the same */
> +
> +	show_log = 1;
> +	if (in_merge_bases(theirs, &ours, 1)) {
> +		msgfmt = "You are ahead of the tracked branch '%s'\n";
> +		show_log = 0;
> +	}
> +	else if (in_merge_bases(ours, &theirs, 1))
> +		msgfmt = "Your branch can be fast-forwarded to the tracked branch '%s'\n";
> +	else
> +		msgfmt = "Both your branch and the tracked branch '%s' have own changes, you would eventually need to merge\n";
> +
> +	if (!prefixcmp(base, "refs/remotes/"))
> +		base += strlen("refs/remotes/");
> +	fprintf(stderr, msgfmt, base);
> +
> +	if (show_log) {
> +		const char *args[32];
> +		int ac;
> +
> +		ac = 0;
> +		args[ac++] = "log";
> +		args[ac++] = "--pretty=oneline";
> +		args[ac++] = "--abbrev-commit";
> +		args[ac++] = "--left-right";
> +		args[ac++] = "--boundary";
> +		args[ac++] = symmetric;
> +		args[ac++] = "--";
> +		args[ac] = NULL;
> +
> +		run_command_v_opt(args, RUN_GIT_CMD);
> +	}
> +}
> +
> +
>  static void update_refs_for_switch(struct checkout_opts *opts,
>  				   struct branch_info *old,
>  				   struct branch_info *new)
> @@ -332,6 +466,8 @@ static void update_refs_for_switch(struct checkout_opts *opts,
>  	}
>  	remove_branch_state();
>  	strbuf_release(&msg);
> +	if (new->path)
> +		adjust_to_tracking(new, opts);
>  }
>  
>  static int switch_branches(struct checkout_opts *opts,
-
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux