Re: [PATCH/RFC/GSoC 04/17] builtin-rebase: parse rebase arguments into a common rebase_options struct

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

 



On Sat, Mar 12, 2016 at 2:46 AM, Paul Tan <pyokagan@xxxxxxxxx> wrote:
> A non-root rebase takes 3 arguments:
>
> * branch_name -- the branch or commit that will be rebased. If it is not
>   specified, the current branch is used.
>
> * upstream -- The upstream commit to compare against. If it is not
>   specified, the configured upstream for the current branch is used.
>
> * onto (or newbase) -- The commit to be used as the starting point to
>   re-apply the commits. If it is not specified, `upstream` is used.
>
> Since these parameters are used by all 3 rebase backends, introduce a
> common rebase_options struct to hold all these options. Teach
> builtin/rebase.c to handle the above arguments and store them in a
> rebase_options struct. In later patches we will pass the rebase_options
> struct to the appropriate backend to perform the rebase.
>
> Signed-off-by: Paul Tan <pyokagan@xxxxxxxxx>
> ---
>  Makefile         |   1 +
>  builtin/rebase.c | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
>  rebase-common.c  |  28 +++++++++
>  rebase-common.h  |  23 +++++++
>  4 files changed, 235 insertions(+), 1 deletion(-)
>  create mode 100644 rebase-common.c
>  create mode 100644 rebase-common.h
>
> diff --git a/Makefile b/Makefile
> index ad98714..b29c672 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -779,6 +779,7 @@ LIB_OBJS += prompt.o
>  LIB_OBJS += quote.o
>  LIB_OBJS += reachable.o
>  LIB_OBJS += read-cache.o
> +LIB_OBJS += rebase-common.o
>  LIB_OBJS += reflog-walk.o
>  LIB_OBJS += refs.o
>  LIB_OBJS += refs/files-backend.o
> diff --git a/builtin/rebase.c b/builtin/rebase.c
> index 04cc1bd..40176ca 100644
> --- a/builtin/rebase.c
> +++ b/builtin/rebase.c
> @@ -4,6 +4,112 @@
>  #include "cache.h"
>  #include "builtin.h"
>  #include "parse-options.h"
> +#include "rebase-common.h"
> +#include "remote.h"
> +#include "branch.h"
> +#include "refs.h"
> +
> +/**
> + * Used by get_curr_branch_upstream_name() as a for_each_remote() callback to
> + * retrieve the name of the remote if the repository only has one remote.
> + */
> +static int get_only_remote(struct remote *remote, void *cb_data)
> +{
> +       const char **remote_name = cb_data;
> +
> +       if (*remote_name)
> +               return -1;
> +
> +       *remote_name = remote->name;
> +       return 0;
> +}
> +
> +const char *get_curr_branch_upstream_name(void)
> +{
> +       const char *upstream_name;
> +       struct branch *curr_branch;
> +
> +       curr_branch = branch_get("HEAD");
> +       if (!curr_branch) {
> +               fprintf_ln(stderr, _("You are not currently on a branch."));
> +               fprintf_ln(stderr, _("Please specify which branch you want to rebase against."));
> +               fprintf_ln(stderr, _("See git-rebase(1) for details."));
> +               fprintf(stderr, "\n");
> +               fprintf_ln(stderr, "    git rebase <branch>");
> +               fprintf(stderr, "\n");
> +               exit(1);
> +       }
> +
> +       upstream_name = branch_get_upstream(curr_branch, NULL);
> +       if (!upstream_name) {
> +               const char *remote_name = NULL;
> +
> +               if (for_each_remote(get_only_remote, &remote_name) || !remote_name)
> +                       remote_name = "<remote>";
> +
> +               fprintf_ln(stderr, _("There is no tracking information for the current branch."));
> +               fprintf_ln(stderr, _("Please specify which branch you want to rebase against."));
> +               fprintf_ln(stderr, _("See git-rebase(1) for details."));
> +               fprintf(stderr, "\n");
> +               fprintf_ln(stderr, "    git rebase <branch>");
> +               fprintf(stderr, "\n");
> +               fprintf_ln(stderr, _("If you wish to set tracking information for this branch you can do so with:"));
> +               fprintf(stderr, "\n");
> +               fprintf_ln(stderr, _("If you wish to set tracking information for this branch you can do so with:\n"
> +               "\n"
> +               "    git branch --set-upstream-to=%s/<branch> %s\n"),
> +               remote_name, curr_branch->name);
> +               exit(1);
> +       }
> +
> +       return upstream_name;
> +}
> +
> +/**
> + * Given the --onto <name>, return the onto hash
> + */
> +static void get_onto_oid(const char *_onto_name, struct object_id *onto)
> +{
> +       char *onto_name = xstrdup(_onto_name);
> +       struct commit *onto_commit;
> +       char *dotdot;
> +
> +       dotdot = strstr(onto_name, "...");

So either the variable should be "dotdotdot" or "threedots",
or there is a dot too much in the strstr.

"dotdot" was is used in some directory handling code, which
this reminded me of. So I first wondered why we need to take
care of parent directories.

> +       if (dotdot) {
> +               const char *left = onto_name;
> +               const char *right = dotdot + 3;
> +               struct commit *left_commit, *right_commit;
> +               struct commit_list *merge_bases;
> +
> +               *dotdot = 0;
> +               if (!*left)
> +                       left = "HEAD";
> +               if (!*right)
> +                       right = "HEAD";
> +
> +               /* git merge-base --all $left $right */
> +               left_commit = lookup_commit_reference_by_name(left);
> +               right_commit = lookup_commit_reference_by_name(right);
> +               if (!left_commit || !right_commit)
> +                       die(_("%s: there is no merge base"), _onto_name);

_onto_name seems to be different than what was passed in,
because of *dotdot = 0 before, is that intentional?

> +
> +               merge_bases = get_merge_bases(left_commit, right_commit);
> +               if (merge_bases && merge_bases->next)
> +                       die(_("%s: there are more than one merge bases"), _onto_name);
> +               else if (!merge_bases)
> +                       die(_("%s: there is no merge base"), _onto_name);
> +
> +               onto_commit = merge_bases->item;
> +               free_commit_list(merge_bases);
> +       } else {
> +               onto_commit = lookup_commit_reference_by_name(onto_name);
> +               if (!onto_commit)
> +                       die(_("invalid upstream %s"), onto_name);
> +       }
> +
> +       free(onto_name);
> +       oidcpy(onto, &onto_commit->object.oid);
> +}
>
>  static int git_rebase_config(const char *k, const char *v, void *cb)
>  {
> @@ -12,20 +118,96 @@ static int git_rebase_config(const char *k, const char *v, void *cb)
>
>  int cmd_rebase(int argc, const char **argv, const char *prefix)
>  {
> +       struct rebase_options rebase_opts;
> +       const char *onto_name = NULL;
> +       const char *branch_name;
> +
>         const char * const usage[] = {
> -               N_("git rebase [options]"),
> +               N_("git rebase [options] [--onto <newbase>] [<upstream>] [<branch>]"),
>                 NULL
>         };
>         struct option options[] = {
> +               OPT_GROUP(N_("Available options are")),
> +               OPT_STRING(0, "onto", &onto_name, NULL,
> +                       N_("rebase onto given branch instead of upstream")),
>                 OPT_END()
>         };
>
>         git_config(git_rebase_config, NULL);
> +       rebase_options_init(&rebase_opts);
> +       rebase_opts.resolvemsg = _("\nWhen you have resolved this problem, run \"git rebase --continue\".\n"
> +                       "If you prefer to skip this patch, run \"git rebase --skip\" instead.\n"
> +                       "To check out the original branch and stop rebasing, run \"git rebase --abort\".");
>
>         argc = parse_options(argc, argv, prefix, options, usage, 0);
>
>         if (read_cache_preload(NULL) < 0)
>                 die(_("failed to read the index"));
>
> +       /*
> +        * Parse command-line arguments:
> +        *    rebase [<options>] [<upstream_name>] [<branch_name>]
> +        */
> +
> +       /* Parse <upstream_name> into rebase_opts.upstream */
> +       {
> +               const char *upstream_name;
> +               if (argc > 2)
> +                       usage_with_options(usage, options);
> +               if (!argc) {
> +                       upstream_name = get_curr_branch_upstream_name();
> +               } else {
> +                       upstream_name = argv[0];
> +                       argv++, argc--;
> +                       if (!strcmp(upstream_name, "-"))
> +                               upstream_name = "@{-1}";
> +               }

So first we extract the upstream_name then branch_name,
so the parsing is rather

     rebase [<options>] [<upstream_name> [<branch_name>]]

i.e. you cannot give branch name only?

> +               if (get_oid_commit(upstream_name, &rebase_opts.upstream))
> +                       die(_("invalid upstream %s"), upstream_name);
> +               if (!onto_name)
> +                       onto_name = upstream_name;
> +       }
> +
> +       /*
> +        * Parse --onto <onto_name> into rebase_opts.onto and
> +        * rebase_opts.onto_name
> +        */
> +       get_onto_oid(onto_name, &rebase_opts.onto);
> +       rebase_opts.onto_name = xstrdup(onto_name);
> +
> +       /*
> +        * Parse <branch_name> into rebase_opts.orig_head and
> +        * rebase_opts.orig_refname
> +        */
> +       branch_name = argv[0];
> +       if (branch_name) {
> +               /* Is branch_name a branch or commit? */
> +               char *ref_name = xstrfmt("refs/heads/%s", branch_name);
> +               struct object_id orig_head_id;
> +
> +               if (!read_ref(ref_name, orig_head_id.hash)) {
> +                       rebase_opts.orig_refname = ref_name;
> +                       if (get_oid_commit(ref_name, &rebase_opts.orig_head))
> +                               die("get_sha1_commit failed");
> +               } else if (!get_oid_commit(branch_name, &rebase_opts.orig_head)) {
> +                       rebase_opts.orig_refname = NULL;
> +                       free(ref_name);
> +               } else {
> +                       die(_("no such branch: %s"), branch_name);
> +               }
> +       } else {
> +               /* Do not need to switch branches, we are already on it */
> +               struct branch *curr_branch = branch_get("HEAD");
> +
> +               if (curr_branch)
> +                       rebase_opts.orig_refname = xstrdup(curr_branch->refname);
> +               else
> +                       rebase_opts.orig_refname = NULL;
> +
> +               if (get_oid_commit("HEAD", &rebase_opts.orig_head))
> +                       die(_("Failed to resolve '%s' as a valid revision."), "HEAD");
> +       }
> +
> +       rebase_options_release(&rebase_opts);
>         return 0;
>  }
> diff --git a/rebase-common.c b/rebase-common.c
> new file mode 100644
> index 0000000..5a49ac4
> --- /dev/null
> +++ b/rebase-common.c
> @@ -0,0 +1,28 @@
> +#include "cache.h"
> +#include "rebase-common.h"
> +
> +void rebase_options_init(struct rebase_options *opts)
> +{
> +       oidclr(&opts->onto);
> +       opts->onto_name = NULL;
> +
> +       oidclr(&opts->upstream);
> +
> +       oidclr(&opts->orig_head);
> +       opts->orig_refname = NULL;
> +
> +       opts->resolvemsg = NULL;
> +}
> +
> +void rebase_options_release(struct rebase_options *opts)
> +{
> +       free(opts->onto_name);
> +       free(opts->orig_refname);
> +}
> +
> +void rebase_options_swap(struct rebase_options *dst, struct rebase_options *src)
> +{
> +       struct rebase_options tmp = *dst;
> +       *dst = *src;
> +       *src = tmp;
> +}
> diff --git a/rebase-common.h b/rebase-common.h
> new file mode 100644
> index 0000000..db5146a
> --- /dev/null
> +++ b/rebase-common.h
> @@ -0,0 +1,23 @@
> +#ifndef REBASE_COMMON_H
> +#define REBASE_COMMON_H
> +
> +/* common rebase backend options */
> +struct rebase_options {
> +       struct object_id onto;
> +       char *onto_name;
> +
> +       struct object_id upstream;
> +
> +       struct object_id orig_head;
> +       char *orig_refname;
> +
> +       const char *resolvemsg;
> +};
> +
> +void rebase_options_init(struct rebase_options *);
> +
> +void rebase_options_release(struct rebase_options *);
> +
> +void rebase_options_swap(struct rebase_options *dst, struct rebase_options *src);
> +
> +#endif /* REBASE_COMMON_H */
> --
> 2.7.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]