From 2c275424a3bb4f20de0b66a28615941d70a02fe1 Mon Sep 17 00:00:00 2001 From: Luca Canali Date: Mon, 6 Sep 2021 12:28:35 +0200 Subject: [PATCH] Add split description in italia and english and add directory it in levels --- levels/it/bisect/bisect | 43 +++++ levels/it/bisect/sequence | 1 + levels/it/branches/branch-create | 44 +++++ levels/it/branches/branch-remove | 47 +++++ levels/it/branches/checkout-commit | 43 +++++ levels/it/branches/fork | 65 +++++++ levels/it/branches/grow | 49 +++++ levels/it/branches/reorder | 89 +++++++++ levels/it/branches/sequence | 6 + levels/it/changing-the-past/rebase | 86 +++++++++ levels/it/changing-the-past/reorder | 78 ++++++++ levels/it/changing-the-past/sequence | 2 + levels/it/files/files-add | 32 ++++ levels/it/files/files-delete | 36 ++++ levels/it/files/sequence | 3 + levels/it/index/add | 38 ++++ levels/it/index/change | 31 +++ levels/it/index/checkout | 25 +++ levels/it/index/compare | 51 +++++ levels/it/index/new | 28 +++ levels/it/index/reset | 37 ++++ levels/it/index/rm | 24 +++ levels/it/index/sequence | 5 + levels/it/index/steps | 53 ++++++ levels/it/intro/cli | 27 +++ levels/it/intro/commit | 32 ++++ levels/it/intro/copies | 42 ++++ levels/it/intro/init | 24 +++ levels/it/intro/remote | 52 +++++ levels/it/intro/risky | 42 ++++ levels/it/intro/sequence | 6 + levels/it/intro/who-are-you | 38 ++++ levels/it/low-level/basics | 26 +++ levels/it/low-level/blob-create | 38 ++++ levels/it/low-level/blob-remove | 27 +++ levels/it/low-level/commit-create | 37 ++++ levels/it/low-level/commit-parents | 31 +++ levels/it/low-level/commit-rhombus | 30 +++ levels/it/low-level/index-add | 37 ++++ levels/it/low-level/index-remove | 29 +++ levels/it/low-level/index-update | 33 ++++ levels/it/low-level/puzzle-apocalypse | 41 ++++ levels/it/low-level/puzzle-precious-blob | 28 +++ .../low-level/puzzle-trees-all-the-way-down | 34 ++++ levels/it/low-level/ref-create | 42 ++++ levels/it/low-level/ref-move | 41 ++++ levels/it/low-level/ref-remove | 41 ++++ levels/it/low-level/sequence | 18 ++ levels/it/low-level/symref-create | 21 ++ levels/it/low-level/symref-no-deref | 46 +++++ levels/it/low-level/tree-create | 35 ++++ levels/it/low-level/tree-nested | 38 ++++ levels/it/low-level/tree-read | 51 +++++ levels/it/low-level/welcome | 33 ++++ levels/it/merge/conflict | 47 +++++ levels/it/merge/merge | 82 ++++++++ levels/it/merge/merge-abort | 54 ++++++ levels/it/merge/sequence | 2 + levels/it/remotes/friend | 47 +++++ levels/it/remotes/problems | 33 ++++ levels/it/remotes/sequence | 2 + levels/it/sandbox/empty | 7 + levels/it/sandbox/remote | 22 +++ levels/it/sandbox/sequence | 3 + levels/it/sandbox/three-commits | 26 +++ levels/it/sequence | 13 ++ levels/it/shit-happens/bad-commit | 30 +++ .../it/shit-happens/pushed-something-broken | 54 ++++++ levels/it/shit-happens/reflog | 26 +++ levels/it/shit-happens/restore-a-file | 22 +++ .../shit-happens/restore-a-file-from-the-past | 21 ++ levels/it/shit-happens/sequence | 5 + levels/it/stash/sequence | 5 + levels/it/stash/stash | 45 +++++ levels/it/stash/stash-branch | 48 +++++ levels/it/stash/stash-clear | 57 ++++++ levels/it/stash/stash-merge | 54 ++++++ levels/it/stash/stash-pop | 46 +++++ levels/it/tags/add-tag | 53 ++++++ levels/it/tags/add-tag-later | 50 +++++ levels/it/tags/remote-tag | 58 ++++++ levels/it/tags/remove-tag | 53 ++++++ levels/it/tags/sequence | 4 + levels/it/unused/checkout | 31 +++ levels/it/unused/clone | 20 ++ levels/it/unused/commit | 27 +++ levels/it/unused/commit-a | 26 +++ levels/it/unused/fetch | 35 ++++ levels/it/unused/files-move | 27 +++ levels/it/unused/index-mv | 26 +++ levels/it/unused/init | 14 ++ levels/it/unused/pull-push | 51 +++++ levels/it/unused/remotes-add | 33 ++++ levels/it/unused/remotes-delete | 23 +++ levels/it/unused/restore | 28 +++ levels/it/unused/split | 26 +++ levels/it/unused/steps | 23 +++ levels/it/unused/who-are-you | 23 +++ levels/it/workflows/gitignore | 18 ++ levels/it/workflows/pr | 25 +++ levels/it/workflows/sequence | 1 + resources/cards.json | 180 ++++++++++++++---- scenes/cards.gd | 5 +- scenes/title.tscn | 1 + 104 files changed, 3510 insertions(+), 38 deletions(-) create mode 100644 levels/it/bisect/bisect create mode 100644 levels/it/bisect/sequence create mode 100644 levels/it/branches/branch-create create mode 100644 levels/it/branches/branch-remove create mode 100644 levels/it/branches/checkout-commit create mode 100644 levels/it/branches/fork create mode 100644 levels/it/branches/grow create mode 100644 levels/it/branches/reorder create mode 100644 levels/it/branches/sequence create mode 100644 levels/it/changing-the-past/rebase create mode 100644 levels/it/changing-the-past/reorder create mode 100644 levels/it/changing-the-past/sequence create mode 100644 levels/it/files/files-add create mode 100644 levels/it/files/files-delete create mode 100644 levels/it/files/sequence create mode 100644 levels/it/index/add create mode 100644 levels/it/index/change create mode 100644 levels/it/index/checkout create mode 100644 levels/it/index/compare create mode 100644 levels/it/index/new create mode 100644 levels/it/index/reset create mode 100644 levels/it/index/rm create mode 100644 levels/it/index/sequence create mode 100644 levels/it/index/steps create mode 100644 levels/it/intro/cli create mode 100644 levels/it/intro/commit create mode 100644 levels/it/intro/copies create mode 100644 levels/it/intro/init create mode 100644 levels/it/intro/remote create mode 100644 levels/it/intro/risky create mode 100644 levels/it/intro/sequence create mode 100644 levels/it/intro/who-are-you create mode 100644 levels/it/low-level/basics create mode 100644 levels/it/low-level/blob-create create mode 100644 levels/it/low-level/blob-remove create mode 100644 levels/it/low-level/commit-create create mode 100644 levels/it/low-level/commit-parents create mode 100644 levels/it/low-level/commit-rhombus create mode 100644 levels/it/low-level/index-add create mode 100644 levels/it/low-level/index-remove create mode 100644 levels/it/low-level/index-update create mode 100644 levels/it/low-level/puzzle-apocalypse create mode 100644 levels/it/low-level/puzzle-precious-blob create mode 100644 levels/it/low-level/puzzle-trees-all-the-way-down create mode 100644 levels/it/low-level/ref-create create mode 100644 levels/it/low-level/ref-move create mode 100644 levels/it/low-level/ref-remove create mode 100644 levels/it/low-level/sequence create mode 100644 levels/it/low-level/symref-create create mode 100644 levels/it/low-level/symref-no-deref create mode 100644 levels/it/low-level/tree-create create mode 100644 levels/it/low-level/tree-nested create mode 100644 levels/it/low-level/tree-read create mode 100644 levels/it/low-level/welcome create mode 100644 levels/it/merge/conflict create mode 100644 levels/it/merge/merge create mode 100644 levels/it/merge/merge-abort create mode 100644 levels/it/merge/sequence create mode 100644 levels/it/remotes/friend create mode 100644 levels/it/remotes/problems create mode 100644 levels/it/remotes/sequence create mode 100644 levels/it/sandbox/empty create mode 100644 levels/it/sandbox/remote create mode 100644 levels/it/sandbox/sequence create mode 100644 levels/it/sandbox/three-commits create mode 100644 levels/it/sequence create mode 100644 levels/it/shit-happens/bad-commit create mode 100644 levels/it/shit-happens/pushed-something-broken create mode 100644 levels/it/shit-happens/reflog create mode 100644 levels/it/shit-happens/restore-a-file create mode 100644 levels/it/shit-happens/restore-a-file-from-the-past create mode 100644 levels/it/shit-happens/sequence create mode 100644 levels/it/stash/sequence create mode 100644 levels/it/stash/stash create mode 100644 levels/it/stash/stash-branch create mode 100644 levels/it/stash/stash-clear create mode 100644 levels/it/stash/stash-merge create mode 100644 levels/it/stash/stash-pop create mode 100644 levels/it/tags/add-tag create mode 100644 levels/it/tags/add-tag-later create mode 100644 levels/it/tags/remote-tag create mode 100644 levels/it/tags/remove-tag create mode 100644 levels/it/tags/sequence create mode 100644 levels/it/unused/checkout create mode 100644 levels/it/unused/clone create mode 100644 levels/it/unused/commit create mode 100644 levels/it/unused/commit-a create mode 100644 levels/it/unused/fetch create mode 100644 levels/it/unused/files-move create mode 100644 levels/it/unused/index-mv create mode 100644 levels/it/unused/init create mode 100644 levels/it/unused/pull-push create mode 100644 levels/it/unused/remotes-add create mode 100644 levels/it/unused/remotes-delete create mode 100644 levels/it/unused/restore create mode 100644 levels/it/unused/split create mode 100644 levels/it/unused/steps create mode 100644 levels/it/unused/who-are-you create mode 100644 levels/it/workflows/gitignore create mode 100644 levels/it/workflows/pr create mode 100644 levels/it/workflows/sequence diff --git a/levels/it/bisect/bisect b/levels/it/bisect/bisect new file mode 100644 index 0000000..0b66edb --- /dev/null +++ b/levels/it/bisect/bisect @@ -0,0 +1,43 @@ +title = Yellow brick road +cards = checkout commit-auto reset-hard bisect-start bisect-good bisect-bad + +[description] + +(Please zoom out a bit using your mouse wheel! :D) + +Oh no! You have lost your key at some point during the day! + +Sure, you could look at every single commit in an attempt to find it - but there's a better way: your time machine has a built-in way to find the point in time where things went wrong quickly! + +First, play the "bisect start" card. Then, go to a commit where you don't have the key, and play the "bisect bad" card. Likewise, go to a commit early on where you have the key *in your pocket*, and play the "bisect good card". + +After you've found the last good commit, reset the main branch to it. What happened to the key after you lost it? + +[setup] + +echo "You still have your key." > you + +for i in {1..30}; do + if test $i -eq 12; then + echo "Your pocket is empty." > you + echo "Is on the ground." > key + fi + if test $i -eq 13; then + echo "Is holding a key in its beak." > bird + rm key + fi + if test $i -eq 14; then + rm bird + fi + git add . + git commit --allow-empty -m "$i" +done + +[win] + +# Find the last good commit +test "$(git log --pretty=%s main | head -1)" -eq 11 + +[congrats] + +Well done! :) The only problem is that you now have to walk all the way back home, again... diff --git a/levels/it/bisect/sequence b/levels/it/bisect/sequence new file mode 100644 index 0000000..e8018fe --- /dev/null +++ b/levels/it/bisect/sequence @@ -0,0 +1 @@ +bisect diff --git a/levels/it/branches/branch-create b/levels/it/branches/branch-create new file mode 100644 index 0000000..110d929 --- /dev/null +++ b/levels/it/branches/branch-create @@ -0,0 +1,44 @@ +title = Creating branches +cards = checkout commit-auto branch branch-delete reset-hard + +[description] + +You were invited to two parties! At one of them, your favorite band is playing - and the other one is your best friend's birthday party. Where should you go? No worries - as a time travel agent in training, you can go to both parties! + +To make it easier to tell which timeline is which, you can create time portals! (We call these "branches".) + +[cli] + +Branches also make it really easy to travel between different places using the command line! As soon as you have a branch called "birthday", you can type `git checkout birthday` to travel to it! + +[setup] + +echo "You wrap the birthday present, and grab your concert ticket." > you +git add . +git commit -m "Evening preparations" +echo "You go to the birthday party!" >> you +git add . +git commit -m "Go to the birthday" + +git checkout HEAD~1 +echo "You go to the concert!" > you +git add . +git commit -m "Go to the concert" + +git checkout HEAD~1 + +git branch -D main + +[win] + +# Create a branch called 'birthday' that points to the birthday timeline. +git show birthday | grep 'to the birthday' + +# Create a branch called 'concert' that points to the concert timeline. +git show concert | grep 'to the concert' + +[congrats] + +Now you can travel between those branches easily (using `git checkout`) - try it! + +Your friend is happy that you made it to the birthday party and you also got your concert ticket signed. Yay! diff --git a/levels/it/branches/branch-remove b/levels/it/branches/branch-remove new file mode 100644 index 0000000..3b576e0 --- /dev/null +++ b/levels/it/branches/branch-remove @@ -0,0 +1,47 @@ +title = Deleting branches +cards = checkout commit-auto reset-hard branch-delete + +[description] + +Life is full of dangers, right? Even when walking to school, it seems like there's a lot of risks! + +This Monday is especially bad. You made it to school, but there's some timelines you definitely don't want to keep around. + +[setup] + +echo You leave your house and start walking to school. > you +git add . +git commit -m "Good morning!" + +echo You walk on the right side of the street. >> you +git commit -am "Right side" + +echo You jump over an manhole in the walkway, and arrive at school on time. >> you +git commit -am "Jump" + +git checkout HEAD^ -b friend +echo Suddenly, you fall down, splash into stinking water, and are eaten by an alligator. >> you +git commit -am "A new friend" + +git checkout HEAD~2 -b music +echo You walk on the left side of the street. >> you +git commit -am "Left side" + +echo Because you\'re kind of late, you start running. Someone throws a piano out of their windows, and it smashes you. >> you +git commit -am "Sounds nice" + +git checkout HEAD^ -b ice-cream +echo You\'re not in a hurry, and walk slowly. You even get some ice cream on your way. You arrive at school too late, your teacher is angry, and you are expelled. >> you +git commit -am "Yum" + +git branch -M main leap +git checkout leap^^ + +[win] + +# Find the bad branches and delete them. Keep only the best one. +test "$(git show-ref --heads | cut -f2 -d' ')" = "$(echo refs/heads/leap)" + +[congrats] + +On second thought, maybe you even prefer the ice cream timeline to the main one? :) diff --git a/levels/it/branches/checkout-commit b/levels/it/branches/checkout-commit new file mode 100644 index 0000000..87061d8 --- /dev/null +++ b/levels/it/branches/checkout-commit @@ -0,0 +1,43 @@ +title = Moving through time +cards = checkout commit-auto + +[description] + +The yellow boxes are frozen points in time, we call them "commits"! You can travel between them using the "checkout" card! (Try it!) + +Can you find out what happened here? Then, while on the last commit, edit the files to fix the problem, and make a new commit! + +[cli] + +To checkout a specific commit, type `git checkout`, then a space, and then right click on the commit you want! + +This will insert the commit's unique identifier! + +[setup] + +echo "This piggy bank belongs to the big sister. +It contains 10 coins." > piggy_bank +git add . +git commit -m "The beginning" + +echo "A young girl with brown, curly hair." > little_sister +git add . +git commit -m "Little sister comes in" + +echo "Has 10 coins." >> little_sister +echo "This piggy bank belongs to the big sister. +It is empty." > piggy_bank +git add . +git commit -m "Little sister does something" + +git checkout HEAD^^ +git branch -df main + +[win] + +# Restore sisterly peace. +{ git show HEAD:piggy_bank | grep "10 coins"; } && { git show HEAD:little_sister | grep -v "10 coins"; } && { git rev-parse HEAD^^^; } + +[congrats] + +Wonderful! Now that you're getting familiar with the time machine, let's look at some more complicated situations... diff --git a/levels/it/branches/fork b/levels/it/branches/fork new file mode 100644 index 0000000..a58dcf5 --- /dev/null +++ b/levels/it/branches/fork @@ -0,0 +1,65 @@ +title = Make parallel commits +cards = checkout commit-auto + +[description] + +Did you know that creating parallel timelines is perfectly legal and safe? It's true! + +Can you find out when things went wrong in this zoo? Then, go back to the last good commit and create a parallel universe where everyone is happy! + +[cli] + +The blue animal represents a concept known as the "HEAD pointer" in Git: It shows you which commit is the current one. + +Here's a cool trick to go to the previous commit: + + git checkout HEAD^ + +You can also go back two commits by typing, for example: + + git checkout HEAD~2 + +[setup] + +mkdir cage +echo "Looks very hungry." > cage/lion + +echo "A small child. +It really loves cats!" > child +git add . +git commit -m "The beginning" + +echo "It's holding a lollipop." >> child +git commit -am "The child buys something" + +mv child cage +git add . +git commit -m "The child climbs somewhere" + +git rm cage/child +echo "Looks happy. :)" > cage/lion +git add . +git commit -m "Oh no" + +echo "It's sleeping." > cage/lion +git add . +git commit -m "Nap time!" + +git checkout --detach +git branch -d main + +[win] + +# Make sure that the child is happy. +git ls-tree --name-only -r HEAD | grep child + +# Make sure that the lion gets something to eat. +git show HEAD:cage/lion | grep -v "very hungry" + +[congrats] + +Whew, good job! This seems like a *much* better outcome. + +Feel free to add more parallel timelines, or make them longer. + +If you're ready, our next mission is already waiting... diff --git a/levels/it/branches/grow b/levels/it/branches/grow new file mode 100644 index 0000000..77fe360 --- /dev/null +++ b/levels/it/branches/grow @@ -0,0 +1,49 @@ +title = Branches grow with you! +cards = checkout commit-auto branch branch-delete reset-hard + +[description] + +Note that there are two options to "travel to the end of a timeline": + +First, you can directly travel to the commit, like we've done it before. + +And second, you can travel to the branch label. In this case, when you make a new commit, the branch will grow with you, and still point at the end of the timeline! + +[cli] + +To travel to a branch, type `git checkout name_of_the_branch`. + +And to travel to the last commit, type `git checkout --detach name_of_the_branch`. + +[setup] + +echo "You wrap the birthday present, and grab your concert ticket." > you +git add . +git commit -m "Evening preparations" +echo "You go to the birthday party!" >> you +git add . +git commit -m "Go to the birthday" +git branch birthday + +git checkout HEAD~1 +echo "You go to the concert!" > you +git add . +git commit -m "Go to the concert" +git branch concert + +git checkout HEAD~1 + +git branch -D main + +[win] + +# Travel directly to the last yellow commit of the birthday timeline, make a change to 'you', and make a commit +for commit in $(git cat-file --batch-check='%(objectname) %(objecttype)' --batch-all-objects | grep 'commit$' | cut -f1 -d' '); do + if test $(git rev-parse $commit^) = $(git rev-parse birthday); then + return 0 + fi +done +return 1 + +# Travel to the blue 'concert' branch, make a change to 'you', and a commit. +git show concert^ | grep "Go to the concert" diff --git a/levels/it/branches/reorder b/levels/it/branches/reorder new file mode 100644 index 0000000..1c36c08 --- /dev/null +++ b/levels/it/branches/reorder @@ -0,0 +1,89 @@ +title = Moving branches around +cards = checkout commit-auto merge reset-hard + +[description] + +One of your colleagues messed up here, and put the branches in the wrong timelines! + +You could delete and re-create these branches - but you can also directly move them to different commits, by using + + git checkout + +on the branch names, and then using + + git reset --hard + +on the commit where you want the branch to be. + +The donut branch is in the right place, but the timeline is still incomplete - make you actually *eat* the donut in that branch! + +[setup] + +echo "You do not have a baguette. + +You do not have coffee. + +You do not have a donut." > you + +git add . +git commit -m "The Beginning" + +git checkout -b coffee +echo "You have a baguette. + +You do not have coffee. + +You do not have a donut." > you +git add . +git commit -m "You buy a baguette" + +echo "You ate a baguette. + +You do not have coffee. + +You do not have a donut." > you +git add . +git commit -m "You eat the baguette" + +git checkout -b baguette main +echo "You do not have a baguette. + +You have coffee. + +You do not have a donut." > you +git add . +git commit -m "You buy some coffee" + +echo "You do not have a baguette. + +You drank coffee. + +You do not have a donut." > you +git add . +git commit -m "You drink the coffee" + +git checkout -b donut main +echo "You do not have a baguette. + +You do not have coffee. + +You have a donut." > you +git add . +git commit -m "You buy a donut" + +git checkout --detach main + +[win] + +# Did you eat a baguette on the baguette branch? +git show baguette:you | grep "You ate.*baguette" + +# Did you drink a coffee on the coffee branch? +git show coffee:you | grep "You drank.*coffee" + +# Did you eat a donut on the donut branch? +git show donut:you | grep "You ate.*donut" + +[actions] + +test "$(git rev-parse HEAD^)" = "$(git rev-parse donut)" && hint "Remember to checkout the blue branch tag when you want it to grow with the timeline." diff --git a/levels/it/branches/sequence b/levels/it/branches/sequence new file mode 100644 index 0000000..2619ea0 --- /dev/null +++ b/levels/it/branches/sequence @@ -0,0 +1,6 @@ +checkout-commit +fork +branch-create +grow +branch-remove +reorder diff --git a/levels/it/changing-the-past/rebase b/levels/it/changing-the-past/rebase new file mode 100644 index 0000000..6ed89a8 --- /dev/null +++ b/levels/it/changing-the-past/rebase @@ -0,0 +1,86 @@ +title = Rebasing +cards = checkout commit-auto reset-hard rebase + +[description] + +Okay - turns out that saving time in the morning by utilizing parallel universes is against the regulations of the International Time Travel Association. You'll have to do your tasks in sequence after all. + +See the "rebase" card? When you drag it to a commit, it will copy the events in your current timeline after the specified one! This way, make a clean, linear timeline where you visit all three shops. + +Again, we want to make that our base reality - the "main" branch should point to that timeline! + +[setup] + +echo "You do not have a baguette. + +You do not have coffee. + +You do not have a donut." > you + +git add . +git commit -m "The Beginning" + +git checkout -b baguette main +echo "You have a baguette. + +You do not have coffee. + +You do not have a donut." > you +git add . +git commit -m "You buy a baguette" + +echo "You ate a baguette. + +You do not have coffee. + +You do not have a donut." > you +git add . +git commit -m "You eat the baguette" + +git checkout -b coffee main +echo "You do not have a baguette. + +You have coffee. + +You do not have a donut." > you +git add . +git commit -m "You buy some coffee" + +echo "You do not have a baguette. + +You drank coffee. + +You do not have a donut." > you +git add . +git commit -m "You drink the coffee" + +git checkout -b donut main +echo "You do not have a baguette. + +You do not have coffee. + +You have a donut." > you +git add . +git commit -m "You buy a donut" + +echo "You do not have a baguette. + +You do not have coffee. + +You ate a donut." > you +git add . +git commit -m "You eat the donut" + +git checkout --detach main + + +[win] + +# Order all tree branches into one and move the main branch ref +{ git show main:you | grep "You ate.*baguette"; } && { git show main:you | grep "You drank.*coffee"; } && { git show main:you | grep "You ate.*donut"; } && { test "$(git log main --oneline | wc -l)" -eq 7; } + +[congrats] + +Notice how the other timelines and commits are still there - if anything goes wrong, you can also travel back to them. + +It's really hard to actually *destroy* stuff with your time machine. diff --git a/levels/it/changing-the-past/reorder b/levels/it/changing-the-past/reorder new file mode 100644 index 0000000..04f66cb --- /dev/null +++ b/levels/it/changing-the-past/reorder @@ -0,0 +1,78 @@ +title = Reordering events +cards = checkout commit-auto reset-hard rebase-interactive cherry-pick + +[description] + +Oops, looks like there's something messed up here. Can you put the events back into their correct order? + +There are two ways to do this: You can drag the "interactive rebase" card to the commit before the one you want to change, then reorder the lines in the file that opens, and save it. + +Or you can reset the main tag to the very first commit, and then cherry-pick single commits in the order you want. You have cards for both approaches! + +[setup] + +echo "You just woke up. + +You are NOT wearing underwear. + +You are NOT wearing pants. + +You are NOT wearing a shirt. + +You are NOT wearing shoes." > you +git add . + +git commit -m "The Beginning" + +echo "You just woke up. + +You are NOT wearing underwear. + +You are NOT wearing pants. + +You are NOT wearing a shirt. + +You are wearing shoes." > you +git commit -am "Put on shoes" + +echo "You just woke up. + +You are NOT wearing underwear. + +You are wearing pants. + +You are NOT wearing a shirt. + +You are wearing shoes." > you +git commit -am "Put on pants" + +echo "You just woke up. + +You are wearing underwear. + +You are wearing pants. + +You are NOT wearing a shirt. + +You are wearing shoes." > you +git commit -am "Put on underwear" + +echo "You just woke up. + +You are wearing underwear. + +You are wearing pants. + +You are wearing a shirt. + +You are wearing shoes." > you +git commit -am "Put on shirt" + +[win] + +# Reorder the commits to dress yourself in the correct way +{ git log main --oneline | perl -0777 -ne'exit(1) if not /shoes[\s\S]*pants[\s\S]*underwear/'; } && { test "$(git log main --oneline | wc -l)" -eq 5; } + +[congrats] + +Feel free to reset the level and try the other strategy! Which one do you like better? diff --git a/levels/it/changing-the-past/sequence b/levels/it/changing-the-past/sequence new file mode 100644 index 0000000..43c5d08 --- /dev/null +++ b/levels/it/changing-the-past/sequence @@ -0,0 +1,2 @@ +rebase +reorder diff --git a/levels/it/files/files-add b/levels/it/files/files-add new file mode 100644 index 0000000..0676df6 --- /dev/null +++ b/levels/it/files/files-add @@ -0,0 +1,32 @@ +title = Interior design +cards = file-new file-delete + +[description] + +Now that your room looks tidy, you can start to unpack your stuff. You brought two new pieces of furniture with you and with a bright smile, +you see that their colors match the color of your bed! + +Build up your two pieces of furniture by playing the touch card. +Then name your furniture - you can choose whatever you like. + +Make sure the colors match! You can find the bed's color in its description. +Don't forget to add a color and description to your new furnitures, too! + +[setup] + +echo A yellow cozy bed. > bed + +[win] + +# Add two more pieces of furniture +NUM_FILES="$(ls | wc -l)" +test "$NUM_FILES" -ge 3 + +# Make sure the colors match your bed's color. +NUM_FILES="$(ls | wc -l)" +YELLOW_FILES="$(grep -li yellow * | wc -l)" +test "$NUM_FILES" -ge 2 && test "$YELLOW_FILES" = "$NUM_FILES" + +[congrats] + +Don't you immediately feel more at home? diff --git a/levels/it/files/files-delete b/levels/it/files/files-delete new file mode 100644 index 0000000..7bf33be --- /dev/null +++ b/levels/it/files/files-delete @@ -0,0 +1,36 @@ +title = Unexpected Roommates +cards = file-delete + +[description] + +The first day at Time Travel School comes to an end and you receive the key to your room. +Full of excitement you open the door just to find... spider webs! Spider webs everywhere! + +Remove all the spider webs you can find with the remove card! + +[cli] + +On the command line, you can easily delete all files ending in -web using this command: + + rm *web + +[setup] + +echo A tiny spider web is next to your window. > tiny_web +echo A big spider web sticks above your bed. > big_web +echo A cozy bed. > bed +echo An extra thick spider web is right beside your door. > thick_web + +[win] + +# Remove all spider webs. +! ls | grep thick_web && +! ls | grep big_web && +! ls | grep tiny_web + +# But make sure you keep your bed! +ls | grep bed + +[congrats] + +Your room looks now very tidy and cozy! Time to unpack your stuff! diff --git a/levels/it/files/sequence b/levels/it/files/sequence new file mode 100644 index 0000000..cb24408 --- /dev/null +++ b/levels/it/files/sequence @@ -0,0 +1,3 @@ +files-delete +files-add + diff --git a/levels/it/index/add b/levels/it/index/add new file mode 100644 index 0000000..e627917 --- /dev/null +++ b/levels/it/index/add @@ -0,0 +1,38 @@ +title = Updating files in the index +cards = add commit checkout + +[description] + +So you start working, and make changes to your files! Git lets you choose which of these changes you want to put in the next commit. This is like updating the index version of that file to the new version. + +This allows you to have smaller commits, that describe better what you changed! + +The command for this is the same - `git add`! + +[setup] + +echo a > a +echo b > b +echo c > c +git add . +git commit -m "Initial commit" + +[win] + +# Make changes to all files! +test "$(cat a)" != "a" && +test "$(cat b)" != "b" && +test "$(cat c)" != "c" + +# Add only the changes of a and c, and make a commit! Finally, make a commit which captures the changes in b! + +test "$(git show main:a)" != "a" && +test "$(git show main:b)" != "b" && +test "$(git show main:c)" != "c" && +test "$(git show main^:a)" != "a" && +test "$(git show main^:b)" == "b" && +test "$(git show main^:c)" != "c" + +[congrats] + +Well done! Try tavelling between the commits using `git checkout`, so you can look at their contents again! diff --git a/levels/it/index/change b/levels/it/index/change new file mode 100644 index 0000000..f1e377d --- /dev/null +++ b/levels/it/index/change @@ -0,0 +1,31 @@ +title = Update files in the index +cards = add commit + +[description] + +When we change files, the index won't change on its own. We have to use `git add` to update the index to the changed version of the file. + +Let's try that! + +The icons in the file browser show you when the actual file (white) and the version in the index (blue) are different, and when they are the same! + +[win] + +Good! The index is sometimes also called the "staging area" - it contains exactly what ends up in the next commit when you use `git commit`! + +[setup] + +echo "The candle is burning with a blue flame." > candle +git add . +git commit -m "The beginning" + +[win] + +# Make a change to the candle. +test "$(git diff --name-only)" = "candle" || file -f .git/candle-changed && touch .git/candle-changed + +# Add the candle. +test "$(git diff --cached --name-only)" = "candle" || file -f .git/candle-added && touch .git/candle-added + +# Make a commit. +test "$(git diff --name-only HEAD HEAD^)" = "candle" diff --git a/levels/it/index/checkout b/levels/it/index/checkout new file mode 100644 index 0000000..3c01b09 --- /dev/null +++ b/levels/it/index/checkout @@ -0,0 +1,25 @@ +title = Checking out files from the index +cards = add reset-file checkout-file commit + +[description] + +So you've made changes to your files, but you decide that you don't want to keep them! You can use `git checkout` for that! + +What happens if you have already update the index, like in file c? You have to reset the index first! + +[setup] + +echo a > a +echo b > b +echo c > c +git add . +git commit -m "Initial commit" +echo x > a +echo x > b +echo x > c +git add c + +[win] + +# Remove all changes in your local files! +test "$(git diff --name-only | wc -l)" -eq 0 diff --git a/levels/it/index/compare b/levels/it/index/compare new file mode 100644 index 0000000..97fa6c7 --- /dev/null +++ b/levels/it/index/compare @@ -0,0 +1,51 @@ +title = Step by step +cards = checkout commit-auto + +[description] + +Welcome to today's lesson! We're going to learn how to make commits with more precision! + +Have a look at these two timelines. They have exactly the same outcome. But one of them makes it much easier to figure out what happened. + +[win] + +# Right! Having each change in its own commit makes it easier to understand what's going on! Let's learn how to do that! +git branch --show-current | grep step-by-step + +[setup] + +echo "A small, but heavy glass ball." > ball +echo "A thin book, that's standing upright." > book +echo "A candle, burning with a blue flame." > candle +echo "A smoke detector. It's absolutely silent." > smoke_detector + +git add . +git commit -m "The beginning" + +git branch -M all-at-once + +echo "The ball is now touching the book." > ball +echo "The book has fallen over." > book +echo "The candle has been blown out." > candle + +git commit -am "The end" + +git checkout HEAD^ + +git checkout -b step-by-step + +echo "The ball is now touching the book." > ball +git commit -am "The ball rolls towards the book" + +echo "The book has fallen over." > book +git commit -am "The book falls over" + +echo "The candle has been blown out." > candle +git commit -am "The book blows out the candle" + +git checkout HEAD~3 + +[win] + +# Pick the timeline that's clearer, and make the alarm go off! +git show step-by-step:smoke_detector | tail -n 1 | grep -v "absolutely silent" diff --git a/levels/it/index/new b/levels/it/index/new new file mode 100644 index 0000000..4df3930 --- /dev/null +++ b/levels/it/index/new @@ -0,0 +1,28 @@ +title = Add new files to the index +cards = add commit + +[description] + +So far, when we made a commit, we've always recorded the current status of all objects, right? + +But Git allows you to pick which changes you want to put in a commit! + +To learn how that works, we need to learn about the "index"! In the index, we can prepare what will be in the next commit. In this game, the index is represented by a blue aura around icons in the file browser! + +Initially, the index is empty. To make a commit that contains a new file, we need to add it! + +[cli] + +You can use tab completion in the terminal! Start typing a filename, then press the tab key to complete its name. This will often save you some time! + +[setup] + +echo "The candle is burning with a blue flame." > candle + +[win] + +# Add the candle. +test "$(git diff --cached --name-only)" = "candle" || file -f .git/candle-added && touch .git/candle-added + +# Make a commit. +test "$(git ls-tree --name-only HEAD)" = "candle" diff --git a/levels/it/index/reset b/levels/it/index/reset new file mode 100644 index 0000000..5e21973 --- /dev/null +++ b/levels/it/index/reset @@ -0,0 +1,37 @@ +title = Resetting files in the index +cards = add reset-file commit + +[description] + +See the dark shadow behind the icons? That's the version of the file in the last commit! + +For example, these candles have been blown out, and that change has been added. + +But you decide that this was a mistake! You only want to blow out the red candle in the next commit! + +If you already have updated the index to a changed file, but want to reset it, you can use `git reset`! + +[setup] + +echo "It's burning!" > red_candle +echo "It's burning!" > green_candle +echo "It's burning!" > blue_candle +git add . +git commit -m "The beginning" + +echo "It's been blown out." > red_candle +echo "It's been blown out." > green_candle +echo "It's been blown out." > blue_candle +git add . + +[win] + +# Reset the changes in the green and blue candles! +git show :green_candle | grep burning && +git show :blue_candle | grep burning && +git show :red_candle | grep -v burning + +# And make a commit! +git show main:green_candle | grep burning && +git show main:blue_candle | grep burning && +git show main:red_candle | grep -v burning diff --git a/levels/it/index/rm b/levels/it/index/rm new file mode 100644 index 0000000..637fd22 --- /dev/null +++ b/levels/it/index/rm @@ -0,0 +1,24 @@ +title = Delete a file in the next commit +cards = add reset-file checkout-file rm file-delete commit + +[description] + +If you want to remove a file in the next commit, you can use `git rm`! This will both delete the file locally, and in the index. + +If a file is modified, you'll need to reset these changes first/reset the files. + +[setup] + +echo a > a +echo x > b +echo x > c +git add . +git commit -m "Initial commit" +echo x > a +echo b > b +git add b + +[win] + +# Make a commit where all files are deleted ¯\_(^_^)_/¯ +test "$(git ls-tree main | wc -l)" -eq 0 diff --git a/levels/it/index/sequence b/levels/it/index/sequence new file mode 100644 index 0000000..42f7f48 --- /dev/null +++ b/levels/it/index/sequence @@ -0,0 +1,5 @@ +compare +new +change +reset +steps diff --git a/levels/it/index/steps b/levels/it/index/steps new file mode 100644 index 0000000..36e7500 --- /dev/null +++ b/levels/it/index/steps @@ -0,0 +1,53 @@ +title = Adding changes step by step +cards = add reset-file commit + +[description] + +The index is really useful, because it allows us to be precise about which changes we want to include in each commit! + +[setup] + +echo "A hammer, balancing on its handle." > hammer +echo "A bottle, containing a clear liquid." > bottle +echo "A white sugar cube." > sugar_cube + +git add . +git commit -m "The beginning" + +[win] + +# Make changes to all three objects, to form a logical sequence of events! +test "$(git diff --name-only | wc -l)" -eq 3 || file -f .git/candle-changed && touch .git/candle-changed + +# Only add one of these changes! +test "$(git diff --cached --name-only | wc -l)" -eq 1 || file -f .git/candle-added && touch .git/candle-added + +# And make a commit. +COUNT=0 +for commit in $(git cat-file --batch-check='%(objectname) %(objecttype)' --batch-all-objects | grep 'commit$' | cut -f1 -d' '); do + if test "$(git diff --name-only $commit $commit^ | wc -l)" -eq 1; then + COUNT=$((COUNT+1)) + fi +done + +test "$COUNT" -ge 1 + +# Make a second commit that only records a single change. +COUNT=0 +for commit in $(git cat-file --batch-check='%(objectname) %(objecttype)' --batch-all-objects | grep 'commit$' | cut -f1 -d' '); do + if test "$(git diff --name-only $commit $commit^ | wc -l)" -eq 1; then + COUNT=$((COUNT+1)) + fi +done + +test "$COUNT" -ge 2 + +# And a third one. +COUNT=0 +for commit in $(git cat-file --batch-check='%(objectname) %(objecttype)' --batch-all-objects | grep 'commit$' | cut -f1 -d' '); do + if test "$(git diff --name-only $commit $commit^ | wc -l)" -eq 1; then + COUNT=$((COUNT+1)) + fi +done + +test "$COUNT" -ge 3 diff --git a/levels/it/intro/cli b/levels/it/intro/cli new file mode 100644 index 0000000..c643fc1 --- /dev/null +++ b/levels/it/intro/cli @@ -0,0 +1,27 @@ +title = La line di comando +cards = + +[description] + +Queste carte da gioco sono state disegnate per essere usate e ricordate facilmente! Ti consigliamo di attenerti a loro se non hai molta esperienza di Git! + +[cli] + +Ma c'è un'altra via per interagire con Git: + +Prova a digitare `git init` nel terminale qui sotto e premere il pulsante Enter! + +[setup] + +rm -rf .git + +[win] + +# Inizializza la macchina del tempo! +test -d .git + +[congrats] + +Perfetto! Al posto di usare le carte da gioco, puoi anche fare tutto dalla line di comando! + +La line di comando e piuttosto potente! A volte, puoi usarla per risolvere le attività più velocemente che con l'interfaccia grafica. diff --git a/levels/it/intro/commit b/levels/it/intro/commit new file mode 100644 index 0000000..1141ae1 --- /dev/null +++ b/levels/it/intro/commit @@ -0,0 +1,32 @@ +title = Il tuo primo commit! +cards = commit-auto + +[description] + +Puoi usare la tua macchina del tempo per scattare istantanee degli oggetti intorno a te! Qui puoi metterlo in pratica! + +(Il tuo insegnante versa un pò di acqua nel bicchiere) + +[cli] +Nuovamente, al posto di usare le carte, puoi scrivere i comandi, che sono stampati sulle carte, nel terminale in basso! + +Questo è totalmente opzionale! Ma questa è una conoscenza super utile nel mondo reale - e ti farà avere un distintivo scintillante! :) + +[setup] + +echo "Il bicchiere è pieno di acqua." > glass + +[win] + +# Crea un'istantanea del bicchiere (un "commit") +git rev-parse HEAD + +# Cambia il contenuto del bicchiere! +! test "$(cat glass)" = "Il bicchiere è pieno di acqua." + +# E crea un secondo "commit"! +git rev-parse HEAD^ && ! test "$(git show main:glass)" = "Il bicchiere è bieno di acqua." + +[congrats] + +Perfetto! Puoi provare a creare altri "commit". Quando ti sentirai a posto, premi su "Next Level". diff --git a/levels/it/intro/copies b/levels/it/intro/copies new file mode 100644 index 0000000..8f8d6ce --- /dev/null +++ b/levels/it/intro/copies @@ -0,0 +1,42 @@ +title = Crea una copia +cards = + +[description] + +Questa volta, stai facendo molte copie di backup - puoi guardarli cliccandoci sopra! + +[congrats] + +Okay, questo è un modo di lavorare. + +Ma sei preoccupato che ti ritroverai con centinaia di copie di questo modulo e sarà difficile tenerne traccia nel tempo. + +Specialmente quando lavori con altre persone, inviare copie avanti ed indietro non sembra l'ideale. + +Fermati, devi provare questa macchina del tempo! + +[setup] + +rm -rf .git + +echo "~ Perchè voglio imparare Git ~ + +(Devo ancora scriverlo.)" >> form.txt + + +echo "~ Perchè volgio imparare Git ~ + +- Così posso annullare gli errori" >> form2.txt + + +echo "~ Perchè volgio imparare Git ~ + +- Così posso annullare gli errori +- Per tracciare i mie progetti attraverso il tempo" >> form2_final.txt + +cp form2_final.txt form2_really_final.txt + +[win] + +# Aggiungi una nuova line al file form2_really_final.txt! +test "$(cat form2_really_final.txt | wc -l )" -ge 5 diff --git a/levels/it/intro/init b/levels/it/intro/init new file mode 100644 index 0000000..260f6e9 --- /dev/null +++ b/levels/it/intro/init @@ -0,0 +1,24 @@ +title = Entra nella machina del tempo +cards = init + +[description] + +Sei stato accettato nella scuola della macchina del tempo! Questo è il tuo primo giorno! Il tuo insegnate ti spiega: + +"Per fare qualsiasi cosa con una macchina del tempo, devi prima inizializzarla!" + +Trascina quella carta blu verso l'alto per usarla! + +[setup] + +rm -rf .git + +[win] + +# Inizializzazione della macchina del tempo +test -d .git + +[congrats] + +Perfetto! Vedi quel piccolo animale che è apparso? Sarà il tuo compagno e ti mostrerà dove ti trovi nel tempo! + diff --git a/levels/it/intro/remote b/levels/it/intro/remote new file mode 100644 index 0000000..c0b582a --- /dev/null +++ b/levels/it/intro/remote @@ -0,0 +1,52 @@ +title = Dai lavoriamo tutti assieme +cards = pull commit-auto push + +[description] + +Aggiungi il tuo nome nella nostra lista di studenti! + +Ho già un secondo "commit" nella mia macchina del tempo - Dai lavoriamo tutti assieme! + +[cli] + +Per tornare in dietro alle vecchie istruzioni, puoi premere la freccia in alto o in basso. In questo modo non devi digitare, nuovamente, le istruzioni. + +[congrats] + +Benvenuto nella scuola del viaggio nel tepo! :) Ci vediamo domani per la tua prima lezione! + +[setup] + +echo "~ Lista degli attuali studenti ~" > students +git add . +git commit -m "Versione iniziale" +git push -u teacher main + +git update-ref -d refs/remotes/teacher/main + +[setup teacher] + +git reset --hard main + +echo " +- Sam +- Alex" >> students + +git add . +git commit -m "Aggiunti due studenti" + +[win] + +# Ottieni il secondo "commit" dal tuo insegnante usando `git pull`. +test "$(git log --oneline teacher/main | wc -l)" -ge 2 + +# Aggiungi il tuo nome alla lista degli studenti. +test "$(cat students |wc -l)" -ge 5 + +# Crea un'istantanea dei risultati. +test "$(git show main:students |wc -l)" -ge 5 + +[win teacher] + +# E usa `git push` per inviarlo al tuo insegnante! +test "$(git show main:students |wc -l)" -ge 5 diff --git a/levels/it/intro/risky b/levels/it/intro/risky new file mode 100644 index 0000000..0203433 --- /dev/null +++ b/levels/it/intro/risky @@ -0,0 +1,42 @@ +title = Vivere è pericoloso +cards = + +[description] + +Quindi hai deciso di fare domanda per la scuola di viaggio nel tempo, per usare la macchina del tempo chiamata "Git"! + +Che emozione! + +Hai quasi concluso le scartoffie! Devi solamente inserire un motivo per il quale vuoi imparare Git. + +[congrats] + +All'improvviso, il tuo gatto salta sul tavolo, strappa via il modulo, e scappa via! Oh no. Tutto il tuo duro lavoro, andato! + +Devi trovare una buona soluzione. + +(Premi "Next Level" appena sei pronto!) + +[setup] + +rm -rf .git + +echo "~ Perchè voglio imparare Git ~ + +- Così posso cancellare gli errori +- Per seguire il mio progetto attraverso il tempo" >> form.txt + +[actions] + +test "$(cat form.txt | wc -l )" -ge 5 && echo "(E' stato rubato dal tuo gatto.) + + + + + +" > form.txt + +[win] + +# Aggiungi un'altra line a form.txt! +test "$(cat form.txt | wc -l )" -ge 5 diff --git a/levels/it/intro/sequence b/levels/it/intro/sequence new file mode 100644 index 0000000..aa225df --- /dev/null +++ b/levels/it/intro/sequence @@ -0,0 +1,6 @@ +risky +copies +init +cli +commit +remote diff --git a/levels/it/intro/who-are-you b/levels/it/intro/who-are-you new file mode 100644 index 0000000..abb94f3 --- /dev/null +++ b/levels/it/intro/who-are-you @@ -0,0 +1,38 @@ +title = Benvenuto nella scuola del viaggio nel tempo! +cards = config-name commit-auto checkout + +[description] + +Sei ancora confuso da tutto quello che sta succedendo. Il giorno seguente, decidi di iscriverti nella scuola del viaggio nel tempo! + +Il tuo insegnante del viaggio nel tempo ti saluta: "Ciao come va! Vuoi dirci il tuo nome?" + +[setup] + +git config --global user.name "TU" + +echo "~ Chi vuole imparare come si usa la macchina del tempo? ~ + +[ ] Per essere sicuro che il mio gatto non mangi il mio lavoro. +[ ] Così non devo tenere copie di tutti i miei saggi. +[ ] Per collaborare con altri studenti del viaggio nel tempo. +[ ] Altro, perfavore specifica:" > form + +[actions] + +test "$(git config user.name)" != "TU" && cat form | grep -v Signature && echo " +Firma: $(git config user.name)" >> form + +[win] + +# Presentati. +test "$(git config user.name)" != "TU" + +# Compila l'iscrizione e inviala! +git show main:form | grep '\[[xX]\]' + +[congrats] + +"Siamo lieti di averti con noi! + +Git puoi aiutarti a correggere i problemi del passato! Ti aiuta a collaborare con gli studenti del viaggio nel tempo! E' davvero potente e e popolare! Ci vediamo domani per la tua prima lezione!" diff --git a/levels/it/low-level/basics b/levels/it/low-level/basics new file mode 100644 index 0000000..55374b5 --- /dev/null +++ b/levels/it/low-level/basics @@ -0,0 +1,26 @@ +[description] + +For this prototype, we assume you have some experience with the command line. Here are some commands that will be useful: + +- ls +- echo content > file +- cat file +- mkdir dir + +Find the riddle in your current directory and put the answer into the file "answer"! + +[congrats] + +Omnomnom! + +For technical reasons, you can't use `cd` in this prototype yet. But there won't be a lot of interaction with the file system anyways. :) + +[setup] + +mkdir riddle +echo "ppl p" > riddle/consonants +echo "ae ie" > riddle/vowels + +[win] + +cat answer | grep -i "apple \\?pie" diff --git a/levels/it/low-level/blob-create b/levels/it/low-level/blob-create new file mode 100644 index 0000000..86f6254 --- /dev/null +++ b/levels/it/low-level/blob-create @@ -0,0 +1,38 @@ +[description] + +At its core, Git is very simple. It stores "objects", which are basically files identified by an "identifier" (short: ID). + +There are four types of objects: blobs, trees, commits, and tags. The simplest type is a "blob", which is just a piece of text. + +Let's create some blobs! To do that, create a file with the desired content, and then use + + git hash-object -w + +The flag -w means "write", and tells Git to actually write the new blob to the disk. + +Create three new blobs! + +[congrats] + +Tip: You can also use a command like this to create a blob in a single line: + + echo "awesome content" | git hash-object -w --stdin + +Did you already notice that you can drag and drop all objects? :) + +[setup] + +[setup goal] + +echo "Hi" > file1 +echo "Ho" > file2 +echo "Hu" > file3 +git hash-object -w file1 +git hash-object -w file2 +git hash-object -w file3 + +[win] + +BLOB_COUNT=$(git cat-file --batch-check='%(objectname) %(objecttype)' --batch-all-objects | grep blob | wc -l) + +test "$BLOB_COUNT" -gt 2 diff --git a/levels/it/low-level/blob-remove b/levels/it/low-level/blob-remove new file mode 100644 index 0000000..bbc1b30 --- /dev/null +++ b/levels/it/low-level/blob-remove @@ -0,0 +1,27 @@ +[description] + +There's a simple command to remove all objects that are not referenced by anything: + + git prune + +Remove all blobs in this repository. + +[congrats] + +Generally, `git prune` will be useful if you want to clean up some objects you made. + +Alternatively, you can also click the "Reload" button to restart a level. + +[setup] + +echo "My master password is a1b2c3d4e5" | git hash-object -w --stdin +echo "This blob really should not exist" | git hash-object -w --stdin +echo "This is a virus" | git hash-object -w --stdin + +[setup goal] + +[win] + +OBJECT_COUNT=$(git cat-file --batch-check='%(objectname) %(objecttype)' --batch-all-objects | wc -l) + +test "$OBJECT_COUNT" -eq 0 diff --git a/levels/it/low-level/commit-create b/levels/it/low-level/commit-create new file mode 100644 index 0000000..79a8e52 --- /dev/null +++ b/levels/it/low-level/commit-create @@ -0,0 +1,37 @@ +[description] + +So a tree describes a directory structure at a specific point in time. + +It would be nice if we could remember when that state existed, and who authored it, right? + +Enter: commits. They are objects that point to a tree and contain some additional metadata. You can create a commit using + + git commit-tree -m "Description of your commit" + +Make a commit from the tree in this repository! + +[setup] + +touch empty_file +git add . +git write-tree + +rm empty_file +git update-index --remove empty_file + +[setup goal] + +touch empty_file +git add . +git write-tree + +rm empty_file +git update-index --remove empty_file + +git commit-tree 3185 -m 'Clever commit message' + +[win] + +COMMIT_COUNT=$(git cat-file --batch-check='%(objectname) %(objecttype)' --batch-all-objects | grep commit | wc -l) + +test "$COMMIT_COUNT" -gt 0 diff --git a/levels/it/low-level/commit-parents b/levels/it/low-level/commit-parents new file mode 100644 index 0000000..a2efb11 --- /dev/null +++ b/levels/it/low-level/commit-parents @@ -0,0 +1,31 @@ +[description] + +When using the commit-tree command, you can optionally specify a parent: + + git commit-tree -m "Description" -p + +Make a string of three commits! + +Hint: You'll need a tree object. What could be the easiest way to obtain one? + +[setup] + +[setup goal] + +git write-tree +FIRST_COMMIT=$(git commit-tree 4b82 -m 'First commit :O') +SECOND_COMMIT=$(git commit-tree 4b82 -p $FIRST_COMMIT -m 'Second commit :D') +THIRD_COMMIT=$(git commit-tree 4b82 -p $SECOND_COMMIT -m 'Third commit \o/') + +[win] + +COMMITS=$(git cat-file --batch-check='%(objectname) %(objecttype)' --batch-all-objects | grep commit | cut -f1 -d" ") + +for COMMIT in $COMMITS; do + echo a commit named $COMMIT + if [ $(git rev-list $COMMIT | wc -l) -ge 3 ]; then + return 0 + fi +done + +return 1 diff --git a/levels/it/low-level/commit-rhombus b/levels/it/low-level/commit-rhombus new file mode 100644 index 0000000..e320ca0 --- /dev/null +++ b/levels/it/low-level/commit-rhombus @@ -0,0 +1,30 @@ +[description] + +A commit can have multiple parents! You can specify the -p option multiple times, like this: + + git commit-tree -m "Description" -p -p + +Build a rhombus shape from commits, where two commits point to the same parent, and then a fourth commit points to both of them. + +[setup] + +[setup goal] + +TREE=$(git write-tree) +SOUTH=$(git commit-tree $TREE -m "South") +EAST=$(git commit-tree $TREE -m "East" -p $SOUTH) +WEST=$(git commit-tree $TREE -m "West" -p $SOUTH) +NORTH=$(git commit-tree $TREE -m "Nort" -p $EAST -p $WEST) + +[win] + +COMMITS=$(git cat-file --batch-check='%(objectname) %(objecttype)' --batch-all-objects | grep commit | cut -f1 -d" ") + +for COMMIT in $COMMITS; do + # My first parent's parents has to be the same as my second parent's parent. + if [ "$(git rev-parse --verify -q $COMMIT^1^)" = "$(git rev-parse --verify -q $COMMIT^2^)" ]; then + return 0 + fi +done + +return 1 diff --git a/levels/it/low-level/index-add b/levels/it/low-level/index-add new file mode 100644 index 0000000..633245c --- /dev/null +++ b/levels/it/low-level/index-add @@ -0,0 +1,37 @@ +[description] + +Blobs usually represent the content of a file. But on their own, they don't have any metadata, not even a name! + +Git has a very powerful concept to store metadata related to blobs: the index! It's a list that relates blobs to filenames and access permissions. + +The most convenient option to add an entry to the index is via an existing file: + + echo "my content" > file + git update-index --add file + +Add three entries to the index! For a bonus challenge: can you add a file that is inside of a directory, like "directory/file"? + +[congrats] + +There's another way to add an entry to the index directly: + + git update-index --add --cacheinfo ,, + +The first three numbers of the mode describe the type of the entry, "100" is a regular file. + +The second three number describe the permissions. Only "644" (non-executable) and "755" (executable) are supported. + +You can insert the hash of an object into the terminal by right-clicking on it! :) + +[setup] + +[setup goal] + +echo "file 1" > file1 +echo "file 2" > file2 +echo "file 3" > file3 +git add . + +[win] + +test "$(git ls-files | wc -l)" -ge 3 diff --git a/levels/it/low-level/index-remove b/levels/it/low-level/index-remove new file mode 100644 index 0000000..9816126 --- /dev/null +++ b/levels/it/low-level/index-remove @@ -0,0 +1,29 @@ +[description] + +To remove an entry from the index, use a command like this: + + git update-index --force-remove + +Remove all entries from the index! + +[setup] + +echo "file 1" > file1 +echo "file 2" > file2 +echo "file 3" > file3 +git add . + +[setup goal] + +echo "file 1" > file1 +echo "file 2" > file2 +echo "file 3" > file3 +git add . + +git update-index --force-remove file1 +git update-index --force-remove file2 +git update-index --force-remove file3 + +[win] + +test "$(git ls-files | wc -l)" -eq 0 diff --git a/levels/it/low-level/index-update b/levels/it/low-level/index-update new file mode 100644 index 0000000..047493d --- /dev/null +++ b/levels/it/low-level/index-update @@ -0,0 +1,33 @@ +[description] + +Instead of removing an entry from the index and adding one with the same name, you can also directly update that entry! + +Put the content you want in a file with a matching name, and then run + + git update-index + +This will create a new blob, and update the hash of the entry to that blob. + +Update an entry in the index! + +[setup] + +echo "file 1" > file1 +echo "file 2" > file2 +echo "file 3" > file3 +git add . + +[setup goal] + +echo "file 1" > file1 +echo "file 2" > file2 +echo "file 3" > file3 +git add . + +echo "new content" > file1 +git update-index file1 + +[win] + +# This is not really a good test for the winning condition... +test "$(git ls-files -s | git hash-object --stdin)" != "10c4b28623e7e44e09f5a596450a50ab7ac31fbe" -a "$(git ls-files | wc -l)" -eq 3 diff --git a/levels/it/low-level/puzzle-apocalypse b/levels/it/low-level/puzzle-apocalypse new file mode 100644 index 0000000..119784d --- /dev/null +++ b/levels/it/low-level/puzzle-apocalypse @@ -0,0 +1,41 @@ +[description] + +Delete all objects in this repository using git commands only! + +Useful commands: + + git prune + git reflog expire + +[setup] + +echo foo > foo +BLOB=$(git hash-object -w foo) +echo bar > bar +git add . +git commit -m "Initial commit" +echo blabber >> bar +git commit -a -m "Second commit" +git update-ref refs/important HEAD +git update-ref refs/interesting "$BLOB" + +[setup goal] + +echo foo > foo +BLOB=$(git hash-object -w foo) +echo bar > bar +git add . +git commit -m "Initial commit" +echo blabber >> bar +git commit -a -m "Second commit" +git update-ref refs/important HEAD +git update-ref refs/interesting "$BLOB" + +TREE=$(git mktree) +git read-tree $TREE +rm -rf .git/refs/* +rm -rf .git/objects/* + +[win] + +test "$(git cat-file --batch-check --batch-all-objects | wc -l)" -eq 0 diff --git a/levels/it/low-level/puzzle-precious-blob b/levels/it/low-level/puzzle-precious-blob new file mode 100644 index 0000000..865cfdf --- /dev/null +++ b/levels/it/low-level/puzzle-precious-blob @@ -0,0 +1,28 @@ +[description] + +Create two trees pointing to the same blob! + +[setup] + +[setup goal] + +BLOB=$(echo "I am precious" | git hash-object -w --stdin) +git update-index --add --cacheinfo 100644,$BLOB,a +git write-tree +git update-index --force-remove a +git update-index --add --cacheinfo 100644,$BLOB,b +git write-tree +git update-index --force-remove b + +[win] + +TREES=$(git cat-file --batch-check='%(objectname) %(objecttype)' --batch-all-objects | grep tree | cut -f1 -d" ") + +ALL_TREE_CHILDREN=$(for TREE in $TREES; do + git cat-file -p $TREE | cut -f1 | cut -f3 -d" " +done) + +NUMBER_OF_CHILDREN=$(echo "$ALL_TREE_CHILDREN" | wc -l) +UNIQUE_CHILDREN=$(echo "$ALL_TREE_CHILDREN" | sort -u | wc -l) + +test "$NUMBER_OF_CHILDREN" -gt "$UNIQUE_CHILDREN" diff --git a/levels/it/low-level/puzzle-trees-all-the-way-down b/levels/it/low-level/puzzle-trees-all-the-way-down new file mode 100644 index 0000000..bc3e58a --- /dev/null +++ b/levels/it/low-level/puzzle-trees-all-the-way-down @@ -0,0 +1,34 @@ +[description] + +Construct a chain of three trees, which don't point to anything else. + +This is hard! The `git mktree` command might be useful. + +[setup] + +[setup goal] + +git mktree +TREE=$(echo -e "040000 tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\tdir" | git mktree) +echo -e "040000 tree $TREE\tdir" | git mktree + +[win] + +TREES=$(git cat-file --batch-check='%(objectname) %(objecttype)' --batch-all-objects | grep tree | cut -f1 -d" ") + +for TREE in $TREES; do + if [ "$(git cat-file -p $TREE | wc -l)" -eq 1 ]; then + if [ "$(git cat-file -p $TREE | cut -f1 | grep tree | wc -l)" -eq 1 ]; then + # So the tree has exactly one child, and it is a tree! + TREE2=$(git cat-file -p $TREE | cut -f1 | grep tree | cut -f3 -d" ") + if [ "$(git cat-file -p $TREE2 | wc -l)" -eq 1 ]; then + if [ "$(git cat-file -p $TREE2 | cut -f1 | grep tree | wc -l)" -eq 1 ]; then + # Same for its child! \o/ + return 0 + fi + fi + fi + fi +done + +return 1 diff --git a/levels/it/low-level/ref-create b/levels/it/low-level/ref-create new file mode 100644 index 0000000..1cc4bcf --- /dev/null +++ b/levels/it/low-level/ref-create @@ -0,0 +1,42 @@ +[description] + +Let's take a look at "refs" (short for "references")! Refs are not objects, but rather very simple *pointers* to objects! They can help you keep track of what's where. + +You can create or update a ref with + + git update-ref refs/ + +Make sure to always start a ref's name with "refs/"! That's a convention that helps Git find all refs you create. If you forget the "refs/", you will not see the ref. + +Create refs that point to all objects in this repository! + +[setup] + +echo hello > hello +echo world > world +BLOB1=$(git hash-object -w hello) +BLOB2=$(git hash-object -w world) +git add . +TREE=$(git write-tree) +COMMIT=$(git commit-tree $TREE -m "Initial commit") + +[setup goal] + +echo hello > hello +echo world > world +BLOB1=$(git hash-object -w hello) +BLOB2=$(git hash-object -w world) +git add . +TREE=$(git write-tree) +COMMIT=$(git commit-tree $TREE -m "Initial commit") + +git update-ref refs/a $BLOB1 +git update-ref refs/b $BLOB2 +git update-ref refs/c $TREE +git update-ref refs/d $COMMIT + +[win] + +OBJECTS=$(git cat-file --batch-check='%(objectname)' --batch-all-objects | sort) +REF_TARGETS=$(git show-ref -s | sort | uniq) +test "$OBJECTS" = "$REF_TARGETS" diff --git a/levels/it/low-level/ref-move b/levels/it/low-level/ref-move new file mode 100644 index 0000000..e3b29e3 --- /dev/null +++ b/levels/it/low-level/ref-move @@ -0,0 +1,41 @@ +[description] + +You can point refs to a new location using the same command you use to create them: + + git update-ref refs/ + +As an exercise, make all refs in this repository point to the tree object! + +[setup] + +echo hello > hello +echo world > world +BLOB1=$(git hash-object -w hello) +BLOB2=$(git hash-object -w world) +git add . +TREE=$(git write-tree) +COMMIT=$(git commit-tree $TREE -m "Initial commit") + +git update-ref refs/a "$BLOB1" +git update-ref refs/b "$COMMIT" + +[setup goal] + +echo hello > hello +echo world > world +BLOB1=$(git hash-object -w hello) +BLOB2=$(git hash-object -w world) +git add . +TREE=$(git write-tree) +COMMIT=$(git commit-tree $TREE -m "Initial commit") + +git update-ref refs/a "$BLOB1" +git update-ref refs/b "$COMMIT" + +for REF in $(git for-each-ref --format='%(refname)'); do + git update-ref "$REF" "$TREE" +done + +[win] + +test "$(git show-ref -s | sort -u)" = "c7863f72467ed8dd44f4b8ffdb8b57ca7d91dc9e" diff --git a/levels/it/low-level/ref-remove b/levels/it/low-level/ref-remove new file mode 100644 index 0000000..d1fa982 --- /dev/null +++ b/levels/it/low-level/ref-remove @@ -0,0 +1,41 @@ +[description] + +And finally, to delete a ref, use + + git update-ref -d refs/ + +Delete all refs! :P (Well, except for HEAD. HEAD is special.) + +[setup] + +echo hello > hello +echo world > world +BLOB1=$(git hash-object -w hello) +BLOB2=$(git hash-object -w world) +git add . +TREE=$(git write-tree) +COMMIT=$(git commit-tree $TREE -m "Initial commit") + +git update-ref refs/best_blob_ever "$BLOB1" +git update-ref refs/beautiful_commit "$COMMIT" + +[setup goal] + +echo hello > hello +echo world > world +BLOB1=$(git hash-object -w hello) +BLOB2=$(git hash-object -w world) +git add . +TREE=$(git write-tree) +COMMIT=$(git commit-tree $TREE -m "Initial commit") + +git update-ref refs/best_blob_ever "$BLOB1" +git update-ref refs/beautiful_commit "$COMMIT" + +for REF in $(git for-each-ref --format='%(refname)'); do + git update-ref -d "$REF" +done + +[win] + +test "$(git show-ref | wc -l)" -eq 0 diff --git a/levels/it/low-level/sequence b/levels/it/low-level/sequence new file mode 100644 index 0000000..5feb0c8 --- /dev/null +++ b/levels/it/low-level/sequence @@ -0,0 +1,18 @@ +welcome +basics +blob-create +blob-remove +index-add +index-remove +index-update +tree-create +tree-read +tree-nested +commit-create +commit-parents +commit-rhombus +ref-create +ref-move +ref-remove +symref-create +symref-no-deref diff --git a/levels/it/low-level/symref-create b/levels/it/low-level/symref-create new file mode 100644 index 0000000..da933cb --- /dev/null +++ b/levels/it/low-level/symref-create @@ -0,0 +1,21 @@ +[description] + +Instead of pointing directly to objects, refs can also point to other refs! + +When that happens, they are called "symbolic refs". You can create or update a symbolic ref using + + git symbolic-ref + +Create a symbolic ref called "refs/rainbow"! + +[setup] + +[setup goal] + +BLOB=$(git hash-object -w --stdin) +git update-ref refs/double "$BLOB" +git symbolic-ref refs/rainbow refs/double + +[win] + +git symbolic-ref refs/rainbow diff --git a/levels/it/low-level/symref-no-deref b/levels/it/low-level/symref-no-deref new file mode 100644 index 0000000..458e669 --- /dev/null +++ b/levels/it/low-level/symref-no-deref @@ -0,0 +1,46 @@ +[description] + +When you have a symbolic ref (a ref pointing at another ref), and you decide you want it to be a regular ref again (pointing to an object), you're in for some trouble! :) + +What happens when you try pointing the symbolic ref directly to the blob using `git update-ref`? + +Oops! Turns out that when you reference a symbolic ref, it acts as if you had specified the ref it points to. To de-symbolic-ize it, use the `--no-deref` option directly after `update-ref`! + +Weird, huh? + +[congrats] + +Whew, we've covered a lot of things: Blobs! The index! Trees! Commits! Refs! + +You now know about almost everything about how Git repositories look like on the inside! We think that's pretty cool! :) + +Everything else is just convention and high-level commands that make interacting with the objects more convenient. + +We haven't covered: + +- tag objects (they are the fourth object type - a bit like refs with a description and an author) +- configuration (allows you to specify remote repositories, for example) +- working with local files (which is, uh, arguably pretty important :P) + +Thanks for playing! You're welcome to check out the "puzzle" levels in the dropdown, some of them are more advanced! + +[setup] + +BLOB1=$(echo delicious | git hash-object -w --stdin) +BLOB2=$(echo very | git hash-object -w --stdin) +git update-ref refs/curly "$BLOB1" +git symbolic-ref refs/fries refs/curly + +[setup goal] + +BLOB1=$(echo delicious | git hash-object -w --stdin) +BLOB2=$(echo very | git hash-object -w --stdin) +git update-ref refs/curly "$BLOB1" +git symbolic-ref refs/fries refs/curly + +git update-ref --no-deref refs/fries "$BLOB2" + +[win] + +git symbolic-ref refs/fries && return 1 +test "$(git show-ref -s refs/fries)" = "035e2968dafeea08e46e8fe6743cb8123e8b9aa6" diff --git a/levels/it/low-level/tree-create b/levels/it/low-level/tree-create new file mode 100644 index 0000000..3da3618 --- /dev/null +++ b/levels/it/low-level/tree-create @@ -0,0 +1,35 @@ +[description] + +After carefully building the index we want, it would be nice to save a permanent snapshot of it, right? + +This is what the second type of objects is for: trees! You can convert the index into a tree using + + git write-tree + +Try it! :) + +[congrats] + +Nice! + +Can you make a different tree? Modify the index, then call `git write-tree` again! + +[setup] + +echo "file 1" > file1 +echo "file 2" > file2 +echo "file 3" > file3 +git add . + +[setup goal] + +echo "file 1" > file1 +echo "file 2" > file2 +echo "file 3" > file3 +git add . + +git write-tree + +[win] + +git cat-file -p 21a638f28022064c1f1df20844278b494d197979 diff --git a/levels/it/low-level/tree-nested b/levels/it/low-level/tree-nested new file mode 100644 index 0000000..c7afce8 --- /dev/null +++ b/levels/it/low-level/tree-nested @@ -0,0 +1,38 @@ +[description] + +Trees can also point to other trees! This way, they can describe nested directory structures. + +When you add a file inside of a directory to the index, and then call `git write-tree`, it will create a nested tree for the directory, and attach the blob to it. + +To solve this level, build a little stick figure, as shown on the left - a tree that points to two blobs, as well to a tree that points to two blobs. + +[setup] + +[setup goal] + +echo "I'm the left arm" > arm1 +echo "I'm the right arm" > arm2 +mkdir hip +echo "I'm the left leg" > hip/leg1 +echo "I'm the right leg" > hip/leg2 +git add . +git write-tree + +[win] + +TREES=$(git cat-file --batch-check='%(objectname) %(objecttype)' --batch-all-objects | grep tree | cut -f1 -d" ") + +for OUTER_TREE in $TREES; do + NUMBER_OF_BLOB_CHILDREN=$(git cat-file -p $OUTER_TREE | cut -f2 -d" " | grep blob | wc -l) + NUMBER_OF_TREE_CHILDREN=$(git cat-file -p $OUTER_TREE | cut -f2 -d" " | grep tree | wc -l) + + if [ $NUMBER_OF_BLOB_CHILDREN -eq 2 -a $NUMBER_OF_TREE_CHILDREN -eq 1 ]; then + TREE_CHILD=$(git cat-file -p $OUTER_TREE | cut -f1 | grep tree | cut -d" " -f3) + NUMBER_OF_BLOB_CHILDREN_OF_TREE_CHILD=$(git cat-file -p $TREE_CHILD | cut -f2 -d" " | grep blob | wc -l) + if [ $NUMBER_OF_BLOB_CHILDREN_OF_TREE_CHILD -eq 2 ]; then + return 0 + fi + fi +done + +return 1 diff --git a/levels/it/low-level/tree-read b/levels/it/low-level/tree-read new file mode 100644 index 0000000..ba8c440 --- /dev/null +++ b/levels/it/low-level/tree-read @@ -0,0 +1,51 @@ +[description] + +As soon as you have some tree objects, you can always read them and set the index exactly to their content! Unsurprisingly, the command is called + + git read-tree + +For , you can provide the hash of any tree object - you can right-click one to insert its hash into the terminal! + +Try reading some of the trees in this repository into the index! + +[setup] + +EMPTY_TREE=$(git write-tree) + +echo "file 1" > file1 +echo "file 2" > file2 +git add . +git write-tree + +rm * +echo "file A" > fileA +echo "file B" > fileB +echo "file C" > fileC +git add . +TRIPLE_TREE=$(git write-tree) + +git read-tree "$EMPTY_TREE" + +[setup goal] + +EMPTY_TREE=$(git write-tree) + +echo "file 1" > file1 +echo "file 2" > file2 +git add . +git write-tree + +rm * +echo "file A" > fileA +echo "file B" > fileB +echo "file C" > fileC +git add . +TRIPLE_TREE=$(git write-tree) + +git read-tree "$EMPTY_TREE" + +git read-tree "$TRIPLE_TREE" + +[win] + +test "$(git ls-files | wc -l)" -gt 0 diff --git a/levels/it/low-level/welcome b/levels/it/low-level/welcome new file mode 100644 index 0000000..d669136 --- /dev/null +++ b/levels/it/low-level/welcome @@ -0,0 +1,33 @@ +[description] + +This is prototype #1 for the Git learning game by @bleeptrack and @blinry. Thanks for checking it out! <3 + +You can interact with the repository labelled "yours" by typing Bash commands in the terminal below! The visualization will show you its internal status. + +Let's get started by initializing an empty Git repository in the current directory by typing: + + git init + +[congrats] + +Well done! + +An empty Git repository is... well, quite empty. The only thing that always exists is a reference called "HEAD" - we'll learn what that is later! + +But first, let's look at some basics! + +(Click "Next Level" as soon as you're ready!) + +[setup] + +rm -rf .git + +[setup goal] + +rm -rf .git + +git init + +[win] + +test -d .git diff --git a/levels/it/merge/conflict b/levels/it/merge/conflict new file mode 100644 index 0000000..1a8b02b --- /dev/null +++ b/levels/it/merge/conflict @@ -0,0 +1,47 @@ +title = Contradictions +cards = checkout commit-auto merge reset-hard + +[description] + +Sometimes, timelines will contradict each other. + +For example, in this case, one of our clients wants these timelines merged, but they ate different things for breakfast in both timelines. + +Try to merge them together! You'll notice that there will be a conflict! The time machine will leave it up to you how to proceed: you can edit the problematic item, it will show you the conflicting sections. You can keep either of the two versions - or create a combination of them! Remove the >>>, <<<, and === markers, and make a new commit to finalize the merge! + +Let your finalized timeline be the "main" one. + +[setup] + +echo "Just woke up. Is hungry." > sam +git add . +git commit -m "The beginning" + +git checkout -b pancakes +echo "Had blueberry pancakes with maple syrup for breakfast." > sam +git add . +git commit -m "Pancakes!" + +echo " +Is at work." >> sam +git commit -am "Go to work" + +git checkout -b muesli main +echo "Had muesli with oats and strawberries for breakfast." > sam +git add . +git commit -m "Muesli!" + +echo " +Is at work." >> sam +git commit -am "Go to work" + +git checkout main + +[win] + +# Make a breakfast compromise in the 'main' branch. +git rev-parse main^ && test "$(git rev-parse main^1^^)" = "$(git rev-parse main^2^^)" + +[congrats] + +Yum, that sounds like a good breakfast! diff --git a/levels/it/merge/merge b/levels/it/merge/merge new file mode 100644 index 0000000..8b45d06 --- /dev/null +++ b/levels/it/merge/merge @@ -0,0 +1,82 @@ +title = Merging timelines +cards = checkout commit-auto merge + +[description] + +Here's a trick so that you can sleep a bit longer: just do all your morning activities in parallel universes, and then at the end, merge them together! + +[setup] + +echo "You do not have a baguette. + +You do not have coffee. + +You do not have a donut." > you + +git add . +git commit -m "The Beginning" + +echo "You have a baguette. + +You do not have coffee. + +You do not have a donut." > you +git add . +git commit -m "You buy a baguette" + +echo "You ate a baguette. + +You do not have coffee. + +You do not have a donut." > you +git add . +git commit -m "You eat the baguette" + +git checkout HEAD~2 +echo "You do not have a baguette. + +You have coffee. + +You do not have a donut." > you +git add . +git commit -m "You buy some coffee" + +echo "You do not have a baguette. + +You drank coffee. + +You do not have a donut." > you +git add . +git commit -m "You drink the coffee" + +git checkout HEAD~2 +echo "You do not have a baguette. + +You do not have coffee. + +You have a donut." > you +git add . +git commit -m "You buy a donut" + +echo "You do not have a baguette. + +You do not have coffee. + +You ate a donut." > you +git add . +git commit -m "You eat the donut" + +git checkout --detach +git branch -D main + +[win] + +# Build a situation where you consumed a baguette, a coffee, *and* a donut. +{ git show HEAD:you | grep "You ate.*baguette"; } && { git show HEAD:you | grep "You drank.*coffee"; } && { git show HEAD:you | grep "You ate.*donut"; } + +# Be on a merge commit. +test "$(git log --pretty=%P -n 1 HEAD | wc -w)" -ge 2 + +[congrats] + +I wonder if you're more relaxed when you *sleep* in parallel timelines... diff --git a/levels/it/merge/merge-abort b/levels/it/merge/merge-abort new file mode 100644 index 0000000..ce7190b --- /dev/null +++ b/levels/it/merge/merge-abort @@ -0,0 +1,54 @@ +title = Abort a merge +cards = checkout commit-auto merge merge-abort + +[description] + +Sometimes you want to merge two commits, but a merge conflict occurs that you currently don't want to resolve. + +In these situations you can abort the merge to merge later. Use + git merge --abort +when you are in a merge process. + +Try to merge both commits and abort the merge afterwards. + +[setup] + +echo "A new day is starting" > you + +git add . +git commit -m "Start" + +echo "Walking down the Main Lane." >> you + +git add . +git commit -m "Main Lane" + + +git checkout HEAD~1 + +echo "Walking down the Side Lane." >> you + +git add . +git commit -m "Side Lane" + +git checkout HEAD~1 + +git branch -D main + +[actions] + +if test -f .git/MERGE_HEAD; then + touch .git/secretfile +fi + +[win] + +# You tried to merge? +test -f .git/secretfile + +# You aborted to merge? +test -f .git/secretfile && ! test -f .git/MERGE_HEAD && ! git rev-parse HEAD^^ + +[congrats] + +Aaah, let's merge later... diff --git a/levels/it/merge/sequence b/levels/it/merge/sequence new file mode 100644 index 0000000..c3b6caf --- /dev/null +++ b/levels/it/merge/sequence @@ -0,0 +1,2 @@ +merge +conflict diff --git a/levels/it/remotes/friend b/levels/it/remotes/friend new file mode 100644 index 0000000..1b70f33 --- /dev/null +++ b/levels/it/remotes/friend @@ -0,0 +1,47 @@ +title = Friend +cards = pull push commit-auto checkout + +[description] + +Your friend added another line to your essay! Get it, add a third one and send it to them! + +Take turns until you have five lines! + +[setup yours] + +echo "Line 1" > essay +git add . +git commit -m "One line" + +git push -u friend main + +[setup friend] + +git checkout main +echo "Line 2, gnihihi" >> essay +git commit -am "Another line" + +[actions friend] + +if test "$(git log --oneline | wc -l)" -eq 3; then + git reset --hard main # Necessary because the working directory isn't updated when we push to the friend. + echo "Line 4, blurbblubb" >> essay + git commit -am "Final line" + hint "Oh nice, I added a fourth line!" +fi + +[win] + +# Got the second line from your friend +git show HEAD:essay | grep gnihihi + +# Got the fourth line from your friend. +git show HEAD:essay | grep blurbblubb + +[win friend] + +# The friend got a third line from you +test "$(git show HEAD:essay | wc -l)" -ge 3 + +# The friend got a fifth line from you +test "$(git show HEAD:essay | wc -l)" -ge 5 diff --git a/levels/it/remotes/problems b/levels/it/remotes/problems new file mode 100644 index 0000000..fab71c5 --- /dev/null +++ b/levels/it/remotes/problems @@ -0,0 +1,33 @@ +title = Problems +cards = checkout add pull push commit-auto merge + +[description] + +Both you and your friend have been working on the file, and want to sync up! + +[setup yours] + +echo "The bike shed should be ???" > file +git add . +git commit -m "initial" + +git push -u friend main + +echo "The bike shed should be green" > file + +[setup friend] + +git checkout main + +echo "The bike shed should be blue" > file +git commit -a -m "friends version" + +[win] + +# Commit your local changes. +test "$(git status -s)" = "" + +[win friend] + +# Look at your friend's suggestion, make a compromise, and push it back. +git rev-parse main^ && test "$(git rev-parse main^1^)" = "$(git rev-parse main^2^)" diff --git a/levels/it/remotes/sequence b/levels/it/remotes/sequence new file mode 100644 index 0000000..13c5715 --- /dev/null +++ b/levels/it/remotes/sequence @@ -0,0 +1,2 @@ +friend +problems diff --git a/levels/it/sandbox/empty b/levels/it/sandbox/empty new file mode 100644 index 0000000..1f954a5 --- /dev/null +++ b/levels/it/sandbox/empty @@ -0,0 +1,7 @@ +title = Empty sandbox + +[description] + +This is an empty sandbox you can play around in. + +[setup] diff --git a/levels/it/sandbox/remote b/levels/it/sandbox/remote new file mode 100644 index 0000000..f0ca2c0 --- /dev/null +++ b/levels/it/sandbox/remote @@ -0,0 +1,22 @@ +title = Sandbox with a remote +cards = checkout commit-auto pull fetch push + +[description] + +Here's a sandbox with a remote! Try pulling, fetching, or pushing! + +How can you push tags and branches on a remote? How can you delete them again? + +[setup yours] + +echo "Line 1" > essay +git add . +git commit -m "Initial commit" + +git push -u friend main + +[setup friend] + +git checkout main +echo "Line 2" >> essay +git commit -am "Another line" diff --git a/levels/it/sandbox/sequence b/levels/it/sandbox/sequence new file mode 100644 index 0000000..29aa91f --- /dev/null +++ b/levels/it/sandbox/sequence @@ -0,0 +1,3 @@ +empty +remote +three-commits diff --git a/levels/it/sandbox/three-commits b/levels/it/sandbox/three-commits new file mode 100644 index 0000000..3e5308c --- /dev/null +++ b/levels/it/sandbox/three-commits @@ -0,0 +1,26 @@ +title = Sandbox with three commits +cards = checkout add reset-file checkout-file commit merge rebase + +[setup] + +echo "You wake up." > you +git add . +git commit -m "The beginning" + +echo "You drink coffee." >> you +git commit -am "First things first" + +echo "You hear a knock on the door." >> you +git commit -am "Who's there?" + +git branch not_main + +[description] + +Here's a sandbox you can play around in. + +You can use both the playing cards, as well as the terminal. This is a real Git terminal! Fun things to try: + +- Make a commit that merges three timelines together at once! +- Create and delete some tags! +- Make a timeline that's completely independent of the rest! diff --git a/levels/it/sequence b/levels/it/sequence new file mode 100644 index 0000000..cefcd7a --- /dev/null +++ b/levels/it/sequence @@ -0,0 +1,13 @@ +intro +files +branches +merge +index +remotes +changing-the-past +shit-happens +workflows +bisect +stash +tags +sandbox diff --git a/levels/it/shit-happens/bad-commit b/levels/it/shit-happens/bad-commit new file mode 100644 index 0000000..10669be --- /dev/null +++ b/levels/it/shit-happens/bad-commit @@ -0,0 +1,30 @@ +title = Undo a bad commit +cards = reset commit-a + +[description] + +Oh no, we made a bad commit! How can we undo making the commit, and go back to a point where we can try again? + +The answer is using `git reset [commit]`, which does two things: + +- It resets the current branch ref to the commit you specify. +- And it resets the index to that commit. + +It does not change your working directory in any way, which means that after that, you can try making the commit you want again. + +[setup] + +echo "1 2 3 4" > numbers +git add . +git commit -m "Initial commit" +echo "1 2 3 4 5 6 7 8 9 11" > numbers +git commit -am "More numberrrrrs" + +[win] + +# In the last main commit, the numbers file contains the numbers from 1 to 10. +test "$(git show main:numbers)" = "1 2 3 4 5 6 7 8 9 10" +# The commit message of that commit is "More numbers". +git log -1 --oneline | grep "More numbers" +# The commit with the typo is not part of the main branch anymore. +git log --oneline | grep -v "rrrrr" diff --git a/levels/it/shit-happens/pushed-something-broken b/levels/it/shit-happens/pushed-something-broken new file mode 100644 index 0000000..f8dce38 --- /dev/null +++ b/levels/it/shit-happens/pushed-something-broken @@ -0,0 +1,54 @@ +title = I pushed something broken +cards = revert push + +[description] + +We were talking about how to undo a commit, and fix it. This only helps when you haven't already pushed it to a remote. When that has happened, and you want to undo the effects of the commit completely, your best option is `git revert` + +[setup] + +echo "this is fine + +? + +? + +?" > text +git add . +git commit -m fine +echo "this is fine + +this is also fine + +? + +?" > text +git commit -am "also fine" +echo "this is fine + +this is also fine + +this is very bad + +?" > text +git commit -am "very bad" +echo "this is fine + +this is also fine + +this is very bad + +this is fine again" > text +git commit -am "fine again" + +git push team main +git branch -u team/main main + +[setup team] + +[win team] + +# The team's main branch no longer contains the bad thing. +! { git show main:text | grep -q "very bad"; } +# And the history has not been modified. +git show main^:text | grep -q "very bad" diff --git a/levels/it/shit-happens/reflog b/levels/it/shit-happens/reflog new file mode 100644 index 0000000..e21c4e7 --- /dev/null +++ b/levels/it/shit-happens/reflog @@ -0,0 +1,26 @@ +title = Go back to where you were before +cards = checkout reflog + +[description] + +Say you were looking at something in the past, and then switched back to the main branch. + +But then, you got reaaally distracted, and after your lunch break, you can't remember on which commit in the past you were before. How can you find out? + +There's a convenient command that shows you all the places your HEAD did point to in the past: + + git reflog + +[setup] + +for i in {1..10}; do + git commit --allow-empty -m $i + git branch $i +done +git checkout 3 +git checkout main + +[win] + +# Find out where you've been before, and go back there! +test "$(git rev-parse HEAD)" = "$(git rev-parse 3)" diff --git a/levels/it/shit-happens/restore-a-file b/levels/it/shit-happens/restore-a-file new file mode 100644 index 0000000..26450f0 --- /dev/null +++ b/levels/it/shit-happens/restore-a-file @@ -0,0 +1,22 @@ +title = Restore a deleted file +cards = checkout + +[description] + +Oops - you deleted the "essay" file, which you worked on all night! + +Luckily, Git is here to help! You can use `git checkout` to restore the file! + +[setup] + +echo important > essay +git add . +git commit -m "Initial commit" +echo "important content" > essay +git commit -am "Improve essay" +rm essay + +[win] + +# Restore the essay to contain "important content" +test "$(cat essay)" = "important content" diff --git a/levels/it/shit-happens/restore-a-file-from-the-past b/levels/it/shit-happens/restore-a-file-from-the-past new file mode 100644 index 0000000..b51c108 --- /dev/null +++ b/levels/it/shit-happens/restore-a-file-from-the-past @@ -0,0 +1,21 @@ +title = Restore a file from the past +cards = checkout checkout-from commit + +[description] + +Here's a similar problem: you really liked the essay from the very first commit, and want to have it back! Well, checkout can also restore things from older commits, Here's how: + + git checkout [commit] [file] + +[setup] + +echo "good version" > essay +git add . +git commit -m "Initial commit" +echo "bad version" > essay +git commit -am "\"Improve\" essay" + +[win] + +# Get the first version of your essay, and make a new commit with it. +test "$(git show main:essay)" = "good version" diff --git a/levels/it/shit-happens/sequence b/levels/it/shit-happens/sequence new file mode 100644 index 0000000..663d5d9 --- /dev/null +++ b/levels/it/shit-happens/sequence @@ -0,0 +1,5 @@ +restore-a-file +restore-a-file-from-the-past +bad-commit +pushed-something-broken +reflog diff --git a/levels/it/stash/sequence b/levels/it/stash/sequence new file mode 100644 index 0000000..9dd02f7 --- /dev/null +++ b/levels/it/stash/sequence @@ -0,0 +1,5 @@ +stash +stash-pop +stash-clear +stash-branch +stash-merge diff --git a/levels/it/stash/stash b/levels/it/stash/stash new file mode 100644 index 0000000..6a4bc61 --- /dev/null +++ b/levels/it/stash/stash @@ -0,0 +1,45 @@ +title = Stashing +cards = checkout commit-auto merge reset-hard + +[description] + +You will encounter situations in which you are working on your project but you need to +put your current changes aside temporarily. To do so, you can use the stash function. Use + git stash push +to add your current changes to the stash stack. + +--- +tipp1 +--- +tipp2 +--- +tipp3 + +[setup] + +echo "Apple Pie:" > recipe + +git add . +git commit -m "creating a recipe" + +echo "- 4 Apples" >> recipe + +git add . +git commit -m "Adding ingredients" + +echo "- 500g Flour" >> recipe + +git checkout main + +[win] + +# Did you stash the current changes? +test "$(git stash list | wc -l)" -ge 1 + +[actions] + + + +[congrats] + +Nice stash you got there! :) diff --git a/levels/it/stash/stash-branch b/levels/it/stash/stash-branch new file mode 100644 index 0000000..16c9eae --- /dev/null +++ b/levels/it/stash/stash-branch @@ -0,0 +1,48 @@ +title = Branch from stash +cards = checkout commit-auto merge reset-hard + +[description] + +If you want to keep your changes but they don't belong to the main branch, you can easily +create a new branch from your stashed changes. Just use + git stash branch +If you just want to use the latest stash entry, you can leave the option empty. + +Create a new branch from the stashed changes! + +--- +tipp1 +--- +tipp2 +--- +tipp3 + +[setup] + +echo "Apple Pie:" > recipe + +git add . +git commit -m "creating a recipe" + +echo "- 4 Apples" >> recipe + +git add . +git commit -m "Adding ingredients" + +echo "- 500g Flour" >> recipe +git stash push + +git checkout main + +[win] + +# Did you create a new branch from the stashed changes? +test "$(git branch --list| wc -l)" -ge 2 + +[actions] + + + +[congrats] + +Stashed changes are in a new branch! :) diff --git a/levels/it/stash/stash-clear b/levels/it/stash/stash-clear new file mode 100644 index 0000000..9a1b697 --- /dev/null +++ b/levels/it/stash/stash-clear @@ -0,0 +1,57 @@ +title = Clear the Stash +cards = checkout commit-auto merge reset-hard + +[description] + +If you want to inspect your stash stack, use the command + git stash list + +Oh, you don't want to keep your stashed changes? There are way too many? Then go ahead and clear the stack with + git stash clear +If you only want to discard a certain stash entry, you can use + git stash drop + +Clear your stash stack! + +--- +tipp1 +--- +tipp2 +--- +tipp3 + +[setup] + +echo "Apple Pie:" > recipe + +git add . +git commit -m "creating a recipe" + +echo "- 4 Apples" >> recipe + +git add . +git commit -m "Adding ingredients" + +echo "- 500g Flour" >> recipe +git stash push + +echo "- 200g Sugar" >> recipe +git stash push + +echo "- Pinch of Salt" >> recipe +git stash push + +git checkout main + +[win] + +# Did you clear your stash stack? +test "$(git stash list | wc -l)" -eq 0 + +[actions] + + + +[congrats] + +All clear! :) diff --git a/levels/it/stash/stash-merge b/levels/it/stash/stash-merge new file mode 100644 index 0000000..a8c9ac1 --- /dev/null +++ b/levels/it/stash/stash-merge @@ -0,0 +1,54 @@ +title = Merging popped stash +cards = checkout commit-auto merge reset-hard + +[description] + +When you want to reapply your changes but you already continued working on your file, you might get +a merge conflict! Let's practise this situation. +Pop the changes from the stash with + git stash pop +and resolve the merge conflict. Commit the resolved changes and clear the stash stack afterwards. + +--- +tipp1 +--- +tipp2 +--- +tipp3 + +[setup] + +echo "Apple Pie:" > recipe + +git add . +git commit -m "creating a recipe" + +echo "- 4 Apples" >> recipe + +git add . +git commit -m "Adding ingredients" + +echo "- 500g Flour" >> recipe + +git stash push + +echo "- Pinch of Salt" >> recipe + +git checkout main +git add recipe + +[win] + +# Did you resolve the confict and commit? +{ git show HEAD | grep "Flour"; } && { git show HEAD | grep "Salt"; } + +# Did you clear stash stack? +test "$(git stash list | wc -l)" -eq 0 + +[actions] + + + +[congrats] + +Yay, you got your changes back! :) diff --git a/levels/it/stash/stash-pop b/levels/it/stash/stash-pop new file mode 100644 index 0000000..5ac12d5 --- /dev/null +++ b/levels/it/stash/stash-pop @@ -0,0 +1,46 @@ +title = Pop from Stash +cards = checkout commit-auto merge reset-hard + +[description] + +When you stashed your changes and you want to apply them back to your current working directory, you can use + git stash pop +This will remove the changes from the stash stack. If you also want to keep the changes on the stash stack, use + git stash apply + +--- +tipp1 +--- +tipp2 +--- +tipp3 + +[setup] + +echo "Apple Pie:" > recipe + +git add . +git commit -m "creating a recipe" + +echo "- 4 Apples" >> recipe + +git add . +git commit -m "Adding ingredients" + +echo "- 500g Flour" >> recipe + +git stash push +git checkout main + +[win] + +# Did you pop the changes from the stash stack? +test "$(git stash list | wc -l)" -eq 0 + +[actions] + + + +[congrats] + +Yay, you got your changes back! :) diff --git a/levels/it/tags/add-tag b/levels/it/tags/add-tag new file mode 100644 index 0000000..9c5703a --- /dev/null +++ b/levels/it/tags/add-tag @@ -0,0 +1,53 @@ +title = Creating tags +cards = checkout commit-auto merge reset-hard + +[description] + +Some of your commits may be special commits. Maybe you reached a milestone or a new version number. + +You can mark these commits with a special flag called 'tag'. + +Write + + git tag + +to tag your commit. + +--- +tipp1 +--- +tipp2 +--- +tipp3 + +[setup] + +echo "event 1" > feature-list + +git add . +git commit -m "Adding feature 1" + +echo "event 2" >> feature-list + +git add . +git commit -m "Adding feature 2" + +echo "event 3" >> feature-list + +git add . +git commit -m "Adding feature 3" + +git checkout --detach main + +[win] + +# Did you create a new tag? +test "$(git tag -l | wc -l)" -ge 1 + +[actions] + + + +[congrats] + +Nice! You tagged your first commit :) diff --git a/levels/it/tags/add-tag-later b/levels/it/tags/add-tag-later new file mode 100644 index 0000000..905647c --- /dev/null +++ b/levels/it/tags/add-tag-later @@ -0,0 +1,50 @@ +title = Tagging later +cards = checkout commit-auto merge reset-hard + +[description] + +But what happens if you forgot to tag your current commit? +No Prob! You can also tag older commits via + + git tag + +Tag the commit "Adding feature 2" with the name "v1"! + +--- +tipp1 +--- +tipp2 +--- +tipp3 + +[setup] + +echo "event 1" > feature-list + +git add . +git commit -m "Adding feature 1" + +echo "event 2" >> feature-list + +git add . +git commit -m "Adding feature 2" + +echo "event 3" >> feature-list + +git add . +git commit -m "Adding feature 3" + +git checkout --detach main + +[win] + +# Did you create a new tag? +test "$(git show v1 -s --format=%h)" = "$(git show HEAD~1 -s --format=%h)" + +[actions] + + + +[congrats] + +Well done :) diff --git a/levels/it/tags/remote-tag b/levels/it/tags/remote-tag new file mode 100644 index 0000000..0726915 --- /dev/null +++ b/levels/it/tags/remote-tag @@ -0,0 +1,58 @@ +title = Remote Tags +cards = pull push commit-auto checkout + +[description] + +When you work with remote repositories, tags are not pushed or pulled automatically. + +You can push a tag with + git push +Or all tags with: + git push --tags + +Deleting tags on your remote works with: + git push --delete + +You can also sync + git fetch --prune --prune-tags + + +Add a tag named "v2" to the last commit and push it to the remote. Also pull the v1 tag to your local repository. +[setup yours] + +git checkout main + +git checkout main +echo "toothbrush sharing" > project-ideas +git add . +git commit -m "First idea" + +echo "Is my phone upside down? App" >> project-ideas +git commit -am "Another idea" + + + +git push friend main + +git branch -u friend/main main + +[setup friend] + + + +[actions friend] + +git tag v1 HEAD~1 + +[win] +# v1 tag in your repo +test "$(git show v1 -s --format=%h)" = "$(git show HEAD~1 -s --format=%h)" + +# v2 tag in your repo +test "$(git show v2 -s --format=%h)" = "$(git show HEAD -s --format=%h)" + + +[win friend] + +# v2 tag in the remote +test "$(git show v2 -s --format=%h)" = "$(git show HEAD -s --format=%h)" diff --git a/levels/it/tags/remove-tag b/levels/it/tags/remove-tag new file mode 100644 index 0000000..98f2800 --- /dev/null +++ b/levels/it/tags/remove-tag @@ -0,0 +1,53 @@ +title = Removing tags +cards = checkout commit-auto merge reset-hard + +[description] + +You added way too many tags? No prob! Delete them with + + git tag -d + +Remove all tags in this repo! + +--- +tipp1 +--- +tipp2 +--- +tipp3 + +[setup] + +echo "event 1" > feature-list + +git add . +git commit -m "Adding feature 1" + +echo "event 2" >> feature-list + +git add . +git commit -m "Adding feature 2" + +echo "event 3" >> feature-list + +git add . +git commit -m "Adding feature 3" + +git tag v1 HEAD~2 +git tag v2 HEAD~1 +git tag v3 + +git checkout --detach main + +[win] + +# Did you remove all tags? +test "$(git tag -l | wc -l)" -eq 0 + +[actions] + + + +[congrats] + +Well done :) diff --git a/levels/it/tags/sequence b/levels/it/tags/sequence new file mode 100644 index 0000000..fd63d9c --- /dev/null +++ b/levels/it/tags/sequence @@ -0,0 +1,4 @@ +add-tag +remove-tag +add-tag-later +remote-tag diff --git a/levels/it/unused/checkout b/levels/it/unused/checkout new file mode 100644 index 0000000..9d9c957 --- /dev/null +++ b/levels/it/unused/checkout @@ -0,0 +1,31 @@ +title = Getting the last version +cards = checkout-file + +[description] + +You've been working on your essay for a while. But - ughh! Now your cat walks over your keyboard and "helps you", so now it's all messed up! :/ + +But Git is here to help! To discard all changes your cat made, and go back to the version in the last commit, use `checkout`! + +[setup] + +echo "A" >> essay.txt +git add . +git commit -m "Initial commit" + +echo "B" >> essay.txt +git commit -a -m "Improved version" + +echo "C" >> essay.txt +git commit -a -m "Even better version" + +echo "D" >> essay.txt +git commit -a -m "Marvelous version" + +echo "blarg +blaaaargh" > essay.txt + +[win] + +# Restore the version from the last commit. +cat essay.txt | grep D diff --git a/levels/it/unused/clone b/levels/it/unused/clone new file mode 100644 index 0000000..8459c08 --- /dev/null +++ b/levels/it/unused/clone @@ -0,0 +1,20 @@ +title = Cloning a repo +cards = clone commit-auto pull push + +[description] + +Get your friend's repo using clone, change something, push it back. + +[setup] + +rm -rf .git + +[setup friend] + +echo hi > file +git add . +git commit -m "Initial commit" + +[win friend] + +test "$(git show main:file)" != hi diff --git a/levels/it/unused/commit b/levels/it/unused/commit new file mode 100644 index 0000000..fd84cc9 --- /dev/null +++ b/levels/it/unused/commit @@ -0,0 +1,27 @@ +title = Make a commit \o/ +cards = add reset checkout commit + +[description] + +For practice, make a commit where all files contain an "x"! + +[setup] + +echo a > a +echo x > b +echo x > c +git add . +git commit -m "Initial commit" +echo x > a +echo b > b +git add b +echo c > c + +[win] + +# File a contains "x" in the last main commit. +test "$(git show main:a)" = x +# File b contains "x" in the last main commit. +test "$(git show main:b)" = x +# File c contains "x" in the last main commit. +test "$(git show main:c)" = x diff --git a/levels/it/unused/commit-a b/levels/it/unused/commit-a new file mode 100644 index 0000000..747a009 --- /dev/null +++ b/levels/it/unused/commit-a @@ -0,0 +1,26 @@ +title = Make a commit, but faster! +cards = add reset checkout commit commit-a + +[description] + +There is a time-saving trick, where instead of a plain `git commit`, you can use + + git commit -a + +This will automatically add all changes you made to local files! Very convenient. + +[setup] + +echo a > a +echo b > b +echo c > c +git add . +git commit -m "Initial commit" +echo x > a +echo x > b +echo x > c + +[win] + +# Make a commit where all files contain "x". +test "$(git show main:a)" = x && test "$(git show main:b)" = x && test "$(git show main:c)" = x diff --git a/levels/it/unused/fetch b/levels/it/unused/fetch new file mode 100644 index 0000000..377355a --- /dev/null +++ b/levels/it/unused/fetch @@ -0,0 +1,35 @@ +title = Fetching from remotes +cards = checkout fetch commit-auto + +[description] + +Here, you already have two remotes configured! You can list them using `git remote`. + +Fetch from both, and look at the suggestions. + +Then, make a new commit on top of your original one that introduces a compromise. + +[setup] + +echo "The bikeshed should be ???" > proposal +git add . +git commit -m "What do you think?" + +[setup friend1] + +git pull yours main +echo "The bikeshed should be green" > proposal +git commit -am "Green" + +[setup friend2] + +git pull yours main +echo "The bikeshed should be blue" > proposal +git commit -am "Blue" + +[win] + +# Your proposal is acceptable for friend1. +git show main:proposal | git grep green +# Your proposal is acceptable for friend2. +git show main:proposal | git grep blue diff --git a/levels/it/unused/files-move b/levels/it/unused/files-move new file mode 100644 index 0000000..8f65710 --- /dev/null +++ b/levels/it/unused/files-move @@ -0,0 +1,27 @@ +title = No sleep required +cards = file-new file-delete file-rename + +[description] + +Actually, you decide that you don't need any sleep. + +Because of that, you won't require a bed, and can build some other piece of furniture from the wood! + + +[setup] + +echo A yellow cupboard with lots of drawers. > cupboard +echo A really big yellow shelf. > shelf +echo A comfortable, yellow bed with yellow cushions. > bed + +[win] + +# Rename the bed into something else, and give it a new description! +NUM_FILES="$(ls | wc -l)" +! test -f bed && test "$NUM_FILES" -ge 3 && ! grep -r "yellow bed" . + +[congrats] + +Neat! It even still looks a bit comfortable! + +You head out, eager for your first lesson at time travel school! diff --git a/levels/it/unused/index-mv b/levels/it/unused/index-mv new file mode 100644 index 0000000..9ff7270 --- /dev/null +++ b/levels/it/unused/index-mv @@ -0,0 +1,26 @@ +title = Rename a file in the next commit +cards = add reset-file checkout-file mv commit + +[description] + +Other times, you might want to rename a file in the next commit. Use + + git mv [file] [new name] + +for that. The effect is very similar as if you had created a copy with a new name, and removed the old version. + +[setup] + +echo a > a +echo SPECIAL > b +echo x > c +git add . +git commit -m "Initial commit" +echo x > a +echo b >> b +git add b + +[win] + +# Make a commit where you rename the file b to "x". +test "$(git ls-tree --name-only main)" = "$(echo -e "a\nc\nx")" diff --git a/levels/it/unused/init b/levels/it/unused/init new file mode 100644 index 0000000..c76daa5 --- /dev/null +++ b/levels/it/unused/init @@ -0,0 +1,14 @@ +title = Welcome! +cards = init + +[description] + + +[setup] + +rm -rf .git + +[win] + +# Again, initialize your time machine! +test -d .git diff --git a/levels/it/unused/pull-push b/levels/it/unused/pull-push new file mode 100644 index 0000000..71ce61d --- /dev/null +++ b/levels/it/unused/pull-push @@ -0,0 +1,51 @@ +title = Helping each other +cards = checkout commit-auto reset-hard pull push + +[description] + +The events and timelines you see are always only what your own time machine knows about! + +Of course, time agents don't have to work alone! Here, your sidekick has already prepared a merge for you! You can use the "pull" card to transfer it to your own time machine. + +Then, add another event on top (what does Sam have for dinner?), and `push` the result, to transfer it back to your sidekick! + +You can only ever manipulate things in your own time machine (the one on the bottom). + +[setup yours] + +echo "Just woke up. Is hungry." > sam +git add . +git commit -m "The beginning" + +git checkout -b pancakes +echo "Had blueberry pancakes with maple syrup for breakfast." > sam +git add . +git commit -m "Pancakes!" + +git checkout -b muesli main +echo "Had muesli with oats and strawberries for breakfast." > sam +git add . +git commit -m "Muesli!" + +git checkout main + +git push -u sidekick main pancakes muesli + +[setup sidekick] + +git checkout main +git merge pancakes +git merge muesli + +echo "Had pancakes with strawberries for breakfast." > sam +git add . +git commit -m "Let's make this breakfast compromise" --author="Sidekick " + +[win sidekick] + +# Below main's parent, there is a rhombus: +git rev-parse main^^ && test "$(git rev-parse main^^1^)" = "$(git rev-parse main^^2^)" + +[congrats] + +In reality, in many cases, a lot of time agents work together to build a really good future together! :) diff --git a/levels/it/unused/remotes-add b/levels/it/unused/remotes-add new file mode 100644 index 0000000..db24b7d --- /dev/null +++ b/levels/it/unused/remotes-add @@ -0,0 +1,33 @@ +title = Adding a remote +cards = checkout + +[description] + +Let's work together with others! Your friend has their own repo at the URL `../friend` - you can add it using + + git remote add [name] [URL] + +where `[name]` is an arbitrary, short name you pick for the remote. + +When you've done that, you can get all commits from that remote using + + git pull friend + +There's a letter for you! + +[setup] + +git remote remove friend + +[setup friend] + +echo "I'm really committed to our friendship! <3" > love_letter +git add . +git commit -m "Write a letter" + +[win] + +# Add a remote that points to ../friend. +git remote -v | grep '../friend' +# Pull from the remote. +git show HEAD:love_letter | grep committed diff --git a/levels/it/unused/remotes-delete b/levels/it/unused/remotes-delete new file mode 100644 index 0000000..c026204 --- /dev/null +++ b/levels/it/unused/remotes-delete @@ -0,0 +1,23 @@ +title = Deleting and renaming a remote +cards = checkout + +[description] + +Here, you already have two remotes configured! You can list them using `git remote`. + +[setup] + +git remote rename friend frend + +[setup friend] + +[setup enemy] + +[win] + +# Rename the remote with the typo (using `git remote rename [old name] [new name]`) +git remote | grep friend +# The remote with the typo is gone. +! grep 'frend' <(git remote) +# Delete the remote you don't want to keep (using `git remote remove [remote]`) +! grep 'enemy' <(git remote) diff --git a/levels/it/unused/restore b/levels/it/unused/restore new file mode 100644 index 0000000..1f9961e --- /dev/null +++ b/levels/it/unused/restore @@ -0,0 +1,28 @@ +title = Looking into the past +cards = checkout-from + +[description] + +You've been working on your essay for a while. But you're not happy with the changes you've made recently. You want to go back to the version called "Best version"! + +No problem, you can use the `checkout` card to restore your essay from an older commit! + +[setup] + +echo "Initial version" > essay.txt +git add . +git commit -m "Initial commit" + +echo "Improved version" > essay.txt +git commit -a -m "Improved version" + +echo "Best version" > essay.txt +git commit -a -m "Best version" + +echo "Less-good version" > essay.txt +git commit -a -m "Less-good version" + +[win] + +# For nostalgic reasons, restore the very first backup you made! +diff essay.txt <(echo "Best version") diff --git a/levels/it/unused/split b/levels/it/unused/split new file mode 100644 index 0000000..274b792 --- /dev/null +++ b/levels/it/unused/split @@ -0,0 +1,26 @@ +title = Split a commit! +cards = checkout commit reset-hard reset add rebase-interactive rebase-continue show + +[description] + +Here, both changes happened in one commit! Split them to be in two commits instead. + +[setup] + +echo something > file1 +echo something else > file2 +git add . +git commit -m "Initial commit" + +echo this should happen first >> file1 +echo and this should happen after that >> file2 +git commit -am "Both together" + +echo this is some other change >> file1 +echo this is some other change >> file2 +git commit -am "Something else" + +[win] + +test "$(git diff-tree --no-commit-id --name-status -r main^)" = "M file2" && +test "$(git diff-tree --no-commit-id --name-status -r main~2)" = "M file1" diff --git a/levels/it/unused/steps b/levels/it/unused/steps new file mode 100644 index 0000000..cb84861 --- /dev/null +++ b/levels/it/unused/steps @@ -0,0 +1,23 @@ +title = One step after another +cards = checkout commit reset-hard add + +[description] + +Sometimes, you might want to record the order in which things changed, instead of making a single commit. + +What happened here? Make two commits from the changes (using the "add" card), in an order that makes sense! + +[setup] + +echo something > file1 +echo something else > file2 +git add . +git commit -m "Initial commit" + +echo this should happen first >> file1 +echo and this should happen after that >> file2 + +[win] + +test "$(git diff-tree --no-commit-id --name-status -r main)" = "M file2" && +test "$(git diff-tree --no-commit-id --name-status -r main^)" = "M file1" diff --git a/levels/it/unused/who-are-you b/levels/it/unused/who-are-you new file mode 100644 index 0000000..f06137c --- /dev/null +++ b/levels/it/unused/who-are-you @@ -0,0 +1,23 @@ +title = Nice to meet you! +cards = config-name config-email + +[description] + +Introduce yourself using + + git config --global user.name Firstname + git config --global user.email "your@mail.com" + +[setup] + +[actions] + +test "$(git config user.name)" != "You" && hint "Hey $(git config user.name), nice to meet you!" + +[win] + +# Have a name configured. +test "$(git config user.name)" != "You" + +# Have an email address configured. +test "$(git config user.email)" != "you@time.agency" diff --git a/levels/it/workflows/gitignore b/levels/it/workflows/gitignore new file mode 100644 index 0000000..dc55243 --- /dev/null +++ b/levels/it/workflows/gitignore @@ -0,0 +1,18 @@ +title = Ignoring files + +[description] + +That chicken is running around a lot, and changing often. We don't want to have it in our commits. + +Add it to the file .gitignore, and try using `git add .`! + +[setup] + +touch .gitignore +echo important > important +git add important +git commit -m "Initial commit" + +[actions] + +echo "$RANDOM" > chicken diff --git a/levels/it/workflows/pr b/levels/it/workflows/pr new file mode 100644 index 0000000..1c72510 --- /dev/null +++ b/levels/it/workflows/pr @@ -0,0 +1,25 @@ +title = Cloning a repo +cards = clone commit-auto reset-hard checkout file-new branch + +[description] + +Your friend has a problem! Clone the repo, create a branch called "solution", and fix the problem in this branch. When you're ready, make a "Pull Request" by using `git tag pr`. + +[setup] + +rm -rf .git + +[setup friend] + +echo "2 + 3 = " > file +git add . +git commit -m "Initial commit" + +[actions friend] + +git ls-remote yours | grep pr && git fetch yours && git merge yours/solution +git show main:file | grep 5 && hint "Thanks!" + +[win friend] + +git show main:file | grep 5 diff --git a/levels/it/workflows/sequence b/levels/it/workflows/sequence new file mode 100644 index 0000000..1be11a8 --- /dev/null +++ b/levels/it/workflows/sequence @@ -0,0 +1 @@ +pr diff --git a/resources/cards.json b/resources/cards.json index 79b51ca..d284b4e 100644 --- a/resources/cards.json +++ b/resources/cards.json @@ -2,181 +2,289 @@ { "id": "init", "command": "git init", - "description": "Drag this card into the empty space above to initialize the time machine!" + "description": { + "en": "Drag this card into the empty space above to initialize the time machine!", + "it": "Trascina questa carta nell'area vuota sopra per inizializzare la macchina del tempo" + } }, { "id": "clone", "command": "git clone ../[remote] .", - "description": "Create your own copy of someone else's repo." + "description": { + "en": "Create your own copy of someone else's repo.", + "it": "Crea la copia del Repo di qualcun'altro." + } }, { "id": "config-name", "command": "git config --global user.name [string]", - "description": "Set your name.\n\n(Will not change anything outside of this game.)" + "description": { + "en": "Set your name.\n\n(Will not change anything outside of this game.)", + "it": "Imposta il tuo nome.\n\n(Non cambierà nulla all'infuori del gioco.)" + } }, { "id": "config-email", "command": "git config --global user.email [string]", - "description": "Set your email address." + "description": { + "en": "Set your email address.", + "it": "Imposta la tua e-mail." + } }, { "id": "checkout", "command": "git checkout [commit, ref]", - "description": "Drag this card to a commit or to a branch to travel to it!" + "description": { + "en": "Drag this card to a commit or to a branch to travel to it!", + "it": "Sposta questa carta su un commit o su una branch per viaggiare in quello specifico momento." + } }, { "id": "checkout-file", "command": "git checkout [file]", - "description": "Reset changes in a local file." + "description": { + "en": "Reset changes in a local file.", + "it": "Resetta i cambiamenti in un file locale" + } }, { "id": "checkout-from", "command": "git checkout [commit, ref] [file]", - "description": "Get the file contents from the specified commits, and reset both the working directory, as well as the index, to it." + "description": { + "en": "Get the file contents from the specified commits, and reset both the working directory, as well as the index, to it.", + "it": "Prendi il contenuto del file da uno specifico commits e resetta sia la directory di lavoro sia l'indice." + } }, { "id": "commit-a", "command": "git commit -a", - "description": "Make a new commit, after automatically adding all changes to the index.\nYou'll be asked to enter a short description of what you changed." + "description": { + "en": "Make a new commit, after automatically adding all changes to the index.\nYou'll be asked to enter a short description of what you changed.", + "it": "Crea un nuovo commit, dopo aver aggiunto automatico tutti i cambiamenti all'indice.|n Ti verrà chiesto di inserire una piccola descrizione di cosa hai cambiato." + } }, { "id": "commit-auto", "command": "git add .; git commit", - "description": "Make a new commit containing your current environment! Type in a description of what changed!" + "description": { + "en": "Make a new commit containing your current environment! Type in a description of what changed!", + "it": "Crea un nuovo commit contenente il tuo ambiente attuale! Scrivi nella descrizione di cosa hai cambiato" + } }, { "id": "merge", "command": "git merge [commit, ref]", - "description": "Merge the specified timeline into yours. If necessary, will create a merge commit." + "description": { + "en": "Merge the specified timeline into yours. If necessary, will create a merge commit.", + "it": "Unisce la linea temporale specificata con la tua. Se necessario, creerà un commit di unione." + } }, { "id": "merge-abort", "command": "git merge --abort", - "description": "Abort the current merge attempt, and reconstruct the previous state." + "description": { + "en": "Abort the current merge attempt, and reconstruct the previous state.", + "it": "Interrompe il corrente tentativo di unione e ricostruisce lo stato precedente." + } }, { "id": "rebase", "command": "git rebase [commit]", - "description": "Put the events in your current timeline on top of the specified one." + "description": { + "en": "Put the events in your current timeline on top of the specified one.", + "it": "Mette l'evento nella tua, corrente, linea temporale sopra a quello specificato." + } }, { "id": "pull", "command": "git pull", - "description": "Get someone else's version of the current timeline, and try to merge it into yours." + "description": { + "en": "Get someone else's version of the current timeline, and try to merge it into yours.", + "it": "Prende la versione corrente della line temporale di qualcun'altro e prova ad unirla alla tua." + } }, { "id": "fetch", "command": "git fetch [remote]", - "description": "Get a someone else's version of the current timeline." + "description": { + "en": "Get a someone else's version of the current timeline.", + "it": "Prende la versione, corrente, della linea temporale di qualcun'altro" + } }, { "id": "push", "command": "git push", - "description": "Give the current timeline to someone else." + "description": { + "en": "Give the current timeline to someone else.", + "it": "Invia la corrente line temporale a qualcun'altro" + } }, { "id": "rebase-interactive", "command": "git rebase -i [commit]", - "description": "Make changes to the events in your current timeline, back to the commit you drag this to." + "description": { + "en": "Make changes to the events in your current timeline, back to the commit you drag this to.", + "it": "Apporta modifiche agli eventi nella tua timeline corrente, torna al commit su cui lo trascini " + } }, { "id": "rebase-continue", "command": "git rebase --continue", - "description": "Continue the current rebasing process." + "description": { + "en": "Continue the current rebasing process.", + "it": "Continua il corrente processo di ricostruzione" + } }, { "id": "reset-hard", "command": "git reset --hard [commit]", - "description": "Move the branch you're on to the specified commit." + "description": { + "en": "Move the branch you're on to the specified commit.", + "it": "Muove il tuo branch (ramo) nello specifico commit." + } }, { "id": "reset", "command": "git reset [commit]", - "description": "Jump to the commit, and update the index. Keep the current environment." + "description": { + "en": "Jump to the commit, and update the index. Keep the current environment.", + "it": "Salta alcommit e aggiorna l'indice. Mette l'ambiente attuale." + } }, { "id": "reset-file", "command": "git reset [file]", - "description": "Reset the index version of a file to the version in the commit you're on." + "description": { + "en": "Reset the index version of a file to the version in the commit you're on.", + "it": "Resetta la versione dell'indice di un file alla versione del commit in cui sei" + } }, { "id": "cherry-pick", "command": "git cherry-pick [commit]", - "description": "Repeat the specified action on top of your current timeline." + "description": { + "en": "Repeat the specified action on top of your current timeline.", + "it": "Ripete l'azione specificata sulla corrente linea temporale" + } }, { "id": "revert", "command": "git revert [commit]", - "description": "Make a new commit that reverts the changes of the speicified commit." + "description": { + "en": "Make a new commit that reverts the changes of the speicified commit.", + "it": "Crea un nuovo commit che annula le modifiche dello specifico commit" + } }, { "id": "bisect-start", "command": "git bisect start", - "description": "Start looking for the commit where things got bad." + "description": { + "en": "Start looking for the commit where things got bad.", + "it": "Inizia a cercare il commit dove le cose sono andate storte" + } }, { "id": "bisect-good", "command": "git bisect good", - "description": "State that the current commit is good! When you're automatically transferred, keep playing the `good` and `bad` cards!" + "description": { + "en": "State that the current commit is good! When you're automatically transferred, keep playing the `good` and `bad` cards!", + "it": "Dichiara che l'attuale commit è buono! Continua a giocare con le carte `buone` e `cattive`!" + } }, { "id": "bisect-bad", "command": "git bisect bad", - "description": "State that the current commit is bad! When you're automatically transferred, keep playing the `good` and `bad` cards!" + "description": { + "en": "State that the current commit is bad! When you're automatically transferred, keep playing the `good` and `bad` cards!", + "it": "Dichiara che l'attuale commit non è valido! Continua a giocare con le carte `buone` e `cattive`!" + } }, { "id": "add", "command": "git add [file]", - "description": "Update the index version of the file to its current real content." + "description": { + "en": "Update the index version of the file to its current real content.", + "it": "Aggiorna l'indice del file con il, corrente, reale contenuto." + } }, { "id": "rm", "command": "git rm [file]", - "description": "Delete a file both in the working directory, as well as the index." + "description": { + "en": "Delete a file both in the working directory, as well as the index.", + "it": "Cancella il file sia dalla corrente directory che dall'indice." + } }, { "id": "commit", "command": "git commit", - "description": "Make a commit from the current index." + "description": { + "en": "Make a commit from the current index.", + "it": "Crea un commit dal corrente indice." + } }, { "id": "show", "command": "git show [commit]", - "description": "Show what changed in the commit." + "description": { + "en": "Show what changed in the commit.", + "it": "Mostra cosa è cambiato nel commit." + } }, { "id": "branch", "command": "git branch [string]", - "description": "Create a new branch at your current location." + "description": { + "en": "Create a new branch at your current location.", + "it": "Crea un nuovo branch (ramo) nella tua corrente posizione." + } }, { "id": "branch-delete", "command": "git branch -D [ref]", - "description": "Delete a branch." + "description": { + "en": "Delete a branch.", + "it": "Cancella un branch.(ramo)" + } }, { "id": "reflog", "command": "git reflog [ref, head]", - "description": "Display a log of where the ref pointed to in the past." + "description": { + "en": "Display a log of where the ref pointed to in the past.", + "it": "Mostra un log di dove il riferimento puntava in passato" + } }, { "id": "file-new", "command": "touch [string]", - "description": "Create a new file." + "description": { + "en": "Create a new file.", + "it": "Crea un nuovo file." + } }, { "id": "file-delete", "command": "rm [file]", - "description": "Delete a file." + "description": { + "en": "Delete a file.", + "it": "Cancella un file." + } }, { "id": "file-rename", "command": "mv [file] [string]", - "description": "Rename a file." + "description": { + "en": "Rename a file.", + "it": "Rinomina un file." + } }, { "id": "file-copy", "command": "cp [file] [string]", - "description": "Make a copy of a file." + "description": { + "en": "Make a copy of a file.", + "it": "Crea una copia di un file." + } } ] diff --git a/scenes/cards.gd b/scenes/cards.gd index 4448d6c..d68dc02 100644 --- a/scenes/cards.gd +++ b/scenes/cards.gd @@ -3,6 +3,7 @@ extends Control var card_store = {} var cards var card_radius = 1500 +var lang = "it" # TODO: Make a global variable to setting dir and cards localizations func _ready(): load_card_store() @@ -16,7 +17,7 @@ func _process(_delta): func load_card_store(): card_store = {} - var cards_json = JSON.parse(helpers.read_file("res://resources/cards.json")).result + var cards_json = JSON.parse(helpers.read_file("res://resources/cards_i18n.json")).result for card in cards_json: card_store[card["id"]] = card @@ -39,7 +40,7 @@ func draw_card(card): new_card.id = card["id"] new_card.command = card["command"] - new_card.description = card["description"] + new_card.description = card["description"][lang] new_card.energy = 0 #card.energy new_card.position = Vector2(rect_size.x, rect_size.y*2) add_child(new_card) diff --git a/scenes/title.tscn b/scenes/title.tscn index 8363e84..f919e19 100644 --- a/scenes/title.tscn +++ b/scenes/title.tscn @@ -72,6 +72,7 @@ text = "Quit" position = Vector2( 967.924, 306.066 ) scale = Vector2( 0.320895, 0.320895 ) texture = ExtResource( 2 ) + [connection signal="pressed" from="VBoxContainer/Button" to="." method="levels"] [connection signal="pressed" from="VBoxContainer/Button3" to="." method="sandbox"] [connection signal="pressed" from="VBoxContainer/Button2" to="." method="quit"]