-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add basic ignore functionality #71
Conversation
git/status.go
Outdated
@@ -346,6 +346,11 @@ func StatusLong(c *Client, files []File, untracked StatusUntrackedMode, linepref | |||
if err != nil { | |||
return "", err | |||
} | |||
// TODO instead of filtering afterwards, perhaps the untracked should be filtered at the source | |||
matches, _ := CheckIgnore(c, CheckIgnoreOptions{}, []File{fname}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why don't we put this check in LsFiles and include ExcludeStandard in the opts? That would go a lot farther than adding a filter in every porcelain command
The latest commits add more capabilities with the ignore patterns. Next step is to integrate the ignores into ls-files so that other plumbing commands get the benefit, such as status and add. |
…nds can use it via ls-files
Thanks @driusan I see that you anticipated that the LsFiles() function would be the place where ignore capabilities are centralized. Hooking it into status/add was really straight forward. With the latest changes in this PR the t0008-ignores.sh official test has only 87 failures out of 392. I'm going to try to whittle this number down further so that I can add it to the travis CI. |
git init && | ||
echo a >a && | ||
git add a && | ||
- git commit -m"commit in submodule" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no space between the -m and the commit message, which causes go's flags package to fail on it and also the git manual specifies a space between -m and the message. It seems that the official git command-line just supports this unspecified syntax and the test here happens to use it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some suggestions for cleanup, but I don't mind merging this as-is if you prefer, because it's still a huge step forward. Just let me know when you're done enough that you want it merged.
git/checkignore.go
Outdated
|
||
for idx, path := range paths { | ||
if !opts.NoIndex { | ||
log.Printf("Checking if %s is tracked by git\n", path.String()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the stringer call is implicit since your formatting directive is "%s"
git/checkignore.go
Outdated
// Be sure to assign the pathname in all cases so that clients can match the outputs to their inputs | ||
patternMatches[idx].PathName = path | ||
|
||
// TODO: consider the other places where ignores can come from, such as core.excludesFile and .git/info/exclude |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the way to do this is to invert your logic: check-ignore should call ls-files, ls-files shouldn't call check-ignore. ls-files has the exclude-per-directory and exclude-from options, while check-ignore doesn't have any way to customize
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking about this also. The trouble in my thinking comes with the check-ignore --no-index option. I'm not sure how to push that option down into ls-files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not an expert on the exclude semantics, but from how I interpret the man page it just affects whether it calls ls-files -o
or ls-files -c -o
git/checkignore.go
Outdated
return "", 0, nil | ||
} | ||
|
||
func matchesGlob(path string, isDir bool, pattern string) bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not replace the File.MatchGlob I added with this instead of adding a new way to do it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now, I was going to keep this local because it doesn't quite fit the File.MatchGlob pattern after all. For example, this ones needs to know if the path points to a directory or not for some of the matching logic, but the path may be relative to arbitrary positions within the work directory.
git/checkignore.go
Outdated
gitignore := filepath.Join(dir, ".gitignore") | ||
log.Printf("Checking .gitignore in %s\n", gitignore) | ||
ignorePath, err := filepath.Rel(dir, abs) | ||
pattern, lineNumber, err := findPatternInIgnoreFile(c, gitignore, ignorePath, isDir) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about matches that come from .gitignores higher up in the tree? I think the logic for directories is easier to reason about recursively than iteratively. For instance, we could parse the ignore file and return a slice of patterns, then pass that slice to children to deal with parent ignores. (Which also has the advantages of only parsing the file once). If that was done and the ls-files inversion, it would be easy to add exclude-per-directory and exclude-from
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I could think about this a bit more. Some of the complexity comes from the fact that in order to match a pattern in a gitignore file you need to make the path of the file to be ignore/not ignored relative to that gitignore file in order to check the matches. That makes it a bit more difficult to use patterns from a git ignore file out of context, but perhaps the gitignore parse result could be made into a type that has helper methods on it to prevent mistakes in usage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Drive-by review.
(It's really impressive how close this is getting to passing all the tests considering how many tests there are. )
} | ||
} | ||
|
||
func TestIgnorePatternMatches(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test seems to want to be a table driven test
defer file.Close() | ||
|
||
ignorePatterns := []IgnorePattern{} | ||
reader := bufio.NewReader(file) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't looked into this loop in detail, but from a cursory glance it seems that this might be easier using a bufio.Scanner to scan lines instead of a bufio.Reader
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wish that were the case, but unfortunately buffo.Scanner will miss the last line of a stream if there is no newline character at the end. :( This is why there is the pain of having to use the reader directly.
return nil, err | ||
} | ||
|
||
// Let's check that this path is in the git work dir first |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check seems useful enough that it might make sense to make it a method on File rather than inline in the loop.
git/file.go
Outdated
@@ -72,6 +72,45 @@ func (f File) IsDir() bool { | |||
|
|||
} | |||
|
|||
// Returns true if the file is inside a submodule and the submodule file. | |||
func (f File) IsInSubmodule(c *Client) (bool, File, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a FIXME saying that when we implement submodules this should be inverted? (Submodules contain Files, not the other way around, but we don't currently have a submodule type..)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
|
||
// Invoke the ignore routines directly, rather than relying on | ||
// LsFiles because we need to be able to do things such as | ||
// return non-matches and match details that aren't supported there. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think I understand this comment. Isn't determining whether nonmatches or matches are returned what the -i
option (which admittedly isn't currently implemented, but that's effectively what this PR is doing) toggles? What details that aren't supported need to be matched?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right the "-i" option is not yet enabled and the LsFiles won't return back the pattern that matched or wasn't matched so that checkignore can output that with the -v flag.
@driusan I've reorganized things here quite a bit hoping that it is a much cleaner implementation with better separation of concerns. I've gotten the number of official git tests down to only 28 out of 395 for the ignore tests. The remainder are mostly related to the fact that I have not yet implemented the negation '!' operator and a couple of edge cases with escaping of whitespace vs. the slash character itself. |
I have added all of the passing official git ignore tests to travis to catch regressions. The majority of the failing tests are because of the negation operator, which I think should be tackled as a separate issue. |
Adds a new check-ignore command. The status command uses the check-ignore backing logic to filter out any untracked changes. The official git t0008-ignores.sh suite went from completely failing to 297/392 passing.