Git Useful Know-how

Dafna Rosenblum
Extend

--

When I first started with Git, I knew three commands:

git add
git commit
git push

So if I wanted my code on a remote source control, I had to run the three of them. I really didn’t want things to get messy, cause I didn’t know how to fix it and it seemed a bit complicated. Well — as with many things in this world, it’s not complicated at all once you have experience with it.

git rebase

The first tool I want to show is rebase. To use it on a specific amount of past commits, run:

git rebase -i @~5

Where as 5 is the number of commits I want to edit. Then one of two things happen:

  1. If I have unstaged files (also visible when running git status, in red, below the text Changes not staged for commit, I will have to either commit them or stash them, by running git stash . I will elaborate on stash later in this post, but to stash a change mean to put it aside, saved but unseen.
  2. Otherwise, an interactive mode will open with the list of last x commits and below them, an explanation about how to use the interactive mode:
pick 4d276af text of commit number -4
pick bb91ebf text of commit number -3
pick 4571e5c text of commit number -2
pick 1d98ed1 text of last commit
# Rebase 2f2f574..1d98ed1 onto 2f2f574 (4 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit

What can we do in this so-useful-mode:

  • Change the order of commits by changing the orders of lines (using dd to cut and p to paste below the line we stand on (vim command mode).
  • Edit commit message by changing the word pick at the beginning of the commit line to reword which will, after saving and exiting the interactive mode, open the commit text for edit.
  • Squash a commit to the commit above it by changing pick to f.
  • And more actions I won’t get into in this post.

git add -p

Another very awesome tool is git add -p, which is used to control the part of the file we want to commit. So in case I make a few changes and want each one of them to be part of a different commit, I will run git add -p index.html, and this will output:

diff --git a/index.html b/index.html
index bf78977..813d376 100755
--- a/index.html
+++ b/index.html
@@ -36,7 +36,7 @@
</head>
<body>
-
+ <div></div>
<nav id="mainNav" class="navbar">
<div class="container-fluid">

Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]?

So now I can type y if I want this chunk to be part of my next commit, n if I don’t want, and have the next chunk to decide about. I can also type s to split the chunk to a smaller chunk or e to manually edit, if the chunk is still mixed. All these commands and more are available if you type ?.

git status

If things go wrong and need fix, many times the instructions will be available in the git status output. For example: If I accidentally added a file and I don’t want to commit it, if I run git status, part of the output will be:

Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: blah

So to unstage I just need to follow the instructions and run git reset HEAD blah. If I change file and want to revert the change and not commit it, then running git status will output:

Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: index.html

And I can run git checkout -- index.html to go back to the committed version of the file.

git stash

Running git stash will keep all the unstaged changes aside, and you can bring them back whenever you want by running git stash pop for the last stashed item, or git stash pop stash@{4} for a specific stashed item (here with ID 4). The list of stashed items can be displayed by running git stash list, and each line contains the branch where the stashed item was on, and the last commit of this branch before the changes. The stash is common to all branches.

git log

I have some aliases defined in a .gitconfig file under my home directory, and the greatest is:

ld = log — pretty=format:\”%h %ad | %s%d [%an]\” — graph — date=short

So when I run git ld -5 I can see the last 5 commits, and their hashes, and then choose hash and run git show 85124b0 (example hash) and see the details of this commit. This is also a good place to remind of git diff blah, which allows watching the changes in a file before adding it.

Working with branches

So much easier than one could imagine, run git checkout -b new-branch-name to create a new branch and check it out. to go back to the original branch: git checkout master.

To merge the branch to master, update (git pull) master, checkout the branch, run git rebase master, which will bring all the new commits of master below the added commits of the branch, then checkout master, run git merge --ff-only new-branch-name. The flag ff-only will abort the merge in case there are conflicts or issues with adding the new commits on top of the pulled commits of master.

Commit message

One last thing, more related to conventions than to how git works: the commit message. Using present simple, all text at length of less than 80 characters, writing a title, leaving an empty line, and then elaborating on the research process in the body. These are the best practices I’ve encountered.

Practice

So here’s a light exercise to practice all this:

  1. Create and configure a new repo in Github.
  2. Create a new branch.
  3. Add a file to it with a function that counts to ten (commit with an indicative message, and push).
  4. Edit the file and add another different function that counts to 20. Commit and push.
  5. Now lets refactor: add to the file a function that counts to x (x is the input of the function), and change the two first functions to use the new function.
  6. Use add -p to create two additional commits: one with the new function, and one with the changes in the first two functions.
  7. Use rebase -i to delete to first two commits and replace them with the new ones.
  8. Use rebase and merge to merge the changes to master, and push to master.
  9. Pat yourself on the shoulder!

Extra recommended reading

cheatsheet, awesome interactive tutorial, git book

--

--