Add change 19 to instructions
This commit is contained in:
parent
d00a7817ab
commit
817f38f49c
90
how_to/Change_19.md
Normal file
90
how_to/Change_19.md
Normal 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.
|
Loading…
Reference in New Issue
Block a user