diff --git a/merge_squash/.gitmastery-exercise.json b/merge_squash/.gitmastery-exercise.json new file mode 100644 index 0000000..3a2d700 --- /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 0000000..df947c2 --- /dev/null +++ b/merge_squash/README.md @@ -0,0 +1,40 @@ +# merge-squash + +You are keeping notes on the cast of a sitcom you've started watching. Initially, you kept main cast and supporting cast on two separate branches. + +```mermaid +gitGraph BT: + commit id: "Add Joey" + commit id: "Add Phoebe" + branch supporting + checkout supporting + commit id: "Add Mike" + commit id: "Add Janice" + checkout main + commit id: "Add Ross" +``` + +Now you wish to keep everything in the `main` branch. + +## Task + +Squash-merge the `supporting` branch onto the `main` branch. + +The result should look as follows: + +```mermaid +gitGraph BT: + commit id: "Add Joey" + commit id: "Add Phoebe" + branch supporting + checkout supporting + commit id: "Add Mike" + commit id: "Add Janice" + checkout main + commit id: "Add Ross" + commit id: "Squash commit" +``` + +## Hints + +You have to commit manually after performing the squash merge. \ No newline at end of file diff --git a/merge_squash/__init__.py b/merge_squash/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/merge_squash/download.py b/merge_squash/download.py new file mode 100644 index 0000000..b54a50c --- /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 0000000..e69de29 diff --git a/merge_squash/tests/specs/base.yml b/merge_squash/tests/specs/base.yml new file mode 100644 index 0000000..c42a04b --- /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 0000000..655f64b --- /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 0000000..71150c8 --- /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 0000000..e89e099 --- /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/test_verify.py b/merge_squash/tests/test_verify.py new file mode 100644 index 0000000..ec45c00 --- /dev/null +++ b/merge_squash/tests/test_verify.py @@ -0,0 +1,32 @@ +from git_autograder import GitAutograderStatus, GitAutograderTestLoader, assert_output + +from ..verify import ( + SQUASH_NOT_USED, + MAIN_COMMITS_INCORRECT, + CHANGES_FROM_SUPPORTING_NOT_PRESENT, + 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]) diff --git a/merge_squash/verify.py b/merge_squash/verify.py new file mode 100644 index 0000000..df0f085 --- /dev/null +++ b/merge_squash/verify.py @@ -0,0 +1,42 @@ +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'." +) + + +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 = [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 or ("Squash" not in commit_messages_in_main[0]): + raise exercise.wrong_answer([SQUASH_NOT_USED]) + + 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]) + + return exercise.to_output(["Good job performing a merge squash!"], GitAutograderStatus.SUCCESSFUL)