commit 249b637c505366856603a932c859a0baf1973d10 from: Stefan Sperling date: Mon Feb 20 12:11:14 2023 UTC have ignore patterns with trailing slashes match directories only ok jamsek commit - 2262237761cd6c8c89a4d4263a8d9c05a7a81d70 commit + 249b637c505366856603a932c859a0baf1973d10 blob - b6cc16e8e5a4b02bf33a4f2af21bc0fd0f58c4bd blob + 06d9ba522ae0001bd8bb697df46c9c952023dd09 --- got/got.1 +++ got/got.1 @@ -788,6 +788,9 @@ and .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 - 010ea00d11c12ab19a0251310a86b1a87bc8a3e9 blob + a3aff0453202ab4f6f93c80713a58d91577c7639 --- lib/worktree.c +++ lib/worktree.c @@ -3550,6 +3550,26 @@ done: got_pathlist_free(ignorelist, GOT_PATHLIST_FREE_PATH); } return err; +} + +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 @@ -3563,14 +3583,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 != '/') @@ -3595,11 +3616,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) + int flags = FNM_LEADING_DIR; + 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" }