git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [GSoC][PATCH v2 0/5] submodule: port subcommand 'summary' from shell to C
@ 2020-08-06 16:40 Shourya Shukla
  2020-08-06 16:40 ` [PATCH v2 1/5] submodule: expose the '--for-status' option of summary Shourya Shukla
                   ` (5 more replies)
  0 siblings, 6 replies; 32+ messages in thread
From: Shourya Shukla @ 2020-08-06 16:40 UTC (permalink / raw)
  To: git
  Cc: gitster, christian.couder, kaartic.sivaraam, johannes.schindelin,
	liu.denton, Shourya Shukla

Greetings,

After a long list of changes proposed by Johannes in the v1, here I
present the v2 of the patch series porting 'summary' from shell to C.
Apart from requested changes in 'submodule--helper.c' and dropping the
commit changing the scope of the 'count_lines()' function, there are
two new commits: one documents the 'for-status' option and one
introduces the test 't7421-submodule-summary-add.sh'. Link to the v1:
https://lore.kernel.org/git/20200702192409.21865-1-shouryashukla.oo@gmail.com/

The reasons for the introduction of the test are covered in a previous
patch series delivered by me last night regarding t7401:
https://lore.kernel.org/git/20200805174921.16000-1-shouryashukla.oo@gmail.com/

But I will touch upon it again in this patch. The test script
't7401-submodule-summary.sh' adds submodules using 'git add' instead of
'git submodule add'. Therefore, some commands such as
'git submodule init' and 'git submodule deinit' do not give the desired
effects since there is no .gitmodules file. Note that, 'summary'
works without issues as it does not depend on the existence of
.gitmodules.

I will tag along the 'range-diff' between this v2 and the v1 at the end
of the cover letter for ease of review.

The changelog:

- Introduce commit [PATCH 1/5] 4f0e4f6ace (submodule: document the
  '--for-status' option, 2020-08-02).

- Improve commit message of [PATCH 2/5] 6e42cc99a7 (submodule: remove
  extra line feeds between callback struct and macro, 2020-06-25).

- Drop commit (Previously [PATCH 3/4]) 745f2bd92e (diff: change scope
  of the function count_lines(), 2017-07-11).

- Introduce commit [PATCH 4/5] ef4cf4be0f (t7421: introduce a test
  script for verifying 'summary' output, 2020-07-23).

- Improve the commit [PATCH 5/5] 331cc1a487 (submodule: port submodule
  subcommand 'summary' from shell to C, 2017-07-11) which includes:
    -> Improve the commit message, focusing more on the specialities of
       the commit rather than describing the flow of the subcommand.

    -> Rename the function 'verify_submodule_object_name()' to
       'verify_submodule_committish()' as the latter is more accurate.
       While at it, rename its argument 'const char *sha1' to 'const
       char *committish' since Git is shifting to SHA256 (covered in the
       patch series by Brian M. Carlson) and we look for a committish in
       the 'rev-parse' called in the function. Also, pass a '--short' in
       the call to 'rev-parse' as this option does the job of '--verify'
       and also shortens the hash the way we desire.

    -> Reuse the hash obtained from the function
      'verify_submodule_committish()' instead of incorrectly calling
      'find_unique_abbrev()' multiple times. Also eliminate the usage
      of 'oid_to_hex()' and instead reuse the aforementioned hash.

    -> Eliminate the variable 'is_sm_git_dir' and therefore, simplify
       the checks involving it since the GIT_DIR is already set to
       '.git/' and therefore we need to check it again.

    -> Eliminate the NEEDSWORK in 'generate_submodule_summary()' and
       instead call the function 'refs_head_ref()' (successor of
       'head_ref_submodule()').

    -> Use 'index_fd()' instead of spawning a process to run 'git
       hash-object' to find the 'oid_dst' in case of a regular file or
       a symbolic link.

    -> In case of an unexpected file mode, give a warning() and continue
       instead of die()ing.

    -> Simplify the 'range' computation by cuddling up a couple of lines
       and thus removing the 'range' variable since it will be useless.
       The 'range' is the range of commits whose summary will be
       computed.

    -> Eliminate the usage of 'count_lines()' since 'git rev-list
       --count' does the job anyway.

    -> Compute the error messages in 'generate_submodule_summary()'
       itself instead of 'print_submodule_summary()' and therefore, pass
       the err_msg into the latter as a 'char *'.

    -> Simplify the if-statement in case of 'for-status' by cuddling up
       various lines and thus removing one whole level of indentation.

    -> Remove the OPT__QUIET from the option struct since 'summary' does
       not support 'quiet'.

    -> Decapitalise the first letter of the option descriptions since
       the guidelines mention so.

    -> Instead of spawning a child process, use
       'is_nonbare_repository_dir()' to check if the submodules are
       checked out or not.

    -> Call 'get_oid()' to find the hash of 'HEAD' instead of spawning a
       process to call 'rev-parse' in 'module_summary()'. While at it,
       simplify the set of if-else statements which fetch the revision
       of the superproject to calculate the summary of.

    -> Pass the revision computed above to
       'compute_summary_module_list()' as 'struct object_id *' instead of
       passing it as a string.

There is a behavioural difference between the shell and the C version.
The shell version printed two newlines at the end of the output
when executed outside the tests but one newline when executed in the
tests. On the other hand, the C version printed only one newline
irrespective of where it was executed. This is due to the difference
in the way they call 'git log'. The shell version does a
'--pretty=format:<fmt>' whereas the C version does a '--pretty=<fmt>'.
The latter is indirectly a '--pretty=tformat:<fmt>' which causes the
difference between the two.

Also, when we try to pass an option-like argument after a non-option
argument:

	git submodule summary HEAD --foo-bar

That argument would be treated like a path to the submodule for which
the user is requesting a summary. So, the option ends up having no
effect. Though, passing `--quiet` is an exception to this:

	git submodule summary HEAD --quiet

While 'summary' doesn't support '--quiet', we don't get an output for
the above command as '--quiet' is treated as a path which means we get
an output only if a submodule whose path is '--quiet' exists.

Also, I want to ask a couple of things:

	1.Whether we can suppress the error message that we get when
	  trying to find the summary of non-existent submodules?
	  For example:

	  fatal: exec 'rev-parse': cd to 'my-subm' failed: No such file or directory
	   * my-subm 35b40f1...0000000:

	 Will it be OK to suppress the above error message?

	2.Is it fine to document and expose the 'for-status' option in
	  'git-submodule.txt'?

Feedback and reviews are appreciated.

Regards,
Shourya Shukla
-----

-:  ---------- > 1:  c063390a94 submodule: expose the '--for-status' option of summary
1:  add55c1d22 ! 2:  561d03351b submodule: amend extra line feed between callback struct and macro
    @@ Metadata
     Author: Shourya Shukla <shouryashukla.oo@gmail.com>
     
      ## Commit message ##
    -    submodule: amend extra line feed between callback struct and macro
    +    submodule: remove extra line feeds between callback struct and macro
     
    -    All subcommands of 'git submodule' using a callback mechanism had
    -    absence of an extra linefeed between their callback structs and
    -    macros. Subcommands 'init', 'status' and 'sync' did not follow suit.
    -    Amend the extra line feed.
    +    Many `submodule--helper` subcommands follow the convention that a struct
    +    defines their callback data, and the declaration of that struct is
    +    followed immediately by a macro to use in static initializers, without
    +    any separating empty line.
    +
    +    Let's align the `init`, `status` and `sync` subcommands with that convention.
     
         Mentored-by: Christian Couder <chriscool@tuxfamily.org>
         Mentored-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com>
    +    Helped-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
    +    Helped-by: Philip Oakley <philipoakley@iee.email>
         Signed-off-by: Shourya Shukla <shouryashukla.oo@gmail.com>
     
      ## builtin/submodule--helper.c ##
