Skip to content

Commit

Permalink
[patch] radically reducing the need for custom digger code by smarter…
Browse files Browse the repository at this point in the history
… use of git cli
  • Loading branch information
robertfmurdock committed Aug 12, 2024
1 parent 93f597e commit 6a15f5d
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,86 +2,30 @@ package com.zegreatrob.tools.digger.core

fun DiggerGitWrapper.allContributionCommits(): List<Pair<TagRef?, List<CommitRef>>> {
val log = log()
val tags = listTags().filter { log.map(CommitRef::id).contains(it.commitId) }
val trunkPath = tags.findTrunkPath(log)
return tags
.relateToCommits(trunkPath)
.foldInBranches(log - trunkPath.toSet())
.map { (tag, commitSet) -> tag to commitSet.map { it.id }.toSet() }
.withCommitsInOriginalOrder(log)
}

private fun List<TagRef>.findTrunkPath(log: List<CommitRef>) = allPaths(log, log.first(), map { it.commitId }.toSet())
.shortestPathWithMostTags(taggedCommitIds = map { it.commitId }.toSet())
?: log.alwaysLeftParent()
val tags = listTags()
.distinctBy { it.commitId }
.filter { log.map(CommitRef::id).contains(it.commitId) }

private fun MutableList<List<CommitRef>>.shortestPathWithMostTags(taggedCommitIds: Set<String>): List<CommitRef>? {
val pathTagCountGroups = groupBy { path ->
path.count { taggedCommitIds.contains(it.id) }
}
return pathTagCountGroups[pathTagCountGroups.keys.max()]
?.minBy { it.size }
}

private fun List<Pair<TagRef?, Set<CommitRef>>>.foldInBranches(offMainCommits: List<CommitRef>) = reversed()
.fold(emptyList<Pair<TagRef?, Set<CommitRef>>>()) { acc, (tag, commitSet) ->
acc + (tag to foldInBranches(commitSet, offMainCommits - acc.flatMap { it.second }.toSet()))
}.reversed()

private fun foldInBranches(
commitSet: Set<CommitRef>,
remainingOffMainCommits: List<CommitRef>,
): Set<CommitRef> = commitSet.fold(emptySet()) { acc, commit ->
acc + commit + if (commit.parents.size <= 1) {
emptyList()
} else {
branchCommits(
commit.parents[0],
remainingOffMainCommits - acc,
) + branchCommits(
commit.parents[1],
remainingOffMainCommits - acc,
)
}
return sortIntoTagSets(tags, log)
}

