git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: Paul Tan <pyokagan@gmail.com>
To: Johannes Schindelin <johannes.schindelin@gmx.de>
Cc: Git List <git@vger.kernel.org>,
	Stefan Beller <sbeller@google.com>,
	Stephen Robin <stephen.robin@gmail.com>
Subject: Re: [PATCH 11/14] pull: teach git pull about --rebase
Date: Sun, 31 May 2015 16:18:56 +0800	[thread overview]
Message-ID: <CACRoPnRvjyjtbdT4CY7f4kFqq1rTiksp7eUnpFza+h8ZAq-4gg@mail.gmail.com> (raw)
In-Reply-To: <ea07c7ecec761a0bb2d9f4936a8d2411@www.dscho.org>

Hi Johannes,

On Tue, May 19, 2015 at 9:04 PM, Johannes Schindelin
<johannes.schindelin@gmx.de> wrote:
> On 2015-05-18 17:06, Paul Tan wrote:
>> +/**
>> + * Given a refspec, returns the merge branch. Returns NULL if the refspec src
>> + * does not refer to a branch.
>> + *
>> + * FIXME: It should return the tracking branch. Currently only works with the
>> + * default mapping.
>> + */
>> +static char *get_merge_branch_2(const char *repo, const char *refspec)
>> +{
>> +     struct refspec *spec;
>> +     const char *remote;
>> +     char *merge_branch;
>> +
>> +     spec = parse_fetch_refspec(1, &refspec);
>> +     remote = spec->src;
>> +     if (!*remote || !strcmp(remote, "HEAD"))
>> +             remote = "HEAD";
>> +     else if (skip_prefix(remote, "heads/", &remote))
>> +             ;
>> +     else if (skip_prefix(remote, "refs/heads/", &remote))
>> +             ;
>> +     else if (starts_with(remote, "refs/") ||
>> +             starts_with(remote, "tags/") ||
>> +             starts_with(remote, "remotes/"))
>> +             remote = "";
>> +
>> +     if (*remote) {
>> +             if (!strcmp(repo, "."))
>> +                     merge_branch = mkpathdup("refs/heads/%s", remote);
>> +             else
>> +                     merge_branch = mkpathdup("refs/remotes/%s/%s", repo, remote);
>> +     } else
>> +             merge_branch = NULL;
>> +
>> +     free_refspec(1, spec);
>> +     return merge_branch;
>> +}
>
> I have to admit that it took me a substantial amount of time to deduce from the code what `get_merge_branch_2()` really does (judging from the description, I thought that it would be essentially the same as `get_merge_branch_1()` without the hard-coded "HEAD"). If the comment above the function would have said something like:
>
> /**
>  * Given a refspec, returns the name of the local tracking ref.
>  */
>
> I would have had an easier time. Also, I wonder if something like this would do the job:

Yeah whoops, this came from a confusion over the difference over
"merge branch" and "remote tracking branch". A merge branch would be a
remote tracking branch, but a remote tracking branch is not
necessarily a merge branch.

>         spec = parse_fetch_refspec(1, &refspec);
>         if (spec->dst)
>                 return spec->dst;

Hmm, I notice that get_remote_merge_branch() only looks at the src
part of the refspec. However, I guess it is true that if the dst part
is provided, the user may be wishing for that to be interpreted as the
"remote tracking branch", so we should be looking at it to calculate
the fork point.

>         if (!(remote = get_remote(remote_name)))
>                 return NULL;
>         if (remote_find_tracking(remote, spec))
>                 return spec->dst;

... and if the dst part of the refspec is not provided, we fall back
to see if there is any remote tracking branch in the repo for the src
part of the ref, which matches the intention of
get_remote_merge_branch() I think, while being better because
remote_find_tracking() takes into account the actual configured fetch
refspecs for the remote.

However, we also need to consider if the user provided a wildcard
refspec, as it will not make sense in this case. From my reading,
remote_find_tracking(), which calls query_refspecs(), would just match
the src part literally, so I guess we should explicitly detect and
error out in this case.