2:  75c986a5e0 = 3:  e0e0c3ba4b submodule: rename helper functions to avoid ambiguity
3:  745f2bd92e < -:  ---------- diff: change scope of the function count_lines()
-:  ---------- > 4:  ec6e6c9e64 t7421: introduce a test script for verifying 'summary' output
4:  c5baf68ecf ! 5:  9222b4168b submodule: port submodule subcommand 'summary' from shell to C
    @@ Metadata
      ## Commit message ##
         submodule: port submodule subcommand 'summary' from shell to C
     
    -    The submodule subcommand 'summary' is ported in the process of
    -    making git-submodule a builtin. The function cmd_summary() from
    -    git-submodule.sh is ported to functions module_summary(),
    -    compute_summary_module_list(), prepare_submodule_summary() and
    -    generate_submodule_summary(), print_submodule_summary().
    +    Convert submodule subcommand 'summary' to a builtin and call it via
    +    'git-submodule.sh'.
     
    -    The first function module_summary() parses the options of submodule
    -    subcommand and also acts as the front-end of this subcommand.
    -    After parsing them, it calls the compute_summary_module_list()
    +    The shell version had to call $diff_cmd twice, once to find the modified
    +    modules cared by the user and then again, with that list of modules
    +    to do various operations for computing the summary of those modules.
    +    On the other hand, the C version does not need a second call to
    +    $diff_cmd since it reuses the module list from the first call to do the
    +    aforementioned tasks.
     
    -    The functions compute_summary_module_list() runs the diff_cmd,
    -    and generates the modules list, as required by the subcommand.
    -    The generation of this module list is done by the using the
    -    callback function submodule_summary_callback(), and stored in the
    -    structure module_cb.
    +    In the C version, we use the combination of setting a child process'
    +    working directory to the submodule path and then calling
    +    'prepare_submodule_repo_env()' which also sets the 'GIT_DIR' to '.git',
    +    so that we can be certain that those spawned processes will not access
    +    the superproject's ODB by mistake.
     
    -    Once the module list is generated, prepare_submodule_summary()
    -    further goes through the list and filters the list, for
    -    eventually calling the generate_submodule_summary() function.
    +    A behavioural difference between the C and the shell version is that the
    +    shell version outputs two line feeds after the 'git log' output when run
    +    outside of the tests while the C version outputs one line feed in any
    +    case. The reason for this is that the shell version calls log with
    +    '--pretty=format:<fmt>' whose output is followed by two echo
    +    calls; 'format' does not have "terminator" semantics like its 'tformat'
    +    counterpart. So, the log output is terminated by a newline only when
    +    invoked by the user and not when invoked from the scripts. This results
    +    in the one & two line feed differences in the shell version.
    +    On the other hand, the C version calls log with '--pretty=<fmt>'
    +    which is equivalent to '--pretty:tformat:<fmt>' which is then
    +    followed by a 'printf("\n")'. Due to its "terminator" semantics the
    +    log output is always terminated by newline and hence one line feed in
    +    any case.
     
    -    The function generate_submodule_summary() takes care of generating
    -    the summary for each submodule and then calls the function
    -    print_submodule_summary() for printing it.
    +    Also, when we try to pass an option-like argument after a non-option
    +    argument, for instance:
     
    -    Mentored-by: Christian Couder <christian.couder@gmail.com>
    -    Mentored-by: Stefan Beller <sbeller@google.com>
    +        git submodule summary HEAD --foo-bar
    +
    +        (or)
    +
    +        git submodule summary HEAD --cached
    +
    +    That argument would be treated like a path to the submodule for which
    +    the user is requesting a summary. So, the option ends up having no
    +    effect. Though, passing '--quiet' is an exception to this:
    +
    +        git submodule summary HEAD --quiet
    +
    +    While 'summary' doesn't support '--quiet', we don't get an output for
    +    the above command as '--quiet' is treated as a path which means we get
    +    an output only if a submodule whose path is '--quiet' exists.
    +
    +    The error message in case of computing a summary for non-existent
    +    submodules in the C version is different from that of the shell version.
    +    Since the new error message is not marked for translation, change the
    +    'test_i18ngrep' in t7421.4 to 'grep'.
    +
    +    Mentored-by: Christian Couder <chriscool@tuxfamily.org>
    +    Mentored-by: Stefan Beller <stefanbeller@gmail.com>
         Mentored-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com>
    +    Helped-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
         Signed-off-by: Prathamesh Chavan <pc44800@gmail.com>
         Signed-off-by: Shourya Shukla <shouryashukla.oo@gmail.com>
     
    @@ builtin/submodule--helper.c: static int module_name(int argc, const char **argv,
     +  const char *prefix;
     +  unsigned int cached: 1;
     +  unsigned int for_status: 1;
    -+  unsigned int quiet: 1;
     +  unsigned int files: 1;
     +  int summary_limit;
     +};
    -+#define SUMMARY_CB_INIT { 0, NULL, NULL, 0, 0, 0, 0, 0 }
    ++#define SUMMARY_CB_INIT { 0, NULL, NULL, 0, 0, 0, 0 }
     +
     +enum diff_cmd {
     +  DIFF_INDEX,
     +  DIFF_FILES
     +};
     +
    -+static int verify_submodule_object_name(const char *sm_path,
    -+                                    const char *sha1)
    ++static char* verify_submodule_committish(const char *sm_path,
    ++                                   const char *committish)
     +{
     +  struct child_process cp_rev_parse = CHILD_PROCESS_INIT;
    ++  struct strbuf result = STRBUF_INIT;
     +
     +  cp_rev_parse.git_cmd = 1;
    -+  cp_rev_parse.no_stdout = 1;
     +  cp_rev_parse.dir = sm_path;
     +  prepare_submodule_repo_env(&cp_rev_parse.env_array);
     +  argv_array_pushl(&cp_rev_parse.args, "rev-parse", "-q",
    -+                   "--verify", NULL);
    -+  argv_array_pushf(&cp_rev_parse.args, "%s^0", sha1);
    ++                   "--short", NULL);
    ++  argv_array_pushf(&cp_rev_parse.args, "%s^0", committish);
    ++  argv_array_push(&cp_rev_parse.args, "--");
     +
    -+  if (run_command(&cp_rev_parse))
    -+          return 1;
    ++  if (capture_command(&cp_rev_parse, &result, 0))
    ++          return NULL;
     +
    -+  return 0;
    ++  strbuf_trim_trailing_newline(&result);
    ++  return strbuf_detach(&result, NULL);
     +}
     +
    -+static void print_submodule_summary(struct summary_cb *info, int errmsg,
    -+                                int total_commits, int missing_src,
    -+                                int missing_dst, const char *displaypath,
    -+                                int is_sm_git_dir, struct module_cb *p)
    ++static void print_submodule_summary(struct summary_cb *info, char* errmsg,
    ++                              int total_commits, const char *displaypath,
    ++                              const char *src_abbrev, const char *dst_abbrev,
    ++                              int missing_src, int missing_dst,
    ++                              struct module_cb *p)
     +{
     +  if (p->status == 'T') {
     +          if (S_ISGITLINK(p->mod_dst))
     +                  printf(_("* %s %s(blob)->%s(submodule)"),
    -+                           displaypath, find_unique_abbrev(&p->oid_src, 7),
    -+                           find_unique_abbrev(&p->oid_dst, 7));
    ++                           displaypath, src_abbrev, dst_abbrev);
     +          else
     +                  printf(_("* %s %s(submodule)->%s(blob)"),
    -+                           displaypath, find_unique_abbrev(&p->oid_src, 7),
    -+                           find_unique_abbrev(&p->oid_dst, 7));
    ++                           displaypath, src_abbrev, dst_abbrev);
     +  } else {
     +          printf("* %s %s...%s",
    -+                  displaypath, find_unique_abbrev(&p->oid_src, 7),
    -+                  find_unique_abbrev(&p->oid_dst, 7));
    ++                  displaypath, src_abbrev, dst_abbrev);
     +  }
     +
     +  if (total_commits < 0)
    @@ builtin/submodule--helper.c: static int module_name(int argc, const char **argv,
     +          printf(" (%d):\n", total_commits);
     +
     +  if (errmsg) {
    -+          /*
    -+           * Don't give error msg for modification whose dst is not
    -+           * submodule, i.e. deleted or changed to blob
    -+           */
    -+          if (S_ISGITLINK(p->mod_src)) {
    -+                  if (missing_src && missing_dst) {
    -+                          printf(_("  Warn: %s doesn't contain commits %s and %s\n"),
    -+                                 displaypath, oid_to_hex(&p->oid_src),
    -+                                 oid_to_hex(&p->oid_dst));
    -+                  } else if (missing_src) {
    -+                          printf(_("  Warn: %s doesn't contain commit %s\n"),
    -+                                 displaypath, oid_to_hex(&p->oid_src));
    -+                  } else {
    -+                          printf(_("  Warn: %s doesn't contain commit %s\n"),
    -+                                 displaypath, oid_to_hex(&p->oid_dst));
    -+                  }
    -+          }
    -+  } else if (is_sm_git_dir) {
    ++          printf(_("%s"), errmsg);
    ++  } else if (total_commits > 0) {
     +          struct child_process cp_log = CHILD_PROCESS_INIT;
     +
     +          cp_log.git_cmd = 1;
    @@ builtin/submodule--helper.c: static int module_name(int argc, const char **argv,
     +                  argv_array_pushl(&cp_log.args, "--pretty=  %m %s",
     +                                   "--first-parent", NULL);
     +                  argv_array_pushf(&cp_log.args, "%s...%s",
    -+                                   oid_to_hex(&p->oid_src),
    -+                                   oid_to_hex(&p->oid_dst));
    ++                                   src_abbrev,
    ++                                   dst_abbrev);
     +          } else if (S_ISGITLINK(p->mod_dst)) {
     +                  argv_array_pushl(&cp_log.args, "--pretty=  > %s",
    -+                                   "-1", oid_to_hex(&p->oid_dst), NULL);
    ++                                   "-1", dst_abbrev, NULL);
     +          } else {
     +                  argv_array_pushl(&cp_log.args, "--pretty=  < %s",
    -+                                   "-1", oid_to_hex(&p->oid_src), NULL);
    ++                                   "-1", src_abbrev, NULL);
     +          }
     +          run_command(&cp_log);
     +  }
    @@ builtin/submodule--helper.c: static int module_name(int argc, const char **argv,
     +static void generate_submodule_summary(struct summary_cb *info,
     +                                 struct module_cb *p)
     +{
    -+  int missing_src = 0;
    -+  int missing_dst = 0;
    -+  char *displaypath;
    -+  int errmsg = 0;
    ++  char *displaypath, *src_abbrev, *dst_abbrev;
    ++  int missing_src = 0, missing_dst = 0;
    ++  char *errmsg = NULL;
     +  int total_commits = -1;
    -+  int is_sm_git_dir = 0;
    -+  struct strbuf sm_git_dir_sb = STRBUF_INIT;
     +
     +  if (!info->cached && oideq(&p->oid_dst, &null_oid)) {
     +          if (S_ISGITLINK(p->mod_dst)) {
    -+                  /*
    -+                   * NEEDSWORK: avoid using separate process with
    -+                   * the help of the function head_ref_submodule()
    -+                   */
    -+                  struct child_process cp_rev_parse = CHILD_PROCESS_INIT;
    -+                  struct strbuf sb_rev_parse = STRBUF_INIT;
    -+
    -+                  cp_rev_parse.git_cmd = 1;
    -+                  cp_rev_parse.no_stderr = 1;
    -+                  cp_rev_parse.dir = p->sm_path;
    -+                  prepare_submodule_repo_env(&cp_rev_parse.env_array);
    -+
    -+                  argv_array_pushl(&cp_rev_parse.args, "rev-parse",
    -+                                   "HEAD", NULL);
    -+                  if (!capture_command(&cp_rev_parse, &sb_rev_parse, 0)) {
    -+                          strbuf_strip_suffix(&sb_rev_parse, "\n");
    -+                          get_oid_hex(sb_rev_parse.buf, &p->oid_dst);
    -+                  }
    -+                  strbuf_release(&sb_rev_parse);
    ++                  struct ref_store *refs = get_submodule_ref_store(p->sm_path);
    ++                  if (refs)
    ++                          refs_head_ref(refs, handle_submodule_head_ref, &p->oid_dst);
     +          } else if (S_ISLNK(p->mod_dst) || S_ISREG(p->mod_dst)) {
    -+                  struct child_process cp_hash_object = CHILD_PROCESS_INIT;
    -+                  struct strbuf sb_hash_object = STRBUF_INIT;
    -+
    -+                  cp_hash_object.git_cmd = 1;
    -+                  argv_array_pushl(&cp_hash_object.args, "hash-object",
    -+                                   p->sm_path, NULL);
    -+                  if (!capture_command(&cp_hash_object,
    -+                                       &sb_hash_object, 0)) {
    -+                          strbuf_strip_suffix(&sb_hash_object, "\n");
    -+                          get_oid_hex(sb_hash_object.buf, &p->oid_dst);
    -+                  }
    -+                  strbuf_release(&sb_hash_object);
    ++                  struct stat st;
    ++                  int fd = open(p->sm_path, O_RDONLY);
    ++
    ++                  if (fd < 0 || fstat(fd, &st) < 0 ||
    ++                      index_fd(&the_index, &p->oid_dst, fd, &st, OBJ_BLOB,
    ++                               p->sm_path, 0))
    ++                          error(_("couldn't hash object from '%s'"), p->sm_path);
     +          } else {
    ++                  /* for a submodule removal (mode:0000000), don't warn */
     +                  if (p->mod_dst)
    -+                          die(_("unexpected mode %d\n"), p->mod_dst);
    ++                          warning(_("unexpected mode %d\n"), p->mod_dst);
     +          }
     +  }
     +
    -+  strbuf_addstr(&sm_git_dir_sb, p->sm_path);
    -+  if (is_nonbare_repository_dir(&sm_git_dir_sb))
    -+          is_sm_git_dir = 1;
    -+
    -+  if (is_sm_git_dir && S_ISGITLINK(p->mod_src))
    -+          missing_src = verify_submodule_object_name(p->sm_path,
    -+                                                     oid_to_hex(&p->oid_src));
    ++  if (S_ISGITLINK(p->mod_src)) {
    ++          src_abbrev = verify_submodule_committish(p->sm_path,
    ++                                                   oid_to_hex(&p->oid_src));
    ++          if (!src_abbrev) {
    ++                  missing_src = 1;
    ++                  /*
    ++                   * As `rev-parse` failed, we fallback to getting
    ++                   * the abbreviated hash using oid_src. We do
    ++                   * this as we might still need the abbreviated
    ++                   * hash in cases like a submodule type change, etc.
    ++                   */
    ++                  src_abbrev = xstrndup(oid_to_hex(&p->oid_src), 7);
    ++          }
    ++  } else {
    ++          /*
    ++           * The source does not point to a submodule.
    ++           * So, we fallback to getting the abbreviation using
    ++           * oid_src as we might still need the abbreviated
    ++           * hash in cases like submodule add, etc.
    ++           */
    ++          src_abbrev = xstrndup(oid_to_hex(&p->oid_src), 7);
    ++  }
     +
    -+  if (is_sm_git_dir && S_ISGITLINK(p->mod_dst))
    -+          missing_dst = verify_submodule_object_name(p->sm_path,
    -+                                                     oid_to_hex(&p->oid_dst));
    ++  if (S_ISGITLINK(p->mod_dst)) {
    ++          dst_abbrev = verify_submodule_committish(p->sm_path,
    ++                                                   oid_to_hex(&p->oid_dst));
    ++          if (!dst_abbrev) {
    ++                  missing_dst = 1;
    ++                  /*
    ++                   * As `rev-parse` failed, we fallback to getting
    ++                   * the abbreviated hash using oid_dst. We do
    ++                   * this as we might still need the abbreviated
    ++                   * hash in cases like a submodule type change, etc.
    ++                   */
    ++                  dst_abbrev = xstrndup(oid_to_hex(&p->oid_dst), 7);
    ++          }
    ++  } else {
    ++          /*
    ++           * The destination does not point to a submodule.
    ++           * So, we fallback to getting the abbreviation using
    ++           * oid_dst as we might still need the abbreviated
    ++           * hash in cases like a submodule removal, etc.
    ++           */
    ++          dst_abbrev = xstrndup(oid_to_hex(&p->oid_dst), 7);
    ++  }
     +
     +  displaypath = get_submodule_displaypath(p->sm_path, info->prefix);
     +
    -+  if (!missing_dst && !missing_src) {
    -+          if (is_sm_git_dir) {
    -+                  struct child_process cp_rev_list = CHILD_PROCESS_INIT;
    -+                  struct strbuf sb_rev_list = STRBUF_INIT;
    -+                  char *range;
    -+
    -+                  if (S_ISGITLINK(p->mod_src) && S_ISGITLINK(p->mod_dst))
    -+                          range = xstrfmt("%s...%s", oid_to_hex(&p->oid_src),
    -+                                          oid_to_hex(&p->oid_dst));
    -+                  else if (S_ISGITLINK(p->mod_src))
    -+                          range = xstrdup(oid_to_hex(&p->oid_src));
    -+                  else
    -+                          range = xstrdup(oid_to_hex(&p->oid_dst));
    -+
    -+                  cp_rev_list.git_cmd = 1;
    -+                  cp_rev_list.dir = p->sm_path;
    -+                  prepare_submodule_repo_env(&cp_rev_list.env_array);
    -+
    -+                  argv_array_pushl(&cp_rev_list.args, "rev-list",
    -+                                   "--first-parent", range, "--", NULL);
    -+                  if (!capture_command(&cp_rev_list, &sb_rev_list, 0)) {
    -+                          if (sb_rev_list.len)
    -+                                  total_commits = count_lines(sb_rev_list.buf,
    -+                                                              sb_rev_list.len);
    -+                          else
    -+                                  total_commits = 0;
    -+                  }
    ++  if (!missing_src && !missing_dst) {
    ++          struct child_process cp_rev_list = CHILD_PROCESS_INIT;
    ++          struct strbuf sb_rev_list = STRBUF_INIT;
     +
    -+                  free(range);
    -+                  strbuf_release(&sb_rev_list);
    -+          }
    ++          argv_array_pushl(&cp_rev_list.args, "rev-list",
    ++                           "--first-parent", "--count", NULL);
    ++          if (S_ISGITLINK(p->mod_src) && S_ISGITLINK(p->mod_dst))
    ++                  argv_array_pushf(&cp_rev_list.args, "%s...%s",
    ++                                   src_abbrev,
    ++                                   dst_abbrev);
    ++          else
    ++                  argv_array_push(&cp_rev_list.args,
    ++                                  S_ISGITLINK(p->mod_src) ?
    ++                                  src_abbrev :
    ++                                  dst_abbrev);
    ++          argv_array_push(&cp_rev_list.args, "--");
    ++
    ++          cp_rev_list.git_cmd = 1;
    ++          cp_rev_list.dir = p->sm_path;
    ++          prepare_submodule_repo_env(&cp_rev_list.env_array);
    ++
    ++          if (!capture_command(&cp_rev_list, &sb_rev_list, 0))
    ++                  total_commits = atoi(sb_rev_list.buf);
    ++
    ++          strbuf_release(&sb_rev_list);
     +  } else {
    -+          errmsg = 1;
    ++          /*
    ++           * Don't give error msg for modification whose dst is not
    ++           * submodule, i.e., deleted or changed to blob
    ++           */
    ++          if (S_ISGITLINK(p->mod_dst)) {
    ++                  struct strbuf errmsg_str = STRBUF_INIT;
    ++                  if (missing_src && missing_dst) {
    ++                          strbuf_addf(&errmsg_str, "  Warn: %s doesn't contain commits %s and %s\n",
    ++                                      displaypath, oid_to_hex(&p->oid_src),
    ++                                      oid_to_hex(&p->oid_dst));
    ++                  } else {
    ++                          strbuf_addf(&errmsg_str, "  Warn: %s doesn't contain commit %s\n",
    ++                                      displaypath, missing_src ?
    ++                                      oid_to_hex(&p->oid_src) :
    ++                                      oid_to_hex(&p->oid_dst));
    ++                  }
    ++                  errmsg = strbuf_detach(&errmsg_str, NULL);
    ++          }
     +  }
     +
     +  print_submodule_summary(info, errmsg, total_commits,
    -+                          missing_src, missing_dst,
    -+                          displaypath, is_sm_git_dir, p);
    ++                          displaypath, src_abbrev,
    ++                          dst_abbrev, missing_src,
    ++                          missing_dst, p);
     +
     +  free(displaypath);
    -+  strbuf_release(&sm_git_dir_sb);
    ++  free(src_abbrev);
    ++  free(dst_abbrev);
     +}
     +
     +static void prepare_submodule_summary(struct summary_cb *info,
    @@ builtin/submodule--helper.c: static int module_name(int argc, const char **argv,
     +{
     +  int i;
     +  for (i = 0; i < list->nr; i++) {
    ++          const struct submodule *sub;
     +          struct module_cb *p = list->entries[i];
    -+          struct child_process cp_rev_parse = CHILD_PROCESS_INIT;
    ++          struct strbuf sm_gitdir = STRBUF_INIT;
     +
     +          if (p->status == 'D' || p->status == 'T') {
     +                  generate_submodule_summary(info, p);
     +                  continue;
     +          }
     +
    -+          if (info->for_status) {
    -+                  char *config_key;
    -+                  const char *ignore_config = "none";
    ++          if (info->for_status && p->status != 'A' &&
    ++              (sub = submodule_from_path(the_repository,
    ++                                         &null_oid, p->sm_path))) {
    ++                  char *config_key = NULL;
     +                  const char *value;
    -+                  const struct submodule *sub = submodule_from_path(the_repository,
    -+                                                                    &null_oid,
    -+                                                                    p->sm_path);
    -+
    -+                  if (sub && p->status != 'A') {
    -+                          config_key = xstrfmt("submodule.%s.ignore",
    -+                                               sub->name);
    -+                          if (!git_config_get_string_const(config_key, &value))
    -+                                  ignore_config = value;
    -+                          else if (sub->ignore)
    -+                                  ignore_config = sub->ignore;
    -+
    -+                          free(config_key);
    -+                          if (!strcmp(ignore_config, "all"))
    -+                                  continue;
    -+                  }
    ++                  int ignore_all = 0;
    ++
    ++                  config_key = xstrfmt("submodule.%s.ignore",
    ++                                       sub->name);
    ++                  if (!git_config_get_string_const(config_key, &value))
    ++                          ignore_all = !strcmp(value, "all");
    ++                  else if (sub->ignore)
    ++                          ignore_all = !strcmp(sub->ignore, "all");
    ++
    ++                  free(config_key);
    ++                  if (ignore_all)
    ++                          continue;
     +          }
     +
     +          /* Also show added or modified modules which are checked out */
    -+          cp_rev_parse.dir = p->sm_path;
    -+          cp_rev_parse.git_cmd = 1;
    -+          cp_rev_parse.no_stderr = 1;
    -+          cp_rev_parse.no_stdout = 1;
    -+
    -+          argv_array_pushl(&cp_rev_parse.args, "rev-parse",
    -+                           "--git-dir", NULL);
    -+
    -+          if (!run_command(&cp_rev_parse))
    ++          strbuf_addstr(&sm_gitdir, p->sm_path);
    ++          if (is_nonbare_repository_dir(&sm_gitdir))
     +                  generate_submodule_summary(info, p);
    ++          strbuf_release(&sm_gitdir);
     +  }
     +}
     +
    @@ builtin/submodule--helper.c: static int module_name(int argc, const char **argv,
     +  }
     +}
     +
    -+static int compute_summary_module_list(char *head,
    -+                                   struct summary_cb *info,
    -+                                   enum diff_cmd diff_cmd)
    ++static int compute_summary_module_list(struct object_id *head_oid,
    ++                                 struct summary_cb *info,
    ++                                 enum diff_cmd diff_cmd)
     +{
     +  struct argv_array diff_args = ARGV_ARRAY_INIT;
     +  struct rev_info rev;
    @@ builtin/submodule--helper.c: static int module_name(int argc, const char **argv,
     +          argv_array_push(&diff_args, "--cached");
     +  argv_array_pushl(&diff_args, "--ignore-submodules=dirty", "--raw",
     +                   NULL);
    -+  if (head)
    -+          argv_array_push(&diff_args, head);
    ++  if (head_oid)
    ++          argv_array_push(&diff_args, oid_to_hex(head_oid));
     +  argv_array_push(&diff_args, "--");
     +  if (info->argc)
     +          argv_array_pushv(&diff_args, info->argv);
    @@ builtin/submodule--helper.c: static int module_name(int argc, const char **argv,
     +  rev.diffopt.format_callback_data = &list;
     +
     +  if (!info->cached) {
    -+          if (diff_cmd ==  DIFF_INDEX)
    ++          if (diff_cmd == DIFF_INDEX)
     +                  setup_work_tree();
     +          if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
     +                  perror("read_cache_preload");
    @@ builtin/submodule--helper.c: static int module_name(int argc, const char **argv,
     +  struct summary_cb info = SUMMARY_CB_INIT;
     +  int cached = 0;
     +  int for_status = 0;
    -+  int quiet = 0;
     +  int files = 0;
     +  int summary_limit = -1;
    -+  struct child_process cp_rev = CHILD_PROCESS_INIT;
    -+  struct strbuf sb = STRBUF_INIT;
     +  enum diff_cmd diff_cmd = DIFF_INDEX;
    ++  struct object_id head_oid;
     +  int ret;
     +
     +  struct option module_summary_options[] = {
    -+          OPT__QUIET(&quiet, N_("Suppress output of summarising submodules")),
     +          OPT_BOOL(0, "cached", &cached,
    -+                   N_("Use the commit stored in the index instead of the submodule HEAD")),
    ++                   N_("use the commit stored in the index instead of the submodule HEAD")),
     +          OPT_BOOL(0, "files", &files,
    -+                   N_("To compare the commit in the index with that in the submodule HEAD")),
    ++                   N_("to compare the commit in the index with that in the submodule HEAD")),
     +          OPT_BOOL(0, "for-status", &for_status,
    -+                   N_("Skip submodules with 'ignore_config' value set to 'all'")),
    ++                   N_("skip submodules with 'ignore_config' value set to 'all'")),
     +          OPT_INTEGER('n', "summary-limit", &summary_limit,
    -+                       N_("Limit the summary size")),
    ++                       N_("limit the summary size")),
     +          OPT_END()
     +  };
     +
    @@ builtin/submodule--helper.c: static int module_name(int argc, const char **argv,
     +  if (!summary_limit)
     +          return 0;
     +
    -+  cp_rev.git_cmd = 1;
    -+  argv_array_pushl(&cp_rev.args, "rev-parse", "-q", "--verify",
    -+                   argc ? argv[0] : "HEAD", NULL);
    -+
    -+  if (!capture_command(&cp_rev, &sb, 0)) {
    -+          strbuf_strip_suffix(&sb, "\n");
    ++  if (!get_oid(argc ? argv[0] : "HEAD", &head_oid)) {
     +          if (argc) {
     +                  argv++;
     +                  argc--;
     +          }
     +  } else if (!argc || !strcmp(argv[0], "HEAD")) {
     +          /* before the first commit: compare with an empty tree */
    -+          struct stat st;
    -+          struct object_id oid;
    -+          if (fstat(0, &st) < 0 || index_fd(&the_index, &oid, 0, &st, 2,
    -+                                            prefix, 3))
    -+                  die("Unable to add %s to database", oid.hash);
    -+          strbuf_addstr(&sb, oid_to_hex(&oid));
    ++          oidcpy(&head_oid, the_hash_algo->empty_tree);
     +          if (argc) {
     +                  argv++;
     +                  argc--;
     +          }
     +  } else {
    -+          strbuf_addstr(&sb, "HEAD");
    ++          if (get_oid("HEAD", &head_oid))
    ++                  die(_("could not fetch a revision for HEAD"));
     +  }
     +
     +  if (files) {
    @@ builtin/submodule--helper.c: static int module_name(int argc, const char **argv,
     +  info.argv = argv;
     +  info.prefix = prefix;
     +  info.cached = !!cached;
    ++  info.files = !!files;
     +  info.for_status = !!for_status;
    -+  info.quiet = quiet;
    -+  info.files = files;
     +  info.summary_limit = summary_limit;
     +
    -+  ret = compute_summary_module_list((diff_cmd == DIFF_FILES) ? NULL : sb.buf,
    -+                                     &info, diff_cmd);
    -+  strbuf_release(&sb);
    ++  ret = compute_summary_module_list((diff_cmd == DIFF_INDEX) ? &head_oid : NULL,
    ++                                    &info, diff_cmd);
     +  return ret;
     +}
     +
    @@ git-submodule.sh: cmd_summary() {
     -          fi
     -          echo
     -  done
    -+  git ${wt_prefix:+-C "$wt_prefix"} submodule--helper summary ${GIT_QUIET:+--quiet} ${prefix:+--prefix "$prefix"} ${for_status:+--for-status} ${files:+--files} ${cached:+--cached} ${summary_limit:+-n $summary_limit} "$@"
    ++  git ${wt_prefix:+-C "$wt_prefix"} submodule--helper summary ${prefix:+--prefix "$prefix"} ${files:+--files} ${cached:+--cached} ${for_status:+--for-status} ${summary_limit:+-n $summary_limit} -- "$@"
      }
      #
      # List all submodules, prefixed with:
    +
    + ## t/t7421-submodule-summary-add.sh ##
    +@@ t/t7421-submodule-summary-add.sh: test_expect_success 'submodule summary output for submodules with changed paths'
    +   git commit -m "change submodule path" &&
    +   rev=$(git -C sm rev-parse --short HEAD^) &&
    +   git submodule summary HEAD^^ -- my-subm >actual 2>err &&
    +-  test_i18ngrep "fatal:.*my-subm" err &&
    ++  grep "fatal:.*my-subm" err &&
    +   cat >expected <<-EOF &&
    +   * my-subm ${rev}...0000000:
    + 

Prathamesh Chavan (1):
  submodule: port submodule subcommand 'summary' from shell to C

Shourya Shukla (4):
  submodule: expose the '--for-status' option of summary
  submodule: remove extra line feeds between callback struct and macro
  submodule: rename helper functions to avoid ambiguity
  t7421: introduce a test script for verifying 'summary' output

 Documentation/git-submodule.txt        |   8 +-
 builtin/submodule--helper.c            | 439 ++++++++++++++++++++++++-
 contrib/completion/git-completion.bash |   2 +-
 diff.c                                 |   2 +-
 git-submodule.sh                       | 188 +----------
 submodule.c                            |  10 +-
 submodule.h                            |   2 +-
 t/t7421-submodule-summary-add.sh       |  69 ++++
 8 files changed, 522 insertions(+), 198 deletions(-)
 create mode 100755 t/t7421-submodule-summary-add.sh

-- 
2.28.0


^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH v2 1/5] submodule: expose the '--for-status' option of summary
  2020-08-06 16:40 [GSoC][PATCH v2 0/5] submodule: port subcommand 'summary' from shell to C Shourya Shukla
@ 2020-08-06 16:40 ` Shourya Shukla
  2020-08-08 14:40   ` Kaartic Sivaraam
  2020-08-06 16:40 ` [PATCH v2 2/5] submodule: remove extra line feeds between callback struct and macro Shourya Shukla
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 32+ messages in thread
From: Shourya Shukla @ 2020-08-06 16:40 UTC (permalink / raw)
  To: git
  Cc: gitster, christian.couder, kaartic.sivaraam, johannes.schindelin,
	liu.denton, Shourya Shukla, Christian Couder

The 'for-status' option is used to compute the summary of submodule(s)
in a superproject by skipping the ignored submdules i.e., those with
'submodule.<name>.ignore' set to 'all' in the '.gitmodules' or
'.git/config', with the latter taking precedence over the former.

The option was introduced in d0f64dd44d (git-submodule summary:
--for-status option, 2008-04-12), refined in 3ba7407b8b (submodule
summary: ignore --for-status option, 2013-09-06) and finally perfected
in 927b26f87a (submodule: don't print status output with ignore=all,
2013-09-01). But, it was not mentioned in the 'git submodule'
Documentation.

Expose the '--for-status' option accepted by the command 'git submodule
summary'.

Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Mentored-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com>
Signed-off-by: Shourya Shukla <shouryashukla.oo@gmail.com>
---
 Documentation/git-submodule.txt        | 8 +++++++-
 contrib/completion/git-completion.bash | 2 +-
 git-submodule.sh                       | 2 +-
 3 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt
index 7e5f995f77..d944e4c817 100644
--- a/Documentation/git-submodule.txt
+++ b/Documentation/git-submodule.txt
@@ -190,7 +190,7 @@ set-url [--] <path> <newurl>::
 	automatically synchronize the submodule's new remote URL
 	configuration.
 
-summary [--cached|--files] [(-n|--summary-limit) <n>] [commit] [--] [<path>...]::
+summary [--cached|--files] [--for-status] [(-n|--summary-limit) <n>] [commit] [--] [<path>...]::
 	Show commit summary between the given commit (defaults to HEAD) and
 	working tree/index. For a submodule in question, a series of commits
 	in the submodule between the given super project commit and the
@@ -309,6 +309,12 @@ OPTIONS
 	compares the commit in the index with that in the submodule HEAD
 	when this option is used.
 
+--for-status::
+	This option is only valid for the summary command. This command
+	skips the submodules with `submodule.<name>.ignore` set to `all`
+	in the `.gitmodules` or `.git/config`. The configuration in
+	`.git/config` overrides the configuration in `.gitmodules`.
+
 -n::
 --summary-limit::
 	This option is only valid for the summary command.
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 0fdb5da83b..2b7b033c17 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -3059,7 +3059,7 @@ _git_submodule ()
 		__gitcomp "--default --branch"
 		;;
 	summary,--*)
-		__gitcomp "--cached --files --summary-limit"
+		__gitcomp "--cached --files --for-status --summary-limit"
 		;;
 	foreach,--*|sync,--*)
 		__gitcomp "--recursive"
diff --git a/git-submodule.sh b/git-submodule.sh
index 43eb6051d2..dda3fee167 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -13,7 +13,7 @@ USAGE="[--quiet] [--cached]
    or: $dashless [--quiet] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--[no-]recommend-shallow] [--reference <repository>] [--recursive] [--[no-]single-branch] [--] [<path>...]
    or: $dashless [--quiet] set-branch (--default|--branch <branch>) [--] <path>
    or: $dashless [--quiet] set-url [--] <path> <newurl>
-   or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
+   or: $dashless [--quiet] summary [--cached|--files] [--for-status] [--summary-limit <n>] [commit] [--] [<path>...]
    or: $dashless [--quiet] foreach [--recursive] <command>
    or: $dashless [--quiet] sync [--recursive] [--] [<path>...]
    or: $dashless [--quiet] absorbgitdirs [--] [<path>...]"
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 2/5] submodule: remove extra line feeds between callback struct and macro
  2020-08-06 16:40 [GSoC][PATCH v2 0/5] submodule: port subcommand 'summary' from shell to C Shourya Shukla
  2020-08-06 16:40 ` [PATCH v2 1/5] submodule: expose the '--for-status' option of summary Shourya Shukla
@ 2020-08-06 16:40 ` Shourya Shukla
  2020-08-06 16:41 ` [PATCH v2 3/5] submodule: rename helper functions to avoid ambiguity Shourya Shukla
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 32+ messages in thread
From: Shourya Shukla @ 2020-08-06 16:40 UTC (permalink / raw)
  To: git
  Cc: gitster, christian.couder, kaartic.sivaraam, johannes.schindelin,
	liu.denton, Shourya Shukla, Christian Couder,
	Johannes Schindelin, Philip Oakley

Many `submodule--helper` subcommands follow the convention that a struct
defines their callback data, and the declaration of that struct is
followed immediately by a macro to use in static initializers, without
any separating empty line.

Let's align the `init`, `status` and `sync` subcommands with that convention.

Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Mentored-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com>
Helped-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Helped-by: Philip Oakley <philipoakley@iee.email>
Signed-off-by: Shourya Shukla <shouryashukla.oo@gmail.com>
---
 builtin/submodule--helper.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index a1c75607c7..3641718d0a 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -612,7 +612,6 @@ struct init_cb {
 	const char *prefix;
 	unsigned int flags;
 };
-
 #define INIT_CB_INIT { NULL, 0 }
 
 static void init_submodule(const char *path, const char *prefix,
@@ -742,7 +741,6 @@ struct status_cb {
 	const char *prefix;
 	unsigned int flags;
 };
-
 #define STATUS_CB_INIT { NULL, 0 }
 
 static void print_status(unsigned int flags, char state, const char *path,
@@ -933,7 +931,6 @@ struct sync_cb {
 	const char *prefix;
 	unsigned int flags;
 };
-
 #define SYNC_CB_INIT { NULL, 0 }
 
 static void sync_submodule(const char *path, const char *prefix,
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 3/5] submodule: rename helper functions to avoid ambiguity
  2020-08-06 16:40 [GSoC][PATCH v2 0/5] submodule: port subcommand 'summary' from shell to C Shourya Shukla
  2020-08-06 16:40 ` [PATCH v2 1/5] submodule: expose the '--for-status' option of summary Shourya Shukla
  2020-08-06 16:40 ` [PATCH v2 2/5] submodule: remove extra line feeds between callback struct and macro Shourya Shukla
@ 2020-08-06 16:41 ` Shourya Shukla
  2020-08-06 16:41 ` [PATCH v2 4/5] t7421: introduce a test script for verifying 'summary' output Shourya Shukla
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 32+ messages in thread
From: Shourya Shukla @ 2020-08-06 16:41 UTC (permalink / raw)
  To: git
  Cc: gitster, christian.couder, kaartic.sivaraam, johannes.schindelin,
	liu.denton, Shourya Shukla, Christian Couder

The helper functions: show_submodule_summary(),
prepare_submodule_summary() and print_submodule_summary() are used by
the builtin_diff() function in diff.c to generate a summary of
submodules in the context of a diff. Functions with similar names are to
be introduced in the upcoming port of submodule's summary subcommand.

So, rename the helper functions to '*_diff_submodule_summary()' to avoid
ambiguity.

Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Mentored-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com>
Signed-off-by: Shourya Shukla <shouryashukla.oo@gmail.com>
---
 diff.c      |  2 +-
 submodule.c | 10 +++++-----
 submodule.h |  2 +-
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/diff.c b/diff.c
index d24aaa3047..4a2c631c37 100644
--- a/diff.c
+++ b/diff.c
@@ -3429,7 +3429,7 @@ static void builtin_diff(const char *name_a,
 	if (o->submodule_format == DIFF_SUBMODULE_LOG &&
 	    (!one->mode || S_ISGITLINK(one->mode)) &&
 	    (!two->mode || S_ISGITLINK(two->mode))) {
-		show_submodule_summary(o, one->path ? one->path : two->path,
+		show_submodule_diff_summary(o, one->path ? one->path : two->path,
 				&one->oid, &two->oid,
 				two->dirty_submodule);
 		return;
diff --git a/submodule.c b/submodule.c
index e2ef5698c8..097902ee67 100644
--- a/submodule.c
+++ b/submodule.c
@@ -438,7 +438,7 @@ void handle_ignore_submodules_arg(struct diff_options *diffopt,
 	 */
 }
 
-static int prepare_submodule_summary(struct rev_info *rev, const char *path,
+static int prepare_submodule_diff_summary(struct rev_info *rev, const char *path,
 		struct commit *left, struct commit *right,
 		struct commit_list *merge_bases)
 {
@@ -459,7 +459,7 @@ static int prepare_submodule_summary(struct rev_info *rev, const char *path,
 	return prepare_revision_walk(rev);
 }
 
-static void print_submodule_summary(struct repository *r, struct rev_info *rev, struct diff_options *o)
+static void print_submodule_diff_summary(struct repository *r, struct rev_info *rev, struct diff_options *o)
 {
 	static const char format[] = "  %m %s";
 	struct strbuf sb = STRBUF_INIT;
@@ -610,7 +610,7 @@ static void show_submodule_header(struct diff_options *o,
 	strbuf_release(&sb);
 }
 
-void show_submodule_summary(struct diff_options *o, const char *path,
+void show_submodule_diff_summary(struct diff_options *o, const char *path,
 		struct object_id *one, struct object_id *two,
 		unsigned dirty_submodule)
 {
@@ -632,12 +632,12 @@ void show_submodule_summary(struct diff_options *o, const char *path,
 		goto out;
 
 	/* Treat revision walker failure the same as missing commits */
-	if (prepare_submodule_summary(&rev, path, left, right, merge_bases)) {
+	if (prepare_submodule_diff_summary(&rev, path, left, right, merge_bases)) {
 		diff_emit_submodule_error(o, "(revision walker failed)\n");
 		goto out;
 	}
 
-	print_submodule_summary(sub, &rev, o);
+	print_submodule_diff_summary(sub, &rev, o);
 
 out:
 	if (merge_bases)
diff --git a/submodule.h b/submodule.h
index 4dad649f94..22db9e1832 100644
--- a/submodule.h
+++ b/submodule.h
@@ -69,7 +69,7 @@ int parse_submodule_update_strategy(const char *value,
 				    struct submodule_update_strategy *dst);
 const char *submodule_strategy_to_string(const struct submodule_update_strategy *s);
 void handle_ignore_submodules_arg(struct diff_options *, const char *);
-void show_submodule_summary(struct diff_options *o, const char *path,
+void show_submodule_diff_summary(struct diff_options *o, const char *path,
 			    struct object_id *one, struct object_id *two,
 			    unsigned dirty_submodule);
 void show_submodule_inline_diff(struct diff_options *o, const char *path,
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 4/5] t7421: introduce a test script for verifying 'summary' output
  2020-08-06 16:40 [GSoC][PATCH v2 0/5] submodule: port subcommand 'summary' from shell to C Shourya Shukla
                   ` (2 preceding siblings ...)
  2020-08-06 16:41 ` [PATCH v2 3/5] submodule: rename helper functions to avoid ambiguity Shourya Shukla
@ 2020-08-06 16:41 ` Shourya Shukla
  2020-08-06 16:41 ` [PATCH v2 5/5] submodule: port submodule subcommand 'summary' from shell to C Shourya Shukla
  2020-08-12 19:44 ` [GSoC][PATCH v3 0/4] submodule: port " Shourya Shukla
  5 siblings, 0 replies; 32+ messages in thread
From: Shourya Shukla @ 2020-08-06 16:41 UTC (permalink / raw)
  To: git
  Cc: gitster, christian.couder, kaartic.sivaraam, johannes.schindelin,
	liu.denton, Shourya Shukla, Christian Couder

't7401-submodule-summary.sh' uses 'git add' to add submodules. Therefore,
some commands such as 'git submodule init' and 'git submodule deinit'
do not work as expected.

So, introduce a test script for verifying the 'summary' output for
submodules added using 'git submodule add'.

Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Mentored-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com>
Signed-off-by: Shourya Shukla <shouryashukla.oo@gmail.com>
---
 t/t7421-submodule-summary-add.sh | 69 ++++++++++++++++++++++++++++++++
 1 file changed, 69 insertions(+)
 create mode 100755 t/t7421-submodule-summary-add.sh

diff --git a/t/t7421-submodule-summary-add.sh b/t/t7421-submodule-summary-add.sh
new file mode 100755
index 0000000000..829fe26d6d
--- /dev/null
+++ b/t/t7421-submodule-summary-add.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+#
+# Copyright (C) 2020 Shourya Shukla
+#
+
+test_description='Summary support for submodules, adding them using git submodule add
+
+This test script tries to verify the sanity of summary subcommand of git submodule
+while making sure to add submodules using `git submodule add` instead of
+`git add` as done in t7401.
+'
+
+. ./test-lib.sh
+
+test_expect_success 'summary test environment setup' '
+	git init sm &&
+	test_commit -C sm "add file" file file-content file-tag &&
+
+	git submodule add ./sm my-subm &&
+	test_tick &&
+	git commit -m "add submodule"
+'
+
+test_expect_success 'submodule summary output for initialized submodule' '
+	test_commit -C sm "add file2" file2 file2-content file2-tag &&
+	git submodule update --remote &&
+	test_tick &&
+	git commit -m "update submodule" my-subm &&
+	git submodule summary HEAD^ >actual &&
+	rev1=$(git -C sm rev-parse --short HEAD^) &&
+	rev2=$(git -C sm rev-parse --short HEAD) &&
+	cat >expected <<-EOF &&
+	* my-subm ${rev1}...${rev2} (1):
+	  > add file2
+
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'submodule summary output for deinitialized submodule' '
+	git submodule deinit my-subm &&
+	git submodule summary HEAD^ >actual &&
+	test_must_be_empty actual &&
+	git submodule update --init my-subm &&
+	git submodule summary HEAD^ >actual &&
+	rev1=$(git -C sm rev-parse --short HEAD^) &&
+	rev2=$(git -C sm rev-parse --short HEAD) &&
+	cat >expected <<-EOF &&
+	* my-subm ${rev1}...${rev2} (1):
+	  > add file2
+
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'submodule summary output for submodules with changed paths' '
+	git mv my-subm subm &&
+	git commit -m "change submodule path" &&
+	rev=$(git -C sm rev-parse --short HEAD^) &&
+	git submodule summary HEAD^^ -- my-subm >actual 2>err &&
+	test_i18ngrep "fatal:.*my-subm" err &&
+	cat >expected <<-EOF &&
+	* my-subm ${rev}...0000000:
+
+	EOF
+	test_cmp expected actual
+'
+
+test_done
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 5/5] submodule: port submodule subcommand 'summary' from shell to C
  2020-08-06 16:40 [GSoC][PATCH v2 0/5] submodule: port subcommand 'summary' from shell to C Shourya Shukla
                   ` (3 preceding siblings ...)
  2020-08-06 16:41 ` [PATCH v2 4/5] t7421: introduce a test script for verifying 'summary' output Shourya Shukla
@ 2020-08-06 16:41 ` Shourya Shukla
  2020-08-06 22:45   ` Junio C Hamano
  2020-08-12 19:44 ` [GSoC][PATCH v3 0/4] submodule: port " Shourya Shukla
  5 siblings, 1 reply; 32+ messages in thread
From: Shourya Shukla @ 2020-08-06 16:41 UTC (permalink / raw)
  To: git
  Cc: gitster, christian.couder, kaartic.sivaraam, johannes.schindelin,
	liu.denton, Prathamesh Chavan, Christian Couder, Stefan Beller,
	Johannes Schindelin, Shourya Shukla

From: Prathamesh Chavan <pc44800@gmail.com>

Convert submodule subcommand 'summary' to a builtin and call it via
'git-submodule.sh'.

The shell version had to call $diff_cmd twice, once to find the modified
modules cared by the user and then again, with that list of modules
to do various operations for computing the summary of those modules.
On the other hand, the C version does not need a second call to
$diff_cmd since it reuses the module list from the first call to do the
aforementioned tasks.

In the C version, we use the combination of setting a child process'
working directory to the submodule path and then calling
'prepare_submodule_repo_env()' which also sets the 'GIT_DIR' to '.git',
so that we can be certain that those spawned processes will not access
the superproject's ODB by mistake.

A behavioural difference between the C and the shell version is that the
shell version outputs two line feeds after the 'git log' output when run
outside of the tests while the C version outputs one line feed in any
case. The reason for this is that the shell version calls log with
'--pretty=format:<fmt>' whose output is followed by two echo
calls; 'format' does not have "terminator" semantics like its 'tformat'
counterpart. So, the log output is terminated by a newline only when
invoked by the user and not when invoked from the scripts. This results
in the one & two line feed differences in the shell version.
On the other hand, the C version calls log with '--pretty=<fmt>'
which is equivalent to '--pretty:tformat:<fmt>' which is then
followed by a 'printf("\n")'. Due to its "terminator" semantics the
log output is always terminated by newline and hence one line feed in
any case.

Also, when we try to pass an option-like argument after a non-option
argument, for instance:

    git submodule summary HEAD --foo-bar

    (or)

    git submodule summary HEAD --cached

That argument would be treated like a path to the submodule for which
the user is requesting a summary. So, the option ends up having no
effect. Though, passing '--quiet' is an exception to this:

    git submodule summary HEAD --quiet

While 'summary' doesn't support '--quiet', we don't get an output for
the above command as '--quiet' is treated as a path which means we get
an output only if a submodule whose path is '--quiet' exists.

The error message in case of computing a summary for non-existent
submodules in the C version is different from that of the shell version.
Since the new error message is not marked for translation, change the
'test_i18ngrep' in t7421.4 to 'grep'.

Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Mentored-by: Stefan Beller <stefanbeller@gmail.com>
Mentored-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com>
Helped-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Prathamesh Chavan <pc44800@gmail.com>
Signed-off-by: Shourya Shukla <shouryashukla.oo@gmail.com>
---
 builtin/submodule--helper.c      | 436 +++++++++++++++++++++++++++++++
 git-submodule.sh                 | 186 +------------
 t/t7421-submodule-summary-add.sh |   2 +-
 3 files changed, 438 insertions(+), 186 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 3641718d0a..43ed2f645f 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -927,6 +927,441 @@ static int module_name(int argc, const char **argv, const char *prefix)
 	return 0;
 }
 
+struct module_cb {
+	unsigned int mod_src;
+	unsigned int mod_dst;
+	struct object_id oid_src;
+	struct object_id oid_dst;
+	char status;
+	const char *sm_path;
+};
+#define MODULE_CB_INIT { 0, 0, NULL, NULL, '\0', NULL }
+
+struct module_cb_list {
+	struct module_cb **entries;
+	int alloc, nr;
+};
+#define MODULE_CB_LIST_INIT { NULL, 0, 0 }
+
+struct summary_cb {
+	int argc;
+	const char **argv;
+	const char *prefix;
+	unsigned int cached: 1;
+	unsigned int for_status: 1;
+	unsigned int files: 1;
+	int summary_limit;
+};
+#define SUMMARY_CB_INIT { 0, NULL, NULL, 0, 0, 0, 0 }
+
+enum diff_cmd {
+	DIFF_INDEX,
+	DIFF_FILES
+};
+
+static char* verify_submodule_committish(const char *sm_path,
+					 const char *committish)
+{
+	struct child_process cp_rev_parse = CHILD_PROCESS_INIT;
+	struct strbuf result = STRBUF_INIT;
+
+	cp_rev_parse.git_cmd = 1;
+	cp_rev_parse.dir = sm_path;
+	prepare_submodule_repo_env(&cp_rev_parse.env_array);
+	argv_array_pushl(&cp_rev_parse.args, "rev-parse", "-q",
+			 "--short", NULL);
+	argv_array_pushf(&cp_rev_parse.args, "%s^0", committish);
+	argv_array_push(&cp_rev_parse.args, "--");
+
+	if (capture_command(&cp_rev_parse, &result, 0))
+		return NULL;
+
+	strbuf_trim_trailing_newline(&result);
+	return strbuf_detach(&result, NULL);
+}
+
+static void print_submodule_summary(struct summary_cb *info, char* errmsg,
+				    int total_commits, const char *displaypath,
+				    const char *src_abbrev, const char *dst_abbrev,
+				    int missing_src, int missing_dst,
+				    struct module_cb *p)
+{
+	if (p->status == 'T') {
+		if (S_ISGITLINK(p->mod_dst))
+			printf(_("* %s %s(blob)->%s(submodule)"),
+				 displaypath, src_abbrev, dst_abbrev);
+		else
+			printf(_("* %s %s(submodule)->%s(blob)"),
+				 displaypath, src_abbrev, dst_abbrev);
+	} else {
+		printf("* %s %s...%s",
+			displaypath, src_abbrev, dst_abbrev);
+	}
+
+	if (total_commits < 0)
+		printf(":\n");
+	else
+		printf(" (%d):\n", total_commits);
+
+	if (errmsg) {
+		printf(_("%s"), errmsg);
+	} else if (total_commits > 0) {
+		struct child_process cp_log = CHILD_PROCESS_INIT;
+
+		cp_log.git_cmd = 1;
+		cp_log.dir = p->sm_path;
+		prepare_submodule_repo_env(&cp_log.env_array);
+		argv_array_pushl(&cp_log.args, "log", NULL);
+
+		if (S_ISGITLINK(p->mod_src) && S_ISGITLINK(p->mod_dst)) {
+			if (info->summary_limit > 0)
+				argv_array_pushf(&cp_log.args, "-%d",
+						 info->summary_limit);
+
+			argv_array_pushl(&cp_log.args, "--pretty=  %m %s",
+					 "--first-parent", NULL);
+			argv_array_pushf(&cp_log.args, "%s...%s",
+					 src_abbrev,
+					 dst_abbrev);
+		} else if (S_ISGITLINK(p->mod_dst)) {
+			argv_array_pushl(&cp_log.args, "--pretty=  > %s",
+					 "-1", dst_abbrev, NULL);
+		} else {
+			argv_array_pushl(&cp_log.args, "--pretty=  < %s",
+					 "-1", src_abbrev, NULL);
+		}
+		run_command(&cp_log);
+	}
+	printf("\n");
+}
+
+static void generate_submodule_summary(struct summary_cb *info,
+				       struct module_cb *p)
+{
+	char *displaypath, *src_abbrev, *dst_abbrev;
+	int missing_src = 0, missing_dst = 0;
+	char *errmsg = NULL;
+	int total_commits = -1;
+
+	if (!info->cached && oideq(&p->oid_dst, &null_oid)) {
+		if (S_ISGITLINK(p->mod_dst)) {
+			struct ref_store *refs = get_submodule_ref_store(p->sm_path);
+			if (refs)
+				refs_head_ref(refs, handle_submodule_head_ref, &p->oid_dst);
+		} else if (S_ISLNK(p->mod_dst) || S_ISREG(p->mod_dst)) {
+			struct stat st;
+			int fd = open(p->sm_path, O_RDONLY);
+
+			if (fd < 0 || fstat(fd, &st) < 0 ||
+			    index_fd(&the_index, &p->oid_dst, fd, &st, OBJ_BLOB,
+				     p->sm_path, 0))
+				error(_("couldn't hash object from '%s'"), p->sm_path);
+		} else {
+			/* for a submodule removal (mode:0000000), don't warn */
+			if (p->mod_dst)
+				warning(_("unexpected mode %d\n"), p->mod_dst);
+		}
+	}
+
+	if (S_ISGITLINK(p->mod_src)) {
+		src_abbrev = verify_submodule_committish(p->sm_path,
+							 oid_to_hex(&p->oid_src));
+		if (!src_abbrev) {
+			missing_src = 1;
+			/*
+			 * As `rev-parse` failed, we fallback to getting
+			 * the abbreviated hash using oid_src. We do
+			 * this as we might still need the abbreviated
+			 * hash in cases like a submodule type change, etc.
+			 */
+			src_abbrev = xstrndup(oid_to_hex(&p->oid_src), 7);
+		}
+	} else {
+		/*
+		 * The source does not point to a submodule.
+		 * So, we fallback to getting the abbreviation using
+		 * oid_src as we might still need the abbreviated
+		 * hash in cases like submodule add, etc.
+		 */
+		src_abbrev = xstrndup(oid_to_hex(&p->oid_src), 7);
+	}
+
+	if (S_ISGITLINK(p->mod_dst)) {
+		dst_abbrev = verify_submodule_committish(p->sm_path,
+							 oid_to_hex(&p->oid_dst));
+		if (!dst_abbrev) {
+			missing_dst = 1;
+			/*
+			 * As `rev-parse` failed, we fallback to getting
+			 * the abbreviated hash using oid_dst. We do
+			 * this as we might still need the abbreviated
+			 * hash in cases like a submodule type change, etc.
+			 */
+			dst_abbrev = xstrndup(oid_to_hex(&p->oid_dst), 7);
+		}
+	} else {
+		/*
+		 * The destination does not point to a submodule.
+		 * So, we fallback to getting the abbreviation using
+		 * oid_dst as we might still need the abbreviated
+		 * hash in cases like a submodule removal, etc.
+		 */
+		dst_abbrev = xstrndup(oid_to_hex(&p->oid_dst), 7);
+	}
+
+	displaypath = get_submodule_displaypath(p->sm_path, info->prefix);
+
+	if (!missing_src && !missing_dst) {
+		struct child_process cp_rev_list = CHILD_PROCESS_INIT;
+		struct strbuf sb_rev_list = STRBUF_INIT;
+
+		argv_array_pushl(&cp_rev_list.args, "rev-list",
+				 "--first-parent", "--count", NULL);
+		if (S_ISGITLINK(p->mod_src) && S_ISGITLINK(p->mod_dst))
+			argv_array_pushf(&cp_rev_list.args, "%s...%s",
+					 src_abbrev,
+					 dst_abbrev);
+		else
+			argv_array_push(&cp_rev_list.args,
+					S_ISGITLINK(p->mod_src) ?
+					src_abbrev :
+					dst_abbrev);
+		argv_array_push(&cp_rev_list.args, "--");
+
+		cp_rev_list.git_cmd = 1;
+		cp_rev_list.dir = p->sm_path;
+		prepare_submodule_repo_env(&cp_rev_list.env_array);
+
+		if (!capture_command(&cp_rev_list, &sb_rev_list, 0))
+			total_commits = atoi(sb_rev_list.buf);
+
+		strbuf_release(&sb_rev_list);
+	} else {
+		/*
+		 * Don't give error msg for modification whose dst is not
+		 * submodule, i.e., deleted or changed to blob
+		 */
+		if (S_ISGITLINK(p->mod_dst)) {
+			struct strbuf errmsg_str = STRBUF_INIT;
+			if (missing_src && missing_dst) {
+				strbuf_addf(&errmsg_str, "  Warn: %s doesn't contain commits %s and %s\n",
+					    displaypath, oid_to_hex(&p->oid_src),
+					    oid_to_hex(&p->oid_dst));
+			} else {
+				strbuf_addf(&errmsg_str, "  Warn: %s doesn't contain commit %s\n",
+					    displaypath, missing_src ?
+					    oid_to_hex(&p->oid_src) :
+					    oid_to_hex(&p->oid_dst));
+			}
+			errmsg = strbuf_detach(&errmsg_str, NULL);
+		}
+	}
+
+	print_submodule_summary(info, errmsg, total_commits,
+				displaypath, src_abbrev,
+				dst_abbrev, missing_src,
+				missing_dst, p);
+
+	free(displaypath);
+	free(src_abbrev);
+	free(dst_abbrev);
+}
+
+static void prepare_submodule_summary(struct summary_cb *info,
+				      struct module_cb_list *list)
+{
+	int i;
+	for (i = 0; i < list->nr; i++) {
+		const struct submodule *sub;
+		struct module_cb *p = list->entries[i];
+		struct strbuf sm_gitdir = STRBUF_INIT;
+
+		if (p->status == 'D' || p->status == 'T') {
+			generate_submodule_summary(info, p);
+			continue;
+		}
+
+		if (info->for_status && p->status != 'A' &&
+		    (sub = submodule_from_path(the_repository,
+					       &null_oid, p->sm_path))) {
+			char *config_key = NULL;
+			const char *value;
+			int ignore_all = 0;
+
+			config_key = xstrfmt("submodule.%s.ignore",
+					     sub->name);
+			if (!git_config_get_string_const(config_key, &value))
+				ignore_all = !strcmp(value, "all");
+			else if (sub->ignore)
+				ignore_all = !strcmp(sub->ignore, "all");
+
+			free(config_key);
+			if (ignore_all)
+				continue;
+		}
+
+		/* Also show added or modified modules which are checked out */
+		strbuf_addstr(&sm_gitdir, p->sm_path);
+		if (is_nonbare_repository_dir(&sm_gitdir))
+			generate_submodule_summary(info, p);
+		strbuf_release(&sm_gitdir);
+	}
+}
+
+static void submodule_summary_callback(struct diff_queue_struct *q,
+				       struct diff_options *options,
+				       void *data)
+{
+	int i;
+	struct module_cb_list *list = data;
+	for (i = 0; i < q->nr; i++) {
+		struct diff_filepair *p = q->queue[i];
+		struct module_cb *temp;
+
+		if (!S_ISGITLINK(p->one->mode) && !S_ISGITLINK(p->two->mode))
+			continue;
+		temp = (struct module_cb*)malloc(sizeof(struct module_cb));
+		temp->mod_src = p->one->mode;
+		temp->mod_dst = p->two->mode;
+		temp->oid_src = p->one->oid;
+		temp->oid_dst = p->two->oid;
+		temp->status = p->status;
+		temp->sm_path = xstrdup(p->one->path);
+
+		ALLOC_GROW(list->entries, list->nr + 1, list->alloc);
+		list->entries[list->nr++] = temp;
+	}
+}
+
+static const char *get_diff_cmd(enum diff_cmd diff_cmd)
+{
+	switch (diff_cmd) {
+	case DIFF_INDEX: return "diff-index";
+	case DIFF_FILES: return "diff-files";
+	default: BUG("bad diff_cmd value %d", diff_cmd);
+	}
+}
+
+static int compute_summary_module_list(struct object_id *head_oid,
+				       struct summary_cb *info,
+				       enum diff_cmd diff_cmd)
+{
+	struct argv_array diff_args = ARGV_ARRAY_INIT;
+	struct rev_info rev;
+	struct module_cb_list list = MODULE_CB_LIST_INIT;
+
+	argv_array_push(&diff_args, get_diff_cmd(diff_cmd));
+	if (info->cached)
+		argv_array_push(&diff_args, "--cached");
+	argv_array_pushl(&diff_args, "--ignore-submodules=dirty", "--raw",
+			 NULL);
+	if (head_oid)
+		argv_array_push(&diff_args, oid_to_hex(head_oid));
+	argv_array_push(&diff_args, "--");
+	if (info->argc)
+		argv_array_pushv(&diff_args, info->argv);
+
+	git_config(git_diff_basic_config, NULL);
+	init_revisions(&rev, info->prefix);
+	rev.abbrev = 0;
+	precompose_argv(diff_args.argc, diff_args.argv);
+
+	diff_args.argc = setup_revisions(diff_args.argc, diff_args.argv,
+					 &rev, NULL);
+	rev.diffopt.output_format = DIFF_FORMAT_NO_OUTPUT | DIFF_FORMAT_CALLBACK;
+	rev.diffopt.format_callback = submodule_summary_callback;
+	rev.diffopt.format_callback_data = &list;
+
+	if (!info->cached) {
+		if (diff_cmd == DIFF_INDEX)
+			setup_work_tree();
+		if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
+			perror("read_cache_preload");
+			return -1;
+		}
+	} else if (read_cache() < 0) {
+		perror("read_cache");
+		return -1;
+	}
+
+	if (diff_cmd == DIFF_INDEX)
+		run_diff_index(&rev, info->cached);
+	else
+		run_diff_files(&rev, 0);
+	prepare_submodule_summary(info, &list);
+	return 0;
+}
+
+static int module_summary(int argc, const char **argv, const char *prefix)
+{
+	struct summary_cb info = SUMMARY_CB_INIT;
+	int cached = 0;
+	int for_status = 0;
+	int files = 0;
+	int summary_limit = -1;
+	enum diff_cmd diff_cmd = DIFF_INDEX;
+	struct object_id head_oid;
+	int ret;
+
+	struct option module_summary_options[] = {
+		OPT_BOOL(0, "cached", &cached,
+			 N_("use the commit stored in the index instead of the submodule HEAD")),
+		OPT_BOOL(0, "files", &files,
+			 N_("to compare the commit in the index with that in the submodule HEAD")),
+		OPT_BOOL(0, "for-status", &for_status,
+			 N_("skip submodules with 'ignore_config' value set to 'all'")),
+		OPT_INTEGER('n', "summary-limit", &summary_limit,
+			     N_("limit the summary size")),
+		OPT_END()
+	};
+
+	const char *const git_submodule_helper_usage[] = {
+		N_("git submodule--helper summary [<options>] [commit] [--] [<path>]"),
+		NULL
+	};
+
+	argc = parse_options(argc, argv, prefix, module_summary_options,
+			     git_submodule_helper_usage, 0);
+
+	if (!summary_limit)
+		return 0;
+
+	if (!get_oid(argc ? argv[0] : "HEAD", &head_oid)) {
+		if (argc) {
+			argv++;
+			argc--;
+		}
+	} else if (!argc || !strcmp(argv[0], "HEAD")) {
+		/* before the first commit: compare with an empty tree */
+		oidcpy(&head_oid, the_hash_algo->empty_tree);
+		if (argc) {
+			argv++;
+			argc--;
+		}
+	} else {
+		if (get_oid("HEAD", &head_oid))
+			die(_("could not fetch a revision for HEAD"));
+	}
+
+	if (files) {
+		if (cached)
+			die(_("--cached and --files are mutually exclusive"));
+		diff_cmd = DIFF_FILES;
+	}
+
+	info.argc = argc;
+	info.argv = argv;
+	info.prefix = prefix;
+	info.cached = !!cached;
+	info.files = !!files;
+	info.for_status = !!for_status;
+	info.summary_limit = summary_limit;
+
+	ret = compute_summary_module_list((diff_cmd == DIFF_INDEX) ? &head_oid : NULL,
+					  &info, diff_cmd);
+	return ret;
+}
+
 struct sync_cb {
 	const char *prefix;
 	unsigned int flags;
@@ -2341,6 +2776,7 @@ static struct cmd_struct commands[] = {
 	{"print-default-remote", print_default_remote, 0},
 	{"sync", module_sync, SUPPORT_SUPER_PREFIX},
 	{"deinit", module_deinit, 0},
+	{"summary", module_summary, SUPPORT_SUPER_PREFIX},
 	{"remote-branch", resolve_remote_submodule_branch, 0},
 	{"push-check", push_check, 0},
 	{"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
diff --git a/git-submodule.sh b/git-submodule.sh
index dda3fee167..236a08f27d 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -59,31 +59,6 @@ die_if_unmatched ()
 	fi
 }
 
-#
-# Print a submodule configuration setting
-#
-# $1 = submodule name
-# $2 = option name
-# $3 = default value
-#
-# Checks in the usual git-config places first (for overrides),
-# otherwise it falls back on .gitmodules.  This allows you to
-# distribute project-wide defaults in .gitmodules, while still
-# customizing individual repositories if necessary.  If the option is
-# not in .gitmodules either, print a default value.
-#
-get_submodule_config () {
-	name="$1"
-	option="$2"
-	default="$3"
-	value=$(git config submodule."$name"."$option")
-	if test -z "$value"
-	then
-		value=$(git submodule--helper config submodule."$name"."$option")
-	fi
-	printf '%s' "${value:-$default}"
-}
-
 isnumber()
 {
 	n=$(($1 + 0)) 2>/dev/null && test "$n" = "$1"
@@ -831,166 +806,7 @@ cmd_summary() {
 		shift
 	done
 
-	test $summary_limit = 0 && return
-
-	if rev=$(git rev-parse -q --verify --default HEAD ${1+"$1"})
-	then
-		head=$rev
-		test $# = 0 || shift
-	elif test -z "$1" || test "$1" = "HEAD"
-	then
-		# before the first commit: compare with an empty tree
-		head=$(git hash-object -w -t tree --stdin </dev/null)
-		test -z "$1" || shift
-	else
-		head="HEAD"
-	fi
-
-	if [ -n "$files" ]
-	then
-		test -n "$cached" &&
-		die "$(gettext "The --cached option cannot be used with the --files option")"
-		diff_cmd=diff-files
-		head=
-	fi
-
-	cd_to_toplevel
-	eval "set $(git rev-parse --sq --prefix "$wt_prefix" -- "$@")"
-	# Get modified modules cared by user
-	modules=$(git $diff_cmd $cached --ignore-submodules=dirty --raw $head -- "$@" |
-		sane_egrep '^:([0-7]* )?160000' |
-		while read -r mod_src mod_dst sha1_src sha1_dst status sm_path
-		do
-			# Always show modules deleted or type-changed (blob<->module)
-			if test "$status" = D || test "$status" = T
-			then
-				printf '%s\n' "$sm_path"
-				continue
-			fi
-			# Respect the ignore setting for --for-status.
-			if test -n "$for_status"
-			then
-				name=$(git submodule--helper name "$sm_path")
-				ignore_config=$(get_submodule_config "$name" ignore none)
-				test $status != A && test $ignore_config = all && continue
-			fi
-			# Also show added or modified modules which are checked out
-			GIT_DIR="$sm_path/.git" git rev-parse --git-dir >/dev/null 2>&1 &&
-			printf '%s\n' "$sm_path"
-		done
-	)
-
-	test -z "$modules" && return
-
-	git $diff_cmd $cached --ignore-submodules=dirty --raw $head -- $modules |
-	sane_egrep '^:([0-7]* )?160000' |
-	cut -c2- |
-	while read -r mod_src mod_dst sha1_src sha1_dst status name
-	do
-		if test -z "$cached" &&
-			is_zero_oid $sha1_dst
-		then
-			case "$mod_dst" in
-			160000)
-				sha1_dst=$(GIT_DIR="$name/.git" git rev-parse HEAD)
-				;;
-			100644 | 100755 | 120000)
-				sha1_dst=$(git hash-object $name)
-				;;
-			000000)
-				;; # removed
-			*)
-				# unexpected type
-				eval_gettextln "unexpected mode \$mod_dst" >&2
-				continue ;;
-			esac
-		fi
-		missing_src=
-		missing_dst=
-
-		test $mod_src = 160000 &&
-		! GIT_DIR="$name/.git" git rev-parse -q --verify $sha1_src^0 >/dev/null &&
-		missing_src=t
-
-		test $mod_dst = 160000 &&
-		! GIT_DIR="$name/.git" git rev-parse -q --verify $sha1_dst^0 >/dev/null &&
-		missing_dst=t
-
-		display_name=$(git submodule--helper relative-path "$name" "$wt_prefix")
-
-		total_commits=
-		case "$missing_src,$missing_dst" in
-		t,)
-			errmsg="$(eval_gettext "  Warn: \$display_name doesn't contain commit \$sha1_src")"
-			;;
-		,t)
-			errmsg="$(eval_gettext "  Warn: \$display_name doesn't contain commit \$sha1_dst")"
-			;;
-		t,t)
-			errmsg="$(eval_gettext "  Warn: \$display_name doesn't contain commits \$sha1_src and \$sha1_dst")"
-			;;
-		*)
-			errmsg=
-			total_commits=$(
-			if test $mod_src = 160000 && test $mod_dst = 160000
-			then
-				range="$sha1_src...$sha1_dst"
-			elif test $mod_src = 160000
-			then
-				range=$sha1_src
-			else
-				range=$sha1_dst
-			fi
-			GIT_DIR="$name/.git" \
-			git rev-list --first-parent $range -- | wc -l
-			)
-			total_commits=" ($(($total_commits + 0)))"
-			;;
-		esac
-
-		sha1_abbr_src=$(GIT_DIR="$name/.git" git rev-parse --short $sha1_src 2>/dev/null ||
-			echo $sha1_src | cut -c1-7)
-		sha1_abbr_dst=$(GIT_DIR="$name/.git" git rev-parse --short $sha1_dst 2>/dev/null ||
-			echo $sha1_dst | cut -c1-7)
-
-		if test $status = T
-		then
-			blob="$(gettext "blob")"
-			submodule="$(gettext "submodule")"
-			if test $mod_dst = 160000
-			then
-				echo "* $display_name $sha1_abbr_src($blob)->$sha1_abbr_dst($submodule)$total_commits:"
-			else
-				echo "* $display_name $sha1_abbr_src($submodule)->$sha1_abbr_dst($blob)$total_commits:"
-			fi
-		else
-			echo "* $display_name $sha1_abbr_src...$sha1_abbr_dst$total_commits:"
-		fi
-		if test -n "$errmsg"
-		then
-			# Don't give error msg for modification whose dst is not submodule
-			# i.e. deleted or changed to blob
-			test $mod_dst = 160000 && echo "$errmsg"
-		else
-			if test $mod_src = 160000 && test $mod_dst = 160000
-			then
-				limit=
-				test $summary_limit -gt 0 && limit="-$summary_limit"
-				GIT_DIR="$name/.git" \
-				git log $limit --pretty='format:  %m %s' \
-				--first-parent $sha1_src...$sha1_dst
-			elif test $mod_dst = 160000
-			then
-				GIT_DIR="$name/.git" \
-				git log --pretty='format:  > %s' -1 $sha1_dst
-			else
-				GIT_DIR="$name/.git" \
-				git log --pretty='format:  < %s' -1 $sha1_src
-			fi
-			echo
-		fi
-		echo
-	done
+	git ${wt_prefix:+-C "$wt_prefix"} submodule--helper summary ${prefix:+--prefix "$prefix"} ${files:+--files} ${cached:+--cached} ${for_status:+--for-status} ${summary_limit:+-n $summary_limit} -- "$@"
 }
 #
 # List all submodules, prefixed with:
diff --git a/t/t7421-submodule-summary-add.sh b/t/t7421-submodule-summary-add.sh
index 829fe26d6d..59a9b00467 100755
--- a/t/t7421-submodule-summary-add.sh
+++ b/t/t7421-submodule-summary-add.sh
@@ -58,7 +58,7 @@ test_expect_success 'submodule summary output for submodules with changed paths'
 	git commit -m "change submodule path" &&
 	rev=$(git -C sm rev-parse --short HEAD^) &&
 	git submodule summary HEAD^^ -- my-subm >actual 2>err &&
-	test_i18ngrep "fatal:.*my-subm" err &&
+	grep "fatal:.*my-subm" err &&
 	cat >expected <<-EOF &&
 	* my-subm ${rev}...0000000:
 
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 5/5] submodule: port submodule subcommand 'summary' from shell to C
  2020-08-06 16:41 ` [PATCH v2 5/5] submodule: port submodule subcommand 'summary' from shell to C Shourya Shukla
@ 2020-08-06 22:45   ` Junio C Hamano
  2020-08-07 16:31     ` Shourya Shukla
  0 siblings, 1 reply; 32+ messages in thread
From: Junio C Hamano @ 2020-08-06 22:45 UTC (permalink / raw)
  To: Shourya Shukla
  Cc: git, christian.couder, kaartic.sivaraam, johannes.schindelin,
	liu.denton, Prathamesh Chavan, Christian Couder, Stefan Beller

Shourya Shukla <shouryashukla.oo@gmail.com> writes:

> ...

> +			argv_array_pushl(&cp_log.args, "--pretty=  %m %s",
> +					 "--first-parent", NULL);
> +			argv_array_pushf(&cp_log.args, "%s...%s",
> +					 src_abbrev,
> +					 dst_abbrev);

> ...

> +	diff_args.argc = setup_revisions(diff_args.argc, diff_args.argv,
> +					 &rev, NULL);

Peff's jk/strvec topic will soon be in 'master', and basing the
series on top of 'master' after that happens would make these lines
to read like

			strvec_pushl(&cp_log.args, "--pretty=  %m %s",
				     "--first-parent", NULL);
			strvec_pushf(&cp_log.args, "%s...%s",
				     src_abbrev,
				     dst_abbrev);

	diff_args.nr = setup_revisions(diff_args.nr, diff_args.v,
				       &rev, NULL);

We may even be able to reduce line wrapping thanks to shortening a
few common words:

    argv_array => strvec
    argc       => nc
    argv       => v

For today's integration, I dealt with these as conflict resolution,
so let's keep review discussion going, and hope jk/strvec is in
'master' by the time this topic becomes ready.

Thanks.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 5/5] submodule: port submodule subcommand 'summary' from shell to C
  2020-08-06 22:45   ` Junio C Hamano
@ 2020-08-07 16:31     ` Shourya Shukla
  2020-08-07 17:15       ` Junio C Hamano
  0 siblings, 1 reply; 32+ messages in thread
From: Shourya Shukla @ 2020-08-07 16:31 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, christian.couder, kaartic.sivaraam, johannes.schindelin,
	liu.denton, pc44800, chriscool, stefanbeller

On 06/08 03:45, Junio C Hamano wrote:
> Shourya Shukla <shouryashukla.oo@gmail.com> writes:
> 
> > ...
> 
> > +			argv_array_pushl(&cp_log.args, "--pretty=  %m %s",
> > +					 "--first-parent", NULL);
> > +			argv_array_pushf(&cp_log.args, "%s...%s",
> > +					 src_abbrev,
> > +					 dst_abbrev);
> 
> > ...
> 
> > +	diff_args.argc = setup_revisions(diff_args.argc, diff_args.argv,
> > +					 &rev, NULL);
> 
> Peff's jk/strvec topic will soon be in 'master', and basing the
> series on top of 'master' after that happens would make these lines
> to read like
> 
> 			strvec_pushl(&cp_log.args, "--pretty=  %m %s",
> 				     "--first-parent", NULL);
> 			strvec_pushf(&cp_log.args, "%s...%s",
> 				     src_abbrev,
> 				     dst_abbrev);
> 
> 	diff_args.nr = setup_revisions(diff_args.nr, diff_args.v,
> 				       &rev, NULL);
> 
> We may even be able to reduce line wrapping thanks to shortening a
> few common words:
> 
>     argv_array => strvec
>     argc       => nc
>     argv       => v
> 
> For today's integration, I dealt with these as conflict resolution,
> so let's keep review discussion going, and hope jk/strvec is in
> 'master' by the time this topic becomes ready.

Understood. I will base this patch on the above series. Seems like a
great series of  great change! BTW, I asked a couple of things in the
cover-letter which I think you might have missed. Quoting them here:
-----8<-----
Also, I want to ask a couple of things:

	1.Whether we can suppress the error message that we get when
	  trying to find the summary of non-existent submodules?
	  For example:

	  fatal: exec 'rev-parse': cd to 'my-subm' failed: No such file or directory
	   * my-subm 35b40f1...0000000:

	 Will it be OK to suppress the above error message?

	2.Is it fine to document and expose the 'for-status' option in
	  'git-submodule.txt'?
----->8-----

Regards,
Shourya Shukla

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 5/5] submodule: port submodule subcommand 'summary' from shell to C
  2020-08-07 16:31     ` Shourya Shukla
@ 2020-08-07 17:15       ` Junio C Hamano
  0 siblings, 0 replies; 32+ messages in thread
From: Junio C Hamano @ 2020-08-07 17:15 UTC (permalink / raw)
  To: Shourya Shukla
  Cc: git, christian.couder, kaartic.sivaraam, johannes.schindelin,
	liu.denton, pc44800, chriscool, stefanbeller

Shourya Shukla <shouryashukla.oo@gmail.com> writes:

> Understood. I will base this patch on the above series. Seems like a
> great series of  great change! BTW, I asked a couple of things in the
> cover-letter which I think you might have missed.

No I didn't miss. I didn't resopnd because I didn't have particular
answers to them.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 1/5] submodule: expose the '--for-status' option of summary
  2020-08-06 16:40 ` [PATCH v2 1/5] submodule: expose the '--for-status' option of summary Shourya Shukla
@ 2020-08-08 14:40   ` Kaartic Sivaraam
  2020-08-08 20:25     ` Christian Couder
  0 siblings, 1 reply; 32+ messages in thread
From: Kaartic Sivaraam @ 2020-08-08 14:40 UTC (permalink / raw)
  To: Shourya Shukla, git
  Cc: gitster, christian.couder, johannes.schindelin, liu.denton,
	Christian Couder

On 06-08-2020 22:10, Shourya Shukla wrote:
> The 'for-status' option is used to compute the summary of submodule(s)
> in a superproject by skipping the ignored submdules i.e., those with
> 'submodule.<name>.ignore' set to 'all' in the '.gitmodules' or
> '.git/config', with the latter taking precedence over the former.
> 
> The option was introduced in d0f64dd44d (git-submodule summary:
> --for-status option, 2008-04-12), refined in 3ba7407b8b (submodule
> summary: ignore --for-status option, 2013-09-06) and finally perfected
> in 927b26f87a (submodule: don't print status output with ignore=all,
> 2013-09-01). But, it was not mentioned in the 'git submodule'
> Documentation.
> 
> Expose the '--for-status' option accepted by the command 'git submodule
> summary'.
>

I've had one concern about exposing '--for-status'. As of now, the name
of the option has no relation with the behaviour that we get as a
consequence. So long, the option has been internal and this wasn't a
problem. Now that we're considering to expose it in the docs, usage and
autocomplete, I would say it should be done after renaming it
appropriately given that it's easy to do now than later. As to name
suggestions, I really don't have any.

Also, as to whether exposing this would be useful at all, I really don't
know.

-- 
Sivaraam

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 1/5] submodule: expose the '--for-status' option of summary
  2020-08-08 14:40   ` Kaartic Sivaraam
@ 2020-08-08 20:25     ` Christian Couder
  2020-08-08 23:26       ` Junio C Hamano
  0 siblings, 1 reply; 32+ messages in thread
From: Christian Couder @ 2020-08-08 20:25 UTC (permalink / raw)
  To: Kaartic Sivaraam
  Cc: Shourya Shukla, git, Junio C Hamano, Johannes Schindelin,
	Denton Liu, Christian Couder

Le sam. 8 août 2020 à 16:40, Kaartic Sivaraam
<kaartic.sivaraam@gmail.com> a écrit :
>
> On 06-08-2020 22:10, Shourya Shukla wrote:
> > The 'for-status' option is used to compute the summary of submodule(s)
> > in a superproject by skipping the ignored submdules i.e., those with
> > 'submodule.<name>.ignore' set to 'all' in the '.gitmodules' or
> > '.git/config', with the latter taking precedence over the former.

The above seems to suggest that a name like --skip-ignored could fit,
if we wanted to rename --for-status.

> > The option was introduced in d0f64dd44d (git-submodule summary:
> > --for-status option, 2008-04-12), refined in 3ba7407b8b (submodule
> > summary: ignore --for-status option, 2013-09-06) and finally perfected
> > in 927b26f87a (submodule: don't print status output with ignore=all,
> > 2013-09-01). But, it was not mentioned in the 'git submodule'
> > Documentation.

After this we would need to tell why it's a good idea to actually
document this option (and perhaps rename it if we are going to do
that). It could be a good idea, if it could help users to see a
summary without the ignored submodules.

So for example a possibly good justification could be that in a repo
with many ignored submodules it might be interesting for users to get
a summary that contains information only about the non-ignored
submodules.

An example output of `git submodule summary` both with and without
--for-status (or --skip-ignored) in an interesting case (where there
are many ignored submodule) could help convince people that it's a
possibly useful option, and that it's worth documenting.

> > Expose the '--for-status' option accepted by the command 'git submodule
> > summary'.
> >
>
> I've had one concern about exposing '--for-status'. As of now, the name
> of the option has no relation with the behaviour that we get as a
> consequence. So long, the option has been internal and this wasn't a
> problem. Now that we're considering to expose it in the docs, usage and
> autocomplete, I would say it should be done after renaming it
> appropriately given that it's easy to do now than later. As to name
> suggestions, I really don't have any.

Yeah, I agree that finding a good name and a good use case for the
option would surely help.

> Also, as to whether exposing this would be useful at all, I really don't
> know.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 1/5] submodule: expose the '--for-status' option of summary
  2020-08-08 20:25     ` Christian Couder
@ 2020-08-08 23:26       ` Junio C Hamano
  0 siblings, 0 replies; 32+ messages in thread
From: Junio C Hamano @ 2020-08-08 23:26 UTC (permalink / raw)
  To: Christian Couder
  Cc: Kaartic Sivaraam, Shourya Shukla, git, Johannes Schindelin,
	Denton Liu, Christian Couder

Christian Couder <christian.couder@gmail.com> writes:

> Yeah, I agree that finding a good name and a good use case for the
> option would surely help.

That makes it sound like a solution looking for a problem.

The option was added and discussed in [*1*], and it was quite clear
that it was merely an implementation detail to show the same info as
"git submodule summary" in a format that would fit better in the
context of "git status".  I doubt that anything changed since then
in the past 12 years to make the option deserve more attention by
the end users, but what this patch (which is the first in a 5-patch
series) does may be worth doing if a later patch in the series
serves as that "good use case".

On the other hand, if there is no such "good use case" example in
the other changes in this series, the option can and should be kept
as an implementation detail of "git status", I would think.

Thanks.


[Reference]

*1*
https://lore.kernel.org/git/1205416085-23431-1-git-send-email-pkufranky@gmail.com/


^ permalink raw reply	[flat|nested] 32+ messages in thread

* [GSoC][PATCH v3 0/4] submodule: port subcommand 'summary' from shell to C
  2020-08-06 16:40 [GSoC][PATCH v2 0/5] submodule: port subcommand 'summary' from shell to C Shourya Shukla
                   ` (4 preceding siblings ...)
  2020-08-06 16:41 ` [PATCH v2 5/5] submodule: port submodule subcommand 'summary' from shell to C Shourya Shukla
@ 2020-08-12 19:44 ` Shourya Shukla
  2020-08-12 19:44   ` [PATCH v3 1/4] submodule: remove extra line feeds between callback struct and macro Shourya Shukla
                     ` (3 more replies)
  5 siblings, 4 replies; 32+ messages in thread
From: Shourya Shukla @ 2020-08-12 19:44 UTC (permalink / raw)
  To: shouryashukla.oo
  Cc: christian.couder, git, gitster, Johannes.Schindelin,
	kaartic.sivaraam, liu.denton

Greetings,

This is the v3 of the patch series titled 'submodule: port subcommand
'summary' from shell to C':
https://lore.kernel.org/git/20200806164102.6707-1-shouryashukla.oo@gmail.com/

After suggestions from Junio, Kaartic and Christian I made some
changes:

-> Drop the commit c063390a94 (submodule: expose the '--for-status'
   option of summary, 2020-08-02) since it felt a bit unnecessary to
   expose the option given its lack of major use cases. Hence, after
   feedback from Junio, Kaartic and Christian I dropped this patch
   for now.

-> Adapt the patch to jk/strvec. It is a big change for Git hence it was
   necessary to make the port as per this patch.

Feedback and reviews are appreciated. I am tagging along a range-diff
between the v3 and v2 for ease of review.

Regards,
Shourya Shukla

-----
range-diff:

1:  c063390a94 < -:  ---------- submodule: expose the '--for-status' option of summary
2:  561d03351b = 1:  2a66d8c2bb submodule: remove extra line feeds between callback struct and macro
3:  e0e0c3ba4b = 2:  0ae3b92e31 submodule: rename helper functions to avoid ambiguity
4:  ec6e6c9e64 ! 3:  e2912a042f t7421: introduce a test script for verifying 'summary' output
    @@ Commit message
         do not work as expected.

         So, introduce a test script for verifying the 'summary' output for
    -    submodules added using 'git submodule add'.
    +    submodules added using 'git submodule add' and notify regarding the
    +    above mentioned behaviour in t7401 itself.

         Mentored-by: Christian Couder <chriscool@tuxfamily.org>
         Mentored-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com>
         Signed-off-by: Shourya Shukla <shouryashukla.oo@gmail.com>

    + ## t/t7401-submodule-summary.sh ##
    +@@ t/t7401-submodule-summary.sh: test_description='Summary support for submodules
    +
    + This test tries to verify the sanity of summary subcommand of git submodule.
    + '
    ++# NOTE: This test script uses 'git add' instead of 'git submodule add' to add
    ++# submodules to the superproject. Some submodule subcommands such as init and
    ++# deinit might not work as expected in this script. t7421 does not have this
    ++# caveat.
    +
    + . ./test-lib.sh
    +
    +
      ## t/t7421-submodule-summary-add.sh (new) ##
     @@
     +#!/bin/sh
5:  9222b4168b ! 4:  879cb902b2 submodule: port submodule subcommand 'summary' from shell to C
    @@ builtin/submodule--helper.c: static int module_name(int argc, const char **argv,
     +  cp_rev_parse.git_cmd = 1;
     +  cp_rev_parse.dir = sm_path;
     +  prepare_submodule_repo_env(&cp_rev_parse.env_array);
    -+  argv_array_pushl(&cp_rev_parse.args, "rev-parse", "-q",
    -+                   "--short", NULL);
    -+  argv_array_pushf(&cp_rev_parse.args, "%s^0", committish);
    -+  argv_array_push(&cp_rev_parse.args, "--");
    ++  strvec_pushl(&cp_rev_parse.args, "rev-parse", "-q", "--short", NULL);
    ++  strvec_pushf(&cp_rev_parse.args, "%s^0", committish);
    ++  strvec_push(&cp_rev_parse.args, "--");
     +
     +  if (capture_command(&cp_rev_parse, &result, 0))
     +          return NULL;
    @@ builtin/submodule--helper.c: static int module_name(int argc, const char **argv,
     +          cp_log.git_cmd = 1;
     +          cp_log.dir = p->sm_path;
     +          prepare_submodule_repo_env(&cp_log.env_array);
    -+          argv_array_pushl(&cp_log.args, "log", NULL);
    ++          strvec_pushl(&cp_log.args, "log", NULL);
     +
     +          if (S_ISGITLINK(p->mod_src) && S_ISGITLINK(p->mod_dst)) {
     +                  if (info->summary_limit > 0)
    -+                          argv_array_pushf(&cp_log.args, "-%d",
    -+                                           info->summary_limit);
    -+
    -+                  argv_array_pushl(&cp_log.args, "--pretty=  %m %s",
    -+                                   "--first-parent", NULL);
    -+                  argv_array_pushf(&cp_log.args, "%s...%s",
    -+                                   src_abbrev,
    -+                                   dst_abbrev);
    ++                          strvec_pushf(&cp_log.args, "-%d",
    ++                                       info->summary_limit);
    ++
    ++                  strvec_pushl(&cp_log.args, "--pretty=  %m %s",
    ++                               "--first-parent", NULL);
    ++                  strvec_pushf(&cp_log.args, "%s...%s",
    ++                               src_abbrev, dst_abbrev);
     +          } else if (S_ISGITLINK(p->mod_dst)) {
    -+                  argv_array_pushl(&cp_log.args, "--pretty=  > %s",
    -+                                   "-1", dst_abbrev, NULL);
    ++                  strvec_pushl(&cp_log.args, "--pretty=  > %s",
    ++                               "-1", dst_abbrev, NULL);
     +          } else {
    -+                  argv_array_pushl(&cp_log.args, "--pretty=  < %s",
    -+                                   "-1", src_abbrev, NULL);
    ++                  strvec_pushl(&cp_log.args, "--pretty=  < %s",
    ++                               "-1", src_abbrev, NULL);
     +          }
     +          run_command(&cp_log);
     +  }
    @@ builtin/submodule--helper.c: static int module_name(int argc, const char **argv,
     +          struct child_process cp_rev_list = CHILD_PROCESS_INIT;
     +          struct strbuf sb_rev_list = STRBUF_INIT;
     +
    -+          argv_array_pushl(&cp_rev_list.args, "rev-list",
    -+                           "--first-parent", "--count", NULL);
    ++          strvec_pushl(&cp_rev_list.args, "rev-list",
    ++                       "--first-parent", "--count", NULL);
     +          if (S_ISGITLINK(p->mod_src) && S_ISGITLINK(p->mod_dst))
    -+                  argv_array_pushf(&cp_rev_list.args, "%s...%s",
    -+                                   src_abbrev,
    -+                                   dst_abbrev);
    ++                  strvec_pushf(&cp_rev_list.args, "%s...%s",
    ++                               src_abbrev, dst_abbrev);
     +          else
    -+                  argv_array_push(&cp_rev_list.args,
    -+                                  S_ISGITLINK(p->mod_src) ?
    -+                                  src_abbrev :
    -+                                  dst_abbrev);
    -+          argv_array_push(&cp_rev_list.args, "--");
    ++                  strvec_push(&cp_rev_list.args, S_ISGITLINK(p->mod_src) ?
    ++                              src_abbrev : dst_abbrev);
    ++          strvec_push(&cp_rev_list.args, "--");
     +
     +          cp_rev_list.git_cmd = 1;
     +          cp_rev_list.dir = p->sm_path;
    @@ builtin/submodule--helper.c: static int module_name(int argc, const char **argv,
     +                                 struct summary_cb *info,
     +                                 enum diff_cmd diff_cmd)
     +{
    -+  struct argv_array diff_args = ARGV_ARRAY_INIT;
    ++  struct strvec diff_args = STRVEC_INIT;
     +  struct rev_info rev;
     +  struct module_cb_list list = MODULE_CB_LIST_INIT;
     +
    -+  argv_array_push(&diff_args, get_diff_cmd(diff_cmd));
    ++  strvec_push(&diff_args, get_diff_cmd(diff_cmd));
     +  if (info->cached)
    -+          argv_array_push(&diff_args, "--cached");
    -+  argv_array_pushl(&diff_args, "--ignore-submodules=dirty", "--raw",
    -+                   NULL);
    ++          strvec_push(&diff_args, "--cached");
    ++  strvec_pushl(&diff_args, "--ignore-submodules=dirty", "--raw", NULL);
     +  if (head_oid)
    -+          argv_array_push(&diff_args, oid_to_hex(head_oid));
    -+  argv_array_push(&diff_args, "--");
    ++          strvec_push(&diff_args, oid_to_hex(head_oid));
    ++  strvec_push(&diff_args, "--");
     +  if (info->argc)
    -+          argv_array_pushv(&diff_args, info->argv);
    ++          strvec_pushv(&diff_args, info->argv);
     +
     +  git_config(git_diff_basic_config, NULL);
     +  init_revisions(&rev, info->prefix);
     +  rev.abbrev = 0;
    -+  precompose_argv(diff_args.argc, diff_args.argv);
    -+
    -+  diff_args.argc = setup_revisions(diff_args.argc, diff_args.argv,
    -+                                   &rev, NULL);
    ++  precompose_argv(diff_args.nr, diff_args.v);
    ++  setup_revisions(diff_args.nr, diff_args.v, &rev, NULL);
     +  rev.diffopt.output_format = DIFF_FORMAT_NO_OUTPUT | DIFF_FORMAT_CALLBACK;
     +  rev.diffopt.format_callback = submodule_summary_callback;
     +  rev.diffopt.format_callback_data = &list;
    @@ builtin/submodule--helper.c: static int module_name(int argc, const char **argv,
     +  else
     +          run_diff_files(&rev, 0);
     +  prepare_submodule_summary(info, &list);
    ++  strvec_clear(&diff_args);
     +  return 0;
     +}
     +

-----

Prathamesh Chavan (1):
  submodule: port submodule subcommand 'summary' from shell to C

Shourya Shukla (3):
  submodule: remove extra line feeds between callback struct and macro
  submodule: rename helper functions to avoid ambiguity
  t7421: introduce a test script for verifying 'summary' output

 builtin/submodule--helper.c      | 432 ++++++++++++++++++++++++++++++-
 diff.c                           |   2 +-
 git-submodule.sh                 | 186 +------------
 submodule.c                      |  10 +-
 submodule.h                      |   2 +-
 t/t7401-submodule-summary.sh     |   4 +
 t/t7421-submodule-summary-add.sh |  69 +++++
 7 files changed, 510 insertions(+), 195 deletions(-)
 create mode 100755 t/t7421-submodule-summary-add.sh

-- 
2.28.0


^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH v3 1/4] submodule: remove extra line feeds between callback struct and macro
  2020-08-12 19:44 ` [GSoC][PATCH v3 0/4] submodule: port " Shourya Shukla
@ 2020-08-12 19:44   ` Shourya Shukla
  2020-08-12 19:44   ` [PATCH v3 2/4] submodule: rename helper functions to avoid ambiguity Shourya Shukla
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 32+ messages in thread
From: Shourya Shukla @ 2020-08-12 19:44 UTC (permalink / raw)
  To: shouryashukla.oo
  Cc: christian.couder, git, gitster, Johannes.Schindelin,
	kaartic.sivaraam, liu.denton, Christian Couder, Philip Oakley

Many `submodule--helper` subcommands follow the convention that a struct
defines their callback data, and the declaration of that struct is
followed immediately by a macro to use in static initializers, without
any separating empty line.

Let's align the `init`, `status` and `sync` subcommands with that convention.

Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Mentored-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com>
Helped-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Helped-by: Philip Oakley <philipoakley@iee.email>
Signed-off-by: Shourya Shukla <shouryashukla.oo@gmail.com>
---
 builtin/submodule--helper.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index df135abbf1..a03dc84ea4 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -612,7 +612,6 @@ struct init_cb {
 	const char *prefix;
 	unsigned int flags;
 };
-
 #define INIT_CB_INIT { NULL, 0 }
 
 static void init_submodule(const char *path, const char *prefix,
@@ -742,7 +741,6 @@ struct status_cb {
 	const char *prefix;
 	unsigned int flags;
 };
-
 #define STATUS_CB_INIT { NULL, 0 }
 
 static void print_status(unsigned int flags, char state, const char *path,
@@ -933,7 +931,6 @@ struct sync_cb {
 	const char *prefix;
 	unsigned int flags;
 };
-
 #define SYNC_CB_INIT { NULL, 0 }
 
 static void sync_submodule(const char *path, const char *prefix,
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v3 2/4] submodule: rename helper functions to avoid ambiguity
  2020-08-12 19:44 ` [GSoC][PATCH v3 0/4] submodule: port " Shourya Shukla
  2020-08-12 19:44   ` [PATCH v3 1/4] submodule: remove extra line feeds between callback struct and macro Shourya Shukla
@ 2020-08-12 19:44   ` Shourya Shukla
  2020-08-12 19:44   ` [PATCH v3 3/4] t7421: introduce a test script for verifying 'summary' output Shourya Shukla
  2020-08-12 19:44   ` [PATCH v3 4/4] submodule: port submodule subcommand 'summary' from shell to C Shourya Shukla
  3 siblings, 0 replies; 32+ messages in thread
From: Shourya Shukla @ 2020-08-12 19:44 UTC (permalink / raw)
  To: shouryashukla.oo
  Cc: christian.couder, git, gitster, Johannes.Schindelin,
	kaartic.sivaraam, liu.denton, Christian Couder

The helper functions: show_submodule_summary(),
prepare_submodule_summary() and print_submodule_summary() are used by
the builtin_diff() function in diff.c to generate a summary of
submodules in the context of a diff. Functions with similar names are to
be introduced in the upcoming port of submodule's summary subcommand.

So, rename the helper functions to '*_diff_submodule_summary()' to avoid
ambiguity.

Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Mentored-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com>
Signed-off-by: Shourya Shukla <shouryashukla.oo@gmail.com>
---
 diff.c      |  2 +-
 submodule.c | 10 +++++-----
 submodule.h |  2 +-
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/diff.c b/diff.c
index f9709de7b4..46175e40a6 100644
--- a/diff.c
+++ b/diff.c
@@ -3429,7 +3429,7 @@ static void builtin_diff(const char *name_a,
 	if (o->submodule_format == DIFF_SUBMODULE_LOG &&
 	    (!one->mode || S_ISGITLINK(one->mode)) &&
 	    (!two->mode || S_ISGITLINK(two->mode))) {
-		show_submodule_summary(o, one->path ? one->path : two->path,
+		show_submodule_diff_summary(o, one->path ? one->path : two->path,
 				&one->oid, &two->oid,
 				two->dirty_submodule);
 		return;
diff --git a/submodule.c b/submodule.c
index a52b93a87f..8273647b73 100644
--- a/submodule.c
+++ b/submodule.c
@@ -438,7 +438,7 @@ void handle_ignore_submodules_arg(struct diff_options *diffopt,
 	 */
 }
 
-static int prepare_submodule_summary(struct rev_info *rev, const char *path,
+static int prepare_submodule_diff_summary(struct rev_info *rev, const char *path,
 		struct commit *left, struct commit *right,
 		struct commit_list *merge_bases)
 {
@@ -459,7 +459,7 @@ static int prepare_submodule_summary(struct rev_info *rev, const char *path,
 	return prepare_revision_walk(rev);
 }
 
-static void print_submodule_summary(struct repository *r, struct rev_info *rev, struct diff_options *o)
+static void print_submodule_diff_summary(struct repository *r, struct rev_info *rev, struct diff_options *o)
 {
 	static const char format[] = "  %m %s";
 	struct strbuf sb = STRBUF_INIT;
@@ -610,7 +610,7 @@ static void show_submodule_header(struct diff_options *o,
 	strbuf_release(&sb);
 }
 
-void show_submodule_summary(struct diff_options *o, const char *path,
+void show_submodule_diff_summary(struct diff_options *o, const char *path,
 		struct object_id *one, struct object_id *two,
 		unsigned dirty_submodule)
 {
@@ -632,12 +632,12 @@ void show_submodule_summary(struct diff_options *o, const char *path,
 		goto out;
 
 	/* Treat revision walker failure the same as missing commits */
-	if (prepare_submodule_summary(&rev, path, left, right, merge_bases)) {
+	if (prepare_submodule_diff_summary(&rev, path, left, right, merge_bases)) {
 		diff_emit_submodule_error(o, "(revision walker failed)\n");
 		goto out;
 	}
 
-	print_submodule_summary(sub, &rev, o);
+	print_submodule_diff_summary(sub, &rev, o);
 
 out:
 	if (merge_bases)
diff --git a/submodule.h b/submodule.h
index 9ce85c03fe..4ac6e31cf1 100644
--- a/submodule.h
+++ b/submodule.h
@@ -69,7 +69,7 @@ int parse_submodule_update_strategy(const char *value,
 				    struct submodule_update_strategy *dst);
 const char *submodule_strategy_to_string(const struct submodule_update_strategy *s);
 void handle_ignore_submodules_arg(struct diff_options *, const char *);
-void show_submodule_summary(struct diff_options *o, const char *path,
+void show_submodule_diff_summary(struct diff_options *o, const char *path,
 			    struct object_id *one, struct object_id *two,
 			    unsigned dirty_submodule);
 void show_submodule_inline_diff(struct diff_options *o, const char *path,
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v3 3/4] t7421: introduce a test script for verifying 'summary' output
  2020-08-12 19:44 ` [GSoC][PATCH v3 0/4] submodule: port " Shourya Shukla
  2020-08-12 19:44   ` [PATCH v3 1/4] submodule: remove extra line feeds between callback struct and macro Shourya Shukla
  2020-08-12 19:44   ` [PATCH v3 2/4] submodule: rename helper functions to avoid ambiguity Shourya Shukla
@ 2020-08-12 19:44   ` Shourya Shukla
  2020-08-12 19:44   ` [PATCH v3 4/4] submodule: port submodule subcommand 'summary' from shell to C Shourya Shukla
  3 siblings, 0 replies; 32+ messages in thread
From: Shourya Shukla @ 2020-08-12 19:44 UTC (permalink / raw)
  To: shouryashukla.oo
  Cc: christian.couder, git, gitster, Johannes.Schindelin,
	kaartic.sivaraam, liu.denton, Christian Couder

't7401-submodule-summary.sh' uses 'git add' to add submodules. Therefore,
some commands such as 'git submodule init' and 'git submodule deinit'
do not work as expected.

So, introduce a test script for verifying the 'summary' output for
submodules added using 'git submodule add' and notify regarding the
above mentioned behaviour in t7401 itself.

Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Mentored-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com>
Signed-off-by: Shourya Shukla <shouryashukla.oo@gmail.com>
---
 t/t7401-submodule-summary.sh     |  4 ++
 t/t7421-submodule-summary-add.sh | 69 ++++++++++++++++++++++++++++++++
 2 files changed, 73 insertions(+)
 create mode 100755 t/t7421-submodule-summary-add.sh

diff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh
index 9bc841d085..45c5d2424e 100755
--- a/t/t7401-submodule-summary.sh
+++ b/t/t7401-submodule-summary.sh
@@ -7,6 +7,10 @@ test_description='Summary support for submodules
 
 This test tries to verify the sanity of summary subcommand of git submodule.
 '
+# NOTE: This test script uses 'git add' instead of 'git submodule add' to add
+# submodules to the superproject. Some submodule subcommands such as init and
+# deinit might not work as expected in this script. t7421 does not have this
+# caveat.
 
 . ./test-lib.sh
 
diff --git a/t/t7421-submodule-summary-add.sh b/t/t7421-submodule-summary-add.sh
new file mode 100755
index 0000000000..829fe26d6d
--- /dev/null
+++ b/t/t7421-submodule-summary-add.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+#
+# Copyright (C) 2020 Shourya Shukla
+#
+
+test_description='Summary support for submodules, adding them using git submodule add
+
+This test script tries to verify the sanity of summary subcommand of git submodule
+while making sure to add submodules using `git submodule add` instead of
+`git add` as done in t7401.
+'
+
+. ./test-lib.sh
+
+test_expect_success 'summary test environment setup' '
+	git init sm &&
+	test_commit -C sm "add file" file file-content file-tag &&
+
+	git submodule add ./sm my-subm &&
+	test_tick &&
+	git commit -m "add submodule"
+'
+
+test_expect_success 'submodule summary output for initialized submodule' '
+	test_commit -C sm "add file2" file2 file2-content file2-tag &&
+	git submodule update --remote &&
+	test_tick &&
+	git commit -m "update submodule" my-subm &&
+	git submodule summary HEAD^ >actual &&
+	rev1=$(git -C sm rev-parse --short HEAD^) &&
+	rev2=$(git -C sm rev-parse --short HEAD) &&
+	cat >expected <<-EOF &&
+	* my-subm ${rev1}...${rev2} (1):
+	  > add file2
+
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'submodule summary output for deinitialized submodule' '
+	git submodule deinit my-subm &&
+	git submodule summary HEAD^ >actual &&
+	test_must_be_empty actual &&
+	git submodule update --init my-subm &&
+	git submodule summary HEAD^ >actual &&
+	rev1=$(git -C sm rev-parse --short HEAD^) &&
+	rev2=$(git -C sm rev-parse --short HEAD) &&
+	cat >expected <<-EOF &&
+	* my-subm ${rev1}...${rev2} (1):
+	  > add file2
+
+	EOF
+	test_cmp expected actual
+'
+
+test_expect_success 'submodule summary output for submodules with changed paths' '
+	git mv my-subm subm &&
+	git commit -m "change submodule path" &&
+	rev=$(git -C sm rev-parse --short HEAD^) &&
+	git submodule summary HEAD^^ -- my-subm >actual 2>err &&
+	test_i18ngrep "fatal:.*my-subm" err &&
+	cat >expected <<-EOF &&
+	* my-subm ${rev}...0000000:
+
+	EOF
+	test_cmp expected actual
+'
+
+test_done
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v3 4/4] submodule: port submodule subcommand 'summary' from shell to C
  2020-08-12 19:44 ` [GSoC][PATCH v3 0/4] submodule: port " Shourya Shukla
                     ` (2 preceding siblings ...)
  2020-08-12 19:44   ` [PATCH v3 3/4] t7421: introduce a test script for verifying 'summary' output Shourya Shukla
@ 2020-08-12 19:44   ` Shourya Shukla
  2020-08-18  2:08     ` Jeff King
                       ` (2 more replies)
  3 siblings, 3 replies; 32+ messages in thread
From: Shourya Shukla @ 2020-08-12 19:44 UTC (permalink / raw)
  To: shouryashukla.oo
  Cc: christian.couder, git, gitster, Johannes.Schindelin,
	kaartic.sivaraam, liu.denton, Prathamesh Chavan,
	Christian Couder, Stefan Beller

From: Prathamesh Chavan <pc44800@gmail.com>

Convert submodule subcommand 'summary' to a builtin and call it via
'git-submodule.sh'.

The shell version had to call $diff_cmd twice, once to find the modified
modules cared by the user and then again, with that list of modules
to do various operations for computing the summary of those modules.
On the other hand, the C version does not need a second call to
$diff_cmd since it reuses the module list from the first call to do the
aforementioned tasks.

In the C version, we use the combination of setting a child process'
working directory to the submodule path and then calling
'prepare_submodule_repo_env()' which also sets the 'GIT_DIR' to '.git',
so that we can be certain that those spawned processes will not access
the superproject's ODB by mistake.

A behavioural difference between the C and the shell version is that the
shell version outputs two line feeds after the 'git log' output when run
outside of the tests while the C version outputs one line feed in any
case. The reason for this is that the shell version calls log with
'--pretty=format:<fmt>' whose output is followed by two echo
calls; 'format' does not have "terminator" semantics like its 'tformat'
counterpart. So, the log output is terminated by a newline only when
invoked by the user and not when invoked from the scripts. This results
in the one & two line feed differences in the shell version.
On the other hand, the C version calls log with '--pretty=<fmt>'
which is equivalent to '--pretty:tformat:<fmt>' which is then
followed by a 'printf("\n")'. Due to its "terminator" semantics the
log output is always terminated by newline and hence one line feed in
any case.

Also, when we try to pass an option-like argument after a non-option
argument, for instance:

    git submodule summary HEAD --foo-bar

    (or)

    git submodule summary HEAD --cached

That argument would be treated like a path to the submodule for which
the user is requesting a summary. So, the option ends up having no
effect. Though, passing '--quiet' is an exception to this:

    git submodule summary HEAD --quiet

While 'summary' doesn't support '--quiet', we don't get an output for
the above command as '--quiet' is treated as a path which means we get
an output only if a submodule whose path is '--quiet' exists.

The error message in case of computing a summary for non-existent
submodules in the C version is different from that of the shell version.
Since the new error message is not marked for translation, change the
'test_i18ngrep' in t7421.4 to 'grep'.

Mentored-by: Christian Couder <chriscool@tuxfamily.org>
Mentored-by: Stefan Beller <stefanbeller@gmail.com>
Mentored-by: Kaartic Sivaraam <kaartic.sivaraam@gmail.com>
Helped-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Prathamesh Chavan <pc44800@gmail.com>
Signed-off-by: Shourya Shukla <shouryashukla.oo@gmail.com>
---
 builtin/submodule--helper.c      | 429 +++++++++++++++++++++++++++++++
 git-submodule.sh                 | 186 +-------------
 t/t7421-submodule-summary-add.sh |   2 +-
 3 files changed, 431 insertions(+), 186 deletions(-)

diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index a03dc84ea4..63ea39025d 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -927,6 +927,434 @@ static int module_name(int argc, const char **argv, const char *prefix)
 	return 0;
 }
 
+struct module_cb {
+	unsigned int mod_src;
+	unsigned int mod_dst;
+	struct object_id oid_src;
+	struct object_id oid_dst;
+	char status;
+	const char *sm_path;
+};
+#define MODULE_CB_INIT { 0, 0, NULL, NULL, '\0', NULL }
+
+struct module_cb_list {
+	struct module_cb **entries;
+	int alloc, nr;
+};
+#define MODULE_CB_LIST_INIT { NULL, 0, 0 }
+
+struct summary_cb {
+	int argc;
+	const char **argv;
+	const char *prefix;
+	unsigned int cached: 1;
+	unsigned int for_status: 1;
+	unsigned int files: 1;
+	int summary_limit;
+};
+#define SUMMARY_CB_INIT { 0, NULL, NULL, 0, 0, 0, 0 }
+
+enum diff_cmd {
+	DIFF_INDEX,
+	DIFF_FILES
+};
+
+static char* verify_submodule_committish(const char *sm_path,
+					 const char *committish)
+{
+	struct child_process cp_rev_parse = CHILD_PROCESS_INIT;
+	struct strbuf result = STRBUF_INIT;
+
+	cp_rev_parse.git_cmd = 1;
+	cp_rev_parse.dir = sm_path;
+	prepare_submodule_repo_env(&cp_rev_parse.env_array);
+	strvec_pushl(&cp_rev_parse.args, "rev-parse", "-q", "--short", NULL);
+	strvec_pushf(&cp_rev_parse.args, "%s^0", committish);
+	strvec_push(&cp_rev_parse.args, "--");
+
+	if (capture_command(&cp_rev_parse, &result, 0))
+		return NULL;
+
+	strbuf_trim_trailing_newline(&result);
+	return strbuf_detach(&result, NULL);
+}
+
+static void print_submodule_summary(struct summary_cb *info, char* errmsg,
+				    int total_commits, const char *displaypath,
+				    const char *src_abbrev, const char *dst_abbrev,
+				    int missing_src, int missing_dst,
+				    struct module_cb *p)
+{
+	if (p->status == 'T') {
+		if (S_ISGITLINK(p->mod_dst))
+			printf(_("* %s %s(blob)->%s(submodule)"),
+				 displaypath, src_abbrev, dst_abbrev);
+		else
+			printf(_("* %s %s(submodule)->%s(blob)"),
+				 displaypath, src_abbrev, dst_abbrev);
+	} else {
+		printf("* %s %s...%s",
+			displaypath, src_abbrev, dst_abbrev);
+	}
+
+	if (total_commits < 0)
+		printf(":\n");
+	else
+		printf(" (%d):\n", total_commits);
+
+	if (errmsg) {
+		printf(_("%s"), errmsg);
+	} else if (total_commits > 0) {
+		struct child_process cp_log = CHILD_PROCESS_INIT;
+
+		cp_log.git_cmd = 1;
+		cp_log.dir = p->sm_path;
+		prepare_submodule_repo_env(&cp_log.env_array);
+		strvec_pushl(&cp_log.args, "log", NULL);
+
+		if (S_ISGITLINK(p->mod_src) && S_ISGITLINK(p->mod_dst)) {
+			if (info->summary_limit > 0)
+				strvec_pushf(&cp_log.args, "-%d",
+					     info->summary_limit);
+
+			strvec_pushl(&cp_log.args, "--pretty=  %m %s",
+				     "--first-parent", NULL);
+			strvec_pushf(&cp_log.args, "%s...%s",
+				     src_abbrev, dst_abbrev);
+		} else if (S_ISGITLINK(p->mod_dst)) {
+			strvec_pushl(&cp_log.args, "--pretty=  > %s",
+				     "-1", dst_abbrev, NULL);
+		} else {
+			strvec_pushl(&cp_log.args, "--pretty=  < %s",
+				     "-1", src_abbrev, NULL);
+		}
+		run_command(&cp_log);
+	}
+	printf("\n");
+}
+
+static void generate_submodule_summary(struct summary_cb *info,
+				       struct module_cb *p)
+{
+	char *displaypath, *src_abbrev, *dst_abbrev;
+	int missing_src = 0, missing_dst = 0;
+	char *errmsg = NULL;
+	int total_commits = -1;
+
+	if (!info->cached && oideq(&p->oid_dst, &null_oid)) {
+		if (S_ISGITLINK(p->mod_dst)) {
+			struct ref_store *refs = get_submodule_ref_store(p->sm_path);
+			if (refs)
+				refs_head_ref(refs, handle_submodule_head_ref, &p->oid_dst);
+		} else if (S_ISLNK(p->mod_dst) || S_ISREG(p->mod_dst)) {
+			struct stat st;
+			int fd = open(p->sm_path, O_RDONLY);
+
+			if (fd < 0 || fstat(fd, &st) < 0 ||
+			    index_fd(&the_index, &p->oid_dst, fd, &st, OBJ_BLOB,
+				     p->sm_path, 0))
+				error(_("couldn't hash object from '%s'"), p->sm_path);
+		} else {
+			/* for a submodule removal (mode:0000000), don't warn */
+			if (p->mod_dst)
+				warning(_("unexpected mode %d\n"), p->mod_dst);
+		}
+	}
+
+	if (S_ISGITLINK(p->mod_src)) {
+		src_abbrev = verify_submodule_committish(p->sm_path,
+							 oid_to_hex(&p->oid_src));
+		if (!src_abbrev) {
+			missing_src = 1;
+			/*
+			 * As `rev-parse` failed, we fallback to getting
+			 * the abbreviated hash using oid_src. We do
+			 * this as we might still need the abbreviated
+			 * hash in cases like a submodule type change, etc.
+			 */
+			src_abbrev = xstrndup(oid_to_hex(&p->oid_src), 7);
+		}
+	} else {
+		/*
+		 * The source does not point to a submodule.
+		 * So, we fallback to getting the abbreviation using
+		 * oid_src as we might still need the abbreviated
+		 * hash in cases like submodule add, etc.
+		 */
+		src_abbrev = xstrndup(oid_to_hex(&p->oid_src), 7);
+	}
+
+	if (S_ISGITLINK(p->mod_dst)) {
+		dst_abbrev = verify_submodule_committish(p->sm_path,
+							 oid_to_hex(&p->oid_dst));
+		if (!dst_abbrev) {
+			missing_dst = 1;
+			/*
+			 * As `rev-parse` failed, we fallback to getting
+			 * the abbreviated hash using oid_dst. We do
+			 * this as we might still need the abbreviated
+			 * hash in cases like a submodule type change, etc.
+			 */
+			dst_abbrev = xstrndup(oid_to_hex(&p->oid_dst), 7);
+		}
+	} else {
+		/*
+		 * The destination does not point to a submodule.
+		 * So, we fallback to getting the abbreviation using
+		 * oid_dst as we might still need the abbreviated
+		 * hash in cases like a submodule removal, etc.
+		 */
+		dst_abbrev = xstrndup(oid_to_hex(&p->oid_dst), 7);
+	}
+
+	displaypath = get_submodule_displaypath(p->sm_path, info->prefix);
+
+	if (!missing_src && !missing_dst) {
+		struct child_process cp_rev_list = CHILD_PROCESS_INIT;
+		struct strbuf sb_rev_list = STRBUF_INIT;
+
+		strvec_pushl(&cp_rev_list.args, "rev-list",
+			     "--first-parent", "--count", NULL);
+		if (S_ISGITLINK(p->mod_src) && S_ISGITLINK(p->mod_dst))
+			strvec_pushf(&cp_rev_list.args, "%s...%s",
+				     src_abbrev, dst_abbrev);
+		else
+			strvec_push(&cp_rev_list.args, S_ISGITLINK(p->mod_src) ?
+				    src_abbrev : dst_abbrev);
+		strvec_push(&cp_rev_list.args, "--");
+
+		cp_rev_list.git_cmd = 1;
+		cp_rev_list.dir = p->sm_path;
+		prepare_submodule_repo_env(&cp_rev_list.env_array);
+
+		if (!capture_command(&cp_rev_list, &sb_rev_list, 0))
+			total_commits = atoi(sb_rev_list.buf);
+
+		strbuf_release(&sb_rev_list);
+	} else {
+		/*
+		 * Don't give error msg for modification whose dst is not
+		 * submodule, i.e., deleted or changed to blob
+		 */
+		if (S_ISGITLINK(p->mod_dst)) {
+			struct strbuf errmsg_str = STRBUF_INIT;
+			if (missing_src && missing_dst) {
+				strbuf_addf(&errmsg_str, "  Warn: %s doesn't contain commits %s and %s\n",
+					    displaypath, oid_to_hex(&p->oid_src),
+					    oid_to_hex(&p->oid_dst));
+			} else {
+				strbuf_addf(&errmsg_str, "  Warn: %s doesn't contain commit %s\n",
+					    displaypath, missing_src ?
+					    oid_to_hex(&p->oid_src) :
+					    oid_to_hex(&p->oid_dst));
+			}
+			errmsg = strbuf_detach(&errmsg_str, NULL);
+		}
+	}
+
+	print_submodule_summary(info, errmsg, total_commits,
+				displaypath, src_abbrev,
+				dst_abbrev, missing_src,
+				missing_dst, p);
+
+	free(displaypath);
+	free(src_abbrev);
+	free(dst_abbrev);
+}
+
+static void prepare_submodule_summary(struct summary_cb *info,
+				      struct module_cb_list *list)
+{
+	int i;
+	for (i = 0; i < list->nr; i++) {
+		const struct submodule *sub;
+		struct module_cb *p = list->entries[i];
+		struct strbuf sm_gitdir = STRBUF_INIT;
+
+		if (p->status == 'D' || p->status == 'T') {
+			generate_submodule_summary(info, p);
+			continue;
+		}
+
+		if (info->for_status && p->status != 'A' &&
+		    (sub = submodule_from_path(the_repository,
+					       &null_oid, p->sm_path))) {
+			char *config_key = NULL;
+			const char *value;
+			int ignore_all = 0;
+
+			config_key = xstrfmt("submodule.%s.ignore",
+					     sub->name);
+			if (!git_config_get_string_const(config_key, &value))
+				ignore_all = !strcmp(value, "all");
+			else if (sub->ignore)
+				ignore_all = !strcmp(sub->ignore, "all");
+
+			free(config_key);
+			if (ignore_all)
+				continue;
+		}
+
+		/* Also show added or modified modules which are checked out */
+		strbuf_addstr(&sm_gitdir, p->sm_path);
+		if (is_nonbare_repository_dir(&sm_gitdir))
+			generate_submodule_summary(info, p);
+		strbuf_release(&sm_gitdir);
+	}
+}
+
+static void submodule_summary_callback(struct diff_queue_struct *q,
+				       struct diff_options *options,
+				       void *data)
+{
+	int i;
+	struct module_cb_list *list = data;
+	for (i = 0; i < q->nr; i++) {
+		struct diff_filepair *p = q->queue[i];
+		struct module_cb *temp;
+
+		if (!S_ISGITLINK(p->one->mode) && !S_ISGITLINK(p->two->mode))
+			continue;
+		temp = (struct module_cb*)malloc(sizeof(struct module_cb));
+		temp->mod_src = p->one->mode;
+		temp->mod_dst = p->two->mode;
+		temp->oid_src = p->one->oid;
+		temp->oid_dst = p->two->oid;
+		temp->status = p->status;
+		temp->sm_path = xstrdup(p->one->path);
+
+		ALLOC_GROW(list->entries, list->nr + 1, list->alloc);
+		list->entries[list->nr++] = temp;
+	}
+}
+
+static const char *get_diff_cmd(enum diff_cmd diff_cmd)
+{
+	switch (diff_cmd) {
+	case DIFF_INDEX: return "diff-index";
+	case DIFF_FILES: return "diff-files";
+	default: BUG("bad diff_cmd value %d", diff_cmd);
+	}
+}
+
+static int compute_summary_module_list(struct object_id *head_oid,
+				       struct summary_cb *info,
+				       enum diff_cmd diff_cmd)
+{
+	struct strvec diff_args = STRVEC_INIT;
+	struct rev_info rev;
+	struct module_cb_list list = MODULE_CB_LIST_INIT;
+
+	strvec_push(&diff_args, get_diff_cmd(diff_cmd));
+	if (info->cached)
+		strvec_push(&diff_args, "--cached");
+	strvec_pushl(&diff_args, "--ignore-submodules=dirty", "--raw", NULL);
+	if (head_oid)
+		strvec_push(&diff_args, oid_to_hex(head_oid));
+	strvec_push(&diff_args, "--");
+	if (info->argc)
+		strvec_pushv(&diff_args, info->argv);
+
+	git_config(git_diff_basic_config, NULL);
+	init_revisions(&rev, info->prefix);
+	rev.abbrev = 0;
+	precompose_argv(diff_args.nr, diff_args.v);
+	setup_revisions(diff_args.nr, diff_args.v, &rev, NULL);
+	rev.diffopt.output_format = DIFF_FORMAT_NO_OUTPUT | DIFF_FORMAT_CALLBACK;
+	rev.diffopt.format_callback = submodule_summary_callback;
+	rev.diffopt.format_callback_data = &list;
+
+	if (!info->cached) {
+		if (diff_cmd == DIFF_INDEX)
+			setup_work_tree();
+		if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
+			perror("read_cache_preload");
+			return -1;
+		}
+	} else if (read_cache() < 0) {
+		perror("read_cache");
+		return -1;
+	}
+
+	if (diff_cmd == DIFF_INDEX)
+		run_diff_index(&rev, info->cached);
+	else
+		run_diff_files(&rev, 0);
+	prepare_submodule_summary(info, &list);
+	strvec_clear(&diff_args);
+	return 0;
+}
+
+static int module_summary(int argc, const char **argv, const char *prefix)
+{
+	struct summary_cb info = SUMMARY_CB_INIT;
+	int cached = 0;
+	int for_status = 0;
+	int files = 0;
+	int summary_limit = -1;
+	enum diff_cmd diff_cmd = DIFF_INDEX;
+	struct object_id head_oid;
+	int ret;
+
+	struct option module_summary_options[] = {
+		OPT_BOOL(0, "cached", &cached,
+			 N_("use the commit stored in the index instead of the submodule HEAD")),
+		OPT_BOOL(0, "files", &files,
+			 N_("to compare the commit in the index with that in the submodule HEAD")),
+		OPT_BOOL(0, "for-status", &for_status,
+			 N_("skip submodules with 'ignore_config' value set to 'all'")),
+		OPT_INTEGER('n', "summary-limit", &summary_limit,
+			     N_("limit the summary size")),
+		OPT_END()
+	};
+
+	const char *const git_submodule_helper_usage[] = {
+		N_("git submodule--helper summary [<options>] [commit] [--] [<path>]"),
+		NULL
+	};
+
+	argc = parse_options(argc, argv, prefix, module_summary_options,
+			     git_submodule_helper_usage, 0);
+
+	if (!summary_limit)
+		return 0;
+
+	if (!get_oid(argc ? argv[0] : "HEAD", &head_oid)) {
+		if (argc) {
+			argv++;
+			argc--;
+		}
+	} else if (!argc || !strcmp(argv[0], "HEAD")) {
+		/* before the first commit: compare with an empty tree */
+		oidcpy(&head_oid, the_hash_algo->empty_tree);
+		if (argc) {
+			argv++;
+			argc--;
+		}
+	} else {
+		if (get_oid("HEAD", &head_oid))
+			die(_("could not fetch a revision for HEAD"));
+	}
+
+	if (files) {
+		if (cached)
+			die(_("--cached and --files are mutually exclusive"));
+		diff_cmd = DIFF_FILES;
+	}
+
+	info.argc = argc;
+	info.argv = argv;
+	info.prefix = prefix;
+	info.cached = !!cached;
+	info.files = !!files;
+	info.for_status = !!for_status;
+	info.summary_limit = summary_limit;
+
+	ret = compute_summary_module_list((diff_cmd == DIFF_INDEX) ? &head_oid : NULL,
+					  &info, diff_cmd);
+	return ret;
+}
+
 struct sync_cb {
 	const char *prefix;
 	unsigned int flags;
@@ -2341,6 +2769,7 @@ static struct cmd_struct commands[] = {
 	{"print-default-remote", print_default_remote, 0},
 	{"sync", module_sync, SUPPORT_SUPER_PREFIX},
 	{"deinit", module_deinit, 0},
+	{"summary", module_summary, SUPPORT_SUPER_PREFIX},
 	{"remote-branch", resolve_remote_submodule_branch, 0},
 	{"push-check", push_check, 0},
 	{"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
diff --git a/git-submodule.sh b/git-submodule.sh
index 43eb6051d2..6fb12585cb 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -59,31 +59,6 @@ die_if_unmatched ()
 	fi
 }
 
-#
-# Print a submodule configuration setting
-#
-# $1 = submodule name
-# $2 = option name
-# $3 = default value
-#
-# Checks in the usual git-config places first (for overrides),
-# otherwise it falls back on .gitmodules.  This allows you to
-# distribute project-wide defaults in .gitmodules, while still
-# customizing individual repositories if necessary.  If the option is
-# not in .gitmodules either, print a default value.
-#
-get_submodule_config () {
-	name="$1"
-	option="$2"
-	default="$3"
-	value=$(git config submodule."$name"."$option")
-	if test -z "$value"
-	then
-		value=$(git submodule--helper config submodule."$name"."$option")
-	fi
-	printf '%s' "${value:-$default}"
-}
-
 isnumber()
 {
 	n=$(($1 + 0)) 2>/dev/null && test "$n" = "$1"
@@ -831,166 +806,7 @@ cmd_summary() {
 		shift
 	done
 
-	test $summary_limit = 0 && return
-
-	if rev=$(git rev-parse -q --verify --default HEAD ${1+"$1"})
-	then
-		head=$rev
-		test $# = 0 || shift
-	elif test -z "$1" || test "$1" = "HEAD"
-	then
-		# before the first commit: compare with an empty tree
-		head=$(git hash-object -w -t tree --stdin </dev/null)
-		test -z "$1" || shift
-	else
-		head="HEAD"
-	fi
-
-	if [ -n "$files" ]
-	then
-		test -n "$cached" &&
-		die "$(gettext "The --cached option cannot be used with the --files option")"
-		diff_cmd=diff-files
-		head=
-	fi
-
-	cd_to_toplevel
-	eval "set $(git rev-parse --sq --prefix "$wt_prefix" -- "$@")"
-	# Get modified modules cared by user
-	modules=$(git $diff_cmd $cached --ignore-submodules=dirty --raw $head -- "$@" |
-		sane_egrep '^:([0-7]* )?160000' |
-		while read -r mod_src mod_dst sha1_src sha1_dst status sm_path
-		do
-			# Always show modules deleted or type-changed (blob<->module)
-			if test "$status" = D || test "$status" = T
-			then
-				printf '%s\n' "$sm_path"
-				continue
-			fi
-			# Respect the ignore setting for --for-status.
-			if test -n "$for_status"
-			then
-				name=$(git submodule--helper name "$sm_path")
-				ignore_config=$(get_submodule_config "$name" ignore none)
-				test $status != A && test $ignore_config = all && continue
-			fi
-			# Also show added or modified modules which are checked out
-			GIT_DIR="$sm_path/.git" git rev-parse --git-dir >/dev/null 2>&1 &&
-			printf '%s\n' "$sm_path"
-		done
-	)
-
-	test -z "$modules" && return
-
-	git $diff_cmd $cached --ignore-submodules=dirty --raw $head -- $modules |
-	sane_egrep '^:([0-7]* )?160000' |
-	cut -c2- |
-	while read -r mod_src mod_dst sha1_src sha1_dst status name
-	do
-		if test -z "$cached" &&
-			is_zero_oid $sha1_dst
-		then
-			case "$mod_dst" in
-			160000)
-				sha1_dst=$(GIT_DIR="$name/.git" git rev-parse HEAD)
-				;;
-			100644 | 100755 | 120000)
-				sha1_dst=$(git hash-object $name)
-				;;
-			000000)
-				;; # removed
-			*)
-				# unexpected type
-				eval_gettextln "unexpected mode \$mod_dst" >&2
-				continue ;;
-			esac
-		fi
-		missing_src=
-		missing_dst=
-
-		test $mod_src = 160000 &&
-		! GIT_DIR="$name/.git" git rev-parse -q --verify $sha1_src^0 >/dev/null &&
-		missing_src=t
-
-		test $mod_dst = 160000 &&
-		! GIT_DIR="$name/.git" git rev-parse -q --verify $sha1_dst^0 >/dev/null &&
-		missing_dst=t
-
-		display_name=$(git submodule--helper relative-path "$name" "$wt_prefix")
-
-		total_commits=
-		case "$missing_src,$missing_dst" in
-		t,)
-			errmsg="$(eval_gettext "  Warn: \$display_name doesn't contain commit \$sha1_src")"
-			;;
-		,t)
-			errmsg="$(eval_gettext "  Warn: \$display_name doesn't contain commit \$sha1_dst")"
-			;;
-		t,t)
-			errmsg="$(eval_gettext "  Warn: \$display_name doesn't contain commits \$sha1_src and \$sha1_dst")"
-			;;
-		*)
-			errmsg=
-			total_commits=$(
-			if test $mod_src = 160000 && test $mod_dst = 160000
-			then
-				range="$sha1_src...$sha1_dst"
-			elif test $mod_src = 160000
-			then
-				range=$sha1_src
-			else
-				range=$sha1_dst
-			fi
-			GIT_DIR="$name/.git" \
-			git rev-list --first-parent $range -- | wc -l
-			)
-			total_commits=" ($(($total_commits + 0)))"
-			;;
-		esac
-
-		sha1_abbr_src=$(GIT_DIR="$name/.git" git rev-parse --short $sha1_src 2>/dev/null ||
-			echo $sha1_src | cut -c1-7)
-		sha1_abbr_dst=$(GIT_DIR="$name/.git" git rev-parse --short $sha1_dst 2>/dev/null ||
-			echo $sha1_dst | cut -c1-7)
-
-		if test $status = T
-		then
-			blob="$(gettext "blob")"
-			submodule="$(gettext "submodule")"
-			if test $mod_dst = 160000
-			then
-				echo "* $display_name $sha1_abbr_src($blob)->$sha1_abbr_dst($submodule)$total_commits:"
-			else
-				echo "* $display_name $sha1_abbr_src($submodule)->$sha1_abbr_dst($blob)$total_commits:"
-			fi
-		else
-			echo "* $display_name $sha1_abbr_src...$sha1_abbr_dst$total_commits:"
-		fi
-		if test -n "$errmsg"
-		then
-			# Don't give error msg for modification whose dst is not submodule
-			# i.e. deleted or changed to blob
-			test $mod_dst = 160000 && echo "$errmsg"
-		else
-			if test $mod_src = 160000 && test $mod_dst = 160000
-			then
-				limit=
-				test $summary_limit -gt 0 && limit="-$summary_limit"
-				GIT_DIR="$name/.git" \
-				git log $limit --pretty='format:  %m %s' \
-				--first-parent $sha1_src...$sha1_dst
-			elif test $mod_dst = 160000
-			then
-				GIT_DIR="$name/.git" \
-				git log --pretty='format:  > %s' -1 $sha1_dst
-			else
-				GIT_DIR="$name/.git" \
-				git log --pretty='format:  < %s' -1 $sha1_src
-			fi
-			echo
-		fi
-		echo
-	done
+	git ${wt_prefix:+-C "$wt_prefix"} submodule--helper summary ${prefix:+--prefix "$prefix"} ${files:+--files} ${cached:+--cached} ${for_status:+--for-status} ${summary_limit:+-n $summary_limit} -- "$@"
 }
 #
 # List all submodules, prefixed with:
diff --git a/t/t7421-submodule-summary-add.sh b/t/t7421-submodule-summary-add.sh
index 829fe26d6d..59a9b00467 100755
--- a/t/t7421-submodule-summary-add.sh
+++ b/t/t7421-submodule-summary-add.sh
@@ -58,7 +58,7 @@ test_expect_success 'submodule summary output for submodules with changed paths'
 	git commit -m "change submodule path" &&
 	rev=$(git -C sm rev-parse --short HEAD^) &&
 	git submodule summary HEAD^^ -- my-subm >actual 2>err &&
-	test_i18ngrep "fatal:.*my-subm" err &&
+	grep "fatal:.*my-subm" err &&
 	cat >expected <<-EOF &&
 	* my-subm ${rev}...0000000:
 
-- 
2.28.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 4/4] submodule: port submodule subcommand 'summary' from shell to C
  2020-08-12 19:44   ` [PATCH v3 4/4] submodule: port submodule subcommand 'summary' from shell to C Shourya Shukla
@ 2020-08-18  2:08     ` Jeff King
  2020-08-21  5:22       ` Shourya Shukla
  2020-08-21 15:17     ` Johannes Schindelin
  2020-08-24 17:54     ` Junio C Hamano
  2 siblings, 1 reply; 32+ messages in thread
From: Jeff King @ 2020-08-18  2:08 UTC (permalink / raw)
  To: Shourya Shukla
  Cc: christian.couder, git, gitster, Johannes.Schindelin,
	kaartic.sivaraam, liu.denton, Prathamesh Chavan,
	Christian Couder, Stefan Beller

On Thu, Aug 13, 2020 at 01:14:04AM +0530, Shourya Shukla wrote:

> +static void print_submodule_summary(struct summary_cb *info, char* errmsg,
> +				    int total_commits, const char *displaypath,
> +				    const char *src_abbrev, const char *dst_abbrev,
> +				    int missing_src, int missing_dst,
> +				    struct module_cb *p)

The "missing_src" and "missing_dst" parameters in this function are
unused.

I _think_ they can be safely removed, and are not a sign of a bug. We
seem to fully handle them in the calling function. But this is the first
time I looked at the code, and I didn't dig too deeply.

-Peff

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 4/4] submodule: port submodule subcommand 'summary' from shell to C
  2020-08-18  2:08     ` Jeff King
@ 2020-08-21  5:22       ` Shourya Shukla
  0 siblings, 0 replies; 32+ messages in thread
From: Shourya Shukla @ 2020-08-21  5:22 UTC (permalink / raw)
  To: peff
  Cc: Johannes.Schindelin, chriscool, christian.couder, git, gitster,
	kaartic.sivaraam, liu.denton, pc44800, shouryashukla.oo,
	stefanbeller

> The "missing_src" and "missing_dst" parameters in this function are
> unused.

Yes, they are unused.

> I _think_ they can be safely removed, and are not a sign of a bug. We
> seem to fully handle them in the calling function. But this is the first
> time I looked at the code, and I didn't dig too deeply.

Sure. They an be removed. I am sending a patch for the same.


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 4/4] submodule: port submodule subcommand 'summary' from shell to C
  2020-08-12 19:44   ` [PATCH v3 4/4] submodule: port submodule subcommand 'summary' from shell to C Shourya Shukla
  2020-08-18  2:08     ` Jeff King
@ 2020-08-21 15:17     ` Johannes Schindelin
  2020-08-21 16:35       ` Junio C Hamano
  2020-08-24 17:54     ` Junio C Hamano
  2 siblings, 1 reply; 32+ messages in thread
From: Johannes Schindelin @ 2020-08-21 15:17 UTC (permalink / raw)
  To: Shourya Shukla
  Cc: christian.couder, git, gitster, kaartic.sivaraam, liu.denton,
	Prathamesh Chavan, Christian Couder, Stefan Beller

Hi Shourya,

On Thu, 13 Aug 2020, Shourya Shukla wrote:

> [...]
> diff --git a/t/t7421-submodule-summary-add.sh b/t/t7421-submodule-summary-add.sh
> index 829fe26d6d..59a9b00467 100755
> --- a/t/t7421-submodule-summary-add.sh
> +++ b/t/t7421-submodule-summary-add.sh
> @@ -58,7 +58,7 @@ test_expect_success 'submodule summary output for submodules with changed paths'
>  	git commit -m "change submodule path" &&
>  	rev=$(git -C sm rev-parse --short HEAD^) &&
>  	git submodule summary HEAD^^ -- my-subm >actual 2>err &&
> -	test_i18ngrep "fatal:.*my-subm" err &&
> +	grep "fatal:.*my-subm" err &&

Sadly, this breaks on Windows: on Linux (and before this patch, also on
Windows), the error message reads somewhat like this:

	fatal: exec 'rev-parse': cd to 'my-subm' failed: No such file or directory

However, with the built-in `git submodule summary`, on Windows the error
message reads like this:

	error: cannot spawn git: No such file or directory

Now, this is of course not the best way to present this error message, but
please note that even providing a better error message does not fix the
erroneous expectation of the `fatal:` prefix (Git typically produces this
when `die()`ing, which can be done in the POSIX version that uses `fork()`
and `exec()` but not in the Windows version that needs to use
`CreateProcessW()` instead).

Therefore, I propose this patch on top:

-- snipsnap --
[PATCH] mingw: mention if `mingw_spawnve()` failed due to a missing directory

When we recently converted the `summary` subcommand of `git submodule`
to be mostly built-in, a bug was uncovered where a very unhelpful error
message was produced when a process could not be spawned because the
directory in which it was supposed to be run does not exist.

Even so, we _still_ have to adjust the `git submodule summary` test, to
accommodate for the fact that the `mingw_spawnve()` function will return
with an error instead of `die()`ing.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 compat/mingw.c                   | 4 ++++
 t/t7421-submodule-summary-add.sh | 2 +-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/compat/mingw.c b/compat/mingw.c
index 1a64d4efb26b..3c30d0cab589 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -1850,6 +1850,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
 	/* Make sure to override previous errors, if any */
 	errno = 0;

+	if (dir && !is_directory(dir))
+		return error_errno(_("could not exec '%s' in '%s'"),
+				   argv[0], dir);
+
 	if (restrict_handle_inheritance < 0)
 		restrict_handle_inheritance = core_restrict_inherited_handles;
 	/*
diff --git a/t/t7421-submodule-summary-add.sh b/t/t7421-submodule-summary-add.sh
index 59a9b00467dc..f00d69ca29ea 100755
--- a/t/t7421-submodule-summary-add.sh
+++ b/t/t7421-submodule-summary-add.sh
@@ -58,7 +58,7 @@ test_expect_success 'submodule summary output for submodules with changed paths'
 	git commit -m "change submodule path" &&
 	rev=$(git -C sm rev-parse --short HEAD^) &&
 	git submodule summary HEAD^^ -- my-subm >actual 2>err &&
-	grep "fatal:.*my-subm" err &&
+	grep "my-subm" err &&
 	cat >expected <<-EOF &&
 	* my-subm ${rev}...0000000:

--
2.28.0.windows.1


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 4/4] submodule: port submodule subcommand 'summary' from shell to C
  2020-08-21 15:17     ` Johannes Schindelin
@ 2020-08-21 16:35       ` Junio C Hamano
  2020-08-21 17:17         ` Shourya Shukla
  0 siblings, 1 reply; 32+ messages in thread
From: Junio C Hamano @ 2020-08-21 16:35 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Shourya Shukla, christian.couder, git, kaartic.sivaraam,
	liu.denton, Prathamesh Chavan, Christian Couder, Stefan Beller

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> Sadly, this breaks on Windows: on Linux (and before this patch, also on
> Windows), the error message reads somewhat like this:
>
> 	fatal: exec 'rev-parse': cd to 'my-subm' failed: No such file or directory
>
> However, with the built-in `git submodule summary`, on Windows the error
> message reads like this:
>
> 	error: cannot spawn git: No such file or directory

I think a test that relies on platform-specific error string is a
bug.  It's like expecting an exact string out of strerror(), which
we had to fix a few times.

So I am not sure we would want to butcher compat/mingw.c only to
match such an expectation by a (buggy) test.


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 4/4] submodule: port submodule subcommand 'summary' from shell to C
  2020-08-21 16:35       ` Junio C Hamano
@ 2020-08-21 17:17         ` Shourya Shukla
  2020-08-21 18:09           ` Junio C Hamano
  0 siblings, 1 reply; 32+ messages in thread
From: Shourya Shukla @ 2020-08-21 17:17 UTC (permalink / raw)
  To: gitster
  Cc: Johannes.Schindelin, chriscool, christian.couder, git,
	kaartic.sivaraam, liu.denton, pc44800, shouryashukla.oo,
	stefanbeller

> I think a test that relies on platform-specific error string is a
> bug.  It's like expecting an exact string out of strerror(), which
> we had to fix a few times.

> So I am not sure we would want to butcher compat/mingw.c only to
> match such an expectation by a (buggy) test.

Alright. That is understandable. What alternative do you suggest? Should
we change the check in the test?


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 4/4] submodule: port submodule subcommand 'summary' from shell to C
  2020-08-21 17:17         ` Shourya Shukla
@ 2020-08-21 18:09           ` Junio C Hamano
  2020-08-21 18:54             ` Kaartic Sivaraam
  0 siblings, 1 reply; 32+ messages in thread
From: Junio C Hamano @ 2020-08-21 18:09 UTC (permalink / raw)
  To: Shourya Shukla
  Cc: Johannes.Schindelin, chriscool, christian.couder, git,
	kaartic.sivaraam, liu.denton, pc44800, stefanbeller

Shourya Shukla <shouryashukla.oo@gmail.com> writes:

>> I think a test that relies on platform-specific error string is a
>> bug.  It's like expecting an exact string out of strerror(), which
>> we had to fix a few times.
>
>> So I am not sure we would want to butcher compat/mingw.c only to
>> match such an expectation by a (buggy) test.
>
> Alright. That is understandable. What alternative do you suggest? Should
> we change the check in the test?

A buggy check should of course be changed.

It should be sufficient to ensure "git submodule summary" fails,
regardless of what exact error message it issues, no?

If the command does not exit with non-zero exit status, when it
gives a "fatal" error message, that may indicate another bug,
though.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 4/4] submodule: port submodule subcommand 'summary' from shell to C
  2020-08-21 18:09           ` Junio C Hamano
@ 2020-08-21 18:54             ` Kaartic Sivaraam
  2020-08-21 19:54               ` Junio C Hamano
  0 siblings, 1 reply; 32+ messages in thread
From: Kaartic Sivaraam @ 2020-08-21 18:54 UTC (permalink / raw)
  To: Junio C Hamano, Shourya Shukla
  Cc: Johannes.Schindelin, chriscool, christian.couder, git,
	liu.denton, pc44800, stefanbeller

On Fri, 2020-08-21 at 11:09 -0700, Junio C Hamano wrote:
> Shourya Shukla <shouryashukla.oo@gmail.com> writes:
> 
> > > I think a test that relies on platform-specific error string is a
> > > bug.  It's like expecting an exact string out of strerror(),
> > > which
> > > we had to fix a few times.
> > > So I am not sure we would want to butcher compat/mingw.c only to
> > > match such an expectation by a (buggy) test.
> > 
> > Alright. That is understandable. What alternative do you suggest?
> > Should
> > we change the check in the test?
> 
> A buggy check should of course be changed.
> 
> It should be sufficient to ensure "git submodule summary" fails,
> regardless of what exact error message it issues, no?
> 

Unfortunately, we can't do that here. See below.

> If the command does not exit with non-zero exit status, when it
> gives a "fatal" error message, that may indicate another bug,
> though.

Here's the error message with context of the trash directory of that
test:

-- 8< --
$ cd t
$ ./t7421-submodule-summary-add.sh  -d
$ cd trash\ directory.t7421-submodule-summary-add/

$ git submodule summary HEAD^^
fatal: exec 'rev-parse': cd to 'my-subm' failed: No such file or directory
* my-subm 35b40f1...0000000:

* subm 0000000...dbd5fc8 (2):
  > add file2

-- >8 --

That 'fatal' is a consequence of spawning a process in
`verify_submodule_committish` of `builtin/submodule--helper.c` even for
non-existent submodules. I don't think that 'fatal: ' message is giving
any useful information here. The fact that submodule 'my-subm' doesn't
exist can easily be inferred just by looking at the destination mode
(0000000). If anything that 'fatal' message is just confusing and
unnecessary, IMO.

So, we could easily suppress it by doing something like this (while
also fixing the test):

-- 8< --
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 63ea39025d..d45be7fbdf 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -972,7 +972,7 @@ static char* verify_submodule_committish(const char *sm_path,
        strvec_pushf(&cp_rev_parse.args, "%s^0", committish);
        strvec_push(&cp_rev_parse.args, "--");
 
-       if (capture_command(&cp_rev_parse, &result, 0))
+       if (!is_directory(sm_path) || capture_command(&cp_rev_parse, &result, 0))
                return NULL;
 
        strbuf_trim_trailing_newline(&result);
diff --git a/t/t7421-submodule-summary-add.sh b/t/t7421-submodule-summary-add.sh
index 59a9b00467..8a2c2b38b6 100755
--- a/t/t7421-submodule-summary-add.sh
+++ b/t/t7421-submodule-summary-add.sh
@@ -58,7 +58,6 @@ test_expect_success 'submodule summary output for submodules with changed paths'
        git commit -m "change submodule path" &&
        rev=$(git -C sm rev-parse --short HEAD^) &&
        git submodule summary HEAD^^ -- my-subm >actual 2>err &&
-       grep "fatal:.*my-subm" err &&
        cat >expected <<-EOF &&
        * my-subm ${rev}...0000000:
 
-- >8 --

BTW, I noted that `print_submodule_summary` has the following
definition:

   static void print_submodule_summary(struct summary_cb *info, char* errmsg
   				    ...

Note how '*' is placed near 'char' for 'errmsg' with an incorrect style. Ditto
for the return type of `verify_submodule_committish`. This might make
for a nice cleanup patch.

-- 
Sivaraam



^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 4/4] submodule: port submodule subcommand 'summary' from shell to C
  2020-08-21 18:54             ` Kaartic Sivaraam
@ 2020-08-21 19:54               ` Junio C Hamano
  2020-08-23 20:03                 ` Kaartic Sivaraam
  0 siblings, 1 reply; 32+ messages in thread
From: Junio C Hamano @ 2020-08-21 19:54 UTC (permalink / raw)
  To: Kaartic Sivaraam
  Cc: Shourya Shukla, Johannes.Schindelin, chriscool, christian.couder,
	git, liu.denton, pc44800, stefanbeller

Kaartic Sivaraam <kaartic.sivaraam@gmail.com> writes:

> Here's the error message with context of the trash directory of that
> test:
>
> -- 8< --
> $ cd t
> $ ./t7421-submodule-summary-add.sh  -d
> $ cd trash\ directory.t7421-submodule-summary-add/
>
> $ git submodule summary HEAD^^
> fatal: exec 'rev-parse': cd to 'my-subm' failed: No such file or directory
> * my-subm 35b40f1...0000000:
>
> * subm 0000000...dbd5fc8 (2):
>   > add file2
>
> -- >8 --
>
> That 'fatal' is a consequence of spawning a process in
> `verify_submodule_committish` of `builtin/submodule--helper.c` even for
> non-existent submodules.

Oh, so doing something that would cause the error message to be
emitted itself is a bug.

> I don't think that 'fatal: ' message is giving
> any useful information here. The fact that submodule 'my-subm' doesn't
> exist can easily be inferred just by looking at the destination mode
> (0000000). If anything that 'fatal' message is just confusing and
> unnecessary, IMO.

Yes, I 100% agree.

> So, we could easily suppress it by doing something like this (while
> also fixing the test):

Yup.  That is a very good idea.  

Or the caller of verify_submodule_committish() should refrain from
calling it for the path?  After all, it is checking sm_path is a
path to where a submodule should be before calling the function
(instead of calling it for every random path), iow its criteria to
make the call currently may be "the path in the index says it is a
submodule", but it should easily be updated to "the path in the
index says it is a submodule, and the submodule actually is
populated", right?

> @@ -972,7 +972,7 @@ static char* verify_submodule_committish(const char *sm_path,
>         strvec_pushf(&cp_rev_parse.args, "%s^0", committish);
> ...
> BTW, I noted that `print_submodule_summary` has the following
> definition:
>
>    static void print_submodule_summary(struct summary_cb *info, char* errmsg
>    				    ...
>
> Note how '*' is placed near 'char' for 'errmsg' with an incorrect style. Ditto
> for the return type of `verify_submodule_committish`. This might make
> for a nice cleanup patch.

Yup.  It would have been nicer to catch these before the topic hit
'next'.

Thanks.

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 4/4] submodule: port submodule subcommand 'summary' from shell to C
  2020-08-21 19:54               ` Junio C Hamano
@ 2020-08-23 20:03                 ` Kaartic Sivaraam
  2020-08-23 20:12                   ` Kaartic Sivaraam
  2020-08-24  7:26                   ` Shourya Shukla
  0 siblings, 2 replies; 32+ messages in thread
From: Kaartic Sivaraam @ 2020-08-23 20:03 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Shourya Shukla, Johannes.Schindelin, chriscool, christian.couder,
	git, liu.denton, pc44800, stefanbeller

On Fri, 2020-08-21 at 12:54 -0700, Junio C Hamano wrote:
> Kaartic Sivaraam <kaartic.sivaraam@gmail.com> writes:
> 
> > Here's the error message with context of the trash directory of that
> > test:
> > 
> > -- 8< --
> > $ cd t
> > $ ./t7421-submodule-summary-add.sh  -d
> > $ cd trash\ directory.t7421-submodule-summary-add/
> > 
> > $ git submodule summary HEAD^^
> > fatal: exec 'rev-parse': cd to 'my-subm' failed: No such file or directory
> > * my-subm 35b40f1...0000000:
> > 
> > * subm 0000000...dbd5fc8 (2):
> >   > add file2
> > 
> > -- >8 --
> > 
> > That 'fatal' is a consequence of spawning a process in
> > `verify_submodule_committish` of `builtin/submodule--helper.c` even for
> > non-existent submodules.
> 
> Oh, so doing something that would cause the error message to be
> emitted itself is a bug.
> 

Exactly.

> > I don't think that 'fatal: ' message is giving
> > any useful information here. The fact that submodule 'my-subm' doesn't
> > exist can easily be inferred just by looking at the destination mode
> > (0000000). If anything that 'fatal' message is just confusing and
> > unnecessary, IMO.
> 
> Yes, I 100% agree.
> 
> > So, we could easily suppress it by doing something like this (while
> > also fixing the test):
> 
> Yup.  That is a very good idea.  
> 
> Or the caller of verify_submodule_committish() should refrain from
> calling it for the path?  After all, it is checking sm_path is a
> path to where a submodule should be before calling the function
> (instead of calling it for every random path), iow its criteria to
> make the call currently may be "the path in the index says it is a
> submodule", but it should easily be updated to "the path in the
> index says it is a submodule, and the submodule actually is
> populated", right?
> 

Ah, this reminds me of the initial version of the patch which did
exactly that. Quoting it here for reference:

+	strbuf_addstr(&sm_git_dir_sb, p->sm_path);
+	if (is_nonbare_repository_dir(&sm_git_dir_sb))
+		is_sm_git_dir = 1;
+
+	if (is_sm_git_dir && S_ISGITLINK(p->mod_src))
+		missing_src = verify_submodule_object_name(p->sm_path,
+							   oid_to_hex(&p->oid_src));
+
+	if (is_sm_git_dir && S_ISGITLINK(p->mod_dst))
+		missing_dst = verify_submodule_object_name(p->sm_path,
+							   oid_to_hex(&p->oid_dst));
+

Note: `verify_submodule_object_name` is now renamed to
`verify_submodule_committish`.

That does sound like a sane approach to me. There's not much point in
invoking `rev-parse` in a non-populated (a.k.a. de-initialized) or non-
existent submodule but we removed that check as we thought it was
unnecessary redundant because `capture_command` would fail anyway.
Looks like we failed to notice the additional `fatal` message fallout
then.

Also, I think it would be better to something like the following in
t7421 to ensure that `fatal` doesn't sneak up accidentally in the
future:

-- 8< --
diff --git t/t7421-submodule-summary-add.sh t/t7421-submodule-summary-add.sh
index 59a9b00467..b070f13714 100755
--- t/t7421-submodule-summary-add.sh
+++ t/t7421-submodule-summary-add.sh
@@ -58,7 +58,7 @@ test_expect_success 'submodule summary output for submodules with changed paths'
        git commit -m "change submodule path" &&
        rev=$(git -C sm rev-parse --short HEAD^) &&
        git submodule summary HEAD^^ -- my-subm >actual 2>err &&
-       grep "fatal:.*my-subm" err &&
+       test_must_be_empty err &&
        cat >expected <<-EOF &&
        * my-subm ${rev}...0000000:
 
-- >8 --

-- 
Sivaraam



^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 4/4] submodule: port submodule subcommand 'summary' from shell to C
  2020-08-23 20:03                 ` Kaartic Sivaraam
@ 2020-08-23 20:12                   ` Kaartic Sivaraam
  2020-08-24  7:26                   ` Shourya Shukla
  1 sibling, 0 replies; 32+ messages in thread
From: Kaartic Sivaraam @ 2020-08-23 20:12 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Shourya Shukla, Johannes.Schindelin, chriscool, christian.couder,
	git, liu.denton, pc44800, stefanbeller

On Mon, 2020-08-24 at 01:33 +0530, Kaartic Sivaraam wrote:
> On Fri, 2020-08-21 at 12:54 -0700, Junio C Hamano wrote:
> > Kaartic Sivaraam <kaartic.sivaraam@gmail.com> writes:
> > 
> > > So, we could easily suppress it by doing something like this (while
> > > also fixing the test):
> > 
> > Yup.  That is a very good idea.  
> > 
> > Or the caller of verify_submodule_committish() should refrain from
> > calling it for the path?  After all, it is checking sm_path is a
> > path to where a submodule should be before calling the function
> > (instead of calling it for every random path), iow its criteria to
> > make the call currently may be "the path in the index says it is a
> > submodule", but it should easily be updated to "the path in the
> > index says it is a submodule, and the submodule actually is
> > populated", right?
> > 
> 
> Ah, this reminds me of the initial version of the patch which did
> exactly that. Quoting it here for reference:
> 
> +	strbuf_addstr(&sm_git_dir_sb, p->sm_path);
> +	if (is_nonbare_repository_dir(&sm_git_dir_sb))
> +		is_sm_git_dir = 1;
> +
> +	if (is_sm_git_dir && S_ISGITLINK(p->mod_src))
> +		missing_src = verify_submodule_object_name(p->sm_path,
> +							   oid_to_hex(&p->oid_src));
> +
> +	if (is_sm_git_dir && S_ISGITLINK(p->mod_dst))
> +		missing_dst = verify_submodule_object_name(p->sm_path,
> +							   oid_to_hex(&p->oid_dst));
> +
> 
> Note: `verify_submodule_object_name` is now renamed to
> `verify_submodule_committish`.
> 
> That does sound like a sane approach to me. There's not much point in
> invoking `rev-parse` in a non-populated (a.k.a. de-initialized) or non-
> existent submodule but we removed that check as we thought it was
> unnecessary redundant because `capture_command` would fail anyway.
> Looks like we failed to notice the additional `fatal` message fallout
> then.
> 

Here's a link to the start of the relevant discussion, just in case:

https://lore.kernel.org/git/nycvar.QRO.7.76.6.2007031712160.50@tvgsbejvaqbjf.bet/

> Also, I think it would be better to something like the following in
> t7421 to ensure that `fatal` doesn't sneak up accidentally in the
> future:
> 
> -- 8< --
> diff --git t/t7421-submodule-summary-add.sh t/t7421-submodule-summary-add.sh
> index 59a9b00467..b070f13714 100755
> --- t/t7421-submodule-summary-add.sh
> +++ t/t7421-submodule-summary-add.sh
> @@ -58,7 +58,7 @@ test_expect_success 'submodule summary output for submodules with changed paths'
>         git commit -m "change submodule path" &&
>         rev=$(git -C sm rev-parse --short HEAD^) &&
>         git submodule summary HEAD^^ -- my-subm >actual 2>err &&
> -       grep "fatal:.*my-subm" err &&
> +       test_must_be_empty err &&
>         cat >expected <<-EOF &&
>         * my-subm ${rev}...0000000:
>  
> -- >8 --
> 


-- 
Sivaraam



^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 4/4] submodule: port submodule subcommand 'summary' from shell to C
  2020-08-23 20:03                 ` Kaartic Sivaraam
  2020-08-23 20:12                   ` Kaartic Sivaraam
@ 2020-08-24  7:26                   ` Shourya Shukla
  2020-08-24  8:46                     ` Shourya Shukla
  1 sibling, 1 reply; 32+ messages in thread
From: Shourya Shukla @ 2020-08-24  7:26 UTC (permalink / raw)
  To: Kaartic Sivaraam
  Cc: git, christian.couder, liu.denton, kaartic.sivaraam,
	Johannes.Schindelin, gitster, pc44800, stefanbeller

On 24/08 01:33, Kaartic Sivaraam wrote:
> > Or the caller of verify_submodule_committish() should refrain from
> > calling it for the path?  After all, it is checking sm_path is a
> > path to where a submodule should be before calling the function
> > (instead of calling it for every random path), iow its criteria to
> > make the call currently may be "the path in the index says it is a
> > submodule", but it should easily be updated to "the path in the
> > index says it is a submodule, and the submodule actually is
> > populated", right?
> > 
> 
> Ah, this reminds me of the initial version of the patch which did
> exactly that. Quoting it here for reference:
> 
> +	strbuf_addstr(&sm_git_dir_sb, p->sm_path);
> +	if (is_nonbare_repository_dir(&sm_git_dir_sb))
> +		is_sm_git_dir = 1;
> +
> +	if (is_sm_git_dir && S_ISGITLINK(p->mod_src))
> +		missing_src = verify_submodule_object_name(p->sm_path,
> +							   oid_to_hex(&p->oid_src));
> +
> +	if (is_sm_git_dir && S_ISGITLINK(p->mod_dst))
> +		missing_dst = verify_submodule_object_name(p->sm_path,
> +							   oid_to_hex(&p->oid_dst));
> +
> 
> Note: `verify_submodule_object_name` is now renamed to
> `verify_submodule_committish`.
> 
> That does sound like a sane approach to me. There's not much point in
> invoking `rev-parse` in a non-populated (a.k.a. de-initialized) or non-
> existent submodule but we removed that check as we thought it was
> unnecessary redundant because `capture_command` would fail anyway.
> Looks like we failed to notice the additional `fatal` message fallout
> then.

This is what I have tried to implement after your suggestion:

-----8<-----
strbuf_addstr(&sb, p->sm_path);
	if (is_nonbare_repository_dir(&sb) && S_ISGITLINK(p->mod_src)) {
		src_abbrev = verify_submodule_committish(p->sm_path,
							 oid_to_hex(&p->oid_src));
		if (!src_abbrev) {
			missing_src = 1;
			/*
			 * As `rev-parse` failed, we fallback to getting
			 * the abbreviated hash using oid_src. We do
			 * this as we might still need the abbreviated
			 * hash in cases like a submodule type change, etc.
			 */
			src_abbrev = xstrndup(oid_to_hex(&p->oid_src), 7);
		}
	} else {
		/*
		 * The source does not point to a submodule.
		 * So, we fallback to getting the abbreviation using
		 * oid_src as we might still need the abbreviated
		 * hash in cases like submodule add, etc.
		 */
		src_abbrev = xstrndup(oid_to_hex(&p->oid_src), 7);
	}

	if (is_nonbare_repository_dir(&sb) && S_ISGITLINK(p->mod_dst)) {
		dst_abbrev = verify_submodule_committish(p->sm_path,
							 oid_to_hex(&p->oid_dst));
		if (!dst_abbrev) {
			missing_dst = 1;
			/*
			 * As `rev-parse` failed, we fallback to getting
			 * the abbreviated hash using oid_dst. We do
			 * this as we might still need the abbreviated
			 * hash in cases like a submodule type change, etc.
			 */
			dst_abbrev = xstrndup(oid_to_hex(&p->oid_dst), 7);
		}
	} else {
		/*
		 * The destination does not point to a submodule.
		 * So, we fallback to getting the abbreviation using
		 * oid_dst as we might still need the abbreviated
		 * hash in cases like a submodule removal, etc.
		 */
		dst_abbrev = xstrndup(oid_to_hex(&p->oid_dst), 7);
	}
