"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Stefan Sperling <stsp@stsp.name>
Subject:
Re: ignored files can't handle trailing slashes
To:
Lucas <lucas@sexy.is>
Cc:
gameoftrees@openbsd.org
Date:
Sun, 19 Feb 2023 21:40:36 +0100

Download raw body.

Thread
  • Stefan Sperling:

    ignored files can't handle trailing slashes

  • On Sun, Feb 05, 2023 at 02:05:30PM +0000, Lucas wrote:
    > Hello list,
    > 
    > As I shared on IRC, when `got import -I dir/` is used, `dir/` is *not*
    > ignored: fnmatch is called with the name in dirent's d_name, which
    > doesn't include a trailing slash if the entry is of type directory.
    > Do note that calling got_path_strip_trailing_slashes on `-I` argument
    > is not an option: 'dir*/' can have some nasty side effects. The patch
    > for regress/cmdline/import.sh below makes this explicit.
    > 
    > Same can be observed for .{cvs,git}ignore.
    
    This patch fixes the same problem for 'got status'. ok?
    
    "return FNM_NOMATCH; /* XXX */" in the diff is because there is no way
    to return struct got_error from within this function without further
    refactoring. I think that is fine for now because paths longer than
    PATH_MAX can probably not even be checked out, and such long patterns
    will be very rare if they exist at all.
     
     make 'got status' ignore patterns with trailing slashes match on directories
     
    diff d0f874e072cfb4119033a71b9f162ae02eca44ab a6e726d31e2e25a79e7f5d4b6b747d2778b15c5d
    commit - d0f874e072cfb4119033a71b9f162ae02eca44ab
    commit + a6e726d31e2e25a79e7f5d4b6b747d2778b15c5d
    blob - b6cc16e8e5a4b02bf33a4f2af21bc0fd0f58c4bd
    blob + 06d9ba522ae0001bd8bb697df46c9c952023dd09
    --- got/got.1
    +++ got/got.1
    @@ -788,6 +788,9 @@ As an extension to
     .Pa .gitignore
     files in each traversed directory and will not display unversioned files
     which match these patterns.
    +Ignore patterns which end with a slash,
    +.Dq / ,
    +will only match directories.
     As an extension to
     .Xr glob 7
     matching rules,
    blob - 2d75c470b84a9c0f07ca7ccd39595267ab24fd49
    blob + a5e3af7acc609d51f0bd553fae407d173c3df895
    --- lib/worktree.c
    +++ lib/worktree.c
    @@ -3559,6 +3559,26 @@ match_ignores(struct got_pathlist_head *ignores, const
     }
     
     static int
    +match_path(const char *pattern, size_t pattern_len, const char *path,
    +    int flags)
    +{
    +	char buf[PATH_MAX];
    +
    +	/*
    +	 * Trailing slashes signify directories.
    +	 * Append a * to make such patterns conform to fnmatch rules.
    +	 */
    +	if (pattern_len > 0 && pattern[pattern_len - 1] == '/') {
    +		if (snprintf(buf, sizeof(buf), "%s*", pattern) >= sizeof(buf))
    +			return FNM_NOMATCH; /* XXX */
    +
    +		return fnmatch(buf, path, flags);
    +	}
    +
    +	return fnmatch(pattern, path, flags);
    +}
    +
    +static int
     match_ignores(struct got_pathlist_head *ignores, const char *path)
     {
     	struct got_pathlist_entry *pe;
    @@ -3569,14 +3589,15 @@ match_ignores(struct got_pathlist_head *ignores, const
     		struct got_pathlist_entry *pi;
     
     		TAILQ_FOREACH(pi, ignorelist, entry) {
    -			const char *p, *pattern = pi->path;
    +			const char *p;
     
    -			if (strncmp(pattern, "**/", 3) != 0)
    +			if (pi->path_len < 3 ||
    +			    strncmp(pi->path, "**/", 3) != 0)
     				continue;
    -			pattern += 3;
     			p = path;
     			while (*p) {
    -				if (fnmatch(pattern, p,
    +				if (match_path(pi->path + 3,
    +				    pi->path_len - 3, p,
     				    FNM_PATHNAME | FNM_LEADING_DIR)) {
     					/* Retry in next directory. */
     					while (*p && *p != '/')
    @@ -3601,11 +3622,11 @@ match_ignores(struct got_pathlist_head *ignores, const
     			struct got_pathlist_head *ignorelist = pe->data;
     			struct got_pathlist_entry *pi;
     			TAILQ_FOREACH(pi, ignorelist, entry) {
    -				const char *pattern = pi->path;
     				int flags = FNM_LEADING_DIR;
    -				if (strstr(pattern, "/**/") == NULL)
    +				if (strstr(pi->path, "/**/") == NULL)
     					flags |= FNM_PATHNAME;
    -				if (fnmatch(pattern, path, flags))
    +				if (match_path(pi->path, pi->path_len,
    +				    path, flags))
     					continue;
     				return 1;
     			}
    blob - f992b65b13667e8a421e2e410937179db7c27aac
    blob + f2d3d5dfe1904cd50bfc165b2d05f293cb4fad5f
    --- regress/cmdline/status.sh
    +++ regress/cmdline/status.sh
    @@ -709,17 +709,20 @@ test_status_gitignore_trailing_slashes() {
     	echo "unversioned file" > $testroot/wt/epsilon/bar
     	echo "unversioned file" > $testroot/wt/epsilon/boo
     	echo "unversioned file" > $testroot/wt/epsilon/moo
    -	echo "epsilon/" > $testroot/wt/.gitignore
    +	echo "unversioned file" > $testroot/wt/upsilon
     
    +	# Match the directory epsilon but not the regular file upsilon
    +	echo "*psilon/" > $testroot/wt/.gitignore
    +
     	echo '?  .gitignore' > $testroot/stdout.expected
     	echo '?  foo' >> $testroot/stdout.expected
    +	echo '?  upsilon' >> $testroot/stdout.expected
     	(cd $testroot/wt && got status > $testroot/stdout)
     
     	cmp -s $testroot/stdout.expected $testroot/stdout
     	ret=$?
     	if [ $ret -ne 0 ]; then
    -		#diff -u $testroot/stdout.expected $testroot/stdout
    -		ret="xfail trailing slashes not matched"
    +		diff -u $testroot/stdout.expected $testroot/stdout
     	fi
     	test_done "$testroot" "$ret"
     }
    
    
  • Stefan Sperling:

    ignored files can't handle trailing slashes