Rename chapters into "internals" and "intro"

This commit is contained in:
Sebastian Morr 2020-10-01 15:03:02 +02:00
parent 70fe577788
commit f3ff5c5147
97 changed files with 0 additions and 0 deletions

View file

@ -0,0 +1,3 @@
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. :)

View file

@ -0,0 +1,8 @@
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"!

View file

@ -0,0 +1,3 @@
mkdir riddle
echo "ppl p" > riddle/consonants
echo "ae ie" > riddle/vowels

View file

@ -0,0 +1 @@
cat answer | grep -i "apple \\?pie"

View file

@ -0,0 +1,5 @@
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? :)

View file

@ -0,0 +1,11 @@
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!

View file

@ -0,0 +1,6 @@
echo "Hi" > file1
echo "Ho" > file2
echo "Hu" > file3
git hash-object -w file1
git hash-object -w file2
git hash-object -w file3

View file

View file

@ -0,0 +1,3 @@
BLOB_COUNT=$(git cat-file --batch-check='%(objectname) %(objecttype)' --batch-all-objects | grep blob | wc -l)
test "$BLOB_COUNT" -gt 2

View file

@ -0,0 +1,3 @@
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.

View file

@ -0,0 +1,5 @@
There's a simple command to remove all objects that are not referenced by anything:
git prune
Remove all blobs in this repository.

View file

@ -0,0 +1 @@
git prune

View file

@ -0,0 +1,3 @@
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

View file

@ -0,0 +1,3 @@
OBJECT_COUNT=$(git cat-file --batch-check='%(objectname) %(objecttype)' --batch-all-objects | wc -l)
test "$OBJECT_COUNT" -eq 0

View file

@ -0,0 +1,9 @@
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!

View file

@ -0,0 +1 @@
git commit-tree 3185 -m 'Clever commit message'

View file

@ -0,0 +1,6 @@
touch empty_file
git add .
git write-tree
rm empty_file
git update-index --remove empty_file

View file

@ -0,0 +1,3 @@
COMMIT_COUNT=$(git cat-file --batch-check='%(objectname) %(objecttype)' --batch-all-objects | grep commit | wc -l)
test "$COMMIT_COUNT" -gt 0

View file

@ -0,0 +1,7 @@
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?

View file

@ -0,0 +1,4 @@
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/')

View file

View file

@ -0,0 +1,10 @@
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

View file

@ -0,0 +1,5 @@
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.

View file

@ -0,0 +1,5 @@
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)

View file

View file

@ -0,0 +1,10 @@
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

View file

@ -0,0 +1,5 @@
(This is not a real puzzle yet.)
Try merging the two branches together!
git merge <otherbranch>

View file

@ -0,0 +1,5 @@
git merge alternative
echo blue-green > bikeshed_color
git add .
git commit -m "Merge"
git prune

View file

@ -0,0 +1,12 @@
echo ? > bikeshed_color
git add .
git commit -m "Initial commit"
echo green > bikeshed_color
git commit -a -m "My suggestion"
git switch -c alternative HEAD^
echo blue > bikeshed_color
git commit -a -m "This is way better"
git switch main

View file

@ -0,0 +1,9 @@
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! :)

View file

@ -0,0 +1,10 @@
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"?

View file

@ -0,0 +1,4 @@
echo "file 1" > file1
echo "file 2" > file2
echo "file 3" > file3
git add .

View file

View file

@ -0,0 +1 @@
test "$(git ls-files | wc -l)" -ge 3

View file

@ -0,0 +1,5 @@
To remove an entry from the index, use a command like this:
git update-index --force-remove <file>
Remove all entries from the index!

View file

@ -0,0 +1,3 @@
git update-index --force-remove file1
git update-index --force-remove file2
git update-index --force-remove file3

View file

@ -0,0 +1,4 @@
echo "file 1" > file1
echo "file 2" > file2
echo "file 3" > file3
git add .

View file

@ -0,0 +1 @@
test "$(git ls-files | wc -l)" -eq 0

View file

@ -0,0 +1,9 @@
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!

View file

@ -0,0 +1,2 @@
echo "new content" > file1
git update-index file1

View file

@ -0,0 +1,4 @@
echo "file 1" > file1
echo "file 2" > file2
echo "file 3" > file3
git add .

View file

@ -0,0 +1,2 @@
# 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

View file

@ -0,0 +1,9 @@
Delete all objects in this repository using git commands only!
Useful commands:
git prune
git fsck
git reflog expire
Note: I'm not sure how to beat this level. :D

