My notes on git

ยท

7 min read

I began using Git on the very first day I started coding, and it has become a tool I rely on nearly every day. Reflecting on my experience, here are the Git notes I wish I had known earlier.

I will just quickly run through the concept without the "How to do it".

.git folder

If you want to track a project using Git. Run git init at the root folder of that project. Git will create a .git folder where it saves all git-related things there.

You can verify the .git folder is there by running ls -a

To make the project untracked by git, simply delete the .git folder

git commit

When you commit sth, Git will generate a sha key which has 40 characters. This sha is calculated from the contents of change, author, time, and more.

Example: 8784bb991899fe9e20b73984d43ec2265de79f24

You only need the first 7 characters of a sha for git to identify what commit you are referring to (eg: 8784bb9 )

// git log --oneline

83bc0ec (HEAD -> master) Third commit
d969431 Second commit
a8514ed First commit

That explains why if we commit the same things, our sha key is different - since we commit at different times and the author is also different.

Each time you commit, Git saves the entire project - not the changes. Thus, with the sha, you have the assurance that you can recover the entire project as it existed at that specific commit

git config

The first time you set up your git, remember to add your use.name and user.email .

git config --add --global user.name "your-name"
git config --add --global user.email "your-email"

Git comes with a config that is global and local (project) level. And it works like a Javascript Object.assign . The local config will override the global config.

const config = Object.assign({}, globalConfig, localConfig)

The config key is in the following shaped <section>.<key>

Useful commands

git config --list
git config --add <key> "value"
git config --get <key>
git config --unset <key>

Use the flag --global or --local to set the scope.

Example: view all global config git config --list --global

Nothing is magical here as git saves all its config to a file. For example, you can find your local config in the config file in the .git folder.

git merge

A merge is an attempt to combine two histories together that have diverged at some point. The common point between the two is called "merge base"

Let's say we want to merge the foo branch into the master

๐Ÿ‘‰ Example 1: the foo branch and the master is diverged

The A commit here is a merge base.

  B --- C    foo
 /
A --- D --- E master

After the merge

  B --- C --------         foo
 /                 \
A --- D --- E --- ccf9a73  master

Since there are also some changes to the to both master and the foo branch. Git will apply the "3 way merge". From the sets of different commits, git creates a new (merged) commit at the tip of the branch that is being merged.

ccf9a73 here is a merged commit and it contains the changes of B and C. Note that the merged commit would have 2 parents C and E

๐Ÿ‘‰ Example 2:

This time there is no change to the master after we branch out the foo from the E commit. Git then does the fast-forward merge

              X - Y     foo
             /
A --- D --- E           master

After the merge

              X - Y          foo
             /
A --- D --- E --- X --- Y    master

Note that there is no merge commit. Git simply appends the commit X and Y to the tip of the master branch.

git rebase

With the following setup, let's say we are working on the foo branch and someone updates the master. We then want to keep the foo branch updated with the master

  B --- C                  foo
 /
A --- D --- E --- X --- Y  master

We can do the merge. Let's merge the master into our foo branch

  B --- C------------------- ccf9a73   foo
 /                            /
A --- D --- E --- X --- Y ---          master

Git then creates a merged commit ccf9a73 to our foo branch.

As we keep working and syncing the foo branch with the master. Our commit history would look like this

 B --- C --- ccf9a73 --- D --- E --- ccf9a72 --- F

We got "random" merged commits in between our commits!

๐Ÿ‘‰ Let's see if we can do if we use rebase instead of merge

  B --- C                  foo
 /
A --- D --- E --- X --- Y  master

After rebasing

                             B --- C  foo
                         /
A --- D --- E --- X --- Y             master

What rebase does is to checkout the master branch. It then applies one commit at a time to the foo branch (B and C) on top of the master branch. Once finished, it will update the foobranch to the current commit (C).

Now we can update the foo branch with the master WITHOUT adding a random merged commit to our history.

rebase will change the history of our foo branch. If we have a foo public branch in remote, we need to push --force to update it. It's not a good idea to push --force if someone else is also working on that branch.

The problem with rebase is that if you have a conflict with the master branch, every time you rebase, you have to resolve the same conflict again and again. To solve this issue, read more about the config.rerere config.

git squash

Basically, you can squash many commits into one. Example:

ieuwj23 Add 3
f3bcfw3 Add 2
83bc0ec Add 1
d969431 Second commit
a8514ed First commit

We can now squash the Add 1, Add 2 and Add 3 commit into one commit Add 1, 2 and 3

ki32dio Add 1, 2 and 3
d969431 Second commit
a8514ed First commit

The primary use case is that you can commit often. And at the end, we can squash these small commits into one with a proper message to have a nice history.

In case we want to undo the changes, instead of revert 3 commits, we only revert 1.

We can do the squashing using interactive rebases. You can read more about it.

git rebase --i

git bisect

Let's say you found a bug in your code and want to find out the exact commit that caused the bug. And you had 100+ commits.

We can use git log and search for a specific commit based on the commit message. Example: find a commit with the word redirect

git log --grep redirect

It's quite tricky as we may have no idea which keyword we should look for + people do not usually write a good commit message.

The solution: use git bisect . Just google how to use it!

git reset

When you want to get rid of the changes or walk back the commit

soft: it will move back to a commit and alter the staging area to match the content of the commit

it's useful when you need to make a commit that is partially finished and you want to edit that commit and change the contents. Example:

 git reset --soft HEAD~1

hard: will do the same thing as soft except it will drop changes to the staging. That means any work that is being tracked by git will be destroyed

Obviously, this is dangerous as we have just altered the history of a branch

git stash

When you are working on a feature branch foo and you need to switch to another branch for some reason. What would you do with those changes?

  1. you can commit this change and change the branch. Later, edit the commit with the git reset or continue working and squash those commits.

  2. you can stash the changes and change the branch. Later, you switch back to the foo branch and apply the changes from the stash back to the branch.

git stash will take every change tracked by git (change to index + change to work tree) and store that result, much like a commit, into the "stash."

git stash
git stash -m "message here"
git stash list
git stash pop --index <index>
ย