git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [GSoC][PATCH] submodule: port submodule subcommand 'add' from shell to C

From
Kaartic Sivaraam <kaartic.sivaraam@gmail.com>
Date
Aug 24, 2020, 20:30 UTC
Message-ID
<43337924c09119d43c74fdad3f00d4dab76edb51.camel@gmail.com>
In-Reply-To
<xmqq8se36gev.fsf@gitster.c.googlers.com>
On Mon, 2020-08-24 at 11:35 -0700, Junio C Hamano wrote:
Show 16 quoted lines
> Shourya Shukla <shouryashukla.oo@gmail.com> writes:
> 
> > 	if test -z "$force"
> > 	then
> > 		git ls-files --error-unmatch "$sm_path" > /dev/null 2>&1 &&
> > 		die "$(eval_gettext "'\$sm_path' already exists in the index")"
> > 	else
> > 		git ls-files -s "$sm_path" | sane_grep -v "^160000" > /dev/null 2>&1 &&
> > 		die "$(eval_gettext "'\$sm_path' already exists in the index and is not a submodule")"
> > 	fi
> 
> Hmph.  So,
> 
>  - if we are not being 'force'd, we see if there is anything in the
>    index for the path and error out, whether it is a gitlink or not.
> 
Right.
Show 5 quoted lines
>  - if there is 'force' option, we see what the given path is in the
>    index, and if it is already a gitlink, then die.  That sort of
>    makes sense, as long as the remainder of the code deals with the
>    path that is not a submodule in a sensible way.
> 
With `force, I think it's the opposite of what you describe. That is:
    - if there is 'force' option, we see what the given path is in the
      index, and if it is **not** already a gitlink, then die. 
Note the `-v` passed to sane_grep.
Show 31 quoted lines
> > This is what I have done in C:
> > 
> > 	if (!force) {
> > 		if (is_directory(path) && submodule_from_path(the_repository, &null_oid, path))
> > 			die(_("'%s' already exists in the index"), path);
> 
> The shell version would error out with anything in the index, so I'd
> expect that a faithful conversion would not call is_directory() nor
> submodule_from_path() at all---it would just look path up in the_index
> and complains if anything is found.  For example, the quoted part in
> the original above is what gives the error message when I do
> 
> 	$ git submodule add ./Makefile
> 	'Makefile' already exists in the index.
> 
> I think.  And the above code won't trigger the "already exists" at
> all because 'path' is not a directory.
> 
> > 	} else {
> > 		int err;
> > 		if (index_name_pos(&the_index, path, strlen(path)) >= 0 &&
> > 		    !is_submodule_populated_gently(path, &err))
> > 			die(_("'%s' already exists in the index and is not a "
> > 			      "submodule"), path);
> 
> Likewise.  The above does much more than the original.
> 
> The original was checking if the found cache entry has 160000 mode
> bit, so the second test would not be is_submodule_populated_gently()
> but more like !S_ISGITLINK(ce->ce_mode)
> 
Yeah, the C version does need a more proper check in both cases.
> Now it is a different question if the original is correct to begin
> with ;-).  
> 

By looking at commit message of 619acfc78c (submodule add: extend force flag to add existing repos, 2016-10-06), I'm assuming it's correct. There are chances I might be missing something, though.

Speaking of correctness, I'm surprised how the port passed the following test t7400.63 despite the incorrect check.

-- 8< -- $ ./t7400-submodule-basic.sh ... snip ... ok 62 - add submodules without specifying an explicit path ok 63 - add should fail when path is used by a file ok 64 - add should fail when path is used by an existing directory ... snip ... -- >8 --

Most likely it passed because it slipped through the incorrect check and failed later in the code[1]. That's not good, of course.

Show 6 quoted lines
> > 	}
> > 
> > Is this part correct? I am not very sure about this. This particular
> > part is not covered in any test or test script, so, I do not have a
> > solid method of knowing the correctness of this segment.
> > Feedback and reviews are appreciated.
Show 47 quoted lines
> +static int add_submodule(struct add_data *info)
> +{
> +	/* perhaps the path exists and is already a git repo, else clone it */
> +	if (is_directory(info->sm_path)) {
> +		char *sub_git_path = xstrfmt("%s/.git", info->sm_path);
> +		if (is_directory(sub_git_path) || file_exists(sub_git_path))
> +			printf(_("Adding existing repo at '%s' to the index\n"),
> +				 info->sm_path);
> +		else
> +			die(_("'%s' already exists and is not a valid git repo"),
> +			      info->sm_path);
> +		free(sub_git_path);
> +	} else {
> +		struct strvec clone_args = STRVEC_INIT;
> +		struct child_process cp = CHILD_PROCESS_INIT;
> +		char *submodule_git_dir = xstrfmt(".git/modules/%s", info->sm_name);
> +
> +		if (is_directory(submodule_git_dir)) {
> +			if (!info->force) {
> +				struct child_process cp_rem = CHILD_PROCESS_INIT;
> +				struct strbuf sb_rem = STRBUF_INIT;
> +				cp_rem.git_cmd = 1;
> +				fprintf(stderr, _("A git directory for '%s' is "
> +					"found locally with remote(s):\n"),
> +					info->sm_name);
> +				strvec_pushf(&cp_rem.env_array,
> +					     "GIT_DIR=%s", submodule_git_dir);
> +				strvec_push(&cp_rem.env_array, "GIT_WORK_TREE=.");
> +				strvec_pushl(&cp_rem.args, "remote", "-v", NULL);
> +				if (!capture_command(&cp_rem, &sb_rem, 0)) {
> +					modify_remote_v(&sb_rem);
> +				}
> +				error(_("If you want to reuse this local git "
> +				      "directory instead of cloning again from\n "
> +				      "  %s\n"
> +				      "use the '--force' option. If the local "
> +				      "git directory is not the correct repo\n"
> +				      "or you are unsure what this means choose "
> +				      "another name with the '--name' option."),
> +				      info->realrepo);
> +				return 1;
> +			} else {
> +				printf(_("Reactivating local git directory for "
> +					 "submodule '%s'."), info->sm_path);
> +			}
> +		}
> +		free(submodule_git_dir);

This part results in a difference in error message in shell and C versions.

-- 8< --
$ # Shell version
$ git submodule add ../subm1 sub
A git directory for 'sub' is found locally with remote(s):
  origin        /me/subm1
If you want to reuse this local git directory instead of cloning again from
  /me/subm1
use the '--force' option. If the local git directory is not the correct repo
or you are unsure what this means choose another name with the '--name' option.
$
$ # C version
$ git submodule add ../subm1 sub
A git directory for 'sub' is found locally with remote(s):
  origin        /me/subm1
error: If you want to reuse this local git directory instead of cloning again from
   /me/subm1
use the '--force' option. If the local git directory is not the correct repo
or you are unsure what this means choose another name with the '--name' option.
-- >8 --

Note how the third line is oddly prefixed by a `error` unlike the rest of the lines. It would be nice if we could weed out that inconsistency. We could probably use `advise()` for printing the last four lines and `error()` for the lines above them.

Footnote
---
[1]: Looks like not checking for the error message when a command fails
     has it's own downsides x-(

-- Sivaraam

Previous: Junio C HamanoNext: Junio C Hamano
Message 3 of 12 in “submodule: port submodule subcommand 'add' from shell to C”
  1. Shourya ShuklaAug 24, 2020
  2. Junio C HamanoAug 24, 2020
  3. Kaartic SivaraamAug 24, 2020
  4. Junio C HamanoAug 24, 2020
  5. Shourya ShuklaAug 26, 2020
  6. Kaartic SivaraamAug 26, 2020
  7. Shourya ShuklaAug 26, 2020
  8. Kaartic SivaraamAug 30, 2020
  9. Shourya ShuklaAug 31, 2020
  10. Kaartic SivaraamSep 1, 2020
  11. Shourya ShuklaSep 2, 2020
  12. Kaartic SivaraamSep 3, 2020

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.