diff --git a/gitgud/operations.py b/gitgud/operations.py index 0e440736..afa71496 100644 --- a/gitgud/operations.py +++ b/gitgud/operations.py @@ -278,6 +278,8 @@ def get_current_tree(self): commits = set() visited = set() + commits.add(repo.head.commit) + for branch in repo.branches: commits.add(branch.commit) commit_hash = branch.commit.hexsha diff --git a/gitgud/skills/rampup/__init__.py b/gitgud/skills/rampup/__init__.py index 845fc9ec..299b231a 100644 --- a/gitgud/skills/rampup/__init__.py +++ b/gitgud/skills/rampup/__init__.py @@ -5,7 +5,6 @@ 'Rampup', 'rampup', [ - BasicLevel("Detaching HEAD", 'detaching', __name__), BasicLevel("Relative References I: Using (^)", 'relrefs1', __name__), BasicLevel("Relative References II: Using (~)", 'relrefs2', __name__), BasicLevel("Reversing Changes in Git", 'reversing', __name__) diff --git a/gitgud/skills/rampup/_detaching/explanation.txt b/gitgud/skills/rampup/_detaching/explanation.txt deleted file mode 100644 index f35f9c8e..00000000 --- a/gitgud/skills/rampup/_detaching/explanation.txt +++ /dev/null @@ -1,18 +0,0 @@ -This skill will introduce the idea of detaching your HEAD! ->>> -Don't worry, we don't mean your head; we mean the head of your commit tree. First, what is the HEAD of a commit tree? ->>> -The HEAD is used to refer to the commit that is currently checked out. The HEAD is often used by git commands that make changes to the working tree. ->>> -The HEAD usually points to the commit that you are working on top of. Normally, the HEAD points to the branch that you have checked out. In this way, when bugFix is updated, HEAD is updated as well. -Example: HEAD -> bugFix -> C1 //HEAD points to bugFix. - HEAD -> bugFix -> C2 //A commit moved bugFix to C2, HEAD effectively points to C2 ->>> - What if we HEAD to point at a commit rather than a branch? - (e.g., HEAD -> C1 instead of HEAD -> bugFix -> C1) ->>> -In comes the concept of detaching HEAD. Simply use "git checkout " to modify HEAD so that it points to the commit instead of the branch. ->>> -The commit hash is a unique identifier for each commit node. Use "git gud show-tree" to see the git commit tree. The hashes look something like: "d6ba740...". Since each commit hash is unique, you only need to type the first four characters of the hash to refer to the commit. (e.g. "git checkout d6ba" will detach the HEAD onto the commit of that hash) ->>> -Congratulations! You've learned how to detach HEADs in git. Try using "git gud show-tree" to see how the HEAD changes before and after a detach. Use what you've learned to move the HEAD from master to commit 4. diff --git a/gitgud/skills/rampup/_detaching/goal.txt b/gitgud/skills/rampup/_detaching/goal.txt deleted file mode 100644 index bd32dfda..00000000 --- a/gitgud/skills/rampup/_detaching/goal.txt +++ /dev/null @@ -1 +0,0 @@ -Enter a detached head state by checking out commit 4 \ No newline at end of file diff --git a/gitgud/skills/rampup/_detaching/setup.spec b/gitgud/skills/rampup/_detaching/setup.spec deleted file mode 100644 index fcfb2482..00000000 --- a/gitgud/skills/rampup/_detaching/setup.spec +++ /dev/null @@ -1,5 +0,0 @@ -1 -2 : 1 (master) -3 : 1 -4 (bugFix) -master diff --git a/gitgud/skills/rampup/_detaching/solution.txt b/gitgud/skills/rampup/_detaching/solution.txt deleted file mode 100644 index 43815ac6..00000000 --- a/gitgud/skills/rampup/_detaching/solution.txt +++ /dev/null @@ -1,4 +0,0 @@ -# User would instead check out the commit hash using only one command -git checkout bugFix -git checkout @^ -git checkout @{1} \ No newline at end of file diff --git a/gitgud/skills/rampup/_detaching/test.spec b/gitgud/skills/rampup/_detaching/test.spec deleted file mode 100644 index 0030e139..00000000 --- a/gitgud/skills/rampup/_detaching/test.spec +++ /dev/null @@ -1,5 +0,0 @@ -1 -2 : 1 (master) -3 : 1 -4 (bugFix) -4 diff --git a/gitgud/skills/rewriting/__init__.py b/gitgud/skills/rewriting/__init__.py index a3469a0c..2bbbd7b7 100644 --- a/gitgud/skills/rewriting/__init__.py +++ b/gitgud/skills/rewriting/__init__.py @@ -1,3 +1,5 @@ +from gitgud import operations + from gitgud.skills.level_builder import BasicLevel from gitgud.skills.util import Skill @@ -13,9 +15,24 @@ def status(self): simulate_command('git log --reverse --oneline') +class Truth(SentenceLevel): + def _test(self): + if not super()._test(): + return False + + file_operator = operations.get_operator() + + for branch in file_operator.repo.branches: + if branch.commit == file_operator.repo.head.commit: + return True + + return False + + skill = Skill( 'Rewriting History', 'rewriting', [ + Truth('The Truth', 'truth', __name__) ] ) diff --git a/gitgud/skills/rewriting/_truth/details.yaml b/gitgud/skills/rewriting/_truth/details.yaml new file mode 100644 index 00000000..457a37e2 --- /dev/null +++ b/gitgud/skills/rewriting/_truth/details.yaml @@ -0,0 +1,25 @@ +'1': + message: This + add-files: + This.txt: + - "Hello, this file doesn't do anything." +'2': + message: is + add-files: + is.txt: + - "Hello, this file doesn't do anything." +'3': + message: an + add-files: + an.txt: + - "Hello, this file doesn't do anything." +'4': + message: easy + add-files: + easy.txt: + - "Hello, this file doesn't do anything." +'5': + message: level + add-files: + level.txt: + - "Hello, this file doesn't do anything." diff --git a/gitgud/skills/rewriting/_truth/explanation.txt b/gitgud/skills/rewriting/_truth/explanation.txt new file mode 100644 index 00000000..29fad693 --- /dev/null +++ b/gitgud/skills/rewriting/_truth/explanation.txt @@ -0,0 +1,47 @@ +There's something you need to know about... +>>> +The truth is... +>>> +What you did with "git rebase -i"... +>>> +Can be done by... +>>> +Detatching your head! Eek! +>>> +Let me clarify, I'm actually talking about your HEAD. +>>> +HEAD is used to refer to the commit or branch that is currently checked out. +This basically means that when you're on branch "master", HEAD resolves to "master", which itself resolves to a commit. +>>> +This is important because when you're in a 'detached HEAD' state, cherry-picking still works the same way, and you can do exactly what you just did in the last level. +>>> +Normally, HEAD points to the branch that you have checked out, so it refers to whichever commit the branch does, even if there is a new commit! +For example, if you have bugFix checked out, then when bugFix is updated, HEAD is updated as well, just like this: + +// HEAD points to bugFix. +C1 +^ bugFix < HEAD + +// A new commit (C2) caused bugFix to move to C2, HEAD effectively points to C2 +C1 ---- C2 + ^ bugFix < HEAD + +For us, since no branches point to the first commit, we can't check it out while staying on a branch. +>>> +What if we have HEAD to point at a commit rather than a branch? +(e.g., HEAD > C1 instead of HEAD > bugFix > C1) +>>> +In comes the concept of the 'detached HEAD' state. Simply use "git checkout " to modify HEAD so that it points to the commit instead of the branch. +>>> +As you've seen, a commit hash is a unique identifier for a commit. +Use "git log" or "git gud status" to see the commit tree and to look for the hashes. +>>> +The hashes look something like: "d6ba740". +The full commit hash is 40 characters long, and every one is unique. +>>> +In general, if you only have a few commits, even just the first few characters of the commit hash are going to be unique, so you can use as few as the first four characters to refer to the commit. +Using the example has above "git checkout d6ba" will detach the HEAD so it refers to the commit with the hash "d6ba740" +>>> +Finally, to beat this level, you're going to need to check out the first commit (detaching HEAD) then cherry-pick the rest of the commits so they are in the right order. +Finish the job by making a new branch with the re-ordered commits. +It can be named anything, but don't check it out so we know you detached your HEAD! diff --git a/gitgud/skills/rewriting/_truth/goal.txt b/gitgud/skills/rewriting/_truth/goal.txt new file mode 100644 index 00000000..a3f47f49 --- /dev/null +++ b/gitgud/skills/rewriting/_truth/goal.txt @@ -0,0 +1,3 @@ +Just like last level, make the log messages form the sentence "This is an easy level" +This time, instead of rebasing, detach HEAD using git checkout then use git cherry-pick. +Save your changes on a new branch named whatever you want, but don't check it out. diff --git a/gitgud/skills/rewriting/_truth/setup.spec b/gitgud/skills/rewriting/_truth/setup.spec new file mode 100644 index 00000000..b7eae34d --- /dev/null +++ b/gitgud/skills/rewriting/_truth/setup.spec @@ -0,0 +1,8 @@ +# Commits here are out of order +# When the commits are in the correct order, they read "This is an easy level" +1 +4 +3 +2 +5 (master) +master diff --git a/gitgud/skills/rewriting/_truth/solution.txt b/gitgud/skills/rewriting/_truth/solution.txt new file mode 100644 index 00000000..2f7bccfe --- /dev/null +++ b/gitgud/skills/rewriting/_truth/solution.txt @@ -0,0 +1,6 @@ +git checkout master~4 +git cherry-pick master~1 +git cherry-pick master~2 +git cherry-pick master~3 +git cherry-pick master +git branch random-branch-name diff --git a/gitgud/skills/rewriting/_truth/test.spec b/gitgud/skills/rewriting/_truth/test.spec new file mode 100644 index 00000000..8d65cc46 --- /dev/null +++ b/gitgud/skills/rewriting/_truth/test.spec @@ -0,0 +1,11 @@ +# The commits on master and in setup.spec are out of order +1 +4 +3 +2 +5 (master) +2' : 1 +3' +4' +5' +5' diff --git a/gitgud/test_commands.py b/gitgud/test_commands.py index c454f930..05f584dd 100644 --- a/gitgud/test_commands.py +++ b/gitgud/test_commands.py @@ -8,10 +8,10 @@ def test_load(gg): load_tests = [ ('git gud load 1', all_skills["1"]["1"]), ('git gud load rampup', all_skills["rampup"]["1"]), - ('git gud load 2 detaching', all_skills["2"]["detaching"]), - ('git gud load rampup 4', all_skills["rampup"]["4"]), + ('git gud load 2 relrefs1', all_skills["2"]["relrefs1"]), + ('git gud load rampup 3', all_skills["rampup"]["3"]), ('git gud load 5-octopus', all_skills["5"]["octopus"]), - ('git gud load rampup-4', all_skills["rampup"]["4"]), + ('git gud load rampup-3', all_skills["rampup"]["3"]), ('git gud load -2', all_skills["rampup"]["2"]) ]