>         return NULL;
>
> (I guess we'd have to `xstrdup()` the return values because the return value of `get_merge_branch_1()` needs to be `free()`d, but maybe we can avoid so much `malloc()/free()`ing.) Again, the function name should probably be changed to something clearer, maybe to `get_tracking_branch()`.

Yeah, it should be changed to get_tracking_branch(), since it is
different from get_merge_branch().

> One thing that is not clear at all to me is whether
>
>         git pull --rebase origin master next
>
> would error out as expected, or simply rebase to `origin/master`.

In git-pull.sh, for the rebase fork point calculation it just used the
first refspec. However, when running git-rebase it checked to see if
there was only one merge base, which is replicated here:

    @@ -573,6 +792,10 @@ int cmd_pull(int argc, const char **argv,
const char *prefix)
                    if (merge_heads.nr > 1)
                            die(_("Cannot merge multiple branches into
empty head."));
                    return pull_into_void(*merge_heads.sha1, curr_head);
    +       } else if (opt_rebase) {
    +               if (merge_heads.nr > 1)
    +                       die(_("Cannot rebase onto multiple branches."));
    +               return run_rebase(curr_head, *merge_heads.sha1,
rebase_fork_point);
            } else
                    return run_merge();
     }

Since this is just a 1:1 rewrite I just tried to keep as close to the
original as possible. However, thinking about it, since we *are* just
using the first refspec for fork point calculation, I do agree that we
should probably give an warning() here as well if the user provided
more than one refspec, like "Cannot calculate rebase fork point as you
provided more than one refspec. git-pull will not be able to handle a
rebased upstream". I do not think it is fatal enough that we should
error() or die(), as e.g. the first refspec may be a wildcard refspec
that matches nothing, and the second refspec that matches one merge
head for rebasing. This is pretty contrived though, but still
technically legitimate. I dunno.

>> +/**
>> + * Sets fork_point to the point at which the current branch forked from its
>> + * remote merge branch. Returns 0 on success, -1 on failure.
>> + */
>> +static int get_rebase_fork_point(unsigned char fork_point[GIT_SHA1_RAWSZ],
>> +             const char *repo, const char *refspec)
>> +{
>> +     int ret;
>> +     struct branch *curr_branch;
>> +     char *remote_merge_branch;
>> +     struct argv_array args = ARGV_ARRAY_INIT;
>> +     struct child_process cp = CHILD_PROCESS_INIT;
>> +     struct strbuf sb = STRBUF_INIT;
>> +
>> +     if (!(curr_branch = branch_get("HEAD")))
>> +             return -1;
>> +
>> +     if (refspec)
>> +             remote_merge_branch = get_merge_branch_2(repo, refspec);
>> +     else
>> +             remote_merge_branch = get_merge_branch_1(repo);
>> +
>> +     if (!remote_merge_branch)
>> +             return -1;
>
> We should probably `return error(_"No tracking branch found for %s@, refspec ? refspec : "HEAD");` so that the user has a chance to understand that there has been a problem and how to solve it.

Just like the above, I don't think this is serious enough to be
considered an error() though. Sure, this means that we cannot properly
handle the case where the upstream has been rebased, but this is not
always the case. We could probably have a warning() here, but I think
the message should be improved to tell the user what exactly she is
losing out on. e.g. "No tracking branch found for %s. git-pull will
not be able to handle a rebased upstream."

>> +     argv_array_pushl(&args, "merge-base", "--fork-point",
>> +                     remote_merge_branch, curr_branch->name, NULL);
>> +     cp.argv = args.argv;
>
> Let's just use `cp.args` directly...

Yeah, whoops.

>> +     cp.no_stdin = 1;
>> +     cp.no_stderr = 1;
>> +     cp.git_cmd = 1;
>> +
>> +     if ((ret = capture_command(&cp, &sb, GIT_SHA1_HEXSZ)))
>> +             goto cleanup;
>> +
>> +     if ((ret = get_sha1_hex(sb.buf, fork_point)))
>> +             goto cleanup;
>> +
>> +cleanup:
>> +     free(remote_merge_branch);
>> +     strbuf_release(&sb);
>> +     return ret ? -1 : 0;
>> +}
>> +
>> +[...]
>> +/**
>> + * Given the current HEAD SHA1, the merge head returned from git-fetch and the
>> + * fork point calculated by get_rebase_fork_point(), runs git-rebase with the
>> + * appropriate arguments and returns its exit status.
>> + */
>> +static int run_rebase(unsigned char curr_head[GIT_SHA1_RAWSZ],
>> +             unsigned char merge_head[GIT_SHA1_RAWSZ],
>> +             unsigned char fork_point[GIT_SHA1_RAWSZ])
>> +{
>> +     int ret;
>> +     unsigned char oct_merge_base[GIT_SHA1_RAWSZ];
>> +     struct argv_array args = ARGV_ARRAY_INIT;
>> +
>> +     if (!get_octopus_merge_base(oct_merge_base, curr_head, merge_head,
>> fork_point))
>
> It might be my mail program only that mangled the diff here. But it could also be that this line is a little long (by my count, it is 81 columns wide).

Yeah, it is, but I felt that breaking the line would reduce readability.

Thanks, these were some really hard questions that you raised ;-).

Regards,
Paul

  reply	other threads:[~2015-05-31  8:19 UTC|newest]

Thread overview: 43+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-05-18 15:05 [PATCH 00/14] Make git-pull a builtin Paul Tan
2015-05-18 15:05 ` [PATCH 01/14] pull: implement fetch + merge Paul Tan
2015-05-18 15:55   ` Johannes Schindelin
2015-05-18 15:05 ` [PATCH 02/14] pull: pass verbosity, --progress flags to fetch and merge Paul Tan
2015-05-18 17:41   ` Johannes Schindelin
2015-05-21  9:48     ` Paul Tan
2015-05-21 15:59       ` Johannes Schindelin
2015-05-22 13:38         ` Paul Tan
2015-05-18 15:06 ` [PATCH 03/14] pull: pass git-merge's options to git-merge Paul Tan
2015-05-18 15:06 ` [PATCH 04/14] pull: pass git-fetch's options to git-fetch Paul Tan
2015-05-18 15:06 ` [PATCH 05/14] pull: error on no merge candidates Paul Tan
2015-05-18 18:56   ` Johannes Schindelin
2015-05-18 15:06 ` [PATCH 06/14] pull: support pull.ff config Paul Tan
2015-05-18 19:02   ` Johannes Schindelin
2015-05-21  9:53     ` Paul Tan
2015-05-18 15:06 ` [PATCH 07/14] pull: check if in unresolved merge state Paul Tan
2015-05-18 19:06   ` Johannes Schindelin
2015-05-18 15:06 ` [PATCH 08/14] pull: fast-forward working tree if head is updated Paul Tan
2015-05-18 19:18   ` Johannes Schindelin
2015-05-18 15:06 ` [PATCH 09/14] pull: implement pulling into an unborn branch Paul Tan
2015-05-18 15:06 ` [PATCH 10/14] pull: set reflog message Paul Tan
2015-05-18 19:27   ` Johannes Schindelin
2015-05-18 21:53     ` Junio C Hamano
2015-05-21 10:08       ` Paul Tan
2015-05-18 15:06 ` [PATCH 11/14] pull: teach git pull about --rebase Paul Tan
2015-05-18 23:36   ` Stefan Beller
2015-05-19 13:04   ` Johannes Schindelin
2015-05-31  8:18     ` Paul Tan [this message]
2015-06-02 11:26       ` Paul Tan
2015-05-18 15:06 ` [PATCH 12/14] pull: configure --rebase via branch.<name>.rebase or pull.rebase Paul Tan
2015-05-18 23:58   ` Stefan Beller
2015-05-18 15:06 ` [PATCH 13/14] pull --rebase: exit early when the working directory is dirty Paul Tan
2015-05-18 15:06 ` [PATCH 14/14] pull --rebase: error on no merge candidate cases Paul Tan
2015-05-19  0:12   ` Stefan Beller
2015-05-19 13:10     ` Johannes Schindelin
2015-05-19 16:27       ` Junio C Hamano
2015-05-22 13:48         ` Paul Tan
2015-05-22 14:14           ` Johannes Schindelin
2015-05-22 17:12             ` Stefan Beller
2015-05-18 19:21 ` [PATCH 00/14] Make git-pull a builtin Junio C Hamano
2015-05-30  7:29   ` Paul Tan
2015-05-30  8:00     ` Paul Tan
2015-05-18 19:41 ` Johannes Schindelin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: http://vger.kernel.org/majordomo-info.html

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CACRoPnRvjyjtbdT4CY7f4kFqq1rTiksp7eUnpFza+h8ZAq-4gg@mail.gmail.com \
    --to=pyokagan@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=johannes.schindelin@gmx.de \
    --cc=sbeller@google.com \
    --cc=stephen.robin@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).