mirror of
https://github.com/git-learning-game/oh-my-git.git
synced 2024-11-13 19:04:54 +01:00
Add split description in italia and english and add directory it in levels
This commit is contained in:
parent
943126647b
commit
2c275424a3
104 changed files with 3510 additions and 38 deletions
43
levels/it/bisect/bisect
Normal file
43
levels/it/bisect/bisect
Normal file
|
@ -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...
|
1
levels/it/bisect/sequence
Normal file
1
levels/it/bisect/sequence
Normal file
|
@ -0,0 +1 @@
|
||||||
|
bisect
|
44
levels/it/branches/branch-create
Normal file
44
levels/it/branches/branch-create
Normal file
|
@ -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!
|
47
levels/it/branches/branch-remove
Normal file
47
levels/it/branches/branch-remove
Normal file
|
@ -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? :)
|
43
levels/it/branches/checkout-commit
Normal file
43
levels/it/branches/checkout-commit
Normal file
|
@ -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...
|
65
levels/it/branches/fork
Normal file
65
levels/it/branches/fork
Normal file
|
@ -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...
|
49
levels/it/branches/grow
Normal file
49
levels/it/branches/grow
Normal file
|
@ -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"
|
89
levels/it/branches/reorder
Normal file
89
levels/it/branches/reorder
Normal file
|
@ -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."
|
6
levels/it/branches/sequence
Normal file
6
levels/it/branches/sequence
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
checkout-commit
|
||||||
|
fork
|
||||||
|
branch-create
|
||||||
|
grow
|
||||||
|
branch-remove
|
||||||
|
reorder
|
86
levels/it/changing-the-past/rebase
Normal file
86
levels/it/changing-the-past/rebase
Normal file
|
@ -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.
|
78
levels/it/changing-the-past/reorder
Normal file
78
levels/it/changing-the-past/reorder
Normal file
|
@ -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?
|
2
levels/it/changing-the-past/sequence
Normal file
2
levels/it/changing-the-past/sequence
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
rebase
|
||||||
|
reorder
|
32
levels/it/files/files-add
Normal file
32
levels/it/files/files-add
Normal file
|
@ -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?
|
36
levels/it/files/files-delete
Normal file
36
levels/it/files/files-delete
Normal file
|
@ -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!
|
3
levels/it/files/sequence
Normal file
3
levels/it/files/sequence
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
files-delete
|
||||||
|
files-add
|
||||||
|
|
38
levels/it/index/add
Normal file
38
levels/it/index/add
Normal file
|
@ -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!
|
31
levels/it/index/change
Normal file
31
levels/it/index/change
Normal file
|
@ -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"
|
25
levels/it/index/checkout
Normal file
25
levels/it/index/checkout
Normal file
|
@ -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
|
51
levels/it/index/compare
Normal file
51
levels/it/index/compare
Normal file
|
@ -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"
|
28
levels/it/index/new
Normal file
28
levels/it/index/new
Normal file
|
@ -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"
|
37
levels/it/index/reset
Normal file
37
levels/it/index/reset
Normal file
|
@ -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
|
24
levels/it/index/rm
Normal file
24
levels/it/index/rm
Normal file
|
@ -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
|
5
levels/it/index/sequence
Normal file
5
levels/it/index/sequence
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
compare
|
||||||
|
new
|
||||||
|
change
|
||||||
|
reset
|
||||||
|
steps
|
53
levels/it/index/steps
Normal file
53
levels/it/index/steps
Normal file
|
@ -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
|
27
levels/it/intro/cli
Normal file
27
levels/it/intro/cli
Normal file
|
@ -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.
|
32
levels/it/intro/commit
Normal file
32
levels/it/intro/commit
Normal file
|
@ -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".
|
42
levels/it/intro/copies
Normal file
42
levels/it/intro/copies
Normal file
|
@ -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
|
24
levels/it/intro/init
Normal file
24
levels/it/intro/init
Normal file
|
@ -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!
|
||||||
|
|
52
levels/it/intro/remote
Normal file
52
levels/it/intro/remote
Normal file
|
@ -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
|
42
levels/it/intro/risky
Normal file
42
levels/it/intro/risky
Normal file
|
@ -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
|
6
levels/it/intro/sequence
Normal file
6
levels/it/intro/sequence
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
risky
|
||||||
|
copies
|
||||||
|
init
|
||||||
|
cli
|
||||||
|
commit
|
||||||
|
remote
|
38
levels/it/intro/who-are-you
Normal file
38
levels/it/intro/who-are-you
Normal file
|
@ -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!"
|
26
levels/it/low-level/basics
Normal file
26
levels/it/low-level/basics
Normal file
|
@ -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"
|
38
levels/it/low-level/blob-create
Normal file
38
levels/it/low-level/blob-create
Normal file
|
@ -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 <file>
|
||||||
|
|
||||||
|
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
|
27
levels/it/low-level/blob-remove
Normal file
27
levels/it/low-level/blob-remove
Normal file
|
@ -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
|
37
levels/it/low-level/commit-create
Normal file
37
levels/it/low-level/commit-create
Normal file
|
@ -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 <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
|
31
levels/it/low-level/commit-parents
Normal file
31
levels/it/low-level/commit-parents
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
[description]
|
||||||
|
|
||||||
|
When using the commit-tree command, you can optionally specify a parent:
|
||||||
|
|
||||||
|
git commit-tree <tree> -m "Description" -p <parent commit>
|
||||||
|
|
||||||
|
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
|
30
levels/it/low-level/commit-rhombus
Normal file
30
levels/it/low-level/commit-rhombus
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
[description]
|
||||||
|
|
||||||
|
A commit can have multiple parents! You can specify the -p option multiple times, like this:
|
||||||
|
|
||||||
|
git commit-tree <tree> -m "Description" -p <parent1> -p <parent2>
|
||||||
|
|
||||||
|
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
|
37
levels/it/low-level/index-add
Normal file
37
levels/it/low-level/index-add
Normal file
|
@ -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 <mode>,<blobhash>,<name>
|
||||||
|
|
||||||
|
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
|
29
levels/it/low-level/index-remove
Normal file
29
levels/it/low-level/index-remove
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
[description]
|
||||||
|
|
||||||
|
To remove an entry from the index, use a command like this:
|
||||||
|
|
||||||
|
git update-index --force-remove <file>
|
||||||
|
|
||||||
|
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
|
33
levels/it/low-level/index-update
Normal file
33
levels/it/low-level/index-update
Normal file
|
@ -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 <file>
|
||||||
|
|
||||||
|
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
|
41
levels/it/low-level/puzzle-apocalypse
Normal file
41
levels/it/low-level/puzzle-apocalypse
Normal file
|
@ -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
|
28
levels/it/low-level/puzzle-precious-blob
Normal file
28
levels/it/low-level/puzzle-precious-blob
Normal file
|
@ -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"
|
34
levels/it/low-level/puzzle-trees-all-the-way-down
Normal file
34
levels/it/low-level/puzzle-trees-all-the-way-down
Normal file
|
@ -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
|
42
levels/it/low-level/ref-create
Normal file
42
levels/it/low-level/ref-create
Normal file
|
@ -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/<refname> <newvalue>
|
||||||
|
|
||||||
|
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"
|
41
levels/it/low-level/ref-move
Normal file
41
levels/it/low-level/ref-move
Normal file
|
@ -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/<refname> <object>
|
||||||
|
|
||||||
|
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"
|
41
levels/it/low-level/ref-remove
Normal file
41
levels/it/low-level/ref-remove
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
[description]
|
||||||
|
|
||||||
|
And finally, to delete a ref, use
|
||||||
|
|
||||||
|
git update-ref -d refs/<refname>
|
||||||
|
|
||||||
|
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
|
18
levels/it/low-level/sequence
Normal file
18
levels/it/low-level/sequence
Normal file
|
@ -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
|
21
levels/it/low-level/symref-create
Normal file
21
levels/it/low-level/symref-create
Normal file
|
@ -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 <name> <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
|
46
levels/it/low-level/symref-no-deref
Normal file
46
levels/it/low-level/symref-no-deref
Normal file
|
@ -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"
|
35
levels/it/low-level/tree-create
Normal file
35
levels/it/low-level/tree-create
Normal file
|
@ -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
|
38
levels/it/low-level/tree-nested
Normal file
38
levels/it/low-level/tree-nested
Normal file
|
@ -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
|
51
levels/it/low-level/tree-read
Normal file
51
levels/it/low-level/tree-read
Normal file
|
@ -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 <tree>
|
||||||
|
|
||||||
|
For <tree>, 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
|
33
levels/it/low-level/welcome
Normal file
33
levels/it/low-level/welcome
Normal file
|
@ -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
|
47
levels/it/merge/conflict
Normal file
47
levels/it/merge/conflict
Normal file
|
@ -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!
|
82
levels/it/merge/merge
Normal file
82
levels/it/merge/merge
Normal file
|
@ -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...
|
54
levels/it/merge/merge-abort
Normal file
54
levels/it/merge/merge-abort
Normal file
|
@ -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...
|
2
levels/it/merge/sequence
Normal file
2
levels/it/merge/sequence
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
merge
|
||||||
|
conflict
|
47
levels/it/remotes/friend
Normal file
47
levels/it/remotes/friend
Normal file
|
@ -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
|
33
levels/it/remotes/problems
Normal file
33
levels/it/remotes/problems
Normal file
|
@ -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^)"
|
2
levels/it/remotes/sequence
Normal file
2
levels/it/remotes/sequence
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
friend
|
||||||
|
problems
|
7
levels/it/sandbox/empty
Normal file
7
levels/it/sandbox/empty
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
title = Empty sandbox
|
||||||
|
|
||||||
|
[description]
|
||||||
|
|
||||||
|
This is an empty sandbox you can play around in.
|
||||||
|
|
||||||
|
[setup]
|
22
levels/it/sandbox/remote
Normal file
22
levels/it/sandbox/remote
Normal file
|
@ -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"
|
3
levels/it/sandbox/sequence
Normal file
3
levels/it/sandbox/sequence
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
empty
|
||||||
|
remote
|
||||||
|
three-commits
|
26
levels/it/sandbox/three-commits
Normal file
26
levels/it/sandbox/three-commits
Normal file
|
@ -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!
|
13
levels/it/sequence
Normal file
13
levels/it/sequence
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
intro
|
||||||
|
files
|
||||||
|
branches
|
||||||
|
merge
|
||||||
|
index
|
||||||
|
remotes
|
||||||
|
changing-the-past
|
||||||
|
shit-happens
|
||||||
|
workflows
|
||||||
|
bisect
|
||||||
|
stash
|
||||||
|
tags
|
||||||
|
sandbox
|
30
levels/it/shit-happens/bad-commit
Normal file
30
levels/it/shit-happens/bad-commit
Normal file
|
@ -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"
|
54
levels/it/shit-happens/pushed-something-broken
Normal file
54
levels/it/shit-happens/pushed-something-broken
Normal file
|
@ -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"
|
26
levels/it/shit-happens/reflog
Normal file
26
levels/it/shit-happens/reflog
Normal file
|
@ -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)"
|
22
levels/it/shit-happens/restore-a-file
Normal file
22
levels/it/shit-happens/restore-a-file
Normal 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"
|
21
levels/it/shit-happens/restore-a-file-from-the-past
Normal file
21
levels/it/shit-happens/restore-a-file-from-the-past
Normal file
|
@ -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"
|
5
levels/it/shit-happens/sequence
Normal file
5
levels/it/shit-happens/sequence
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
restore-a-file
|
||||||
|
restore-a-file-from-the-past
|
||||||
|
bad-commit
|
||||||
|
pushed-something-broken
|
||||||
|
reflog
|
5
levels/it/stash/sequence
Normal file
5
levels/it/stash/sequence
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
stash
|
||||||
|
stash-pop
|
||||||
|
stash-clear
|
||||||
|
stash-branch
|
||||||
|
stash-merge
|
45
levels/it/stash/stash
Normal file
45
levels/it/stash/stash
Normal file
|
@ -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! :)
|
48
levels/it/stash/stash-branch
Normal file
48
levels/it/stash/stash-branch
Normal file
|
@ -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 <branchname> <stash>
|
||||||
|
If you just want to use the latest stash entry, you can leave the <stash> 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! :)
|
57
levels/it/stash/stash-clear
Normal file
57
levels/it/stash/stash-clear
Normal file
|
@ -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 <stash>
|
||||||
|
|
||||||
|
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! :)
|
54
levels/it/stash/stash-merge
Normal file
54
levels/it/stash/stash-merge
Normal file
|
@ -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! :)
|
46
levels/it/stash/stash-pop
Normal file
46
levels/it/stash/stash-pop
Normal file
|
@ -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! :)
|
53
levels/it/tags/add-tag
Normal file
53
levels/it/tags/add-tag
Normal file
|
@ -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 <tag-name>
|
||||||
|
|
||||||
|
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 :)
|
50
levels/it/tags/add-tag-later
Normal file
50
levels/it/tags/add-tag-later
Normal file
|
@ -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-name> <commit-hash>
|
||||||
|
|
||||||
|
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 :)
|
58
levels/it/tags/remote-tag
Normal file
58
levels/it/tags/remote-tag
Normal file
|
@ -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 <remote> <tag-name>
|
||||||
|
Or all tags with:
|
||||||
|
git push <remote> --tags
|
||||||
|
|
||||||
|
Deleting tags on your remote works with:
|
||||||
|
git push <remote> --delete <tag-name>
|
||||||
|
|
||||||
|
You can also sync
|
||||||
|
git fetch <remote> --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)"
|
53
levels/it/tags/remove-tag
Normal file
53
levels/it/tags/remove-tag
Normal file
|
@ -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 <tag-name>
|
||||||
|
|
||||||
|
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 :)
|
4
levels/it/tags/sequence
Normal file
4
levels/it/tags/sequence
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
add-tag
|
||||||
|
remove-tag
|
||||||
|
add-tag-later
|
||||||
|
remote-tag
|
31
levels/it/unused/checkout
Normal file
31
levels/it/unused/checkout
Normal file
|
@ -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
|
20
levels/it/unused/clone
Normal file
20
levels/it/unused/clone
Normal file
|
@ -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
|
27
levels/it/unused/commit
Normal file
27
levels/it/unused/commit
Normal file
|
@ -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
|
26
levels/it/unused/commit-a
Normal file
26
levels/it/unused/commit-a
Normal file
|
@ -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
|
35
levels/it/unused/fetch
Normal file
35
levels/it/unused/fetch
Normal file
|
@ -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
|
27
levels/it/unused/files-move
Normal file
27
levels/it/unused/files-move
Normal file
|
@ -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!
|
26
levels/it/unused/index-mv
Normal file
26
levels/it/unused/index-mv
Normal file
|
@ -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")"
|
14
levels/it/unused/init
Normal file
14
levels/it/unused/init
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
title = Welcome!
|
||||||
|
cards = init
|
||||||
|
|
||||||
|
[description]
|
||||||
|
|
||||||
|
|
||||||
|
[setup]
|
||||||
|
|
||||||
|
rm -rf .git
|
||||||
|
|
||||||
|
[win]
|
||||||
|
|
||||||
|
# Again, initialize your time machine!
|
||||||
|
test -d .git
|
51
levels/it/unused/pull-push
Normal file
51
levels/it/unused/pull-push
Normal file
|
@ -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 <sidekick@example.com>"
|
||||||
|
|
||||||
|
[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! :)
|
33
levels/it/unused/remotes-add
Normal file
33
levels/it/unused/remotes-add
Normal file
|
@ -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
|
23
levels/it/unused/remotes-delete
Normal file
23
levels/it/unused/remotes-delete
Normal file
|
@ -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)
|
28
levels/it/unused/restore
Normal file
28
levels/it/unused/restore
Normal file
|
@ -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")
|
26
levels/it/unused/split
Normal file
26
levels/it/unused/split
Normal file
|
@ -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"
|
23
levels/it/unused/steps
Normal file
23
levels/it/unused/steps
Normal file
|
@ -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"
|
23
levels/it/unused/who-are-you
Normal file
23
levels/it/unused/who-are-you
Normal file
|
@ -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"
|
18
levels/it/workflows/gitignore
Normal file
18
levels/it/workflows/gitignore
Normal file
|
@ -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
|
25
levels/it/workflows/pr
Normal file
25
levels/it/workflows/pr
Normal file
|
@ -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
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue