commit 6ae2855c45508b25adb202d57dd345da8500b7bb from: Evan Silberman via: Stefan Sperling date: Tue May 20 08:45:24 2025 UTC tweak 'got status' and 'got add' ignores handling for better git compatibility 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. Patch by: Evan Silberman commit - 26e67dac35191e72095a1e9a7216719d3abfbb7c commit + 6ae2855c45508b25adb202d57dd345da8500b7bb blob - 6652a0e142d22804174660f5bfac8d20073bdec6 blob + de2dc2b7ade5373c735bca1c19cdfd46b3ebef81 --- 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 blob - 72385745a21eb10a6eaf3ec6da928b7bbbaa0037 blob + f1c4b1579504bdb5e4e484b89a2260bf8a6171b2 --- 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 blob - fd2e3ed8fe3b3dbd2e5e8de7acc219ee467ee052 blob + 4e690714aa90a9b5159147f65bf8292f5ed4c327 --- 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 blob - 018736a36965a3780dcc34fa3fd41f8d9f337425 blob + 5e57f89a8f3c5c4f999f1f0bd918335e63d74fb7 --- 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