diff --git a/ff_undo/.gitmastery-exercise.json b/ff_undo/.gitmastery-exercise.json new file mode 100644 index 00000000..53fddb60 --- /dev/null +++ b/ff_undo/.gitmastery-exercise.json @@ -0,0 +1,18 @@ +{ + "exercise_name": "ff-undo", + "tags": [ + "git-branch", + "git-merge", + "git-reset" + ], + "requires_git": true, + "requires_github": false, + "base_files": {}, + "exercise_repo": { + "repo_type": "local", + "repo_name": "play-characters", + "repo_title": null, + "create_fork": null, + "init": true + } +} \ No newline at end of file diff --git a/ff_undo/README.md b/ff_undo/README.md new file mode 100644 index 00000000..05f96661 --- /dev/null +++ b/ff_undo/README.md @@ -0,0 +1 @@ +See https://git-mastery.github.io/lessons/merge/exercise-ff-undo.html diff --git a/ff_undo/__init__.py b/ff_undo/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ff_undo/download.py b/ff_undo/download.py new file mode 100644 index 00000000..0ad7ac5d --- /dev/null +++ b/ff_undo/download.py @@ -0,0 +1,62 @@ +from exercise_utils.git import ( + add, + commit, + checkout, + merge, +) +from exercise_utils.file import ( + create_or_update_file, + append_to_file, +) + + +def setup(verbose: bool = False): + create_or_update_file( + "rick.txt", + """ + Hero + """, + ) + add(["."], verbose) + commit("Add Rick", verbose) + + create_or_update_file( + "morty.txt", + """ + Boy + """, + ) + add(["."], verbose) + commit("Add Morty", verbose) + + checkout("others", True, verbose) + + create_or_update_file( + "birdperson.txt", + """ + No job + """, + ) + add(["."], verbose) + commit("Add Birdperson", verbose) + + append_to_file( + "birdperson.txt", + """ + Cyborg + """, + ) + add(["."], verbose) + commit("Add Cyborg to birdperson.txt", verbose) + + create_or_update_file( + "tammy.txt", + """ + Spy + """, + ) + add(["."], verbose) + commit("Add Tammy", verbose) + + checkout("main", False, verbose) + merge("others", True, verbose) diff --git a/ff_undo/tests/__init__.py b/ff_undo/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ff_undo/tests/specs/base.yml b/ff_undo/tests/specs/base.yml new file mode 100644 index 00000000..8bcd19b2 --- /dev/null +++ b/ff_undo/tests/specs/base.yml @@ -0,0 +1,22 @@ +initialization: + steps: + - type: commit + empty: true + message: Set initial state + id: start + - type: commit + message: "Add Rick" + - type: commit + message: "Add Morty" + - type: branch + branch-name: others + - type: checkout + branch-name: others + - type: commit + message: "Add Birdperson" + - type: commit + message: "Add Cyborg to birdperson.txt" + - type: commit + message: "Add Tammy" + - type: checkout + branch-name: main \ No newline at end of file diff --git a/ff_undo/tests/specs/branch_missing.yml b/ff_undo/tests/specs/branch_missing.yml new file mode 100644 index 00000000..c87c529a --- /dev/null +++ b/ff_undo/tests/specs/branch_missing.yml @@ -0,0 +1,10 @@ +initialization: + steps: + - type: commit + empty: true + message: Set initial state + id: start + - type: commit + message: "Add Rick" + - type: commit + message: "Add Morty" \ No newline at end of file diff --git a/ff_undo/tests/specs/main_commits_incorrect.yml b/ff_undo/tests/specs/main_commits_incorrect.yml new file mode 100644 index 00000000..0e63c2af --- /dev/null +++ b/ff_undo/tests/specs/main_commits_incorrect.yml @@ -0,0 +1,18 @@ +initialization: + steps: + - type: commit + empty: true + message: Set initial state + id: start + - type: commit + message: "Add Morty" + - type: branch + branch-name: others + - type: checkout + branch-name: others + - type: commit + message: "Add Birdperson" + - type: commit + message: "Add Cyborg to birdperson.txt" + - type: commit + message: "Add Tammy" \ No newline at end of file diff --git a/ff_undo/tests/specs/merge_not_undone.yml b/ff_undo/tests/specs/merge_not_undone.yml new file mode 100644 index 00000000..1450b793 --- /dev/null +++ b/ff_undo/tests/specs/merge_not_undone.yml @@ -0,0 +1,26 @@ +initialization: + steps: + - type: commit + empty: true + message: Set initial state + id: start + - type: commit + message: "Add Rick" + - type: commit + message: "Add Morty" + - type: branch + branch-name: others + - type: checkout + branch-name: others + - type: commit + message: "Add Birdperson" + - type: commit + message: "Add Cyborg to birdperson.txt" + - type: commit + message: "Add Tammy" + - type: checkout + branch-name: main + - type: merge + branch-name: others + no-ff: false + message: "Introduce others" \ No newline at end of file diff --git a/ff_undo/tests/specs/others_commits_incorrect.yml b/ff_undo/tests/specs/others_commits_incorrect.yml new file mode 100644 index 00000000..eefe189a --- /dev/null +++ b/ff_undo/tests/specs/others_commits_incorrect.yml @@ -0,0 +1,18 @@ +initialization: + steps: + - type: commit + empty: true + message: Set initial state + id: start + - type: commit + message: "Add Rick" + - type: commit + message: "Add Morty" + - type: branch + branch-name: others + - type: checkout + branch-name: others + - type: commit + message: "Add Birdperson" + - type: commit + message: "Add Tammy" diff --git a/ff_undo/tests/test_verify.py b/ff_undo/tests/test_verify.py new file mode 100644 index 00000000..ca482f92 --- /dev/null +++ b/ff_undo/tests/test_verify.py @@ -0,0 +1,42 @@ +from git_autograder import GitAutograderTestLoader, assert_output +from git_autograder.status import GitAutograderStatus +from ..verify import ( + MERGE_NOT_UNDONE, + MAIN_COMMITS_INCORRECT, + OTHERS_COMMITS_INCORRECT, + OTHERS_BRANCH_MISSING, + verify, +) + +REPOSITORY_NAME = "ff-undo" + +loader = GitAutograderTestLoader(__file__, REPOSITORY_NAME, verify) + + +def test_correct_solution(): + with loader.load("specs/base.yml") as output: + assert_output(output, GitAutograderStatus.SUCCESSFUL) + + +def test_merge_not_undone(): + with loader.load("specs/merge_not_undone.yml") as output: + assert_output(output, GitAutograderStatus.UNSUCCESSFUL, [MERGE_NOT_UNDONE]) + + +def test_branch_missing(): + with loader.load("specs/branch_missing.yml") as output: + assert_output(output, GitAutograderStatus.UNSUCCESSFUL, [OTHERS_BRANCH_MISSING]) + + +def test_main_commits_incorrect(): + with loader.load("specs/main_commits_incorrect.yml") as output: + assert_output( + output, GitAutograderStatus.UNSUCCESSFUL, [MAIN_COMMITS_INCORRECT] + ) + + +def test_others_commits_incorrect(): + with loader.load("specs/others_commits_incorrect.yml") as output: + assert_output( + output, GitAutograderStatus.UNSUCCESSFUL, [OTHERS_COMMITS_INCORRECT] + ) diff --git a/ff_undo/verify.py b/ff_undo/verify.py new file mode 100644 index 00000000..9870dc6a --- /dev/null +++ b/ff_undo/verify.py @@ -0,0 +1,52 @@ +from git_autograder import ( + GitAutograderOutput, + GitAutograderExercise, + GitAutograderStatus, +) + +ADD_RICK = "Add Rick" +ADD_MORTY = "Add Morty" +ADD_BIRDPERSON = "Add Birdperson" +ADD_CYBORG = "Add Cyborg to birdperson.txt" +ADD_TAMMY = "Add Tammy" + +MERGE_NOT_UNDONE = "The merge has not been undone properly." +MAIN_COMMITS_INCORRECT = "The main branch does not contain the expected commits." +OTHERS_COMMITS_INCORRECT = "The others branch does not contain the expected commits." +OTHERS_BRANCH_MISSING = "Missing branch 'others'." + + +def verify(exercise: GitAutograderExercise) -> GitAutograderOutput: + main_branch = exercise.repo.branches.branch("main") + others_branch = exercise.repo.branches.branch_or_none("others") + + if others_branch is None: + raise exercise.wrong_answer([OTHERS_BRANCH_MISSING]) + + # Verify commits in main branch + commit_messages_in_main = [c.commit.message.strip() for c in main_branch.commits] + if any( + msg in commit_messages_in_main + for msg in [ADD_BIRDPERSON, ADD_CYBORG, ADD_TAMMY] + ): + raise exercise.wrong_answer([MERGE_NOT_UNDONE]) + + if len(commit_messages_in_main) != 3 or not all( + msg in commit_messages_in_main for msg in [ADD_RICK, ADD_MORTY] + ): + raise exercise.wrong_answer([MAIN_COMMITS_INCORRECT]) + + # Verify commits in others branch + commit_messages_in_others = [ + c.commit.message.strip() for c in others_branch.commits + ] + if len(commit_messages_in_others) != 6 or not all( + msg in commit_messages_in_others + for msg in [ADD_BIRDPERSON, ADD_CYBORG, ADD_TAMMY, ADD_RICK, ADD_MORTY] + ): + raise exercise.wrong_answer([OTHERS_COMMITS_INCORRECT]) + + return exercise.to_output( + ["You have successfully undone the merge of branch 'others'."], + GitAutograderStatus.SUCCESSFUL, + )