private fun List<TagRef>.relateToCommits(trunkPath: List<CommitRef>): List<Pair<TagRef?, Set<CommitRef>>> {
val tagSets: List<Pair<TagRef?, Set<CommitRef>>> =
trunkPath.fold(emptyList()) { acc, commit ->
val tag = find { it.commitId == commit.id }
if (tag != null) {
acc.plus(element = tag to setOf(commit))
} else {
val lastPair = acc.lastOrNull()
val lastList = lastPair?.second
if (lastList != null) {
acc.slice(0..acc.size - 2).plus(element = lastPair.copy(second = lastList.plusElement(commit)))
} else {
acc.plus(element = null to setOf(commit))
}
}
fun DiggerGitWrapper.sortIntoTagSets(
tagRefs: List<TagRef>,
log: List<CommitRef>,
): List<Pair<TagRef?, List<CommitRef>>> = tagRefs.tagPairs()
.map { (earlierTag, nextTag) ->
earlierTag to if (earlierTag == null && nextTag != null) {
logWithRange("^${nextTag.commitId}", "HEAD")
} else if (earlierTag != null && nextTag != null) {
logWithRange("^${nextTag.commitId}", earlierTag.commitId)
} else if (earlierTag != null) {
logWithRange(earlierTag.commitId)
} else {
log.alwaysLeftParent()
}
return tagSets
}
}.filter { it.second.isNotEmpty() }

private fun List<CommitRef>.alwaysLeftParent() = fold(emptyList<CommitRef>()) { acc, commit ->
if (acc.isEmpty()) {
acc + commit
} else if (commit.id == acc.last().parents.firstOrNull()) {
acc + commit
} else {
acc
private fun List<TagRef>.tagPairs(): List<Pair<TagRef?, TagRef?>> =
listOf(Pair(null, firstOrNull())) + mapIndexed { index, commitRef ->
commitRef to getOrNull(index + 1)
}
}

private fun List<Pair<TagRef?, Set<String>>>.withCommitsInOriginalOrder(
log: List<CommitRef>,
) = map { (tag, commitIds) ->
tag to log.filter { commit -> commitIds.contains(commit.id) }
}

fun branchCommits(id: String, offMainCommits: List<CommitRef>): List<CommitRef> = (0..offMainCommits.size)
.fold(listOf(id) to emptyList<CommitRef>()) { (ids, foundCommits), _ ->
val newCommits = offMainCommits.filter { ids.contains(it.id) }
newCommits.flatMap { it.parents } to (foundCommits + newCommits)
}.second
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ package com.zegreatrob.tools.digger.core
fun DiggerGitWrapper.currentContributionCommits(previousTag: TagRef?): List<CommitRef> = if (previousTag == null) {
log()
} else {
logWithRange(previousTag.name, "HEAD")
logWithRange("^${previousTag.name}", "HEAD")
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,15 @@ class DiggerGitWrapper(private val workingDirectory: String) {
),
)

fun logWithRange(begin: String, end: String): List<CommitRef> = parseLog(
fun logWithRange(begin: String, end: String? = null): List<CommitRef> = parseLog(
runProcess(
listOf(
"git",
"--no-pager",
"log",
"--format=$gitLogFormat",
"$begin..$end",
),
begin,
) + if (end != null) listOf(end) else emptyList(),
workingDirectory,
),
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.zegreatrob.tools.digger.core

fun DiggerGitWrapper.findTrunkPath(tagRefs: List<TagRef>, log: List<CommitRef>): List<CommitRef> = allPaths(
log = log,
firstTagCommit = log.first(),
preferredCommitIds = tagRefs.map { it.commitId }.toSet(),
)
.shortestPathWithMostTags(taggedCommitIds = tagRefs.map { it.commitId }.toSet())
?: log.alwaysLeftParent()

private fun MutableList<List<CommitRef>>.shortestPathWithMostTags(taggedCommitIds: Set<String>): List<CommitRef>? {
val pathTagCountGroups = groupBy { path ->
path.count { taggedCommitIds.contains(it.id) }
}
return pathTagCountGroups[pathTagCountGroups.keys.max()]
?.minBy { it.size }
}

fun List<CommitRef>.alwaysLeftParent() = fold(emptyList<CommitRef>()) { acc, commit ->
if (acc.isEmpty()) {
acc + commit
} else if (commit.id == acc.last().parents.firstOrNull()) {
acc + commit
} else {
acc
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ interface AllContributionTestSpec : SetupWithOverrides {
grgit.checkout { it.branch = "main" }

val mergeToMainCommit = grgit.mergeInBranch("branch", "merge-to-main")
delayLongEnoughToAffectGitDate()
val release2 = grgit.addTag("release-2")

val allOutput = runAllContributionData()
Expand Down Expand Up @@ -172,6 +173,7 @@ interface AllContributionTestSpec : SetupWithOverrides {

grgit.checkout { it.branch = "main" }
val thirdCommit = grgit.addCommitWithMessage("third")
delayLongEnoughToAffectGitDate()
val secondRelease = grgit.addTag("release-2")

grgit.checkout { it.branch = "branch" }
Expand All @@ -181,6 +183,7 @@ interface AllContributionTestSpec : SetupWithOverrides {
grgit.checkout { it.branch = "main" }

grgit.ffOnlyInBranch("branch")
delayLongEnoughToAffectGitDate()
val thirdRelease = grgit.addTag("release-3")

val allOutput = runAllContributionData()
Expand Down Expand Up @@ -223,11 +226,13 @@ interface AllContributionTestSpec : SetupWithOverrides {
grgit.checkout { it.branch = "main" }

val thirdCommit = grgit.addCommitWithMessage("third")
delayLongEnoughToAffectGitDate()
val secondRelease = grgit.addTag("release2")
grgit.checkout { it.branch = "branch1" }
grgit.addCommitWithMessage("fourth")
grgit.checkout { it.branch = "main" }
val mergeCommit = grgit.mergeInBranch("branch1", "merge")
delayLongEnoughToAffectGitDate()
val thirdRelease = grgit.addTag("release3")

val allOutput = runAllContributionData()
Expand Down Expand Up @@ -279,6 +284,7 @@ interface AllContributionTestSpec : SetupWithOverrides {
grgit.addCommitWithMessage("sixth")

val merge2Commit = grgit.mergeInBranch("branch1", "merge2")
delayLongEnoughToAffectGitDate()
val thirdRelease = grgit.addTag("release3")

val allOutput = runAllContributionData()
Expand Down Expand Up @@ -314,12 +320,14 @@ interface AllContributionTestSpec : SetupWithOverrides {
grgit.checkout { it.branch = "main" }
grgit.addCommitWithMessage("third")
val merge1Commit = grgit.mergeInBranch("branch1", "merge1")
delayLongEnoughToAffectGitDate()
val secondRelease = grgit.addTag("release2")

grgit.checkout { it.branch = "branch1" }
val fourthCommit = grgit.addCommitWithMessage("fourth")
grgit.checkout { it.branch = "main" }
val merge2Commit = grgit.mergeInBranch("branch1", "merge2")
delayLongEnoughToAffectGitDate()
val thirdRelease = grgit.addTag("release3")

val allOutput = runAllContributionData()
Expand Down

0 comments on commit 6a15f5d

Please sign in to comment.