notes on git

Now that I moved my blog to github-pages generated by octopress, using git is the only way to make progress. Rather than digressing about my experiences @migrating from wordpress to octopress- I would rather keep this post as my notes on git


getting started

I find it interesting to learn by jumping-in rather than reading tons of volumes first. To get started you need to let git know that you are going to be setting a repository, this needs to happen in a particular directory- which contains stuff you want to manage. The command is:

1
2
git init .
Initialized empty Git repository in /home/sarangb/learn-git/.git/

Once an empty git repo is initialized now the next step is to add some content for git to track. Lets start by adding a a script that fetches weather information (just for fun). Once I have the script the next step is to let git know about it.

1
2
git add *.sh
git commit -m 'initial commit'

This will add all sh files to git, this is two step process:

  1. git add will add the files to a staging area, more on that shortly
  2. git commit will commit files in git repo

Now that scripts have been added. Say you need to tweak the script and recommit the changes.

1
2
git add *.sh
git commit -m "tweaked scripts"

From git's perspective following stages occur:

  1. you started with an unmodified file, that’s the unmodified stage in pic below
  2. you edited the scripts, git tracks filesystem for changes- that’s the modified stage
  3. you add file back- your intent is to eventually commit this file- but you haven’t done that yet. Thus staged stage
  4. on commit, git takes the staged changes and commits them

Here is an image from git scm book, which explains how the files are tracked by git very well:

git various stages


checking status of files

We have been creating and committing things. With projects you will generally have lot more files that just one/two files. And generally I keep an eye on what is modified and what is staged- to get a better picture of what things have changed and what I am committing. That’s where git status comes into picture. For eg: this blog post hasn’t been committed yet so if i do git status on my blog root directory I get:

1
2
3
4
5
6
7
8
9
$ git status
On branch master
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:   source/_posts/2015-05-16-notes-on-git.markdown

no changes added to commit (use "git add" and/or "git commit -a")

Few things to note here:

  1. I am on master branch
  2. git tells me I have modified the file and can undo with git checkout command or stage with git add command

Let’s add a few more files to our scripts directory to go with- namely the pic for git-lifecycle and a README.md Now we have two newly added files and a staged file, git would generate something like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
$ git status
On branch master

Initial commit

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

	new file:   weather.sh

Untracked files:
  (use "git add <file>..." to include in what will be committed)

	README.md
	lifecycle.png

 


tracking & staging files

Now that we know new files have to be added for tracking (and are staged) and modified files have to be added as well (for staging). However one of the things to note is git add command stages file based on it’s contents and at that time.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
$ git add --all
$ git status
On branch master

Initial commit

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

	new file:   README.md
	new file:   lifecycle.png
	new file:   weather.sh
    
$ git commit -m "initial commit"

Let’s add a few things to README.md and then stage the file. After staging file you realize you missed a few thingsi to note so tweak README.md again and if you check git status now you will see:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
$ vim README.md
// tweak a few things
$ git add README.md
// again tweak a few things
$ vim README.md
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	modified:   README.md

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:   README.md

You thought you already staged the file- which you did but that was at earlier point of time, contents have changed. Thing to note is git tracks/stages file based on contents, it internally generates SHA1 HASH. The right way to fix things is to re-stage the README.md file and then commit. Simplest way is to use command git add --all, this will take care of adding/removing/stating files- but it does for everything. For managing what get’s staged you should setup .gitingore file- so you can neglect generated/intermediate stuff that you don’t care about tracking.

 


gitconfig

helps customize your global git settings or just for your repo. Here is list of configs for my local repo. Most common config is to provide for user.name and user.email and a few more tweaks. Even signing commits is a config.

From git-scm:

First, a quick review: Git uses a series of configuration files to determine non-default behavior that you may want. The first place Git looks for these values is in the system-wide /etc/gitconfig file, which contains settings that are applied to every user on the system and all of their repositories. If you pass the option –system to git config, it reads and writes from this file specifically.

The next place Git looks is the ~/.gitconfig (or ~/.config/git/config) file, which is specific to each user. You can make Git read and write to this file by passing the –global option.

