These notes assume you’re familiar with the basic functions of Git.
The Git repository exists entirely in a single “.git” directory in your project root. Objects in Git are identified by hashes.
- Blobs – Binary representation of a file.
- Tree objects – Similar to directories. Points to blobs and other tree objects.
- Similar to a directory with a list of files (blogs) and other tree objects (sub-directories)
- The root tree object has the “big picture.” It is basically a snapshot of your repository.
- If the root directory of your project contains a file (test-file.txt) and a directory (test-dir). Test-dir has a file called test-code.c:
- Tree 831da3
- test-dir (Tree 92ads31)
- test-file.txt (Blob 12asd391)
- Tree 92ads31
- test-code.c (Blob 931bac3e)
- Tree 831da3
- Commit objects – Pointer to a single tree object
- Commits are basically lightweight snapshots of the code at that point in time. Compression could be used. Also only the changes between revisions are tracked as to keep it compact.
- Contains (run “git show –format=raw”)
- Hash of the root tree object at the time of commit
- Hash of parent commits (history of commits)
- Author’s name/email
- Committer’s name/email
- Commit message
- In the above example, the commit object has a hash of FF450. You typically only need the first 4 letters of of the hash to find the object.
- Tag objects – Points to a single commit object, contains metadata.
- References – Maps to a hash so you don’t have to memorize hashes. Points to a single object. Typically a Commit or Tag object.
- .git/refs/heads
- The “master” branch is a ref. The file contains the hash.
- git show –oneline master
- git rev-parse master
- The reference HEAD points to the end of the current branch rather than the commit.
- cat .git/HEAD will point to the refs/heads/master file.
- When pointing to a commit object, you will be in a “detached HEAD state” which means you’re not on a branch.
- .git/refs/heads
- Branches
- They are basically pointers to a specific commit. That’s it.
- Branches are just references. You could find them under .git/refs/heads/[branch name]
- Initially this will point the current commit object.
- When you make a commit on a branch, it simply just changes the current branch (.git/HEAD) to point to the newly created commit object.
- Local Branches
- Remote-tracking Branches
- Tags
- Tags are primarily use to label revisions at specific commit points. Unlike branches, they are immutable references.
- You shouldn’t change or delete a tag once release publicly.
- .get/refs/tags
- By default tags created with ‘git tag [name of tag]’ is just a lightweight tag. It is only a reference to the commit object. Run ‘git cat-file -p [name of tag]’ to see that it’s not a tag object.
- Annotated tags on the other hand could be created with ‘git tag -a -m “[message]” [name of tag]
- Have their own author information. Who created the tag?
- This is a tag object
- Contains pointer to commit
- Tag message
- Timestamped to help keep track of release dates
- Information about the tagger
- Can be signed with a GPG key to prevent commits or email spoofing
- Use git show to see what’s contained within a tag.
- Merging
- We create branches when we need to add a new feature or fix a bug.
- The way Git handles merges behind the scenes isn’t intuitive.
git init echo "index file" > index.html git add index.html git commit -am "first commit" git checkout -b new-feature echo "new feature file" > new-feature.html git add new-feature.html git commit -am "add new feature" git checkout master git checkout -b bug-fix vim index.html (modify the file) git commit -am "fix bug"
Running the above you’ll have three branches: master, new-feature, and bug-fix.
git checkout master git log --oneline d0da44b first commit git checkout new-feature git log --oneline 33d9fc5 add new feature d0da44b first commit git checkout bug-fix git log --oneline b8bd4cc fix the bug d0da44b first commit
- Now let’s try to merge the bug-fix branch into the master.
- Fast-forward means that the commits in bug-fix were upstream from master. Git will simply move the pointer up the tree to bug-fix.
- Now let’s try to merge the new-feature branch into the master.
- We’ll notice here, Git didn’t perform a fast-forward. The reason being, the new-feature branch isn’t upstream from master (remember master was pointing to the same level of the tree as new-feature).
- The merge creates a new commit object. This commit object has two parents. This is called a merge commit. This is particularly useful in code review. When a developer pushes a feature branch out for review, the reviewer could create the merge commit which contains metadata on the reviewers and useful comments.
- If you want only fast-forward merges, you could rebase the branch before the merge.
- Remotes
- Recall that Git stores the entire repository under the .git directory. The entire history could be traversed and the a snapshot of the project could be built.
- A Git remote is just another Git repository.
- Git only needs a working tree to find out which changes have been made since the last commit.
- A bare repository only contains the project’s history without requiring a working tree. This is where collaborators could push and pull from.
- The master branch doesn’t exist yet.
- bare is set to “true”
- You cannot add files to a bare repository. It is meant to be cloned, pulled, and pushed to/from.
- Clone
- Push
- The master branch now exists.
- Specify the location of the repository you want to push to (origin) and the branch (master).
- Generally you don’t want to run a naked “git push” because you may push all remote-tracking branches. There usually isn’t a problem with this but you run into the chance of pushing changes you don’t want other collaborators to pull.
- Push updated the remote’s master to point to afd2 and the afd2 commit object as well as any tree or blob objects related to that change.
- Remote-Tracking Branches
- From .git/config, a remote-tracking branch is the line following [branch “master”]
- This means the configuration is for the current local master branch.
- The “remote” and “merge” configurations specifies when this branch is fetched, it should fetch the master branch from the origin remote. A local copy of the remote branch is stored.
- Fetch
- git fetch – updates your remote-tracking branches under /refs/remote/origin, it doesn’t change your local branches under /refs/head
- [remote “origin”]
- This is a mapping of remote references to local references. All references at the remote origin is placed locally in refs/remotes/origin/*
- fetch does not create a local branch because you may not want it in your local repository.
- If you want to create a local branch, run “git checkout new-feature”
- Git will automatically create the new-feature reference (which points to the same commit as the remote new-feature branch).
- Git will also create a remote-tracking branch entry under .git/config
- Pull
- Very similar to git clone. git pull <remote> <branch>
- git fetch <remote>
- Uses .git/FETCH_HEAD to figure out if <branch> has a remote-tracking branch that should be merged.
- git merge if required
- Git overrides the contents of FETCH_HEAD every time you run “git fetch”
- To demonstrate this, clone the repository and modify the README file on the new-feature branch. After you’ve finished, go back to the original clone.
- Here we perform a manual “git pull.” By looking at .get/refs/heads/new-feature and FETCH_HEAD, we know there were changes in the new-feature branch. We simply perform a merge on FETCH_HEAD to apply the changes.
- Very similar to git clone. git pull <remote> <branch>
great tutorial!