Skip to content

Add isGitIgnored method for checking if a file is ignored by git #1613

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

Merged
merged 11 commits into from
Jun 28, 2018

Conversation

ViktorSimko
Copy link
Contributor

These are the Git related changes that were a part of #1507 .

Add getIgnoredFiles to GitRepository
This makes an improvement performance-wise, because
it can be used to check only the files we are concerned about
…ocation

This way there is no need to create the shell process for every path separately which can improve the performance
public func isGitIgnored(_ paths: [AbsolutePath]) throws -> [Bool] {
return try queue.sync {
let stringPaths = paths.map({ $0.asString })
let args = [Git.tool, "-C", self.path.asString, "check-ignore"] + stringPaths
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might cause issues for two reasons:

  1. Paths with spaces needs to be escaped (we should test this).
  2. The command-line invocation might surpass ARG_MAX.

I recommend implementing this with git's -z and --stdlib options so we can use a file to provide the input list. See https://git-scm.com/docs/git-check-ignore

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we should write the paths to a file and then redirect the file to the stdin of the process and then finally remove the file?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep! We can use the TemporaryFile class from Basic module.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, Thank you!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like the redirection isn't working using Process with args. Maybe it's escaping the <?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think you'll have to use ["sh", "-c", "all args"]

This ensures that we won't surpass ARG_MAX with the arguments to check-ignore,
and also that paths now can include spaces
@ViktorSimko
Copy link
Contributor Author

@aciidb0mb3r Thank you, I've managed to get it working that way.

@ViktorSimko
Copy link
Contributor Author

Should we rename the method to areFilesIgnored(at:)?

@@ -387,6 +398,33 @@ public class GitRepository: Repository, WorkingCheckout {
return localFileSystem.isDirectory(AbsolutePath(firstLine))
}

/// Returns true if the file at `path` is ignored by `git`
public func isGitIgnored(_ paths: [AbsolutePath]) throws -> [Bool] {
Copy link
Contributor

@aciidgh aciidgh Jun 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about arePathsIgnored? or just areIgnored is also fine with me.


let pathsFile = try TemporaryFile()
let pathsData = Data(pathsFileContent.utf8)
pathsFile.fileHandle.write(pathsData)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use BufferedOutputByteStream and localFileSystem to APIs to write to the temp file.

let pathsData = Data(pathsFileContent.utf8)
pathsFile.fileHandle.write(pathsData)

let args = [Git.tool, "-C", self.path.asString, "check-ignore", "-z", "--stdin", "<", "\(pathsFile.path.asString)"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to shell quotes the arguments here as we'll be using sh -c.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should have a test for this.

Copy link
Contributor

@aciidgh aciidgh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! This mostly looks great, I left some minor comments.

@aciidgh aciidgh added the WIP Work in progress label Jun 28, 2018
let pathsFile = try TemporaryFile()
try localFileSystem.writeFileContents(pathsFile.path) { $0 <<< pathsFileContent }

let args = [Git.tool, "-C", self.path.asString, "check-ignore", "-z", "--stdin", "<", "\(pathsFile.path.asString)"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to shell escape the args here, we should also have a test for it.

public func areIgnored(_ paths: [AbsolutePath]) throws -> [Bool] {
return try queue.sync {
let stringPaths = paths.map({ $0.asString })
let pathsFileContent = stringPaths.joined(separator: "\0")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'll be better for performance if you append to the stream directly:

try localFileSystem.writeFileContents(pathsFile.path) { 
    for path in paths {
        $0 <<< path.asString <<< "\0"
    }
}

@@ -92,6 +92,13 @@ public class GitRepositoryProvider: RepositoryProvider {
}
}

public func checkoutExists(at path: AbsolutePath) throws -> Bool {
precondition(exists(path))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we return false if the path doesn't exist?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could return false, but I think that the purpose of this method is to determine if the checkout exists, not the path itself, so in my opinion, it's more like a precondition.

}
}

let args = [Git.tool, "-C", "\"\(self.path.asString)\"", "check-ignore", "-z", "--stdin", "<", "\"\(pathsFile.path.asString)\""]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might not be enough. We can use this method to properly escape: https://github.com/apple/swift-package-manager/blob/master/Sources/Basic/StringConversions.swift#L45

return String(cString: bytes + [0])
})

guard result.exitStatus == .terminated(code: 0) || result.exitStatus == .terminated(code: 1) else {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation says check-ignore will return 1 when "None of the provided paths are ignored". We shouldn't throw fatal in that case, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't throw on exit code 1. Only on exit codes different than 0 or 1.
This case is also included in the unit test.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh ya, right!

@aciidgh
Copy link
Contributor

aciidgh commented Jun 28, 2018

@swift-ci please smoke test

@aciidgh aciidgh merged commit 228f1b0 into swiftlang:master Jun 28, 2018
@aciidgh
Copy link
Contributor

aciidgh commented Jun 28, 2018

Thanks a lot for working on this @ViktorSimko!

@ViktorSimko
Copy link
Contributor Author

@aciidb0mb3r Thank you for helping me to be able to get this done, I've learnt a lot from this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
WIP Work in progress
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants