diff --git a/src/main/scala/com/typesafe/sbt/SbtGit.scala b/src/main/scala/com/typesafe/sbt/SbtGit.scala index c607388..d05f095 100644 --- a/src/main/scala/com/typesafe/sbt/SbtGit.scala +++ b/src/main/scala/com/typesafe/sbt/SbtGit.scala @@ -2,7 +2,7 @@ package com.typesafe.sbt import sbt._ import Keys._ -import git.{ConsoleGitRunner, DefaultReadableGit, GitRunner, JGitRunner, ReadableGit} +import git.{ConsoleGitReadableOnly, ConsoleGitRunner, DefaultReadableGit, GitRunner, JGitRunner, ReadableGit} import sys.process.Process /** This plugin has all the basic 'git' functionality for other plugins. */ @@ -40,6 +40,9 @@ object SbtGit { // The remote repository we're using. val gitRemoteRepo = SettingKey[String]("git-remote-repo", "The remote git repository associated with this project") + + // Git worktree workaround + val useConsoleForROGit = SettingKey[Boolean]("console-ro-runner", "Whether to shell out to git for ro ops in the current build.") } object GitCommand { @@ -110,7 +113,8 @@ object SbtGit { // Build settings. import GitKeys._ def buildSettings = Seq( - gitReader := new DefaultReadableGit(baseDirectory.value), + useConsoleForROGit := false, + gitReader := new DefaultReadableGit(baseDirectory.value, if (useConsoleForROGit.value) Some(new ConsoleGitReadableOnly(ConsoleGitRunner, file("."), ConsoleLogger())) else None), gitRunner := ConsoleGitRunner, gitHeadCommit := gitReader.value.withGit(_.headCommitSha), gitHeadMessage := gitReader.value.withGit(_.headCommitMessage), @@ -153,6 +157,9 @@ object SbtGit { /** A Predefined setting to use JGit runner for git. */ def useJGit: Setting[_] = gitRunner in ThisBuild := JGitRunner + /** Setting to use console git for readable ops, to allow working with git worktrees */ + def useReadableConsoleGit: Setting[_] = useConsoleForROGit in ThisBuild := true + /** Adapts the project prompt to show the current project name *and* the current git branch. */ def showCurrentGitBranch: Setting[_] = shellPrompt := GitCommand.prompt diff --git a/src/main/scala/com/typesafe/sbt/git/ConsoleGitReadableOnly.scala b/src/main/scala/com/typesafe/sbt/git/ConsoleGitReadableOnly.scala new file mode 100644 index 0000000..48e1a17 --- /dev/null +++ b/src/main/scala/com/typesafe/sbt/git/ConsoleGitReadableOnly.scala @@ -0,0 +1,27 @@ +package com.typesafe.sbt.git + +import scala.util.Try + +import sbt.{File, Logger} + +class ConsoleGitReadableOnly(git: GitRunner, cwd: File, log: Logger) extends GitReadonlyInterface { + def branch: String = git("rev-parse", "--abbrev-ref", "HEAD")(cwd, log) + + def headCommitSha: Option[String] = Try(git("rev-parse", "HEAD")(cwd, log)).toOption + + def headCommitDate: Option[String] = Try(git("log", """--pretty="%cI"""", "-n", "1")(cwd, log)).toOption + + def currentTags: Seq[String] = git("tag", "--points-at", "HEAD")(cwd, log).split("\\s+") + + def describedVersion: Option[String] = git("describe", "--tags")(cwd, log).split("\\s+").headOption + + def hasUncommittedChanges: Boolean = git("status")(cwd, log).contains("nothing to commit, working tree clean") + + def branches: Seq[String] = git("branch", "--list")(cwd, log).split("\\s+") + + def remoteBranches: Seq[String] = git("branch", "-l", "--remotes")(cwd, log).split("\\s+") + + def remoteOrigin: String = git("ls-remote", "--get-url", "origin")(cwd, log) + + def headCommitMessage: Option[String] = Try(git("log", "--pretty=%s\n\n%b", "-n", "1")(cwd, log)).toOption +} diff --git a/src/main/scala/com/typesafe/sbt/git/ReadableGit.scala b/src/main/scala/com/typesafe/sbt/git/ReadableGit.scala index 8214d2b..25a98be 100644 --- a/src/main/scala/com/typesafe/sbt/git/ReadableGit.scala +++ b/src/main/scala/com/typesafe/sbt/git/ReadableGit.scala @@ -31,11 +31,13 @@ trait GitReadonlyInterface { } -/** Our default readable git uses JGit instead of a process-forking and reading, for speed/safety. */ -final class DefaultReadableGit(base: sbt.File) extends ReadableGit { +/** Our default readable git uses JGit instead of a process-forking and reading, for speed/safety. However, we allow + * overriding, since JGit doesn't currently work with git worktrees + * */ +final class DefaultReadableGit(base: sbt.File, gitOverride: Option[GitReadonlyInterface]) extends ReadableGit { // TODO - Should we cache git, or just create on each request? // For now, let's cache. - private[this] val git = JGit(base) + private[this] val git = gitOverride getOrElse JGit(base) /** Use the git read-only interface. */ def withGit[A](f: GitReadonlyInterface => A): A = // JGit has concurrency issues so we synchronize access to it.