git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: 胡哲宁 <adlternative@gmail.com>
To: Junio C Hamano <gitster@pobox.com>
Cc: ZheNing Hu via GitGitGadget <gitgitgadget@gmail.com>,
	Git List <git@vger.kernel.org>, Denton Liu <liu.denton@gmail.com>,
	David Aguilar <davvid@gmail.com>,
	John Keeping <john@keeping.me.uk>,
	Ryan Zoeller <rtzoeller@rtzoeller.com>
Subject: Re: [PATCH v3] difftool.c: learn a new way start from specified file
Date: Thu, 11 Feb 2021 01:00:41 +0800	[thread overview]
Message-ID: <CAOLTT8QbutZ2pHZ7Zg7vEJAy=d66YKP12rVW=RSJV+8fH6RRMw@mail.gmail.com> (raw)
In-Reply-To: <xmqqeehp2jis.fsf@gitster.c.googlers.com>

Junio C Hamano <gitster@pobox.com> 于2021年2月10日周三 上午2:17写道:
>
> "ZheNing Hu via GitGitGadget" <gitgitgadget@gmail.com> writes:
>
> > diff --git a/Documentation/git-difftool.txt b/Documentation/git-difftool.txt
> > index 484c485fd06c..552be097dfea 100644
> > --- a/Documentation/git-difftool.txt
> > +++ b/Documentation/git-difftool.txt
> > @@ -34,6 +34,9 @@ OPTIONS
> >       This is the default behaviour; the option is provided to
> >       override any configuration settings.
> >
> > +--start-from::
> > +     Start viewing diff from the specified file.
> > +
>
> There is nothing that specifies a file in the above ;-)
>
>         --start-from=<file>::
>                 Start viewing ...
>
> This is even more important as SYNOPSIS section of this manual page
> does not duplicate the list of options and their arguments.
>
I admit my mistake,thanks for reminding.
> >  -t <tool>::
> >  --tool=<tool>::
> >       Use the diff tool specified by <tool>.  Valid values include
>
> There are many things I dislike about this patch, but do not take it
> as a personal attack.  I'll try to suggest an alternative at the end,
> but read along.
>
Of course I humbly accepted your criticism,I am only a sophomore in
university after all,It is extremely important to listen to your suggestions.
> > diff --git a/builtin/difftool.c b/builtin/difftool.c
> > index 6e18e623fddf..67d2befa1210 100644
> > --- a/builtin/difftool.c
> > +++ b/builtin/difftool.c
> > @@ -690,7 +690,7 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
> >  {
> >       int use_gui_tool = 0, dir_diff = 0, prompt = -1, symlinks = 0,
> >           tool_help = 0, no_index = 0;
> > -     static char *difftool_cmd = NULL, *extcmd = NULL;
> > +     static char *difftool_cmd = NULL, *extcmd = NULL, *start_file = NULL;
> >       struct option builtin_difftool_options[] = {
> >               OPT_BOOL('g', "gui", &use_gui_tool,
> >                        N_("use `diff.guitool` instead of `diff.tool`")),
> > @@ -714,6 +714,8 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
> >               OPT_STRING('x', "extcmd", &extcmd, N_("command"),
> >                          N_("specify a custom command for viewing diffs")),
> >               OPT_ARGUMENT("no-index", &no_index, N_("passed to `diff`")),
> > +             OPT_STRING(0, "start-from", &start_file, N_("start-from"),
> > +                        N_("start viewing diff from the specified file")),
> >               OPT_END()
> >       };
>
> This may be a good UI to "difftool".
>
> > @@ -724,6 +726,9 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
> >                            builtin_difftool_usage, PARSE_OPT_KEEP_UNKNOWN |
> >                            PARSE_OPT_KEEP_DASHDASH);
> >
> > +     if (start_file)
> > +             setenv("START_FILE", start_file, 1);
>
> Unacceptable.  Nothing gives Git the right to squat on such a
> generic name, and there is no hint that it is used to specify the
> starting point of what operation.  In addition, I do not see a good
> reason why we need to use an environment variable in the first
> place.  We run "diff" as an external process, with GIT_EXTERNAL_DIFF
> environment pointing back at us, no?  This information should be
> passed from its command line.
Sure,I using environment variables originally hoped can be used in
git--difftool-helper.sh, later I found it was not easy to deal with, so I am
attempt to use environment variables to transmit information in
`run_external_diff`,now it seems that this method is not very good ...
>
> > diff --git a/diff.c b/diff.c
> > index 69e3bc00ed8f..cdad26f99063 100644
> > --- a/diff.c
> > +++ b/diff.c
> > @@ -4249,6 +4249,7 @@ static void run_external_diff(const char *pgm,
> >                             const char *xfrm_msg,
> >                             struct diff_options *o)
> >  {
> > +     const char *start_file = NULL;
> >       struct strvec argv = STRVEC_INIT;
> >       struct strvec env = STRVEC_INIT;
> >       struct diff_queue_struct *q = &diff_queued_diff;
> > @@ -4272,9 +4273,17 @@ static void run_external_diff(const char *pgm,
> >
> >       diff_free_filespec_data(one);
> >       diff_free_filespec_data(two);
> > +
> > +     start_file = xstrdup_or_null(getenv("START_FILE"));
> > +     if (start_file) {
> > +             if (strcmp(start_file, name))
> > +                     goto finish;
> > +             unsetenv("START_FILE");
> > +     }
>
> Again, an unacceptable "hack".  "start the diff output showing from
> this path" would plausibly a good feature even outside the scope of
> "difftool", and the feature should not be limited to the external
> diff interface.  More on this later.
What you said `from its command line` is using `git difftool
--rotate-to=<file>`,
Maybe I didn’t know whether to deal with the subcommands of `diff`, now I admit
that passing from parameters is better than passing environment variables.
>
> >       if (run_command_v_opt_cd_env(argv.v, RUN_USING_SHELL, NULL, env.v))
> >               die(_("external diff died, stopping at %s"), name);
> >
> > +finish:
> >       remove_tempfile();
> >       strvec_clear(&argv);
> >       strvec_clear(&env);
> > diff --git a/diff.h b/diff.h
> > index 2ff2b1c7f2ca..f67c43f5af95 100644
> > --- a/diff.h
> > +++ b/diff.h
> > @@ -86,18 +86,18 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
> >
> >  typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data);
> >
> > -#define DIFF_FORMAT_RAW              0x0001
> > -#define DIFF_FORMAT_DIFFSTAT 0x0002
> > -#define DIFF_FORMAT_NUMSTAT  0x0004
> > -#define DIFF_FORMAT_SUMMARY  0x0008
> > -#define DIFF_FORMAT_PATCH    0x0010
> > -#define DIFF_FORMAT_SHORTSTAT        0x0020
> > -#define DIFF_FORMAT_DIRSTAT  0x0040
> > +#define DIFF_FORMAT_RAW              (1U<<0)
> > +#define DIFF_FORMAT_DIFFSTAT (1U<<1)
> > +#define DIFF_FORMAT_NUMSTAT  (1U<<2)
> > +#define DIFF_FORMAT_SUMMARY  (1U<<3)
> > +#define DIFF_FORMAT_PATCH    (1U<<4)
> > +#define DIFF_FORMAT_SHORTSTAT        (1U<<5)
> > +#define DIFF_FORMAT_DIRSTAT  (1U<<6)
> >
> >  /* These override all above */
> > -#define DIFF_FORMAT_NAME     0x0100
> > -#define DIFF_FORMAT_NAME_STATUS      0x0200
> > -#define DIFF_FORMAT_CHECKDIFF        0x0400
> > +#define DIFF_FORMAT_NAME     (1U<<8)
> > +#define DIFF_FORMAT_NAME_STATUS      (1U<<9)
> > +#define DIFF_FORMAT_CHECKDIFF        (1U<<10)
>
> Do we need these changes for the new feature to work?
It has no effect on this new feature. I should put this modification
in an additional commit, right?
>
> I also find this "skip and discard ones earlier than the given path"
> makes the utility of the feature artificially narrower than needed,
> when I imagine how else this feature, or a variant of it, would be
> useful in other situations.  For example, consider that there are 5
> paths, and you've seen 3 of them so far before you went off, so you
> are restarting from 4th file.  But wouldn't it be more useful, after
> showing the 4th and 5th file, if the tool wraps around to show 1st,
> 2nd, and 3rd file if the user kept going?
>
Well, it would be better if you can see 1st, 2nd, 3rd again.
> I suspect that this feature fits the overall system much better if
> it is implemented as a new step in the diffcore transformation.  The
> way our diff subsystem works is roughly:
>
>  * The front-end "diff", "diff-files", "diff-index" and "diff-trees"
>    are given two "tree like things" to compare, and feeds bunch of
>    <old, new> tuples to the diff internal machinery.  Each of these
>    tuples are called "filepairs", and a "pair" has two "filespecs",
>    one describing the contents, the mode, and the path in the "old"
>    side of the "tree like things", the other describing the
>    contents, the mode, and the path in the "new" side of the "tree
>    like things".
>
>  * The series of filepairs are given to the "diffcore" machinery,
>    where they may be broken (i.e. a filepair that says that the old
>    contents of "hello.txt" was X, and the new contents of
>    "hello.txt" is Y, may become two filepairs, one that says that
>    the "hello.txt" file with contents X used to be in the old tree
>    but there is nothing corresponding to it in the new tree, and the
>    other says that a new "hello.txt" with contents Y appeared
>    without corresponding thing in the old tree), matched (i.e. there
>    may originally be two filepairs, one that says path A.txt appears
>    on the old side but disappeared on the new side, and the other
>    that says path sub/A.txt did not exist on the old side but
>    appears on the new side---these two filepairs may be merged to
>    express "A.txt on the old side got renamed to sub/A.txt on the
>    new side"), etc.
>
Thank you for these interpretations on "git diff" for me, it will help me
understand its principle.
>  * The set of filepairs processed in the "diffcore" machinery is
>    given to the backend and each filepair will be shown in the
>    output, as a series of patches, a diffstat, etc.
>
> There is a step in the "diffcore" machinery called "diffcore-order".
> The front-ends all feed the filepairs alphabetically to the
> "diffcore" machinery, but "diffcore-order" can reorder them, so that
> the original order that the "git diff HEAD --" frontend found the
> changes may be to "hello.c" and then "hello.h" (because .c sorts
> before .h), but the users can specify with the "-O<orderfile>"
> option that they want to view the header files before the source
> files.  When the set of filepairs exits the diffcore machinery, the
> original "hello.c" then "hello.h" order may get modified to show
> "hello.h" then "hello.c".  It probably is the simplest to model this
> new feature after how "diffcore-order" does it.
>
Very magical "diffcore-order", when I was looking for a solution yesterday,
I noticed that these functions similar to "diffcore_apply_filter" in
"diffcore_std",
> So, perhaps we can introduce a new "diffcore-rotate" step, where the
> filepairs before the specified location are rotated out to the end
> of the filepairs?
>
Awesome idea. In this way, `difftool --rotate-to=<file>` can call
`diff --rotate-to=<file>` , user can choose the starting file, and they can
also see previous files.
> The following is just a quick draft that is only lightly tested by
> running itself with the "--rotate-to=diffcore-rotate.c" option, but
> it should be sufficient to get you started.  You should add a way to
> diagnose that the name given to the "--start-file" option actually
> exists in the diff on the "difftool" side, because a path that does
> not exist in the patch with the following code is simply ignored
> (and that is very much deliberate, because we do not want it to die
> while running "git log -p --rotate-to=X" where some changes may or
> may not touch X).
Yes, I want to know why being so cautious in git log?If the file name is
wrong, why can't I make it exit? :)
>
>
> $ ./git diff --stat -p --rotate-to=diffcore-rotate.c HEAD
>
>  diffcore-rotate.c | 35 +++++++++++++++++++++++++++++++++++
>  diffcore.h        |  1 +
>  Makefile          |  1 +
>  diff.c            |  4 ++++
>  diff.h            |  1 +
>  5 files changed, 42 insertions(+)
>
> diff --git c/diffcore-rotate.c w/diffcore-rotate.c
> new file mode 100644
> index 0000000000..0d17901616
> --- /dev/null
> +++ w/diffcore-rotate.c
> @@ -0,0 +1,35 @@
> +/*
> + * Copyright (C) 2021, Google LLC.
> + * Based on diffcore-order.c, which is Copyright (C) 2005, Junio C Hamano
> + */
> +#include "cache.h"
> +#include "diff.h"
> +#include "diffcore.h"
> +
> +void diffcore_rotate(const char *rotate_to_filename)
> +{
> +       struct diff_queue_struct *q = &diff_queued_diff;
> +       struct diff_queue_struct outq;
> +       int rotate_to, i;
> +
> +       if (!q->nr)
> +               return;
> +
> +       for (i = 0; i < q->nr; i++)
> +               if (!strcmp(rotate_to_filename, q->queue[i]->two->path))
> +                       break;
> +       /* we did not find the specified path */
> +       if (q->nr <= i)
> +               return;
> +
> +       DIFF_QUEUE_CLEAR(&outq);
> +       rotate_to = i;
> +
> +       for (i = rotate_to; i < q->nr; i++)
> +               diff_q(&outq, q->queue[i]);
> +       for (i = 0; i < rotate_to; i++)
> +               diff_q(&outq, q->queue[i]);
> +
> +       free(q->queue);
> +       *q = outq;
> +}
> diff --git c/diffcore.h w/diffcore.h
> index d2a63c5c71..bd5959375b 100644
> --- c/diffcore.h
> +++ w/diffcore.h
> @@ -164,6 +164,7 @@ void diffcore_rename(struct diff_options *);
>  void diffcore_merge_broken(void);
>  void diffcore_pickaxe(struct diff_options *);
>  void diffcore_order(const char *orderfile);
> +void diffcore_rotate(const char *rotate_to_filename);
>
>  /* low-level interface to diffcore_order */
>  struct obj_order {
> diff --git c/Makefile w/Makefile
> index b797033c58..031b0b88e6 100644
> --- c/Makefile
> +++ w/Makefile
> @@ -869,6 +869,7 @@ LIB_OBJS += diffcore-delta.o
>  LIB_OBJS += diffcore-order.o
>  LIB_OBJS += diffcore-pickaxe.o
>  LIB_OBJS += diffcore-rename.o
> +LIB_OBJS += diffcore-rotate.o
>  LIB_OBJS += dir-iterator.o
>  LIB_OBJS += dir.o
>  LIB_OBJS += editor.o
> diff --git c/diff.c w/diff.c
> index 69e3bc00ed..90a8d5abd0 100644
> --- c/diff.c
> +++ w/diff.c
> @@ -5599,6 +5599,8 @@ static void prep_parse_options(struct diff_options *options)
>                           DIFF_PICKAXE_REGEX, PARSE_OPT_NONEG),
>                 OPT_FILENAME('O', NULL, &options->orderfile,
>                              N_("control the order in which files appear in the output")),
> +               OPT_STRING(0, "rotate-to", &options->rotate_to, N_("<path>"),
> +                          N_("show the change in the specified path first")),
>                 OPT_CALLBACK_F(0, "find-object", options, N_("<object-id>"),
>                                N_("look for differences that change the number of occurrences of the specified object"),
>                                PARSE_OPT_NONEG, diff_opt_find_object),
> @@ -6669,6 +6671,8 @@ void diffcore_std(struct diff_options *options)
>                 diffcore_pickaxe(options);
>         if (options->orderfile)
>                 diffcore_order(options->orderfile);
> +       if (options->rotate_to)
> +               diffcore_rotate(options->rotate_to);
>         if (!options->found_follow)
>                 /* See try_to_follow_renames() in tree-diff.c */
>                 diff_resolve_rename_copy();
> diff --git c/diff.h w/diff.h
> index 2ff2b1c7f2..0801469e63 100644
> --- c/diff.h
> +++ w/diff.h
> @@ -226,6 +226,7 @@ enum diff_submodule_format {
>   */
>  struct diff_options {
>         const char *orderfile;
> +       const char *rotate_to;
>
>         /**
>          * A constant string (can and typically does contain newlines to look for

Thanks for the patch!
After that, there was too little work I could do,do i just need to add
the following
 code in `diff_flush_patch_all_file_pairs`?
if (o->rotate_to && q->nr && strcmp(q->queue[0]->one->path, o->rotate_to) &&
strcmp(q->queue[0]->one->path, o->rotate_to)) {
    error(_("could not find start file name '%s'"), o->rotate_to);
        return;
}
In addition, Do I need to do the documentation and tests related to
your `diff --rotate-to`?
If so, I will finish it, otherwise, I will only do corresponding tests
for `git difftool --rotate-to`.

Thank you Junio, you are like a tireless teacher,

Happy Chinese New Year!

--
ZheNing Hu

  reply	other threads:[~2021-02-10 17:00 UTC|newest]

Thread overview: 37+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-02-07 15:19 [PATCH] git-difftool-helper.sh: learn a new way skip to save point 阿德烈 via GitGitGadget
2021-02-07 18:29 ` Junio C Hamano
2021-02-07 19:04 ` Junio C Hamano
2021-02-08  8:06   ` 胡哲宁
2021-02-08 17:02 ` [PATCH v2] git-difftool-helper.sh: learn a new way go back to last " ZheNing Hu via GitGitGadget
2021-02-08 20:13   ` Junio C Hamano
2021-02-08 22:15   ` David Aguilar
2021-02-08 23:34     ` Junio C Hamano
2021-02-09  6:19       ` 胡哲宁
2021-02-09  6:04     ` 胡哲宁
2021-02-09 15:30   ` [PATCH v3] difftool.c: learn a new way start from specified file ZheNing Hu via GitGitGadget
2021-02-09 18:17     ` Junio C Hamano
2021-02-10 17:00       ` 胡哲宁 [this message]
2021-02-10 18:16         ` Junio C Hamano
2021-02-10 20:05           ` Junio C Hamano
2021-02-14  7:53           ` ZheNing Hu
2021-02-14 13:09     ` [PATCH v4] difftool.c: learn a new way start at " ZheNing Hu via GitGitGadget
2021-02-16  1:46       ` Junio C Hamano
2021-02-16 12:56       ` [PATCH v5 0/2] " ZheNing Hu via GitGitGadget
2021-02-16 12:56         ` [PATCH v5 1/2] diff: --{rotate,skip}-to=<path> Junio C Hamano via GitGitGadget
2021-02-16 12:56         ` [PATCH v5 2/2] difftool.c: learn a new way start at specified file ZheNing Hu via GitGitGadget
2021-02-17 10:31           ` Junio C Hamano
2021-02-17 16:18             ` ZheNing Hu
2021-02-16 18:45         ` [PATCH v5 0/2] " Junio C Hamano
2021-02-17  4:12           ` ZheNing Hu
2021-02-17 11:14             ` Denton Liu
2021-02-17 11:40               ` ZheNing Hu
2021-02-17 18:56                 ` Junio C Hamano
2021-02-18  5:20                   ` ZheNing Hu
2021-02-18 15:04                   ` ZheNing Hu
2021-02-18 19:11                     ` Junio C Hamano
2021-02-19 10:59                       ` ZheNing Hu
2021-02-25 11:08                   ` Johannes Schindelin
2021-02-25 19:01                     ` Junio C Hamano
2021-02-19 12:53         ` [PATCH v6] " ZheNing Hu via GitGitGadget
2021-02-22 15:11           ` ZheNing Hu
2021-02-22 21:40           ` Junio C Hamano

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='CAOLTT8QbutZ2pHZ7Zg7vEJAy=d66YKP12rVW=RSJV+8fH6RRMw@mail.gmail.com' \
    --to=adlternative@gmail.com \
    --cc=davvid@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=gitgitgadget@gmail.com \
    --cc=gitster@pobox.com \
    --cc=john@keeping.me.uk \
    --cc=liu.denton@gmail.com \
    --cc=rtzoeller@rtzoeller.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).