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, 16 Feb 2008, Junio C Hamano wrote:

> Junio C Hamano <gitster@xxxxxxxxx> writes:
> 
> > Perhaps making "git-checkout" to notice this and offer (or
> > suggest) fast-forwarding at that point may be safer and make
> > more sense.  You cannot grow your local branch unless you check
> > them out, and your remote tracking will keep growing without the
> > auto-ff you are suggesting, so it is not like people will lose
> > anchoring point to compare between branches if we do not
> > auto-ff.
> 
> So I did this.
> 
> When you are switching to a branch that is marked to merge from
> somewhere else, e.g. when you have:
> 
>     [branch "next"]
>             remote = upstream
>             merge = refs/heads/next
>     [remote "upstream"]
>             url = ...
>             fetch = refs/heads/*:refs/remotes/linus/*
> 
> 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".

I like this idea a lot. I'd actually also like it for commit, although (1) 
and (3a) obviously don't happen there. It would help to combat my tendency 
to forget to push when I mean to.

Note that, in addition to "should I merge before starting to work now", 
this also answers "if I turn off this computer, what can't I get at".

> 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).

I suppose you could use a config option to enable it per-branch.

It would be really clever to make it happen if you do:

$ git checkout next
(fast-forward info)
$ git checkout

But that's probably a bit too clever.

> 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)
> +{

...

I think you want branch_get(), which handles all the config file stuff up 
to approximately here:

> +	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);

We really should be able to do this in-process, although I'm not sure if 
we really can. I don't think I've marked up the history in 
builtin-checkout for anything else yet, anyway.

	-Daniel
*This .sig left intentionally blank*
-
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