Re: [PATCH] push: Provide situational hints for non-fast-forward errors

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

 



An off-topic administrivia. Please do not try to deflect responses meant
for you by setting Mail-Followup-To.

Christopher Tiwald <christiwald@xxxxxxxxx> writes:

> Pushing a non-fast-forward update to a remote repository will result in
> an error, but the hint text doesn't provide the correct resolution in
> every case. Three scenarios may arise depending on your workflow, each
> with a different resolution:

Are we sure there are only three, or is this just "we do not say anything
concrete, but at least we know common three cases, and there may be more"?

I am mostly interested in making sure that we do not give a bad advice.
Giving an advice that is mostly accurate and relevant for 95% of the time
is perfectly fine, as long as following the advice in the remaining 5%
does not result in a disaster.

> 1) If you push a non-fast-forward update to HEAD, you should merge
> remote changes with 'git pull' before pushing again.

You said "to HEAD", but I think you meant the case you push your current
branch (i.e. HEAD) to update any ref on the other side.  In other words,
the push does not have to be "*to*" HEAD over there.  Am I mistaken?

> 2) If you push to a shared repository others push to, and your local
> tracking branches are not kept up to date, the 'matching refs' default
> will generate non-fast-forward errors on outdated branches. If this is
> your workflow, the 'matching refs' default is not for you. Consider
> setting the 'push.default' configuration variable to 'upstream' to
> ensure only your checked-out branch is pushed.

OK.

> 3) If you push with explicit ref matching (e.g. 'git push ... topic:topic')
> while checked out on another branch (e.g. 'master'), the correct
> resolution is checking out the local branch, issuing git pull, and
> merging remote changes before pushing again.

Or you may have misspelled the source side of the refspec and tried to
push a wrong branch.

> Make nonfastforward an enum and teach transport.c to detect the
> scenarios described above. Give situation-specific resolution advice
> when pushes are rejected due to non-fast-forward updates. Finally,
> update other instances of nonfastforward to use the proper enum option.

I think the overall direction of the implemention is good, modulo minor
design nits.

 * I do not particularly find NONFASTFORWARD_NONE that is defined to be 0
   a useful readability measure. Plain vanilla constant 0 says that there
   is nothing magical going on to the readers clearly already.

 * Also NONFASTFORWARD_FROTZ is way too long.  Wouldn't NONFF_FROTZ be
   sufficient and clear?

 * I can see there are three kinds of advices, but I do not see why users
   need to acknowledge that they understand them one by one with separate
   advice configuration.  Isn't it better to have only one variable, "OK,
   I know how to deal with a failed push due to non-fast-forward"?

> Signed-off-by: Christopher Tiwald <christiwald@xxxxxxxxx>
> Based-on-patch-by: Junio C Hamano <gitster@xxxxxxxxx>

These two lines are chronologically swapped.

> ---
> This is a reroll of jc/advise-push-default (2011-12-18).

I lost track of this topic during the last round.  Thanks for picking it
up.

> diff --git a/Documentation/config.txt b/Documentation/config.txt
> index c081657..50d9249 100644
> --- a/Documentation/config.txt
> +++ b/Documentation/config.txt
> @@ -158,6 +158,21 @@ advice.*::
>  		Advice shown when you used linkgit:git-checkout[1] to
>  		move to the detach HEAD state, to instruct how to create
>  		a local branch after the fact.
> +	pullBeforePush::
> +		Advice shown when you ran linkgit:git-push[1] and pushed
> +		a non-fast-forward update to HEAD, instructing you to
> +		linkgit:git-pull[1] before pushing again.
> +	useUpstream::
> +		Advice to set 'push.default' to 'upstream' when you ran
> +		linkgit:git-push[1] and pushed 'matching refs' by default
> +		(i.e. you did not have any explicit refspec on the command
> +		line, and no 'push.default' configuration was set) and it
> +		resulted in a non-fast-forward error.
> +	checkoutPullPush::
> +		Advice shown when you ran linkgit:git-push[1] and pushed
> +		a non-fast-forward update to a non-HEAD branch, instructing
> +		you to checkout the branch and run linkgit:git-pull[1]
> +		before pushing again.

I would prefer to see these consolidated into a single advice.pushNonFF
variable, but I may be missing why it could be a good idea to allow them
turned off selectively.

> diff --git a/builtin/push.c b/builtin/push.c
> index d315475..0fecf06 100644
> --- a/builtin/push.c
> +++ b/builtin/push.c
>  	}
>  }
>  
> +static const char *message_advice_pull_before_push[] = {
> +	"To prevent you from losing history, non-fast-forward updates to HEAD",
> +	"were rejected. Merge the remote changes (e.g. 'git pull') before",
> +	"pushing again. See the 'Note about fast-forwards' section of",
> +	"'git push --help' for details."
> +};

This again says "*to* HEAD".  If this should be "a non-fast-forward update
to send the current branch was rejected" as I suspected above, the message
needs to be rephrased accordingly.

> +static const char *message_advice_use_upstream[] = {
> +	"By default, git pushes all branches that have a matching counterpart",
> +	"on the remote. In this case, some of your local branches were stale",
> +	"with respect to their remote counterparts. If you did not intend to",
> +	"push these branches, you may want to set the 'push.default'",
> +	"configuration variable to 'upstream' to push only the current branch."
> +};

If you drop everything up to and including "In this case, ", the advice
message still teaches exactly what the user needs to learn.

> +static const char *message_advice_checkout_pull_push[] = {
> +	"To prevent you from losing history, your non-fast-forward branch",
> +	"updates were rejected. Checkout the branch and merge the remote",
> +	"changes (e.g. 'git pull') before pushing again. See the",
> +	"'Note about fast-forwards' section of 'git push --help' for",
> +	"details."
> +};

OK.

> @@ -136,15 +193,18 @@ static int push_with_options(struct transport *transport, int flags)
>  
>  	err |= transport_disconnect(transport);
>  
> +	if (nonfastforward == NONFASTFORWARD_HEAD) {
> +		advise_pull_before_push();
> +	} else if (nonfastforward == NONFASTFORWARD_OTHER) {
> +		if (default_matching_used)
> +			advise_use_upstream();
> +		else
> +			advise_checkout_pull_push();
> +	}
		
I suspect that we may find more cases not just three, so

	switch (nonfastforward) {
	default:
        	break;
	case NONFF_HEAD:
        	advice_pull_before_push();
		break;
	case NONFF_OTHER:
		...
	}

would be a more forward-looking way to write it.

Also, shouldn't we be doing this only when err is true, or is it too
defensive?

>  	if (!err)
>  		return 0;
>  
> -	if (nonfastforward && advice_push_nonfastforward) {
> -		fprintf(stderr, _("To prevent you from losing history,...

That is, I am wondering why your "more detailed diag & advice" code is not
here, i.e. after "if (!err) return 0".
--
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]