View file

@ -0,0 +1,4 @@
TREE=$(git mktree)
git read-tree $TREE
rm -rf .git/refs/*
rm -rf .git/objects/*

View file

@ -0,0 +1,9 @@
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"

View file

@ -0,0 +1 @@
test "$(git cat-file --batch-check --batch-all-objects | wc -l)" -eq 0

View file

@ -0,0 +1 @@
Create two trees pointing to the same blob!

View file

@ -0,0 +1,7 @@
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

View file

@ -0,0 +1,10 @@
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"

View file

@ -0,0 +1,3 @@
Construct a chain of three trees, which don't point to anything else.
This is hard! The `git mktree` command might be useful.

View file

@ -0,0 +1,3 @@
git mktree
TREE=$(echo -e "040000 tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\tdir" | git mktree)
echo -e "040000 tree $TREE\tdir" | git mktree

View file

@ -0,0 +1,18 @@
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

View file

@ -0,0 +1,9 @@
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!

View file

@ -0,0 +1,4 @@
git update-ref refs/a $BLOB1
git update-ref refs/b $BLOB2
git update-ref refs/c $TREE
git update-ref refs/d $COMMIT

View file

@ -0,0 +1,7 @@
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")

View file

@ -0,0 +1,3 @@
OBJECTS=$(git cat-file --batch-check='%(objectname)' --batch-all-objects | sort)
REF_TARGETS=$(git show-ref -s | sort | uniq)
test "$OBJECTS" = "$REF_TARGETS"

View file

@ -0,0 +1,5 @@
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!

View file

@ -0,0 +1,3 @@
for REF in $(git for-each-ref --format='%(refname)'); do
git update-ref "$REF" "$TREE"
done

View file

@ -0,0 +1,10 @@
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"

View file

@ -0,0 +1 @@
test "$(git show-ref -s | sort -u)" = "c7863f72467ed8dd44f4b8ffdb8b57ca7d91dc9e"

View file

@ -0,0 +1,5 @@
And finally, to delete a ref, use
git update-ref -d refs/<refname>
Delete all refs! :P (Well, except for HEAD. HEAD is special.)

View file

@ -0,0 +1,3 @@
for REF in $(git for-each-ref --format='%(refname)'); do
git update-ref -d "$REF"
done

View file

@ -0,0 +1,10 @@
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"

View file

@ -0,0 +1 @@
test "$(git show-ref | wc -l)" -eq 0

18
levels/internals/sequence Normal file
View 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

View file

@ -0,0 +1,7 @@
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"!

View file

@ -0,0 +1,3 @@
BLOB=$(git hash-object -w --stdin)
git update-ref refs/double "$BLOB"
git symbolic-ref refs/rainbow refs/double

View file

View file

@ -0,0 +1 @@
git symbolic-ref refs/rainbow

View file

@ -0,0 +1,13 @@
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!

View file

@ -0,0 +1,7 @@
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?

View file

@ -0,0 +1 @@
git update-ref --no-deref refs/fries "$BLOB2"

View file

@ -0,0 +1,4 @@
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

View file

@ -0,0 +1,2 @@
git symbolic-ref refs/fries && return 1
test "$(git show-ref -s refs/fries)" = "035e2968dafeea08e46e8fe6743cb8123e8b9aa6"

View file

@ -0,0 +1,3 @@
Nice!
Can you make a different tree? Modify the index, then call `git write-tree` again!

View file

@ -0,0 +1,7 @@
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! :)

View file

@ -0,0 +1 @@
git write-tree

View file

@ -0,0 +1,4 @@
echo "file 1" > file1
echo "file 2" > file2
echo "file 3" > file3
git add .

View file

@ -0,0 +1 @@
git cat-file -p 21a638f28022064c1f1df20844278b494d197979

View file

@ -0,0 +1,5 @@
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.

View file

@ -0,0 +1,7 @@
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

View file

View file

@ -0,0 +1,16 @@
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

View file

@ -0,0 +1,7 @@
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!

View file

@ -0,0 +1 @@
git read-tree "$TRIPLE_TREE"

View file

@ -0,0 +1,15 @@
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"

View file

@ -0,0 +1 @@
test "$(git ls-files | wc -l)" -gt 0

View file

@ -0,0 +1,7 @@
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!)

View file

@ -0,0 +1,7 @@
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 on the right 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

View file

@ -0,0 +1 @@
git init

View file

@ -0,0 +1 @@
rm -rf .git

View file

@ -0,0 +1 @@
test -d .git