Align level parity docs and fetch/status validation
- Add LevelsCompare.md with setup and validation comparisons against Gazler/githug. - Document that LevelsCompare.md must be updated when level behavior changes. - Track FETCH_HEAD count in RepoState so the Fetch level can match upstream validation. - Mirror upstream Status setup with staged files plus untracked database.yml. - Keep Fetch from being solved by pull despite native Git FETCH_HEAD behavior. - Bump app version and build debug APK.
This commit is contained in:
@@ -31,6 +31,7 @@ data class RepoState(
|
||||
val config: Map<String, String> = emptyMap(),
|
||||
val stashes: List<String> = emptyList(),
|
||||
val fetchedBranches: Set<String> = emptySet(),
|
||||
val fetchHeadCount: Int = 0,
|
||||
val pushedBranches: Set<String> = emptySet(),
|
||||
val pushedTags: Set<String> = emptySet(),
|
||||
val submodules: Map<String, String> = emptyMap(),
|
||||
@@ -55,4 +56,3 @@ data class LevelTestCase(
|
||||
)
|
||||
|
||||
fun sampleLevels(): List<Level> = allGithugLevels()
|
||||
|
||||
|
||||
@@ -631,6 +631,11 @@ class GitRepositoryRuntime private constructor(
|
||||
val exactTagResult = runGit(nativeGit, sandbox, listOf("describe", "--tags", "--exact-match"))
|
||||
val userNameResult = runGit(nativeGit, sandbox, listOf("config", "--get", "user.name"))
|
||||
val userEmailResult = runGit(nativeGit, sandbox, listOf("config", "--get", "user.email"))
|
||||
val fetchHeadCount = File(sandbox, ".git/FETCH_HEAD")
|
||||
.takeIf { it.isFile }
|
||||
?.readLines()
|
||||
?.count { it.isNotBlank() }
|
||||
?: 0
|
||||
val config = buildMap {
|
||||
userNameResult.outputLines.firstOrNull()
|
||||
?.takeIf { userNameResult.exitCode == 0 && it.isNotBlank() }
|
||||
@@ -704,6 +709,7 @@ class GitRepositoryRuntime private constructor(
|
||||
.map { it.removePrefix("*").trim() }
|
||||
.filter { it.isNotBlank() && " -> " !in it }
|
||||
.toSet(),
|
||||
fetchHeadCount = fetchHeadCount,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -738,9 +744,8 @@ class GitRepositoryRuntime private constructor(
|
||||
stashes = mergeDistinct(previousRepo.stashes, inspectedRepo.stashes + "stash@{${previousRepo.stashes.size}}"),
|
||||
)
|
||||
"fetch" -> {
|
||||
val remote = tokens.getOrNull(2)?.takeIf { !it.startsWith("-") } ?: "origin"
|
||||
inspectedRepo.copy(
|
||||
fetchedBranches = inspectedRepo.fetchedBranches + previousRepo.fetchedBranches + "$remote/master" + "$remote/feature_branch" + "$remote/new_branch",
|
||||
fetchedBranches = inspectedRepo.fetchedBranches + previousRepo.fetchedBranches,
|
||||
maintenanceActions = inspectedRepo.maintenanceActions + previousRepo.maintenanceActions + "fetch",
|
||||
)
|
||||
}
|
||||
@@ -749,6 +754,8 @@ class GitRepositoryRuntime private constructor(
|
||||
val branch = tokens.drop(2).lastOrNull()?.takeIf { !it.startsWith("-") && it != remote } ?: inspectedRepo.headBranch
|
||||
inspectedRepo.copy(
|
||||
fetchedBranches = inspectedRepo.fetchedBranches + previousRepo.fetchedBranches + "$remote/$branch",
|
||||
fetchHeadCount = inspectedRepo.fetchHeadCount,
|
||||
maintenanceActions = inspectedRepo.maintenanceActions + previousRepo.maintenanceActions + "pull",
|
||||
)
|
||||
}
|
||||
"push" -> {
|
||||
@@ -795,6 +802,7 @@ class GitRepositoryRuntime private constructor(
|
||||
else -> inspectedRepo.copy(
|
||||
stashes = mergeDistinct(previousRepo.stashes, inspectedRepo.stashes),
|
||||
fetchedBranches = previousRepo.fetchedBranches + inspectedRepo.fetchedBranches,
|
||||
fetchHeadCount = inspectedRepo.fetchHeadCount,
|
||||
pushedBranches = previousRepo.pushedBranches + inspectedRepo.pushedBranches,
|
||||
pushedTags = previousRepo.pushedTags + inspectedRepo.pushedTags,
|
||||
submodules = previousRepo.submodules + inspectedRepo.submodules,
|
||||
|
||||
@@ -63,7 +63,8 @@ object GitSandboxEngine {
|
||||
parts.size >= 2 && parts[1] == "fetch" -> {
|
||||
val remote = parts.getOrNull(2)?.takeIf { !it.startsWith("-") } ?: "origin"
|
||||
repo.copy(
|
||||
fetchedBranches = repo.fetchedBranches + listOf("$remote/master", "$remote/feature_branch", "$remote/new_branch"),
|
||||
fetchedBranches = repo.fetchedBranches + listOf("$remote/master", "$remote/new_branch"),
|
||||
fetchHeadCount = 2,
|
||||
maintenanceActions = repo.maintenanceActions + "fetch",
|
||||
) to emptyList()
|
||||
}
|
||||
@@ -72,7 +73,9 @@ object GitSandboxEngine {
|
||||
val branch = parts.drop(2).lastOrNull()?.takeIf { !it.startsWith("-") && it != remote } ?: repo.headBranch
|
||||
repo.copy(
|
||||
fetchedBranches = repo.fetchedBranches + "$remote/$branch",
|
||||
fetchHeadCount = 1,
|
||||
branches = repo.branches + (repo.headBranch to maxOf(repo.branches[repo.headBranch] ?: 0, 2)),
|
||||
maintenanceActions = repo.maintenanceActions + "pull",
|
||||
) to emptyList()
|
||||
}
|
||||
parts.size >= 2 && parts[1] == "push" -> pushRefs(repo, parts.drop(2))
|
||||
|
||||
@@ -20,6 +20,7 @@ val RepoStateSaver = listSaver<RepoState, Any>(
|
||||
state.pushedTags.toList(),
|
||||
state.submodules.flatMap { listOf(it.key, it.value) },
|
||||
state.maintenanceActions.toList(),
|
||||
state.fetchHeadCount,
|
||||
)
|
||||
},
|
||||
restore = { saved ->
|
||||
@@ -37,6 +38,7 @@ val RepoStateSaver = listSaver<RepoState, Any>(
|
||||
val pushedTags = saved.getOrNull(12) as? List<*> ?: emptyList<Any>()
|
||||
val submoduleParts = saved.getOrNull(13) as? List<*> ?: emptyList<Any>()
|
||||
val maintenanceActions = saved.getOrNull(14) as? List<*> ?: emptyList<Any>()
|
||||
val fetchHeadCount = saved.getOrNull(15) as? Int ?: 0
|
||||
RepoState(
|
||||
initialized = initialized,
|
||||
headBranch = headBranch,
|
||||
@@ -59,6 +61,7 @@ val RepoStateSaver = listSaver<RepoState, Any>(
|
||||
config = configParts.chunked(2).associate { (it[0] as String) to (it[1] as String) },
|
||||
stashes = stashes.filterIsInstance<String>(),
|
||||
fetchedBranches = fetchedBranches.filterIsInstance<String>().toSet(),
|
||||
fetchHeadCount = fetchHeadCount,
|
||||
pushedBranches = pushedBranches.filterIsInstance<String>().toSet(),
|
||||
pushedTags = pushedTags.filterIsInstance<String>().toSet(),
|
||||
submodules = submoduleParts.chunked(2).associate { (it[0] as String) to (it[1] as String) },
|
||||
|
||||
@@ -15,7 +15,15 @@ internal fun fetchLevel(): Level = level(
|
||||
description = "Looks like a new branch was pushed into our remote repository. Get the changes without merging them with the local repository",
|
||||
hints = listOf("Look up the 'git fetch' command"),
|
||||
commandSuggestions = listOf("git fetch origin"),
|
||||
setup = { RepoState(initialized = true, branches = mapOf("master" to 1), remotes = mapOf("origin" to "remote")) },
|
||||
setup = {
|
||||
RepoState(
|
||||
initialized = true,
|
||||
branches = mapOf("master" to 1),
|
||||
remotes = mapOf("origin" to "remote"),
|
||||
fetchedBranches = setOf("origin/master"),
|
||||
fetchHeadCount = 1,
|
||||
)
|
||||
},
|
||||
nativeSetup = {
|
||||
resetFiles()
|
||||
write("master_file")
|
||||
@@ -33,9 +41,9 @@ internal fun fetchLevel(): Level = level(
|
||||
true
|
||||
},
|
||||
validator = repoPredicate { repo ->
|
||||
"fetch" in repo.maintenanceActions &&
|
||||
"origin/new_branch" in repo.fetchedBranches &&
|
||||
repo.headBranch == "master"
|
||||
repo.branches.size == 1 &&
|
||||
repo.fetchHeadCount == 2 &&
|
||||
"pull" !in repo.maintenanceActions
|
||||
},
|
||||
testCases = listOf(
|
||||
levelTestCase("fetch origin", "git fetch origin"),
|
||||
|
||||
@@ -149,6 +149,8 @@ private fun RepoState.validationSnapshot(): String = buildString {
|
||||
append(remotes.toSortedMap())
|
||||
append(", fetchedBranches=")
|
||||
append(fetchedBranches.sorted())
|
||||
append(", fetchHeadCount=")
|
||||
append(fetchHeadCount)
|
||||
append(", pushedBranches=")
|
||||
append(pushedBranches.sorted())
|
||||
append(", pushedTags=")
|
||||
|
||||
@@ -13,7 +13,29 @@ internal fun statusLevel(): Level = level(
|
||||
description = "Among the files in this repository, which of them is untracked?",
|
||||
hints = listOf("You are looking for a command to identify the status of the repository."),
|
||||
commandSuggestions = listOf("git status"),
|
||||
setup = { RepoState(initialized = true, files = listOf(GitFile("database.yml")), branches = mapOf("master" to 0)) },
|
||||
setup = {
|
||||
RepoState(
|
||||
initialized = true,
|
||||
files = listOf(
|
||||
GitFile("config.rb", staged = true),
|
||||
GitFile("README", staged = true),
|
||||
GitFile("setup.rb", staged = true),
|
||||
GitFile("deploy.rb", staged = true),
|
||||
GitFile("Guardfile", staged = true),
|
||||
GitFile("database.yml"),
|
||||
),
|
||||
branches = mapOf("master" to 0),
|
||||
)
|
||||
},
|
||||
nativeSetup = {
|
||||
resetFiles()
|
||||
listOf("config.rb", "README", "setup.rb", "deploy.rb", "Guardfile").forEach { file ->
|
||||
write(file)
|
||||
git("add", file)
|
||||
}
|
||||
write("database.yml")
|
||||
true
|
||||
},
|
||||
validator = commandAnswer("database.yml"),
|
||||
testCases = listOf(
|
||||
levelTestCase("answer untracked file", "database.yml"),
|
||||
|
||||
Reference in New Issue
Block a user