- 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.