Finally, Git looks for configuration values in the configuration file in the Git directory (.git/config) of whatever repository you’re currently using. These values are specific to that single repository, and represent passing the –local option to git config. If you don’t specify which level you want to work with, this is the default.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
git config --list
core.repositoryformatversion=0
core.filemode=false
core.bare=false
core.logallrefupdates=true
remote.origin.url=https://github.com/sarangbaheti/blog.git
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
branch.source.remote=origin
branch.source.merge=refs/heads/source
user.name=Sarang Baheti
user.email=**********@users.noreply.github.com

to display entire list of what you can git config --list & press tab

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
git config --list
Display all 324 possibilities? (y or n)
add.ignoreErrors                color.status.nobranch           diff.mnemonicprefix             http.lowSpeedLimit              pack.window 
advice.amWorkDir                color.status.remoteBranch       diff.noprefix                   http.lowSpeedTime               pack.windowMemory 
advice.commitBeforeMerge        color.status.unmerged           diff.renameLimit                http.maxRequests                pager.
advice.detachedHead             color.status.untracked          diff.renames                    http.minSessions                pretty.
advice.ignoredHook              color.status.updated            diff.statGraphWidth             http.noEPSV                     pull.octopus 
advice.implicitIdentity         color.ui                        diff.submodule                  http.postBuffer                 pull.twohead 
advice.pushAlreadyExists        commit.cleanup                  diff.suppressBlankEmpty         http.proxy                      push.default 
advice.pushFetchFirst           commit.gpgSign                  diff.tool                       http.sslCAInfo                  push.followTags 
advice.pushNeedsForce           commit.status                   diff.wordRegex                  http.sslCAPath                  rebase.autosquash 
advice.pushNonFFCurrent         commit.template                 difftool.                       http.sslCert                    rebase.stat 
advice.pushNonFFMatching        commit.verbose                  difftool.prompt                 http.sslCertPasswordProtected   receive.autogc 
advice.pushUpdateRejected       core.abbrev                     fetch.recurseSubmodules         http.sslCipherList              receive.denyCurrentBranch 
advice.resolveConflict          core.askpass                    fetch.unpackLimit               http.sslKey                     receive.denyDeleteCurrent 
advice.rmHints                  core.attributesfile             format.attach                   http.sslVerify                  receive.denyDeletes 
advice.statusHints              core.autocrlf                   format.cc                       http.sslVersion                 receive.denyNonFastForwards 
advice.statusUoption            core.bare                       format.coverLetter              http.useragent                  receive.fsckObjects 
alias.                          core.bigFileThreshold           format.from                     i18n.commitEncoding             receive.unpackLimit 

 


ignoring files

not every file needs to be tracked in git repository. With C++ compilers generate tons of obj/archives and other temp files which we don’t care about to track. to let git know about what not to track you should. For eg: I was checking Rakefile in blog root dir for pushing stuff to git as part of deploy. I have it open in vim and made a few changes. vim being vim will create a temporary Rakefile.swp, which I don’t care about, so is ideal candidate for ignoring. Here we go about ignoring things: :)

A quick check on my blog root directory says these files are set to be ignored

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
$ more .gitignore 
.bundle
.DS_Store
.sass-cache
.gist-cache
.pygments-cache
_deploy
public
sass.old
source.old
source/_stash
source/stylesheets/screen.css
vendor
node_modules
Gemfile.lock

to add more to it i would vim .\.gitingore and add *.swp at the end and that’s it. You can add files/directories to .gitignore and it should just work fine. Here are a few rules to keep in mind, source

The rules for the patterns you can put in the .gitignore file are as follows:

  1. Blank lines or lines starting with # are ignored.
  2. Standard glob patterns work.
  3. You can end patterns with a forward slash (/) to specify a directory.
  4. You can negate a pattern by starting it with an exclamation point (!).

 


viewing changes

Now that things have been set, it’s time start checking what changes are there before committing or staging. This is one of my most common workflow. I keep on staging and diffing files- love reviewing my own code :)

I just added *.swp to .gitignore, if i do git diff command with no arguments it will tell me about all the files that have changed and the differences:

1
2
3
4
5
6
7
8
9
diff --git a/.gitignore b/.gitignore
index f8a6236..8f85703 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ source/stylesheets/screen.css
 vendor
 node_modules
 Gemfile.lock
+*.swp

