diff --git a/src/main/kotlin/app/hashers/CodeLongevity.kt b/src/main/kotlin/app/hashers/CodeLongevity.kt index 078ea535..56e11cb3 100644 --- a/src/main/kotlin/app/hashers/CodeLongevity.kt +++ b/src/main/kotlin/app/hashers/CodeLongevity.kt @@ -10,7 +10,6 @@ import app.model.Author import app.model.Repo import app.model.Fact import app.utils.FileHelper -import app.utils.RepoHelper import io.reactivex.Observable import org.eclipse.jgit.diff.DiffFormatter import org.eclipse.jgit.diff.DiffEntry @@ -23,15 +22,12 @@ import org.eclipse.jgit.revwalk.RevWalk import org.eclipse.jgit.treewalk.TreeWalk import org.eclipse.jgit.util.io.DisabledOutputStream -import java.io.File import java.io.FileInputStream import java.io.FileNotFoundException import java.io.FileOutputStream import java.io.ObjectOutputStream import java.io.ObjectInputStream import java.io.Serializable -import java.nio.file.Files -import java.nio.file.Paths import java.lang.Exception import java.text.SimpleDateFormat import java.util.Date @@ -149,7 +145,7 @@ class CodeLongevity(private val serverRepo: Repo, val repo: Repository = git.repository val revWalk = RevWalk(repo) val head: RevCommit = - try { revWalk.parseCommit(repo.resolve(RepoHelper.MASTER_BRANCH)) } + try { revWalk.parseCommit(CommitCrawler.getDefaultBranchHead(git)) } catch(e: Exception) { throw Exception("No branch") } val df = DiffFormatter(DisabledOutputStream.INSTANCE) diff --git a/src/main/kotlin/app/hashers/CommitCrawler.kt b/src/main/kotlin/app/hashers/CommitCrawler.kt index 1050cce0..f61dcea6 100644 --- a/src/main/kotlin/app/hashers/CommitCrawler.kt +++ b/src/main/kotlin/app/hashers/CommitCrawler.kt @@ -9,19 +9,61 @@ import app.model.DiffContent import app.model.DiffFile import app.model.DiffRange import app.model.Repo -import app.utils.RepoHelper import io.reactivex.Observable +import org.apache.commons.codec.digest.DigestUtils import org.eclipse.jgit.api.Git import org.eclipse.jgit.diff.DiffEntry import org.eclipse.jgit.diff.DiffFormatter import org.eclipse.jgit.diff.RawText -import org.eclipse.jgit.errors.MissingObjectException import org.eclipse.jgit.lib.ObjectId import org.eclipse.jgit.revwalk.RevCommit import org.eclipse.jgit.revwalk.RevWalk import org.eclipse.jgit.util.io.DisabledOutputStream +import java.util.LinkedList object CommitCrawler { + private val MASTER_BRANCH = "refs/heads/master" + private val REMOTE_HEAD = "refs/remotes/origin/HEAD" + + fun getDefaultBranchHead(git: Git): ObjectId { + val remoteHead = git.branchList()?.repository?.allRefs?.filter { + it.key.contains(REMOTE_HEAD) + }?.entries?.firstOrNull()?.value?.target?.objectId + if (remoteHead != null) { + Logger.debug { "Hashing from remote default branch" } + return remoteHead + } + val masterBranch = git.repository.resolve(MASTER_BRANCH) + if (masterBranch != null) { + Logger.debug { "Hashing from local master branch" } + return masterBranch + } + throw Exception("No remote default or local master branch found") + } + + fun fetchRehashesAndEmails(git: Git): + Pair, HashSet> { + val head: RevCommit = RevWalk(git.repository) + .parseCommit(getDefaultBranchHead(git)) + + val revWalk = RevWalk(git.repository) + revWalk.markStart(head) + + val commitsRehashes = LinkedList() + val emails = hashSetOf() + + var commit: RevCommit? = revWalk.next() + while (commit != null) { + commitsRehashes.add(DigestUtils.sha256Hex(commit.name)) + emails.add(commit.authorIdent.emailAddress) + commit.disposeBody() + commit = revWalk.next() + } + revWalk.dispose() + + return Pair(commitsRehashes, emails) + } + fun getObservable(git: Git, repo: Repo, numCommits: Int = 0): Observable { var curNumCommits = 0 @@ -29,7 +71,7 @@ object CommitCrawler { .create { subscriber -> try { val revWalk = RevWalk(git.repository) - val commitId = git.repository.resolve(RepoHelper.MASTER_BRANCH) + val commitId = getDefaultBranchHead(git) revWalk.markStart(revWalk.parseCommit(commitId)) for (revCommit in revWalk) { subscriber.onNext(Commit(revCommit)) diff --git a/src/main/kotlin/app/hashers/RepoHasher.kt b/src/main/kotlin/app/hashers/RepoHasher.kt index 768e67c2..80e00c96 100644 --- a/src/main/kotlin/app/hashers/RepoHasher.kt +++ b/src/main/kotlin/app/hashers/RepoHasher.kt @@ -3,7 +3,6 @@ package app.hashers -import app.BuildConfig import app.Logger import app.api.Api import app.config.Configurator @@ -12,13 +11,9 @@ import app.model.LocalRepo import app.model.Repo import app.utils.HashingException import app.utils.RepoHelper -import org.apache.commons.codec.digest.DigestUtils import org.eclipse.jgit.api.Git -import org.eclipse.jgit.revwalk.RevCommit -import org.eclipse.jgit.revwalk.RevWalk import java.io.File import java.io.IOException -import java.util.* import kotlin.collections.HashSet class RepoHasher(private val localRepo: LocalRepo, private val api: Api, @@ -35,7 +30,7 @@ class RepoHasher(private val localRepo: LocalRepo, private val api: Api, Logger.info { "Hashing of repo started" } val git = loadGit(localRepo.path) try { - val (rehashes, emails) = fetchRehashesAndEmails(git) + val (rehashes, emails) = CommitCrawler.fetchRehashesAndEmails(git) localRepo.parseGitConfig(git.repository.config) initServerRepo(rehashes.last) @@ -127,29 +122,6 @@ class RepoHasher(private val localRepo: LocalRepo, private val api: Api, initCommitRehash, localRepo)) } - private fun fetchRehashesAndEmails(git: Git): - Pair, HashSet> { - val head: RevCommit = RevWalk(git.repository) - .parseCommit(git.repository.resolve(RepoHelper.MASTER_BRANCH)) - - val revWalk = RevWalk(git.repository) - revWalk.markStart(head) - - val commitsRehashes = LinkedList() - val emails = hashSetOf() - - var commit: RevCommit? = revWalk.next() - while (commit != null) { - commitsRehashes.add(DigestUtils.sha256Hex(commit.name)) - emails.add(commit.authorIdent.emailAddress) - commit.disposeBody() - commit = revWalk.next() - } - revWalk.dispose() - - return Pair(commitsRehashes, emails) - } - private fun filterEmails(emails: HashSet): HashSet { if (localRepo.hashAllContributors) { return emails diff --git a/src/main/kotlin/app/ui/EmailState.kt b/src/main/kotlin/app/ui/EmailState.kt index 2f7a0530..ff743762 100644 --- a/src/main/kotlin/app/ui/EmailState.kt +++ b/src/main/kotlin/app/ui/EmailState.kt @@ -6,6 +6,8 @@ package app.ui import app.Logger import app.api.Api import app.config.Configurator +import app.hashers.CommitCrawler +import app.model.LocalRepo import app.model.User import app.model.UserEmail import app.utils.UiHelper @@ -39,16 +41,26 @@ class EmailState constructor(private val context: Context, // TODO(anatoly): Add user config parsing. // Add emails from git configs. + val reposEmails = hashMapOf>() for (repo in configurator.getLocalRepos()) { + var git: Git? = null try { - val git = Git.open(File(repo.path)) + git = Git.open(File(repo.path)) val email = git.repository .config.getString("user", null, "email") ?: "" if (!knownEmails.contains(email)) { configEmails.add(email) } + // Fetch and save emails from repo for "no-email" warning. + val (_, emails) = CommitCrawler.fetchRehashesAndEmails(git) + reposEmails.put(repo, emails) } catch (e: Exception) { - Logger.error(e, "Error while parsing git config") + Logger.error(e, "Error while parsing repo") + } finally { + if (git != null) { + git.repository?.close() + git.close() + } } } @@ -61,6 +73,35 @@ class EmailState constructor(private val context: Context, } } + // Show warning if no commits + val reposUserMissing = mutableListOf() + for (repo in configurator.getLocalRepos()) { + val presentedEmails = reposEmails.get(repo) + val updatedEmails = knownEmails + newEmails + if (presentedEmails != null) { + var userMissing = true + for (email in presentedEmails) { + if (updatedEmails.contains(email)) { + userMissing = false + break + } + } + if (userMissing) { + reposUserMissing.add(repo) + } + } + } + if (reposUserMissing.isNotEmpty()) { + if (reposUserMissing.size == 1) { + Logger.print("${reposUserMissing.first()} repo does not " + + "contains commits from emails you've specified") + } else { + Logger.print("Following repos do not contain commits from " + + "emails you've specified:") + reposUserMissing.forEach { Logger.print(it) } + } + } + // Ask user to enter his emails. if (UiHelper.confirm("Do you want to specify additional emails " + "that you use in repositories?", defaultIsYes = false)) { diff --git a/src/main/kotlin/app/utils/RepoHelper.kt b/src/main/kotlin/app/utils/RepoHelper.kt index 411eba84..9151423e 100644 --- a/src/main/kotlin/app/utils/RepoHelper.kt +++ b/src/main/kotlin/app/utils/RepoHelper.kt @@ -4,6 +4,7 @@ package app.utils import app.Logger +import app.hashers.CommitCrawler import app.model.LocalRepo import org.apache.commons.codec.digest.DigestUtils import org.eclipse.jgit.api.Git @@ -17,8 +18,6 @@ import java.nio.file.Paths * Class for utility functions on repos. */ object RepoHelper { - val MASTER_BRANCH = "refs/heads/master" - fun isValidRepo(path: String): Boolean { if (!isDirectory(path)) { return false @@ -30,7 +29,7 @@ object RepoHelper { try { git = Git.open(File(path)) repository = git.repository - commitId = repository.resolve(MASTER_BRANCH) + commitId = CommitCrawler.getDefaultBranchHead(git) } catch (e: Exception) { Logger.error(e, "Cannot access repository at specified path") return false