Skip to content

Commit 57da7a8

Browse files
committed
Ignore file excluded from git
Git has a .gitignore which can be used to exclude files from getting into the repository. It's pointless to scan files ignored via this mechanism as we'll fail to stage any changes to them. This approach reads one .gitignore are the top of the file and then use it to check all the paths. This is not _exact_ but I think it's good enough. I say that this is not exact because Git supports .gitignore file nested at arbitrary depth in the repository. To implement that, we'd need to re-implement the `walk` function which is not worth it in the scope of this issue.
1 parent 63f9391 commit 57da7a8

File tree

4 files changed

+45
-14
lines changed

4 files changed

+45
-14
lines changed

build.sbt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ lazy val core = myCrossProject("core")
121121
Dependencies.decline,
122122
Dependencies.fs2Core,
123123
Dependencies.fs2Io,
124+
Dependencies.gitignore,
124125
Dependencies.http4sCirce,
125126
Dependencies.http4sClient,
126127
Dependencies.http4sCore,

modules/core/src/main/scala/org/scalasteward/core/edit/update/ScannerAlg.scala

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ package org.scalasteward.core.edit.update
1818

1919
import better.files.File
2020
import cats.effect.Concurrent
21+
import cats.syntax.functor._
22+
import com.github.arturopala.gitignore.GitIgnore
2123
import fs2.Stream
2224
import org.scalasteward.core.data.{Dependency, Repo, Version}
2325
import org.scalasteward.core.edit.update.data.{ModulePosition, VersionPosition}
@@ -56,19 +58,35 @@ final class ScannerAlg[F[_]](implicit
5658
.compile
5759
.foldMonoid
5860

61+
private def getGitIgnore(repoDir: File): F[Option[GitIgnore]] = {
62+
val gitIgnore = repoDir / ".gitignore"
63+
fileAlg.readFile(gitIgnore).map(_.map(GitIgnore.parse))
64+
}
65+
5966
private def findPathsContaining(
6067
repo: Repo,
6168
config: RepoConfig,
6269
string: String
63-
): Stream[F, FileData] =
64-
Stream.eval(workspaceAlg.repoDir(repo)).flatMap { repoDir =>
65-
val fileFilter = (file: File) => {
66-
val path = repoDir.relativize(file).toString
67-
val cond = !path.startsWith(".git/") &&
68-
config.updates.fileExtensionsOrDefault.exists(path.endsWith)
69-
Option.when(cond)(path)
70-
}
71-
val contentFilter = (content: String) => Some(content).filter(_.contains(string))
72-
fileAlg.findFiles(repoDir, fileFilter, contentFilter).map(FileData.tupled)
70+
): Stream[F, FileData] = {
71+
def fileFilter(repoDir: File, maybeGitIgnore: Option[GitIgnore]) = (file: File) => {
72+
val path = repoDir.relativize(file).toString
73+
val notDotGit = !path.startsWith(".git/")
74+
val onlyKeepConfiguredExtensions =
75+
config.updates.fileExtensionsOrDefault.exists(path.endsWith)
76+
val allowedByGitIgnore = maybeGitIgnore.map(_.isAllowed(path)).getOrElse(true)
77+
val cond = notDotGit &&
78+
onlyKeepConfiguredExtensions &&
79+
allowedByGitIgnore
80+
Option.when(cond)(path)
7381
}
82+
val contentFilter = (content: String) => Some(content).filter(_.contains(string))
83+
84+
for {
85+
repoDir <- Stream.eval(workspaceAlg.repoDir(repo))
86+
maybeRootGitIgnore <- Stream.eval(getGitIgnore(repoDir))
87+
files <- fileAlg
88+
.findFiles(repoDir, fileFilter(repoDir, maybeRootGitIgnore), contentFilter)
89+
.map(FileData.tupled)
90+
} yield files
91+
}
7492
}

