Rework levels, and add some new ones

This commit is contained in:
Sebastian Morr 2020-09-15 22:35:14 +02:00
parent 931e9b3beb
commit 35b4dc098a
47 changed files with 163 additions and 39 deletions

View file

@ -9,3 +9,7 @@ Let's create some blobs! To do that, create a file with the desired content, and
The flag -w means "write", and tells Git to actually write the new blob to the disk.
Create three new blobs!
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

View file

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

1
levels/blob-remove/goal Normal file
View file

@ -0,0 +1 @@
git prune

3
levels/blob-remove/start Normal file
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

3
levels/blob-remove/win Normal file
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

@ -2,4 +2,6 @@ When using the commit-tree command, you can optionally specify a parent:
$ git commit-tree <tree> -m "Description" -p <parent commit>
Can you make a string of three commits? We placed an empty tree in your repo to get you started.
Make a string of three commits!
Hint: What would be the easiest way to get a tree object?

View file

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

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

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 permissions.
Even though it is possible to add an entry directly to the index, it's much more convenient to do it 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"?

4
levels/index-add/goal Normal file
View file

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

0
levels/index-add/start Normal file
View file

1
levels/index-add/win Normal 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!

3
levels/index-remove/goal Normal file
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 .

1
levels/index-remove/win Normal file
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!

2
levels/index-update/goal Normal file
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 .

1
levels/index-update/win Normal file
View file

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

View file

@ -1,9 +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.
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 start a ref's name with "refs/"! That's a convention that helps Git find all refs you create.
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,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!

3
levels/ref-move/goal Normal file
View file

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

10
levels/ref-move/start Normal file
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"

1
levels/ref-move/win Normal file
View file

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

View file

@ -0,0 +1,5 @@
To delete a ref, use
git update-ref -d refs/<refname>
Delete all refs! :P

3
levels/ref-remove/goal Normal file
View file

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

10
levels/ref-remove/start Normal file
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"

1
levels/ref-remove/win Normal file
View file

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

View file

@ -4,6 +4,4 @@ When that happens, they are called "symbolic refs". You can create or update a s
git symbolic-ref <name> <ref>
Usually, you will only encounter a special symbolic ref called "HEAD". This ref is special in that it doesn't start with "refs/"! You might already have seen it in the other levels - it's the only thing that's always there!
Try pointing HEAD to the refs in this repository!
Create a symbolic ref called "refs/rainbow"!

View file

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

View file

@ -1,4 +0,0 @@
TREE=$(git write-tree)
COMMIT=$(git commit-tree $TREE -m "Initial commit")
git update-ref refs/best_commit $COMMIT
git update-ref refs/worst_commit $COMMIT

View file

@ -1 +1 @@
git symbolic-ref HEAD | grep _commit
git symbolic-ref refs/rainbow

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 && exit 1
test "$(git show-ref -s refs/fries)" = "035e2968dafeea08e46e8fe6743cb8123e8b9aa6"

View file

@ -1,21 +1,7 @@
Blobs just have some content and an ID. It would be convenient to be able to give names to a blob, right?
After carefully building the index we want, it would be nice to save a permanent snapshot of it, right?
The second type of Git object is called a "tree" - a tree points a bunch of blobs, and gives each of them a name!
How can we build our own trees? Introducing: the index!
The index is like a "work in progress" version of a tree, where we can add and remove blobs. When we're happy with what we have built, we can make it into a real tree object!
We can use
$ git update-index --add <file>
to make the file's content into a blob, and to add that blob to the index using the file's name.
When we're happy with out index, we can convert it into a tree using
This is what the second type of objects is for: trees! You can convert the index into a tree using
$ git write-tree
That command will output the ID of the newly crated tree.
Build a tree containing just the 'noises' file, which we conveniently placed in your current directory! :)
Try it! :)

View file

@ -1,5 +1 @@
git update-index --add noises
git write-tree
rm noises
git update-index --remove noises

View file

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

View file

@ -1 +1 @@
git cat-file -p c099013035060f20e8d8f5db69e158213fbec519
git cat-file -p 21a638f28022064c1f1df20844278b494d197979

View file

@ -1,5 +1,5 @@
Trees can also point to other trees! This way, they can basically describe nested directory structures.
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 automatically do the correct thing.
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, and to a tree that points to two blobs.
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,5 @@
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>
Try switching between the trees in this repository!

1
levels/tree-read/goal Normal file
View file

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

15
levels/tree-read/start Normal file
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"

1
levels/tree-read/win Normal file
View file

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

View file

@ -0,0 +1,7 @@
To the left, you see an empty Git repository!
The only thing that's always there is the HEAD reference - we'll look at what that is later.
You can drag and drop all nodes on the left, and reorder them how you want!
You can enter Bash commands in the line at the bottom.