Add change 19 to instructions

This commit is contained in:
David Doblas Jiménez 2024-03-29 18:26:20 +01:00
parent d00a7817ab
commit 817f38f49c
1 changed files with 90 additions and 0 deletions

90
how_to/Change_19.md Normal file
View File

@ -0,0 +1,90 @@
- checkout: Read tree and move HEAD
When given a commit OID, `ugit checkout` will "checkout" that commit, meaning
that it will populate the working directory with the content of the commit and
move HEAD to point to it.
This is a small but important change and it greatly expands the power of ugit in
two ways.
First, it allows us to travel conveniently in history. If we've made a handful
of commits and we would like to revisit a previous commit, we can now "checkout"
that commit to the working directory, play with it (compile, run tests, read
code, whatever we want) and checkout the latest commit again to resume working
where we've left.
You might be wondering why `checkout` is needed when we could just use
`read-tree`, and the answer is that moving HEAD in addition to reading the tree
allows us to record which commit is checked out right now. If we would only use
`read-tree` and later forget which commit we are looking at, we will see a bunch
of files in the working directory and have no idea where they came from. On the
other hand, if we use `checkout`, the commit will be recorded in HEAD and we can
always know what we're looking at (by running `ugit log` for example and seeing the first entry).
The second way by which `checkout` expands the power of ugit is by allowing
multiple branches of history. Let me explain: So far we have set HEAD to point
to the latest commit that was created. It means that all our commits were
linear, each new commit was added on top of the previous. The `checkout`
command now allows us to move HEAD to any commit we wish. Then, new commits will
be created on top of the current HEAD commit, which isn't necessarily the last
created commit.
For example, imagine that we're working on some code. So far, we have created a
few commits, represented by a graph:
```
o-----o-----o-----o
^ ^
first commit HEAD
```
Then we wanted to code a new feature. We created a few commits while working on
the feature (new commits represented by @):
```
o-----o-----o-----o-----@-----@-----@
^ ^
first commit HEAD
```
Now we have an alternative idea for implementing that feature. We would like to
go back in time and try a different implementation, without throwing away the
current implementation. We can remember the current HEAD and run `ugit checkout`
to go back in time, by providing the OID of the commit before the new feature
was implemented (that OID can be discovered with `ugit log`).
```
o-----o-----o-----o-----@-----@-----@
^ ^
first commit HEAD
```
The working directory will effectively go back in time. We can start working on
an alternative implementation and create new commit. The new commits will be on
top of HEAD and look like this (represented by $):
```
o-----o-----o-----o-----@-----@-----@
^ \
first commit ----$-----$
^
HEAD
```
See how the history now contains two "branches". We can actually switch back and
forth between them and work on them in parallel. Finally, we can checkout the
preferred implementation and work from it on future code. Assuming that we liked
the second branch, we'll just keep working from it, and future commits will look
like this:
```
o-----o-----o-----o-----@-----@-----@
^ \
first commit ----$-----$-----o-----o-----o-----o-----o
^
HEAD
```
Pretty useful, right? We've just introduced a simple form of branching history.
Note that something pretty cool happened here: The implementation of checkout is
very simple (we just call `read_tree` and update HEAD) but the implications of
checkout are quite big - we can suddenly have a branching workflow which might
look complicated but it is actually a direct consequence of what we implemented
in previous changes. This is why I believe learning Git internals from the
bottom up is useful - we can see how simple concepts compose into complicated
functionality.