modules/core/src/test/scala/org/scalasteward/core/edit/EditAlgTest.scala

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,26 @@ class EditAlgTest extends FunSuite {
2727
val update = ("org.typelevel".g % "cats-core".a % "1.2.0" %> "1.3.0").single
2828
val file1 = repoDir / "build.sbt"
2929
val file2 = repoDir / "project/Dependencies.scala"
30+
val gitignore = repoDir / ".gitignore"
3031

3132
val state = MockState.empty
32-
.addFiles(file1 -> """val catsVersion = "1.2.0"""", file2 -> "")
33+
.addFiles(file1 -> """val catsVersion = "1.2.0"""", file2 -> "", gitignore -> "")
3334
.flatMap(editAlg.applyUpdate(data, update).runS)
3435
.unsafeRunSync()
3536

3637
val expected = MockState.empty.copy(
3738
trace = Vector(
39+
Cmd("read", gitignore.pathAsString),
3840
Cmd("test", "-f", repoDir.pathAsString),
41+
Cmd("test", "-f", gitignore.pathAsString),
3942
Cmd("test", "-f", file1.pathAsString),
4043
Cmd("read", file1.pathAsString),
4144
Cmd("test", "-f", (repoDir / "project").pathAsString),
4245
Cmd("test", "-f", file2.pathAsString),
4346
Cmd("read", file2.pathAsString),
47+
Cmd("read", gitignore.pathAsString),
4448
Cmd("test", "-f", repoDir.pathAsString),
49+
Cmd("test", "-f", gitignore.pathAsString),
4550
Cmd("test", "-f", file1.pathAsString),
4651
Cmd("read", file1.pathAsString),
4752
Cmd("test", "-f", (repoDir / "project").pathAsString),
@@ -51,7 +56,7 @@ class EditAlgTest extends FunSuite {
5156
Cmd("write", file1.pathAsString),
5257
Cmd(gitStatus(repoDir))
5358
),
54-
files = Map(file1 -> """val catsVersion = "1.3.0"""", file2 -> "")
59+
files = Map(file1 -> """val catsVersion = "1.3.0"""", file2 -> "", gitignore -> "")
5560
)
5661

5762
assertEquals(state, expected)
@@ -65,6 +70,7 @@ class EditAlgTest extends FunSuite {
6570
val data = RepoData(repo, cache, RepoConfig.empty)
6671
val repoDir = workspaceAlg.repoDir(repo).unsafeRunSync()
6772
val update = ("org.scalameta".g % "scalafmt-core".a % "2.0.0" %> "2.1.0").single
73+
val gitignore = repoDir / ".gitignore"
6874
val scalafmtConf = repoDir / scalafmtConfName
6975
val scalafmtConfContent = """maxColumn = 100
7076
|version = 2.0.0
@@ -73,18 +79,22 @@ class EditAlgTest extends FunSuite {
7379
val buildSbt = repoDir / "build.sbt"
7480

7581
val state = MockState.empty
76-
.addFiles(scalafmtConf -> scalafmtConfContent, buildSbt -> "")
82+
.addFiles(scalafmtConf -> scalafmtConfContent, buildSbt -> "", gitignore -> "")
7783
.flatMap(editAlg.applyUpdate(data, update).runS)
7884
.unsafeRunSync()
7985

8086
val expected = MockState.empty.copy(
8187
trace = Vector(
88+
Cmd("read", gitignore.pathAsString),
8289
Cmd("test", "-f", repoDir.pathAsString),
90+
Cmd("test", "-f", gitignore.pathAsString),
8391
Cmd("test", "-f", scalafmtConf.pathAsString),
8492
Cmd("read", scalafmtConf.pathAsString),
8593
Cmd("test", "-f", buildSbt.pathAsString),
8694
Cmd("read", buildSbt.pathAsString),
95+
Cmd("read", gitignore.pathAsString),
8796
Cmd("test", "-f", repoDir.pathAsString),
97+
Cmd("test", "-f", gitignore.pathAsString),
8898
Cmd("test", "-f", scalafmtConf.pathAsString),
8999
Cmd("read", scalafmtConf.pathAsString),
90100
Cmd("test", "-f", buildSbt.pathAsString),
@@ -109,7 +119,8 @@ class EditAlgTest extends FunSuite {
109119
|version = 2.1.0
110120
|align.openParenCallSite = false
111121
|""".stripMargin,
112-
buildSbt -> ""
122+
buildSbt -> "",
123+
gitignore -> ""
113124
)
114125
)
115126

project/Dependencies.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ object Dependencies {
2020
val disciplineMunit = "org.typelevel" %% "discipline-munit" % "1.0.9"
2121
val fs2Core = "co.fs2" %% "fs2-core" % "3.6.1"
2222
val fs2Io = "co.fs2" %% "fs2-io" % fs2Core.revision
23+
val gitignore = "com.github.arturopala" %% "gitignore" % "0.4.0"
2324
val http4sCore = "org.http4s" %% "http4s-core" % "1.0.0-M39"
2425
val http4sCirce = "org.http4s" %% "http4s-circe" % http4sCore.revision
2526
val http4sClient = "org.http4s" %% "http4s-client" % http4sCore.revision

0 commit comments

Comments
 (0)