diff --git a/merge_squash/.gitmastery-exercise.json b/merge_squash/.gitmastery-exercise.json new file mode 100644 index 00000000..3a2d7001 --- /dev/null +++ b/merge_squash/.gitmastery-exercise.json @@ -0,0 +1,18 @@ +{ + "exercise_name": "merge-squash", + "tags": [ + "git-branch", + "git-merge", + "git-reset" + ], + "requires_git": true, + "requires_github": false, + "base_files": {}, + "exercise_repo": { + "repo_type": "local", + "repo_name": "friends-cast", + "repo_title": null, + "create_fork": null, + "init": true + } +} \ No newline at end of file diff --git a/merge_squash/README.md b/merge_squash/README.md new file mode 100644 index 00000000..1a9b00c7 --- /dev/null +++ b/merge_squash/README.md @@ -0,0 +1 @@ +See https://git-mastery.github.io/lessons/merge/exercise-merge-squash.html \ No newline at end of file diff --git a/merge_squash/__init__.py b/merge_squash/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/merge_squash/download.py b/merge_squash/download.py new file mode 100644 index 00000000..b54a50c1 --- /dev/null +++ b/merge_squash/download.py @@ -0,0 +1,51 @@ +from exercise_utils.file import create_or_update_file +from exercise_utils.git import add, commit, checkout + + +def setup(verbose: bool = False): + create_or_update_file( + "joey.txt", + """ + Matt LeBlanc + """, + ) + add(["."], verbose) + commit("Add Joey", verbose) + + create_or_update_file( + "phoebe.txt", + """ + Lisa Kudrow + """, + ) + add(["."], verbose) + commit("Add Phoebe", verbose) + + checkout("supporting", True, verbose) + create_or_update_file( + "mike.txt", + """ + Paul Rudd + """, + ) + add(["."], verbose) + commit("Add Mike", verbose) + + create_or_update_file( + "janice.txt", + """ + Maggie Wheeler + """, + ) + add(["."], verbose) + commit("Add Janice", verbose) + + checkout("main", False, verbose) + create_or_update_file( + "ross.txt", + """ + David Schwimmer + """, + ) + add(["."], verbose) + commit("Add Ross", verbose) diff --git a/merge_squash/tests/__init__.py b/merge_squash/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/merge_squash/tests/specs/base.yml b/merge_squash/tests/specs/base.yml new file mode 100644 index 00000000..c42a04b6 --- /dev/null +++ b/merge_squash/tests/specs/base.yml @@ -0,0 +1,67 @@ +initialization: + steps: + - type: commit + empty: true + message: Set initial state + id: start + - type: new-file + filename: joey.txt + contents: | + Matt LeBlanc + - type: add + files: + - joey.txt + - type: commit + message: Add Joey + + - type: new-file + filename: phoebe.txt + contents: | + Lisa Kudrow + - type: add + files: + - phoebe.txt + - type: commit + message: Add Phoebe + + - type: branch + branch-name: supporting + - type: checkout + branch-name: supporting + + - type: new-file + filename: mike.txt + contents: | + Paul Rudd + - type: add + files: + - mike.txt + - type: commit + message: Add Mike + + - type: new-file + filename: janice.txt + contents: | + Maggie Wheeler + - type: add + files: + - janice.txt + - type: commit + message: Add Janice + + - type: checkout + branch-name: main + + - type: new-file + filename: ross.txt + contents: | + David Schwimmer + - type: add + files: + - ross.txt + - type: commit + message: Add Ross + + - type: merge + squash: true + branch-name: supporting diff --git a/merge_squash/tests/specs/missing_main_commits.yml b/merge_squash/tests/specs/missing_main_commits.yml new file mode 100644 index 00000000..655f64ba --- /dev/null +++ b/merge_squash/tests/specs/missing_main_commits.yml @@ -0,0 +1,60 @@ +initialization: + steps: + - type: commit + empty: true + message: Set initial state + id: start + - type: new-file + filename: joey.txt + contents: | + Matt LeBlanc + - type: add + files: + - joey.txt + - type: commit + message: Add Joey + + - type: branch + branch-name: supporting + - type: checkout + branch-name: supporting + + - type: new-file + filename: mike.txt + contents: | + Paul Rudd + - type: add + files: + - mike.txt + - type: commit + message: Add Mike + + - type: new-file + filename: janice.txt + contents: | + Maggie Wheeler + - type: add + files: + - janice.txt + - type: commit + message: Add Janice + + - type: checkout + branch-name: main + + - type: new-file + filename: ross.txt + contents: | + David Schwimmer + - type: add + files: + - ross.txt + - type: commit + message: Add Ross + + - type: merge + branch-name: supporting + squash: true + + - type: commit + message: Squash commit diff --git a/merge_squash/tests/specs/non_squash_merge_used.yml b/merge_squash/tests/specs/non_squash_merge_used.yml new file mode 100644 index 00000000..71150c83 --- /dev/null +++ b/merge_squash/tests/specs/non_squash_merge_used.yml @@ -0,0 +1,66 @@ +initialization: + steps: + - type: commit + empty: true + message: Set initial state + id: start + - type: new-file + filename: joey.txt + contents: | + Matt LeBlanc + - type: add + files: + - joey.txt + - type: commit + message: Add Joey + + - type: new-file + filename: phoebe.txt + contents: | + Lisa Kudrow + - type: add + files: + - phoebe.txt + - type: commit + message: Add Phoebe + + - type: branch + branch-name: supporting + - type: checkout + branch-name: supporting + + - type: new-file + filename: mike.txt + contents: | + Paul Rudd + - type: add + files: + - mike.txt + - type: commit + message: Add Mike + + - type: new-file + filename: janice.txt + contents: | + Maggie Wheeler + - type: add + files: + - janice.txt + - type: commit + message: Add Janice + + - type: checkout + branch-name: main + + - type: new-file + filename: ross.txt + contents: | + David Schwimmer + - type: add + files: + - ross.txt + - type: commit + message: Add Ross + + - type: merge + branch-name: supporting diff --git a/merge_squash/tests/specs/not_merged.yml b/merge_squash/tests/specs/not_merged.yml new file mode 100644 index 00000000..e89e099b --- /dev/null +++ b/merge_squash/tests/specs/not_merged.yml @@ -0,0 +1,63 @@ +initialization: + steps: + - type: commit + empty: true + message: Set initial state + id: start + - type: new-file + filename: joey.txt + contents: | + Matt LeBlanc + - type: add + files: + - joey.txt + - type: commit + message: Add Joey + + - type: new-file + filename: phoebe.txt + contents: | + Lisa Kudrow + - type: add + files: + - phoebe.txt + - type: commit + message: Add Phoebe + + - type: branch + branch-name: supporting + - type: checkout + branch-name: supporting + + - type: new-file + filename: mike.txt + contents: | + Paul Rudd + - type: add + files: + - mike.txt + - type: commit + message: Add Mike + + - type: new-file + filename: janice.txt + contents: | + Maggie Wheeler + - type: add + files: + - janice.txt + - type: commit + message: Add Janice + + - type: checkout + branch-name: main + + - type: new-file + filename: ross.txt + contents: | + David Schwimmer + - type: add + files: + - ross.txt + - type: commit + message: Add Ross diff --git a/merge_squash/tests/specs/wrong_branch_squashed.yml b/merge_squash/tests/specs/wrong_branch_squashed.yml new file mode 100644 index 00000000..cf1c282c --- /dev/null +++ b/merge_squash/tests/specs/wrong_branch_squashed.yml @@ -0,0 +1,70 @@ +initialization: + steps: + - type: commit + empty: true + message: Set initial state + id: start + - type: new-file + filename: joey.txt + contents: | + Matt LeBlanc + - type: add + files: + - joey.txt + - type: commit + message: Add Joey + + - type: new-file + filename: phoebe.txt + contents: | + Lisa Kudrow + - type: add + files: + - phoebe.txt + - type: commit + message: Add Phoebe + + - type: branch + branch-name: supporting + - type: checkout + branch-name: supporting + + - type: new-file + filename: mike.txt + contents: | + Paul Rudd + - type: add + files: + - mike.txt + - type: commit + message: Add Mike + + - type: new-file + filename: janice.txt + contents: | + Maggie Wheeler + - type: add + files: + - janice.txt + - type: commit + message: Add Janice + + - type: checkout + branch-name: main + + - type: new-file + filename: ross.txt + contents: | + David Schwimmer + - type: add + files: + - ross.txt + - type: commit + message: Add Ross + + - type: checkout + branch-name: supporting + + - type: merge + squash: true + branch-name: main diff --git a/merge_squash/tests/test_verify.py b/merge_squash/tests/test_verify.py new file mode 100644 index 00000000..4eca1ec5 --- /dev/null +++ b/merge_squash/tests/test_verify.py @@ -0,0 +1,37 @@ +from git_autograder import GitAutograderStatus, GitAutograderTestLoader, assert_output + +from ..verify import ( + SQUASH_NOT_USED, + MAIN_COMMITS_INCORRECT, + CHANGES_FROM_SUPPORTING_NOT_PRESENT, + SQUASH_ON_SUPPORTING, + verify +) + +REPOSITORY_NAME = "merge-squash" + +loader = GitAutograderTestLoader(__file__, REPOSITORY_NAME, verify) + + +def test_base(): + with loader.load("specs/base.yml") as output: + assert_output(output, GitAutograderStatus.SUCCESSFUL) + + +def test_non_squash_merge_used(): + with loader.load("specs/non_squash_merge_used.yml") as output: + assert_output(output, GitAutograderStatus.UNSUCCESSFUL, [SQUASH_NOT_USED]) + + +def test_not_merged(): + with loader.load("specs/not_merged.yml") as output: + assert_output(output, GitAutograderStatus.UNSUCCESSFUL, [CHANGES_FROM_SUPPORTING_NOT_PRESENT]) + + +def test_missing_main_commits(): + with loader.load("specs/missing_main_commits.yml") as output: + assert_output(output, GitAutograderStatus.UNSUCCESSFUL, [MAIN_COMMITS_INCORRECT]) + +def test_wrong_branch_squashed(): + with loader.load("specs/wrong_branch_squashed.yml") as output: + assert_output(output, GitAutograderStatus.UNSUCCESSFUL, [SQUASH_ON_SUPPORTING]) diff --git a/merge_squash/verify.py b/merge_squash/verify.py new file mode 100644 index 00000000..5863e978 --- /dev/null +++ b/merge_squash/verify.py @@ -0,0 +1,59 @@ +from git_autograder import ( + GitAutograderOutput, + GitAutograderExercise, + GitAutograderStatus, +) + +SQUASH_NOT_USED = ( + "You should be using squash merge, not regular merge." +) + +MAIN_COMMITS_INCORRECT = ( + "The main branch does not contain at least one of these commits: 'Add Joey', 'Add Phoebe' or 'Add Ross'." +) + +CHANGES_FROM_SUPPORTING_NOT_PRESENT = ( + "The main branch does not contain both files 'mike.txt' and 'janice.txt'." +) + +SQUASH_NOT_COMMITTED = ( + "You have not committed the staged files! Remember, you need to manually commit after a squash merge!" +) + +SQUASH_ON_SUPPORTING = ( + "You are working on the wrong branch! Bring the changes from supporting onto main, not the other way around." +) + + +def verify(exercise: GitAutograderExercise) -> GitAutograderOutput: + main_branch = exercise.repo.branches.branch("main") + + with exercise.repo.files.file_or_none("mike.txt") as mike_file: + if mike_file is None: + raise exercise.wrong_answer([CHANGES_FROM_SUPPORTING_NOT_PRESENT]) + + with exercise.repo.files.file_or_none("janice.txt") as janice_file: + if janice_file is None: + raise exercise.wrong_answer([CHANGES_FROM_SUPPORTING_NOT_PRESENT]) + + commit_messages_in_main = [str(c.commit.message.strip()) for c in main_branch.commits] + merge_commits = [c for c in main_branch.commits if len(c.parents) > 1] + + if merge_commits: + raise exercise.wrong_answer([SQUASH_NOT_USED]) + + if exercise.repo.repo.is_dirty(): + raise exercise.wrong_answer([SQUASH_NOT_COMMITTED]) + + if not all( + msg in commit_messages_in_main for msg in ["Add Joey", "Add Phoebe", "Add Ross"] + ): + raise exercise.wrong_answer([MAIN_COMMITS_INCORRECT]) + + try: + exercise.repo.repo.commit("supporting").tree / "ross.txt" + raise exercise.wrong_answer([SQUASH_ON_SUPPORTING]) + except KeyError: + pass + + return exercise.to_output(["Good job performing a merge squash!"], GitAutograderStatus.SUCCESSFUL)