----->8-----

That is, add another check along with the 'S_ISGITLINK()' one. Now, the
thing is that 'rev-list' (called just after this part) starts to bother
and comes up with its own 'fatal' that the directory rev-list does not
exist.

The thing is that 'missing{src,dst}' should be set to 1 in two cases:

    1. If the hash is not found, i.e, when 'verify_submodule..()'
       returns a NULL. Something which is happening right now as well.

    2. If the SM is not reachable for some reason (maybe it does not
       exist like in our case). Something which is NOT happening right
       now.

Or if having the same variable denote two things does not please you,
then we can create another variable for the second check BUT, we will
have to incorporate checking of that variable in the 

-----8<-----
if (!missing_src && !missing_dst) {
		struct child_process cp_rev_list = CHILD_PROCESS_INIT;
		struct strbuf sb_rev_list = STRBUF_INIT;

		strvec_pushl(&cp_rev_list.args, "rev-list",
			     "--first-parent", "--count", NULL);
                 .........
----->8-----

check.

This way, we hit two birds with one stone:

    1. Bypass the 'verify_submodule..()' call when the SM directory does
       not exist. We can then remove the is_directory() test from the
       'verify_submodule_..()' function.

    2. Avoid a 'rev-{parse,list}' fatal error message and thus pass all
       the tests successfully.

Therefore, the final outcome is something like this:

-----8<-----
	if (is_directory(p->sm_path) && S_ISGITLINK(p->mod_src)) {
		src_abbrev = verify_submodule_committish(p->sm_path,
							 oid_to_hex(&p->oid_src));
		if (!src_abbrev) {
			missing_src = 1;
			/*
			 * As `rev-parse` failed, we fallback to getting
			 * the abbreviated hash using oid_src. We do
			 * this as we might still need the abbreviated
			 * hash in cases like a submodule type change, etc.
			 */
			src_abbrev = xstrndup(oid_to_hex(&p->oid_src), 7);
		}
	} else {
		missing_src = 1;
		/*
		 * The source does not point to a submodule.
		 * So, we fallback to getting the abbreviation using
		 * oid_src as we might still need the abbreviated
		 * hash in cases like submodule add, etc.
		 */
		src_abbrev = xstrndup(oid_to_hex(&p->oid_src), 7);
	}

	if (is_directory(p->sm_path) && S_ISGITLINK(p->mod_dst)) {
		dst_abbrev = verify_submodule_committish(p->sm_path,
							 oid_to_hex(&p->oid_dst));
		if (!dst_abbrev) {
			missing_dst = 1;
			/*
			 * As `rev-parse` failed, we fallback to getting
			 * the abbreviated hash using oid_dst. We do
			 * this as we might still need the abbreviated
			 * hash in cases like a submodule type change, etc.
			 */
			dst_abbrev = xstrndup(oid_to_hex(&p->oid_dst), 7);
		}
	} else {
		missing_dst = 1;
		/*
		 * The destination does not point to a submodule.
		 * So, we fallback to getting the abbreviation using
		 * oid_dst as we might still need the abbreviated
		 * hash in cases like a submodule removal, etc.
		 */
		dst_abbrev = xstrndup(oid_to_hex(&p->oid_dst), 7);
	}
----->8-----

Or if is_directory() does not please you then we can make it
'is_nonbare_..()' too. The outcome will be unchanged.

What are your opinions on this?

> Also, I think it would be better to something like the following in
> t7421 to ensure that `fatal` doesn't sneak up accidentally in the
> future:
> 
> -- 8< --
> diff --git t/t7421-submodule-summary-add.sh t/t7421-submodule-summary-add.sh
> index 59a9b00467..b070f13714 100755
> --- t/t7421-submodule-summary-add.sh
> +++ t/t7421-submodule-summary-add.sh
> @@ -58,7 +58,7 @@ test_expect_success 'submodule summary output for submodules with changed paths'
>         git commit -m "change submodule path" &&
>         rev=$(git -C sm rev-parse --short HEAD^) &&
>         git submodule summary HEAD^^ -- my-subm >actual 2>err &&
> -       grep "fatal:.*my-subm" err &&
> +       test_must_be_empty err &&
>         cat >expected <<-EOF &&
>         * my-subm ${rev}...0000000:
>  
> -- >8 --

Yes, this I will do.


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 4/4] submodule: port submodule subcommand 'summary' from shell to C
  2020-08-24  7:26                   ` Shourya Shukla
@ 2020-08-24  8:46                     ` Shourya Shukla
  2020-08-24 11:08                       ` Kaartic Sivaraam
  0 siblings, 1 reply; 32+ messages in thread
From: Shourya Shukla @ 2020-08-24  8:46 UTC (permalink / raw)
  To: shouryashukla.oo
  Cc: Johannes.Schindelin, christian.couder, git, gitster,
	kaartic.sivaraam, liu.denton, pc44800, stefanbeller

Or rather, we can do this:

-----8<-----
if (S_ISGITLINK(p->mod_src)) {
		struct strbuf sb = STRBUF_INIT;
		strbuf_addstr(&sb, p->sm_path);
		if (is_nonbare_repository_dir(&sb))
			src_abbrev = verify_submodule_committish(p->sm_path,
								                     oid_to_hex(&p->oid_src));
		strbuf_release(&sb);
		if (!src_abbrev) {
			missing_src = 1;
			/*
			 * As `rev-parse` failed, we fallback to getting
			 * the abbreviated hash using oid_src. We do
			 * this as we might still need the abbreviated
			 * hash in cases like a submodule type change, etc.
			 */
			src_abbrev = xstrndup(oid_to_hex(&p->oid_src), 7);
		}
	} else {
		/*
		 * The source does not point to a submodule.
		 * So, we fallback to getting the abbreviation using
		 * oid_src as we might still need the abbreviated
		 * hash in cases like submodule add, etc.
		 */
		src_abbrev = xstrndup(oid_to_hex(&p->oid_src), 7);
	}
----->8-----

Similarly for dst as well. This solution passes all the tests and does
not call 'verify_submodule_committish()' all the time. The previous
approach failed a couple of tests, this one seems fine to me.

How is this one?


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 4/4] submodule: port submodule subcommand 'summary' from shell to C
  2020-08-24  8:46                     ` Shourya Shukla
