A little over a year ago I wrote my first post on git with some basics. I just started using it and since then I never looked back. A follow-up post of some more basic to intermediate Git operations ...
Git has definitely saved me a lot of time and gave my projects much better structure (you become better at committing (smaller) chunks of code!). I am also not afraid anymore to make changes, which leads to more freedom when coding. In this article, some random things I learned, in a couple of months I will follow up with an "advanced Git" post ...
Branching
Braches are cheap (41 bytes), and it is very convenient to work on bugs, new features, try out ideas in isolation (without touching the main branch aka master).
So first thing I now do when working on something non-trivial:
$ git checkout -b new_branch_name
Which creates and puts you on the new branch at once (longer version: $ git branch new_branch_name && git checkout new_branch_name). To merge? Simple:
# go to receiver branch
# any changes on branch you come from need to be committed
$ git checkout master
# fold the branch in if happy with changes
$ git merge new_branch_name
Best is to merge often, the longer you diverge from master, the more likely you have to solve merge conflicts.
Rebasing
Today I learned about a new concept: rebasing. This basically allows you to clean up history: you can make it appear that all work happened in series, even though it originally happened in parallel. I also understand it allows you to move branches forward in time so that they stay closer to master.
!! Note that you want to do this on local commits only, because you effectively are changing history, don't do this on code that has been shared (pushed to the remote repo). Now that I branch more often, I can see me using this feature ...
Undo commands
Again, something to do before pushing code to a team, but to correct your last commit, easy:
$ git commit --amend
This not only lets you change your commit message, it also allows you to add new files (git add .) which then will be committed into the last commit you are amending.
Sometimes you are not happy with commits before the last one; --amend cannot help you there, but git reset does. You want to be cautious with this command though:
# undo commit, but keep files in changed state
$ git reset --soft tree-ish (e.g. HEAD^)
# undo commit and also delete the changes, as if the change(s) never happened
$ git reset --hard tree-ish (e.g. HEAD^)
# example wiping out last commit:
$ git log -2
commit d4d479a6121d6757dfcf49795dc0d7134be474f6
..
test commit
commit 60cbc505273e6f583f347844069106fb1ffde5eb
..
$ git reset --hard HEAD^
HEAD is now at 60cbc50 make an extra div for flexible content under the video
$ git status
# On branch new_feature
nothing to commit (working directory clean)
See here for a good explanation of the different switches of git reset.
Also handy: to revert uncommited changes just do :
$ git checkout -- file(s)
... and the changes on the file(s) are cancelled. About undoing in git.
Useful tools
There are a lot, just a few handy things I learned in the tool's space:
- Visualization of git history: check out gitk, or create the following alias to graphically see branches etc in the log:
$ git config --global --add alias.lol "log --graph --decorate --pretty=oneline --abbrev-commit --all"
Learn to use a bare repo. Some time ago I made a script that saves time creating remotes on my hosting server.
Use autocomplete and show the current branch in your shell prompt. Configure the following in your shell init file:
# from .bashrc
..
source ~/.git-completion.bash
source ~/.git-prompt.sh
PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
# prompt now shows active branch
[bbelderbos@Bob-Belderboss-MacBook-Pro youtube_feed (master)]$ git checkout -b new_feature
Switched to a new branch 'new_feature'
[bbelderbos@Bob-Belderboss-MacBook-Pro youtube_feed (new_feature)]$
Stashing allows you to store some edits to a stack when you're not ready to commit them and you need to move branches for example (git doesn't allow to switch branch when there are pending changes):
$ git status
# dirty files
$ git stash save
$ git status # = clean
$ git checkout other_branch # pending changes would abort this command, now I can
$ git stash apply stash@{0} # use "pop" to wipe it out of the stack
$ git status # change now shows up in the destination branch I am on
File changes in >1 commits:
$ git add -p
This is a form of interactive committing, by showing hunks of the file (-p stands for --patch mode) that you can add to the commit or not. Below an example. I actually heard about this feature today, and I just tested it writing this blog. The great thing about Git (and Vim for that matter) is the easiness to test out things which give you direct feedback. As you get handier with the commands you see it is easy to undo anything, understanding a small subset of git commands and you can test things out without making a mess, besides, it is hard to really loose work in Git ...
# making two edits to style.css
# 1. adding padding to body
# 2. increasing font-size of p
$ git diff
diff --git a/style.css b/style.css
..
@@ -2,6 +2,7 @@ body {
..
+ padding: 5px;
}
..
+p {
+ font-size: 1.1em;
+}
# add file in --patch / interactive mode, note the "Stage this hunk" questions
# I only stage the first change
$ git add -p
diff --git a/style.css b/style.css
index 88a2b04..13a1311 100644
--- a/style.css
+++ b/style.css
@@ -2,6 +2,7 @@ body {
font : 75%/1.5 "Lucida Grande", Helvetica, "Lucida Sans Unicode", Arial, Verdana, sans-serif;
color:#000; background-color: #f2f2f2;
margin: 0 5px;
+ padding: 5px;
}
a {
color: #900;
Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]? y
@@ -80,4 +81,7 @@ span.inactive {
padding: 2px 6px 2px 6px;
cursor: default;
}
+p {
+ font-size: 1.1em;
+}
Stage this hunk [y,n,q,a,d,/,K,g,e,?]? n
# gs is a bash alias for "git status"
$ gs
..
..
# modified: style.css
# first commit (with 1 of the 2 changes)
$ git commit -m "added padding"
[new_feature 95c7c53] added padding
1 file changed, 1 insertion(+)
# left is the second change (which was not staged with the first commit)
$ git diff style.css
diff --git a/style.css b/style.css
index c0a0a96..13a1311 100644
--- a/style.css
+++ b/style.css
@@ -81,4 +81,7 @@ span.inactive {
padding: 2px 6px 2px 6px;
cursor: default;
}
+p {
+ font-size: 1.1em;
+}
# adding the 2nd change
$ git add .
$ git commit -m "increased font-size"
[new_feature 809e00a] increased font-size
1 file changed, 3 insertions(+)
# changes have gone in two commits
$ git diff HEAD^..HEAD
diff --git a/style.css b/style.css
index c0a0a96..13a1311 100644
--- a/style.css
+++ b/style.css
@@ -81,4 +81,7 @@ span.inactive {
padding: 2px 6px 2px 6px;
cursor: default;
}
+p {
+ font-size: 1.1em;
+}
$ git diff HEAD^^..HEAD^
diff --git a/style.css b/style.css
index 88a2b04..c0a0a96 100644
--- a/style.css
+++ b/style.css
@@ -2,6 +2,7 @@ body {
font : 75%/1.5 "Lucida Grande", Helvetica, "Lucida Sans Unicode", Arial, Verdana, sans-serif;
color:#000; background-color: #f2f2f2;
margin: 0 5px;
+ padding: 5px;
}
a {
color: #900;
Git is a huge subject
... and learning more of it pays off: it saves time and you can improve software quality by making smart commits, isolate work in branches, etc.
It is also a generic skill: you can use it for any project, be it Javascript, PHP, C++, Python, or whatever changes you want to keep track of: a blog, or if you write a book.
I hope this rather random selection of tips has been useful, what other git operations do you often use to improve / speed up your work flow?
$ git add 2013.03.24_git_intermediate.txt
$ git commit -m "git post done"