From: Evan Silberman Subject: Re: leading separators in ignore patterns To: Stefan Sperling Cc: gameoftrees@openbsd.org Date: Sat, 17 May 2025 13:38:23 -0700 I'm back with a patch. - Skip past leading slashes in ignore patterns. In gitignore(7), these trigger the behavior got has by default. Add regress test. - Lift documentation of ignore patterns to a subsection, referenced by the `add` and `status` docs. - Refine documentation of ignore patterns to discuss the extensions to glob(7) as a whole, and then list caveats relative to git(1) and cvs(1). - Add a file to the test worktree in the main gitignore regress test in order to register a known delta to gitignore(7) that was ambiguously documented previously: for the pattern a/**/foo, git will ignore a/foo, but got will not. The one change to worktree.c here originally happened in read_ignores() but I moved it to match_path() after further consideration. Not sure about my style here, is it unnecessary to use new locals for advancing the pattern pointer/decreasing the length? I note that `got import` references ignore patterns but the patterns given as -I params are not matched the same way. Maybe `got import` should respect .gitignore/.cvsignore? Happy to split this up a bit if it makes review easier, most of the diffstat is from the man page reworks. diff /home/evan/src/got path + /home/evan/src/got commit - 26e67dac35191e72095a1e9a7216719d3abfbb7c blob - 6652a0e142d22804174660f5bfac8d20073bdec6 file + cvg/cvg.1 --- cvg/cvg.1 +++ cvg/cvg.1 @@ -581,6 +581,8 @@ are as follows: .Bl -tag -width Ds .It Fl I Show unversioned files even if they match an ignore pattern. +See +.Sx Ignore Patterns . .It Fl S Ar status-codes Suppress the output of files with a modification status matching any of the single-character status codes contained in the @@ -604,42 +606,6 @@ Cannot be used together with the .Fl S option. .El -.Pp -For compatibility with -.Xr cvs 1 -and -.Xr git 1 , -.Cm got status -reads -.Xr glob 7 -patterns from -.Pa .cvsignore -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, -.Cm got status -supports consecutive asterisks, -.Dq ** , -which will match an arbitrary amount of directories. -Unlike -.Xr cvs 1 , -.Cm got status -only supports a single ignore pattern per line. -Unlike -.Xr git 1 , -.Cm got status -does not support negated ignore patterns prefixed with -.Dq \&! , -and gives no special significance to the location of path component separators, -.Dq / , -in a pattern. .It Xo .Cm log .Op Fl bdPpRs @@ -1045,9 +1011,9 @@ another repository. .Xc Schedule unversioned files in a work tree for addition to the repository in the next commit. -By default, files which match a -.Cm got status -ignore pattern will not be added. +By default, files which match an ignore pattern will not be added. +See +.Sx Ignore Patterns . .Pp If a .Ar path @@ -1073,9 +1039,9 @@ The options for are as follows: .Bl -tag -width Ds .It Fl I -Add files even if they match a -.Cm got status -ignore pattern. +Add files even if they match an ignore pattern. +See +.Xs Ignore Patterns . .It Fl R Permit recursion into directories. If this option is not specified, @@ -1761,6 +1727,61 @@ If a argument corresponds to the work tree's root directory, display information for all tracked files. .El +.Tg ignore +.Ss Ignore Patterns +.Cm cvg status +and +.Cm add +read patterns from +.Pa .cvsignore +and +.Pa .gitignore +files in each traversed directory and will not display or add unversioned files +which match these patterns. +The patterns are matched according to +.Xr glob 7 +rules, with extensions to improve compatibility with +.Xr cvs 1 +and +.Xr git 1 . +Patterns from each +.Pa .cvsignore +or +.Pa .gitignore +file are matched relative to the directory containing that file. +If a pattern begins with two asterisks followed by a slash, +.Dq **/ , +the remainder of the pattern will match at any directory at +or below the directory containing +.Pa .cvsignore +or +.Pa gitignore +file. +Two asterisks surrounded by slashes, +.Dq /**/ , +in the middle of a pattern will match one directory or more. +Patterns which end with a slash, +.Dq / , +will only match directories, and any paths below matching directories will +be ignored. +.Pp +Unlike +.Xr cvs 1 , +.Nm +only supports a single ignore pattern per line. +Unlike +.Xr git 1 , +.Nm +does not support negated ignore patterns prefixed with +.Dq \&! , +and does not match patterns at arbitrary depth relative to the +.Pa .gitignore +file unless they begin with +.Dq **/ . +For better +.Xr git 1 +compatibility, patterns beginning with a slash are matched as if the slash +were not present. .Sh ENVIRONMENT .Bl -tag -width GOT_IGNORE_GITCONFIG .It Ev GOT_AUTHOR commit - 26e67dac35191e72095a1e9a7216719d3abfbb7c blob - 72385745a21eb10a6eaf3ec6da928b7bbbaa0037 file + got/got.1 --- got/got.1 +++ got/got.1 @@ -964,6 +964,8 @@ are as follows: .Bl -tag -width Ds .It Fl I Show unversioned files even if they match an ignore pattern. +See +.Sx Ignore Patterns . .It Fl S Ar status-codes Suppress the output of files with a modification status matching any of the single-character status codes contained in the @@ -987,42 +989,6 @@ Cannot be used together with the .Fl S option. .El -.Pp -For compatibility with -.Xr cvs 1 -and -.Xr git 1 , -.Cm got status -reads -.Xr glob 7 -patterns from -.Pa .cvsignore -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, -.Cm got status -supports consecutive asterisks, -.Dq ** , -which will match an arbitrary amount of directories. -Unlike -.Xr cvs 1 , -.Cm got status -only supports a single ignore pattern per line. -Unlike -.Xr git 1 , -.Cm got status -does not support negated ignore patterns prefixed with -.Dq \&! , -and gives no special significance to the location of path component separators, -.Dq / , -in a pattern. .It Xo .Cm log .Op Fl bdPpRst @@ -1904,9 +1870,9 @@ another repository. .Xc Schedule unversioned files in a work tree for addition to the repository in the next commit. -By default, files which match a -.Cm got status -ignore pattern will not be added. +By default, files which match an ignore pattern will not be added. +See +.Sx Ignore Patterns . .Pp If a .Ar path @@ -1932,9 +1898,9 @@ The options for are as follows: .Bl -tag -width Ds .It Fl I -Add files even if they match a -.Cm got status -ignore pattern. +Add files even if they match an ignore pattern. +See +.Xs Ignore Patterns . .It Fl R Permit recursion into directories. If this option is not specified, @@ -3857,6 +3823,61 @@ If a argument corresponds to the work tree's root directory, display information for all tracked files. .El +.Tg ignore +.Ss Ignore Patterns +.Cm got status +and +.Cm got add +read patterns from +.Pa .cvsignore +and +.Pa .gitignore +files in each traversed directory and will not display or add unversioned files +which match these patterns. +The patterns are matched according to +.Xr glob 7 +rules, with extensions to improve compatibility with +.Xr cvs 1 +and +.Xr git 1 . +Patterns from each +.Pa .cvsignore +or +.Pa .gitignore +file are matched relative to the directory containing that file. +If a pattern begins with two asterisks followed by a slash, +.Dq **/ , +the remainder of the pattern will match at any directory at +or below the directory containing +.Pa .cvsignore +or +.Pa gitignore +file. +Two asterisks surrounded by slashes, +.Dq /**/ , +in the middle of a pattern will match one directory or more. +Patterns which end with a slash, +.Dq / , +will only match directories, and any paths below matching directories will +be ignored. +.Pp +Unlike +.Xr cvs 1 , +.Nm +only supports a single ignore pattern per line. +Unlike +.Xr git 1 , +.Nm +does not support negated ignore patterns prefixed with +.Dq \&! , +and does not match patterns at arbitrary depth relative to the +.Pa .gitignore +file unless they begin with +.Dq **/ . +For better +.Xr git 1 +compatibility, patterns beginning with a slash are matched as if the slash +were not present. .Sh ENVIRONMENT .Bl -tag -width GOT_IGNORE_GITCONFIG .It Ev GOT_AUTHOR commit - 26e67dac35191e72095a1e9a7216719d3abfbb7c blob - fd2e3ed8fe3b3dbd2e5e8de7acc219ee467ee052 file + lib/worktree.c --- lib/worktree.c +++ lib/worktree.c @@ -3813,19 +3813,29 @@ match_path(const char *pattern, size_t pattern_len, co int flags) { char buf[PATH_MAX]; + const char *pat = pattern; + size_t len = pattern_len; /* + * For gitignore(7) compatibility, ignore leading slashes + */ + if (len > 0 && pat[0] == '/') { + pat++; + len--; + } + + /* * 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)) + if (pat > 0 && pat[len - 1] == '/') { + if (snprintf(buf, sizeof(buf), "%s*", pat) >= sizeof(buf)) return FNM_NOMATCH; /* XXX */ return fnmatch(buf, path, flags); } - return fnmatch(pattern, path, flags); + return fnmatch(pat, path, flags); } static int commit - 26e67dac35191e72095a1e9a7216719d3abfbb7c blob - 018736a36965a3780dcc34fa3fd41f8d9f337425 file + regress/cmdline/status.sh --- regress/cmdline/status.sh +++ regress/cmdline/status.sh @@ -654,6 +654,7 @@ test_status_gitignore() { echo "unversioned file" > $testroot/wt/epsilon/boo echo "unversioned file" > $testroot/wt/epsilon/moo mkdir -p $testroot/wt/a/b/c/ + echo "git would ignore this" > $testroot/wt/a/foo echo "unversioned file" > $testroot/wt/a/b/c/foo echo "unversioned file" > $testroot/wt/a/b/c/zoo echo "foo" > $testroot/wt/.gitignore @@ -663,6 +664,7 @@ test_status_gitignore() { echo "**/zoo" >> $testroot/wt/.gitignore echo '? .gitignore' > $testroot/stdout.expected + echo '? a/foo' >> $testroot/stdout.expected echo '? foop' >> $testroot/stdout.expected (cd $testroot/wt && got status > $testroot/stdout) @@ -675,6 +677,7 @@ test_status_gitignore() { fi echo '? .gitignore' > $testroot/stdout.expected + echo '? a/foo' >> $testroot/stdout.expected echo '? foop' >> $testroot/stdout.expected (cd $testroot/wt/gamma && got status > $testroot/stdout) @@ -690,6 +693,7 @@ test_status_gitignore() { ? .gitignore ? a/b/c/foo ? a/b/c/zoo +? a/foo ? barp ? epsilon/bar ? epsilon/boo @@ -713,6 +717,41 @@ EOF test_done "$testroot" "$ret" } +test_status_gitignore_leading_slashes() { + local testroot=`test_init status_gitignore_leading_slashes` + + got checkout $testroot/repo $testroot/wt > /dev/null + ret=$? + if [ $ret -ne 0 ]; then + test_done "$testroot" "$ret" + return 1 + fi + + echo "unversioned file" > $testroot/wt/foo + echo "unversioned file" > $testroot/wt/epsilon/foo + echo "unversioned file" > $testroot/wt/epsilon/bar + mkdir -p $testroot/wt/nu + echo "unversioned file" > $testroot/wt/nu/baz + mkdir -p $testroot/wt/epsilon/nu + echo "unversioned file" > $testroot/wt/epsilon/nu/baz + + echo "/foo" > $testroot/wt/.gitignore + echo "/nu" >> $testroot/wt/.gitignore + + echo '? .gitignore' > $testroot/stdout.expected + echo '? epsilon/bar' >> $testroot/stdout.expected + echo '? epsilon/foo' >> $testroot/stdout.expected + echo '? epsilon/nu/baz' >> $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 + fi + test_done "$testroot" "$ret" +} + test_status_gitignore_trailing_slashes() { local testroot=`test_init status_gitignore_trailing_slashes` @@ -1204,6 +1243,7 @@ run_test test_status_empty_dir_unversioned_file run_test test_status_many_paths run_test test_status_cvsignore run_test test_status_gitignore +run_test test_status_gitignore_leading_slashes run_test test_status_gitignore_trailing_slashes run_test test_status_gitignore_comments run_test test_status_multiple_gitignore_files