@ 2020-08-24 11:08                       ` Kaartic Sivaraam
  2020-08-24 17:50                         ` Shourya Shukla
  0 siblings, 1 reply; 32+ messages in thread
From: Kaartic Sivaraam @ 2020-08-24 11:08 UTC (permalink / raw)
  To: Shourya Shukla
  Cc: Johannes.Schindelin, christian.couder, git, gitster, liu.denton,
	pc44800, stefanbeller

On Mon, 2020-08-24 at 14:16 +0530, Shourya Shukla wrote:
> Or rather, we can do this:
> 
> -----8<-----
> if (S_ISGITLINK(p->mod_src)) {
> 		struct strbuf sb = STRBUF_INIT;
> 		strbuf_addstr(&sb, p->sm_path);
> 		if (is_nonbare_repository_dir(&sb))
> 			src_abbrev = verify_submodule_committish(p->sm_path,
> 								                     oid_to_hex(&p->oid_src));
> 		strbuf_release(&sb);
> 		if (!src_abbrev) {
> 			missing_src = 1;
> 			/*
> 			 * As `rev-parse` failed, we fallback to getting
> 			 * the abbreviated hash using oid_src. We do
> 			 * this as we might still need the abbreviated
> 			 * hash in cases like a submodule type change, etc.
> 			 */
> 			src_abbrev = xstrndup(oid_to_hex(&p->oid_src), 7);
> 		}
> 	} else {
> 		/*
> 		 * The source does not point to a submodule.
> 		 * So, we fallback to getting the abbreviation using
> 		 * oid_src as we might still need the abbreviated
> 		 * hash in cases like submodule add, etc.
> 		 */
> 		src_abbrev = xstrndup(oid_to_hex(&p->oid_src), 7);
> 	}
> ----->8-----
> 
> Similarly for dst as well. This solution passes all the tests and does
> not call 'verify_submodule_committish()' all the time. The previous
> approach failed a couple of tests, this one seems fine to me.
> 
> How is this one?
> 