git diff shows you diffs in what is in your working directory vs staging area- so it highlights changes that have been made since you last staged the file. git diff takes in a lots of parameters but one of the most common is to see what you are committing and can be checked with --staged options. I have staged the .gitignore file so this is what i will be committing

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ git add .gitignore
$ git diff --staged
diff --git a/.gitignore b/.gitignore
index f8a6236..8f85703 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ source/stylesheets/screen.css
 vendor
 node_modules
 Gemfile.lock
+*.swp

In case you want to see list of files that have been staged the command would be:

1
2
3
4
$ git diff --name-only --staged
.gitignore
Gemfile
Rakefile

 


committing

we have been committing changes all this while, this is the last in the various stages we saw earlier. You can directly issue git commit command it will open an editor for you to enter comments, vim for me. In case you have terse comments you can bypass this with -m option with git commit as we have done already.

1
2
3
$ git commit -m "updated .gitignore"
[master 2e55fe1] updated .gitignore
 1 file changed, 1 insertion(+)

git commit commits files that have been staged- at times with small changes two step process is a bit too much. I often make small changes and commit often. For this you can pass -a flag with commit command, would have worked the same

1
2
3
$ git commit -am "updated .gitignore"
[master 2e55fe1] updated .gitignore
 1 file changed, 1 insertion(+)

 


removing files

This is a infrequent task and I would be careful with it. But comes in very handy when you have got un-neccessary files staged/committed. To remove the files from staging command would work.

1
git rm --cached README.md

I would ask you to refer git-scm book for details

 


statistics and history

I have been committing things as I wrote this post. To check the commit history git log is the command:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
$ git log
commit 2e55fe118981f2ca4ee05d91c302b6f6441b8782
Author: sarangb <---@---.com>
Date:   Sat May 16 12:55:02 2015 -0700

    updated .gitignore

commit 39b672252189e9053b05e7cd606a80b5497fae15
Author: sarangb <---@---.com>
Date:   Sat May 16 11:53:00 2015 -0700

    added drafts

commit 41f41aaf3a0fee665ddaa8d1d8a09f5c8e13f4f5
Author: sarangb <---@---.com>
Date:   Sat May 16 11:34:06 2015 -0700

Or I can check in more details what was committed, -p shows difference in each commit and i am limiting it to last commit with 1 as option

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
$ git log -p -1
commit 2e55fe118981f2ca4ee05d91c302b6f6441b8782
Author: sarangb <---@---.com>
Date:   Sat May 16 12:55:02 2015 -0700

    updated .gitignore

diff --git a/.gitignore b/.gitignore
index f8a6236..8f85703 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ source/stylesheets/screen.css
 vendor
 node_modules
 Gemfile.lock
+*.swp

commit 39b672252189e9053b05e7cd606a80b5497fae15

For a brief summary I could alternatively have passed --stat option. Or to generate a list of commits i have done so far with this blog- but in a bit less verbose and human readable form i would do:

1
2
3
4
5
6
7
$ git log --pretty=format:"%h - %an, %ad : %s"
2e55fe1 - sarangb, Sat May 16 12:55:02 2015 -0700 : updated .gitignore
39b6722 - sarangb, Sat May 16 11:53:00 2015 -0700 : added drafts
41f41aa - sarangb, Sat May 16 11:34:06 2015 -0700 : few more updates
9b002b5 - sarangb, Sat May 16 10:17:26 2015 -0700 : fixed build script
06a024f - sarangb, Sat May 16 10:09:43 2015 -0700 : updated read me for source
...

Options to pretty=format are:

    1.  `%h` - will give you abbreviated commit hash
    2.  `%an` - will give you author-name
    3.  `%ad` - will give you the commit date-time
    4.  `%s` - subject or short description from commit message

For complete list of options visit this page

 


in the end of beginning

this is just scatching the surface of what git provides- there is still lot to explore:

  1. branching
  2. merging
  3. various tools and tweaks
  4. local server hosting, not sure if i will ever want to get into this

 

Additional details (click to expand..)

Well, this is most of the stuff I need to get started with git and octopress. Here are few resources that came in handy:

  1. git-scm docs
  2. git-scm book
  3. github-git-cheat-sheet
  4. stack-overflow

 
git