Here's a simple way to think of git as a data structure rather than a magic box (not using any particular syntax):
history: Map<Hash, Commit>
Commit {
changes: Diff,
previous: Hash,
}
branches: Map<String, Hash>
Notice that it's pretty much a hashmap and a linked list put together. Each commit knows the changes that it applies and the hash of the commit that came before it. The history hashmap lets you look up any commit (and therefore, the entire "chain" of it's predecessors in the linked list). The branches are just a way to assign names to hashes.
Suppose you're starting to work on a new feature, FEATURE-0001. You make a
new branch off of master and start working. Your history will probably look
something like this:
Note: To make a graph like this, I use an aliased command glgg (git log graph),
defined like this:
alias glgg="git log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)' --all"
* 8441e8b - (60 seconds ago) FEATURE-0001: Add tests for edge cases - Nick Mosher (HEAD -> FEATURE-0001)
* cb7ff76 - (67 seconds ago) FEATURE-0001: Fix some edge cases - Nick Mosher
* d790b4d - (75 seconds ago) FEATURE-0001: Begin adding tests - Nick Mosher
* 6ebc387 - (82 seconds ago) FEATURE-0001: Start implementation - Nick Mosher
* 2a7545d - (3 days ago) Merge pull request #5 from nmosher/SBGDATA-18205 - Nick Mosher (master)
When you finish your feature, you go to merge everything into master, only to discover that somebody else merged their work first, meaning your work will have a merge conflict. History now looks something like this:
* 00fee6b - (4 seconds ago) Merge branch 'FEATURE-0002' - Nick Mosher (HEAD -> master)
|\
| * 220a42c - (71 seconds ago) FEATURE-0002: Add tests for edge cases - Nick Mosher (FEATURE-0002)
| * 975a800 - (79 seconds ago) FEATURE-0002: Fix some edge cases - Nick Mosher
| * b17a988 - (87 seconds ago) FEATURE-0002: Begin adding tests - Nick Mosher
| * e2bfffa - (2 minutes ago) FEATURE-0002: Start implementation - Nick Mosher
|/
| * 8441e8b - (4 minutes ago) FEATURE-0001: Add tests for edge cases - Nick Mosher (FEATURE-0001)
| * cb7ff76 - (5 minutes ago) FEATURE-0001: Fix some edge cases - Nick Mosher
| * d790b4d - (5 minutes ago) FEATURE-0001: Begin adding tests - Nick Mosher
| * 6ebc387 - (5 minutes ago) FEATURE-0001: Start implementation - Nick Mosher
|/
* 2a7545d - (3 days ago) Merge pull request #5 from nmosher/SBGDATA-18205 - Nick Mosher
When looking at a graph like this, I like to think of every |/ as a "branch base",
and every |\ as a "branch merge". The problem in this example is that the base of
your FEATURE-0001 branch does not point to the current master (it points to where
master was when you started). To fix this, we have to "rebase", i.e. we need to move
the branch base up to the current commit where master is.
When you rebase, make sure you have your feature branch checked out. Then, the name
of the branch you give to the rebase command will be the new "branch base", in this
case that will be master.
git checkout FEATURE-0001
git rebase master
At this point, you may have to solve some merge conflicts if there are any. Be sure
to resolve the conflicts, then add each file using git add, then use
git rebase --continue. Repeat this process until all merge conflicts are resolved.
Your history after this will look like this:
* 491bfbd - (1 second ago) FEATURE-0001: Add tests for edge cases - Nick Mosher (HEAD -> FEATURE-0001)
* b11614c - (10 seconds ago) FEATURE-0001: Fix some edge cases - Nick Mosher
* dcc18a1 - (37 seconds ago) FEATURE-0001: Begin adding tests - Nick Mosher
* 865fb04 - (44 seconds ago) FEATURE-0001: Start implementation - Nick Mosher
* 00fee6b - (12 minutes ago) Merge branch 'FEATURE-0002' - Nick Mosher (master)
|\
| * 220a42c - (13 minutes ago) FEATURE-0002: Add tests for edge cases - Nick Mosher (FEATURE-0002)
| * 975a800 - (14 minutes ago) FEATURE-0002: Fix some edge cases - Nick Mosher
| * b17a988 - (14 minutes ago) FEATURE-0002: Begin adding tests - Nick Mosher
| * e2bfffa - (14 minutes ago) FEATURE-0002: Start implementation - Nick Mosher
|/
* 2a7545d - (3 days ago) Merge pull request #5 from nmosher/SBGDATA-18205 - Nick Mosher
Now we're back to having a "healthy" branch. In my mind, a branch is healthy when all of the commits are stacked in a straight line right on top of master. It also means that merging your branch into master will not cause any merge conflicts.
Once your feature is done, we can merge the branch like usual, and it will look like this:
* a6cdd02 - (4 seconds ago) Merge branch 'FEATURE-0001' - Nick Mosher (HEAD -> master)
|\
| * 491bfbd - (4 minutes ago) FEATURE-0001: Add tests for edge cases - Nick Mosher (FEATURE-0001)
| * b11614c - (5 minutes ago) FEATURE-0001: Fix some edge cases - Nick Mosher
| * dcc18a1 - (5 minutes ago) FEATURE-0001: Begin adding tests - Nick Mosher
| * 865fb04 - (5 minutes ago) FEATURE-0001: Start implementation - Nick Mosher
|/
* 00fee6b - (17 minutes ago) Merge branch 'FEATURE-0002' - Nick Mosher
|\
| * 220a42c - (18 minutes ago) FEATURE-0002: Add tests for edge cases - Nick Mosher (FEATURE-0002)
| * 975a800 - (18 minutes ago) FEATURE-0002: Fix some edge cases - Nick Mosher
| * b17a988 - (18 minutes ago) FEATURE-0002: Begin adding tests - Nick Mosher
| * e2bfffa - (18 minutes ago) FEATURE-0002: Start implementation - Nick Mosher
|/
* 2a7545d - (3 days ago) Merge pull request #5 from nmosher/SBGDATA-18205 - Nick Mosher