This is more or less what I had in mind initially. But later after
being reminded about the fact that there's a code path which calls
`generate_submodule_summary` only when `is_nonbare_repository_dir`
succeeds, I realized any conditional that uses
`is_nonbare_repository_dir` or the likes of it would be confusing. So,
I think a better approach would be something like:

-- 8< --
diff --git builtin/submodule--helper.c builtin/submodule--helper.c
index 63ea39025d..b490108cd9 100644
--- builtin/submodule--helper.c
+++ builtin/submodule--helper.c
@@ -1036,7 +1036,7 @@ static void print_submodule_summary(struct summary_cb *info, char* errmsg,
 static void generate_submodule_summary(struct summary_cb *info,
                                       struct module_cb *p)
 {
-       char *displaypath, *src_abbrev, *dst_abbrev;
+       char *displaypath, *src_abbrev = NULL, *dst_abbrev;
        int missing_src = 0, missing_dst = 0;
        char *errmsg = NULL;
        int total_commits = -1;
@@ -1062,8 +1062,9 @@ static void generate_submodule_summary(struct summary_cb *info,
        }
 
        if (S_ISGITLINK(p->mod_src)) {
-               src_abbrev = verify_submodule_committish(p->sm_path,
-                                                        oid_to_hex(&p->oid_src));
+               if (p->status != 'D')
+                       src_abbrev = verify_submodule_committish(p->sm_path,
+                                                                oid_to_hex(&p->oid_src));
                if (!src_abbrev) {
                        missing_src = 1;
                        /*
diff --git t/t7421-submodule-summary-add.sh t/t7421-submodule-summary-add.sh
index 59a9b00467..b070f13714 100755
--- t/t7421-submodule-summary-add.sh
+++ t/t7421-submodule-summary-add.sh
@@ -58,7 +58,7 @@ test_expect_success 'submodule summary output for submodules with changed paths'
        git commit -m "change submodule path" &&
        rev=$(git -C sm rev-parse --short HEAD^) &&
        git submodule summary HEAD^^ -- my-subm >actual 2>err &&
-       grep "fatal:.*my-subm" err &&
+       test_must_be_empty err &&
        cat >expected <<-EOF &&
        * my-subm ${rev}...0000000:
 
-- >8 --

I suggest this as the other code path that calls
`generate_submodule_summary` without going through the
`is_nonbare_repository_dir` condition is the one where we get
`p->status` as 'T' (typechange) or 'D' (deleted). We don't have to
worry about 'T' as we would want the hash for the new object anyway.
That leaves us with 'D' which we indeed have to handle.

Note that no such handling is required for the similar portion
corresponding to `dst_abbrev` as the conditional `if (S_ISGITLINK(p-
>mod_dst))` already guards the `verify_submodule_committish` when we
have a status of 'D'.

-- 
Sivaraam


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 4/4] submodule: port submodule subcommand 'summary' from shell to C
  2020-08-24 11:08                       ` Kaartic Sivaraam
@ 2020-08-24 17:50                         ` Shourya Shukla
  0 siblings, 0 replies; 32+ messages in thread
From: Shourya Shukla @ 2020-08-24 17:50 UTC (permalink / raw)
  To: Kaartic Sivaraam
  Cc: Johannes.Schindelin, christian.couder, git, gitster, liu.denton,
	pc44800, stefanbeller

On 24/08 04:38, Kaartic Sivaraam wrote:
> On Mon, 2020-08-24 at 14:16 +0530, Shourya Shukla wrote:
> > Or rather, we can do this:
> > 
> > -----8<-----
> > if (S_ISGITLINK(p->mod_src)) {
> > 		struct strbuf sb = STRBUF_INIT;
> > 		strbuf_addstr(&sb, p->sm_path);
> > 		if (is_nonbare_repository_dir(&sb))
> > 			src_abbrev = verify_submodule_committish(p->sm_path,
> > 								                     oid_to_hex(&p->oid_src));
> > 		strbuf_release(&sb);
> > 		if (!src_abbrev) {
> > 			missing_src = 1;
> > 			/*
> > 			 * As `rev-parse` failed, we fallback to getting
> > 			 * the abbreviated hash using oid_src. We do
> > 			 * this as we might still need the abbreviated
> > 			 * hash in cases like a submodule type change, etc.
> > 			 */
> > 			src_abbrev = xstrndup(oid_to_hex(&p->oid_src), 7);
> > 		}
> > 	} else {
> > 		/*
> > 		 * The source does not point to a submodule.
> > 		 * So, we fallback to getting the abbreviation using
> > 		 * oid_src as we might still need the abbreviated
> > 		 * hash in cases like submodule add, etc.
> > 		 */
> > 		src_abbrev = xstrndup(oid_to_hex(&p->oid_src), 7);
> > 	}
> > ----->8-----
> > 
> > Similarly for dst as well. This solution passes all the tests and does
> > not call 'verify_submodule_committish()' all the time. The previous
> > approach failed a couple of tests, this one seems fine to me.
> > 
> > How is this one?
> > 
> 
> This is more or less what I had in mind initially. But later after
> being reminded about the fact that there's a code path which calls
> `generate_submodule_summary` only when `is_nonbare_repository_dir`
> succeeds, I realized any conditional that uses
> `is_nonbare_repository_dir` or the likes of it would be confusing. So,
> I think a better approach would be something like:

Alright. I understand. The case for which we faced the problem got
called using this part:

		if (p->status == 'D' || p->status == 'T') {
			generate_submodule_summary(info, p);
			continue;
		}

But I understand your concern. I will change this.

> -- 8< --
> diff --git builtin/submodule--helper.c builtin/submodule--helper.c
> index 63ea39025d..b490108cd9 100644
> --- builtin/submodule--helper.c
> +++ builtin/submodule--helper.c
> @@ -1036,7 +1036,7 @@ static void print_submodule_summary(struct summary_cb *info, char* errmsg,
>  static void generate_submodule_summary(struct summary_cb *info,
>                                        struct module_cb *p)
>  {
> -       char *displaypath, *src_abbrev, *dst_abbrev;
> +       char *displaypath, *src_abbrev = NULL, *dst_abbrev;
>         int missing_src = 0, missing_dst = 0;
>         char *errmsg = NULL;
>         int total_commits = -1;
> @@ -1062,8 +1062,9 @@ static void generate_submodule_summary(struct summary_cb *info,
>         }
>  
>         if (S_ISGITLINK(p->mod_src)) {
> -               src_abbrev = verify_submodule_committish(p->sm_path,
> -                                                        oid_to_hex(&p->oid_src));
> +               if (p->status != 'D')
> +                       src_abbrev = verify_submodule_committish(p->sm_path,
> +                                                                oid_to_hex(&p->oid_src));
>                 if (!src_abbrev) {
>                         missing_src = 1;
>                         /*
> diff --git t/t7421-submodule-summary-add.sh t/t7421-submodule-summary-add.sh
> index 59a9b00467..b070f13714 100755
> --- t/t7421-submodule-summary-add.sh
> +++ t/t7421-submodule-summary-add.sh
> @@ -58,7 +58,7 @@ test_expect_success 'submodule summary output for submodules with changed paths'
>         git commit -m "change submodule path" &&
>         rev=$(git -C sm rev-parse --short HEAD^) &&
>         git submodule summary HEAD^^ -- my-subm >actual 2>err &&
> -       grep "fatal:.*my-subm" err &&
> +       test_must_be_empty err &&
>         cat >expected <<-EOF &&
>         * my-subm ${rev}...0000000:
>  
> -- >8 --
> 
> I suggest this as the other code path that calls
> `generate_submodule_summary` without going through the
> `is_nonbare_repository_dir` condition is the one where we get
> `p->status` as 'T' (typechange) or 'D' (deleted). We don't have to
> worry about 'T' as we would want the hash for the new object anyway.
> That leaves us with 'D' which we indeed have to handle.

Oh you did mention it here. Yeah, this is perfect.

> Note that no such handling is required for the similar portion
> corresponding to `dst_abbrev` as the conditional `if (S_ISGITLINK(p-
> >mod_dst))` already guards the `verify_submodule_committish` when we
> have a status of 'D'.

Sure I will keep this in mind.


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v3 4/4] submodule: port submodule subcommand 'summary' from shell to C
  2020-08-12 19:44   ` [PATCH v3 4/4] submodule: port submodule subcommand 'summary' from shell to C Shourya Shukla
  2020-08-18  2:08     ` Jeff King
  2020-08-21 15:17     ` Johannes Schindelin
@ 2020-08-24 17:54     ` Junio C Hamano
  2 siblings, 0 replies; 32+ messages in thread
From: Junio C Hamano @ 2020-08-24 17:54 UTC (permalink / raw)
  To: Shourya Shukla
  Cc: christian.couder, git, Johannes.Schindelin, kaartic.sivaraam,
	liu.denton, Prathamesh Chavan, Christian Couder, Stefan Beller

(A few miniscule things I noticed that are irrelevant to the code
structure discussion).

> +static char* verify_submodule_committish(const char *sm_path,

Style: in C, asterisk sticks to the identifier, not type, i.e.

    static char *verify_submodule_committish(const char *sm_path, ...);

> +static void print_submodule_summary(struct summary_cb *info, char* errmsg,

Likewise; "char *errmsg".

> +static void generate_submodule_summary(struct summary_cb *info,
> +				       struct module_cb *p)
> +{
> +	char *displaypath, *src_abbrev, *dst_abbrev;
> +	int missing_src = 0, missing_dst = 0;
> +	char *errmsg = NULL;
> +	int total_commits = -1;
> +
> +	if (!info->cached && oideq(&p->oid_dst, &null_oid)) {
> +		if (S_ISGITLINK(p->mod_dst)) {
> +...
> +		} else {
> +			/* for a submodule removal (mode:0000000), don't warn */
> +			if (p->mod_dst)
> +				warning(_("unexpected mode %d\n"), p->mod_dst);
> +		}
> +	}

Nobody can read mode bits written in decimal.  Use "%o" instead,
perhaps?

Thanks.

^ permalink raw reply	[flat|nested] 32+ messages in thread

end of thread, other threads:[~2020-08-24 17:54 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-08-06 16:40 [GSoC][PATCH v2 0/5] submodule: port subcommand 'summary' from shell to C Shourya Shukla
2020-08-06 16:40 ` [PATCH v2 1/5] submodule: expose the '--for-status' option of summary Shourya Shukla
2020-08-08 14:40   ` Kaartic Sivaraam
2020-08-08 20:25     ` Christian Couder
2020-08-08 23:26       ` Junio C Hamano
2020-08-06 16:40 ` [PATCH v2 2/5] submodule: remove extra line feeds between callback struct and macro Shourya Shukla
2020-08-06 16:41 ` [PATCH v2 3/5] submodule: rename helper functions to avoid ambiguity Shourya Shukla
2020-08-06 16:41 ` [PATCH v2 4/5] t7421: introduce a test script for verifying 'summary' output Shourya Shukla
2020-08-06 16:41 ` [PATCH v2 5/5] submodule: port submodule subcommand 'summary' from shell to C Shourya Shukla
2020-08-06 22:45   ` Junio C Hamano
2020-08-07 16:31     ` Shourya Shukla
2020-08-07 17:15       ` Junio C Hamano
2020-08-12 19:44 ` [GSoC][PATCH v3 0/4] submodule: port " Shourya Shukla
2020-08-12 19:44   ` [PATCH v3 1/4] submodule: remove extra line feeds between callback struct and macro Shourya Shukla
2020-08-12 19:44   ` [PATCH v3 2/4] submodule: rename helper functions to avoid ambiguity Shourya Shukla
2020-08-12 19:44   ` [PATCH v3 3/4] t7421: introduce a test script for verifying 'summary' output Shourya Shukla
2020-08-12 19:44   ` [PATCH v3 4/4] submodule: port submodule subcommand 'summary' from shell to C Shourya Shukla
2020-08-18  2:08     ` Jeff King
2020-08-21  5:22       ` Shourya Shukla
2020-08-21 15:17     ` Johannes Schindelin
2020-08-21 16:35       ` Junio C Hamano
2020-08-21 17:17         ` Shourya Shukla
2020-08-21 18:09           ` Junio C Hamano
2020-08-21 18:54             ` Kaartic Sivaraam
2020-08-21 19:54               ` Junio C Hamano
2020-08-23 20:03                 ` Kaartic Sivaraam
2020-08-23 20:12                   ` Kaartic Sivaraam
2020-08-24  7:26                   ` Shourya Shukla
2020-08-24  8:46                     ` Shourya Shukla
2020-08-24 11:08                       ` Kaartic Sivaraam
2020-08-24 17:50                         ` Shourya Shukla
2020-08-24 17:54     ` Junio C Hamano

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 NNTP newsgroup(s).