mirror of
https://github.com/git-learning-game/oh-my-git.git
synced 2025-05-01 20:42:01 +02:00
Rename "intro" to "high-level" and "internals" to "low-level"
This commit is contained in:
parent
09c54bfbdd
commit
79d89de5e7
27 changed files with 0 additions and 0 deletions
26
levels/low-level/basics
Normal file
26
levels/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/low-level/blob-create
Normal file
38
levels/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/low-level/blob-remove
Normal file
27
levels/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/low-level/commit-create
Normal file
37
levels/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/low-level/commit-parents
Normal file
31
levels/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/low-level/commit-rhombus
Normal file
30
levels/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
|
43
levels/low-level/conflict
Normal file
43
levels/low-level/conflict
Normal file
|
@ -0,0 +1,43 @@
|
|||
[description]
|
||||
|
||||
(This is not a real puzzle yet.)
|
||||
|
||||
Try merging the two branches together!
|
||||
|
||||
git merge <otherbranch>
|
||||
|
||||
[setup]
|
||||
|
||||
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
|
||||
|
||||
[setup goal]
|
||||
|
||||
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
|
||||
|
||||
git merge alternative
|
||||
echo blue-green > bikeshed_color
|
||||
git add .
|
||||
git commit -m "Merge"
|
||||
git prune
|
37
levels/low-level/index-add
Normal file
37
levels/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/low-level/index-remove
Normal file
29
levels/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/low-level/index-update
Normal file
33
levels/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
|
44
levels/low-level/puzzle-apocalypse
Normal file
44
levels/low-level/puzzle-apocalypse
Normal file
|
@ -0,0 +1,44 @@
|
|||
[description]
|
||||
|
||||
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
|
||||
|
||||
[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/low-level/puzzle-precious-blob
Normal file
28
levels/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/low-level/puzzle-trees-all-the-way-down
Normal file
34
levels/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/low-level/ref-create
Normal file
42
levels/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/low-level/ref-move
Normal file
41
levels/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/low-level/ref-remove
Normal file
41
levels/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/low-level/sequence
Normal file
18
levels/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/low-level/symref-create
Normal file
21
levels/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/low-level/symref-no-deref
Normal file
46
levels/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/low-level/tree-create
Normal file
35
levels/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/low-level/tree-nested
Normal file
38
levels/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/low-level/tree-read
Normal file
51
levels/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/low-level/welcome
Normal file
33
levels/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 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
|
||||
|
||||
[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
|
Loading…
Add table
Add a link
Reference in a new issue