Article by on April 6, 2012, last modified on May 6, 2014



Adding color is a must:

$ git config --global color.ui true

Prevent Pushing to Master

Option 1: update hook

In the event that you want everyone to be prevented from pushing to master, say if you are using the Integration-Manager workflow and want to implement the policy in Git, you can add a file "update" to the .git/hooks folder that contains the following code:

if [ $USER != "gitadmin" ]
  if [ "$1" == "refs/heads/master" ]
    echo "Manual pushing to this repo is restricted"
    exit 1

Replace "gitadmin" with whatever user you want to allow push access to master. Also, make sure this file is executable. This is a modification of one that I found on ServerFault:

Option 2: set alternate push url in config

The following will prevent pushing to a remote entirely:

$ git remote set-url --push origin no-push

Now, whenever you attempt to push it will give a fatal error:

$ git push
fatal: 'no-push' does not appear to be a git repository
fatal: The remote end hung up unexpectedly


One of Git's most glorious features is branching. git-checkout is how you switch between branches. The default branch is master, so you will see master as the checked out branch by default:

$ git branch
* master

To checkout a remote branch, you can do so in a variety of ways. As Nick Quaranto shows on his blog, the quickest and easiest way to check out a remote branch is:

$ git checkout -t origin/develop

This checks out the branch and tracks it. If you don't track it, then when you use git-pull you will have to specify what branch you are pulling, which I find annoying.

Checkout a Deleted File

1. Find the hash of the delete commit that deleted the file:

$ git log -n 1 -- <file_path>

2. Checkout the deleted file using the commit BEFORE the delete commit:

$ git checkout <delete_commit_hash>^ -- <file_path>

Make a Branch Track a Remote Branch

By default, Git does not assume much about tracking remote branches. The chief example is when you want to check out a remote branch which I showed above. The second example is when you have a branch that you want to be a remote tracking branch. Here are your options:

Option 1: Re-Create the Branch

$ git branch -D my-branch
$ git checkout -t origin/my-branch

Option 2: Set Upstream

$ git branch --set-upstream my-branch origin/my-branch


Option 3: Use the --set-upstream Flag on Git Push

$ git push --set-upstream origin my-branch

or, more simply,

$ git push -u origin my-branch

I believe that the third option is the easiest, and there was at least one instance where Option 2 didn't work and I received the following error:

$ git branch --set-upstream my-branch origin/my-branch
warning: refname 'origin/my-branch' is ambiguous.
fatal: Ambiguous object name: 'origin/my-branch'.

However, using Option 3 worked.

Prune Your Remote-Tracking Branches

Sometimes remote branches are deleted, but your local repo doesn't recognize that they've been deleted. So, assuming that any branch that was deleted remotely you no longer need yourself, you can "prune" them out:

$ git remote prune origin
Pruning origin
 * [pruned] origin/branch1
 * [pruned] origin/branch2
 * [pruned] origin/branch-my-cousin-made

To be clear, this command does NOT delete any branch on the remote, only the branches locally that AREN'T on the remote.

Checkout a GitHub Pull Request


How to Write a Git Commit

Thank you Tim Pope for writing a pattern that makes sense:

Capitalized, short (50 chars or less) summary

More detailed explanatory text, if necessary. Wrap it to about 72
characters or so.  In some contexts, the first line is treated as the
subject of an email and the rest of the text as the body. The blank
line separating the summary from the body is critical (unless you omit
the body entirely); tools like rebase can get confused if you run the
two together.

Write your commit message in the imperative: "Fix bug" and not "Fixed
bug" or "Fixes bug." This convention matches up with commit messages
generated by commands like git merge and git revert.

Further paragraphs come after blank lines.

- Bullet points are okay, too

- Typically a hyphen or asterisk is used for the bullet, preceded by a
  single space, with blank lines in between, but conventions vary here

- Use a hanging indent


Change the Author and Commit Dates

$ env GIT_COMMITTER_DATE="Thu Apr 17 10:06:40 2013 -0400" git commit --amend --date="Thu Apr 17 10:06:40 2013 -0400"



Merging is the process of taking a branch and merging other branches, commits, or files into it. The two main ways of accomplishing this are git-merge and git-rebase, however, we will go over several unique cases that use other methods like git-cherry-pick.


Merging will create a merge commit. If there are conflicts Git will block out the conflicting sections of code to let you decide which parts you want to keep.

Important! By default, if a branch can be fast forwarded to complete the merge, Git will NOT create a merge commit. This is BAD news! This means that your history will look seamless and you won't be able to tell what branch a commit came from. See the section "Preserving History".


Rebase will do the exact same thing as merge except there is no merge commit. While this can be handy, since merges are transparent it makes the history obscure, which can be difficult when trying to debug an issue.

Cherry Picking on Top of Unmerged Files

Imagine you pull and there is a conflict with only one file and that file is VERY conflicted, so much so that you don't want to resolve it. You simply want to scrap whatever is there and use a file of your choice, say from HEAD, a previous revision, or even a remote branch. What do you do?

Option 1: Checkout (reference1, reference2)

$ git checkout application/lib/Utilities.php
 error: path 'application/lib/Utilities.php' is unmerged


Option 2: Hard Reset

$ git reset --hard -- application/lib/Utilities.php
 fatal: Cannot do hard reset with paths.

Fail again!

Option 3: Commit and Amend

  1. Do the pull or stash apply that creates the merge conflicts.
  2. Add everything (including the unmerged paths) and commit:
    $ git add .
    $ git commit

    * Make sure you remember which files were unmerged! We will overwrite them next

    * Also, you can make the commit message whatever you want since we will be amending it later. If you want to make sure you keep track that this commit is a bad commit you can mark it somehow, like saying "BAD COMMIT - UNMERGED FILES!". Or, if you will immediately be ammending, you can simply put the commit message you will eventually want it to be.

  3. Replace the unmerged files with all the files you really wanted them to be. For example, if you really wanted the file that was in your HEAD (now HEAD~), do:
    $ git checkout HEAD~ application/lib/Utilities.php
  4. Finally, amend your commit:
    $ git commit --amend

    * Here is where you will get to modify your commit message.

A few quick no brainers:

  • Do NOT push unmerged files. Make sure you keep track of all the unmerged paths you added in Step 2 and overwrite them in Step 3.
  • You can tell if a file is unmerged, even if git doesn't, by opening the file and looking for things like: "<<<<<<< Updated upstream".

Undo a Merge

$ git revert -m 1 HEAD

Abort a Rebase

$ git rebase --abort

Also see:

Preserving History

It is extremely important to preserve history when you need to debug something. The most common issue that arises is when you don't force Git to create merge commits. Here is an example: if you merge a branch that can be fast forwarded, your history by default will look like:

* 194cb58 2012-08-15 | Commit 3 (HEAD, master, feature1) [Joseph D. Purcell]
* 779ed05 2012-08-03 | Commit 2 [Joseph D. Purcell]
* d9a63b1 2012-08-21 | Commit 1 (origin/master, origin/HEAD) [Joseph D. Purcell]

However, if you use --no-ff=false, then you can see the commits that were part of the branch:

* c7576d9 2012-08-21 | Merge branch 'feature1' (HEAD, master) [Joseph D. Purcell]
| * 194cb58 2012-08-15 | Commit 3 (feature1) [Joseph D. Purcell]
| * 779ed05 2012-08-03 | Commit 2 [Joseph D. Purcell]
* d9a63b1 2012-08-21 | Commit 1 (origin/master, origin/HEAD) [Joseph D. Purcell]

To ensure a merge commit is always made, you can add the following to your .git/config:

    ff = false

Or, simply run:

$ git config merge.ff false


Or, to make this config true for all your repositories add it to your ~/.gitconfig:

$ git config --global merge.ff false

Now, this will present an issue: whenever you run git pull it will attempt to create a merge commit. More than likely you don't care that you merged what was in the remote into your local copy of the branch. So, instead you can run:

$ git rebase origin/master


$ git pull --rebase origin master

Or, you can simply add the following to your .git/config:

    rebase = true

Or, to have git add this to the config for you run:

$ git config pull.rebase true

Or, to make this config true for all your repositories add it to your ~/.gitconfig:

$ git config --global pull.rebase true

Or, to make this config true for only one branch in your repo:

$ git config branch.master.rebase true

If you added it to your config then every time you run a git pull it will simply rebase instead of attempting to merge, and thus create a merge commit.

Preserving Merge Commits When Rebasing

One issue to be aware of is that when you rebase, by default merge commits are squashed (see StackOverflow thread or the "Rebasing Merge Commits in Git" article by the folks at envato). As a result, when you rebase you will want to use the "--preserve-merges" flag like so:

$ git rebase --preserve-merges develop

Preserving Dates When Rebasing

There is no way to "preserve" the commit date of a commit. The commit date of a commit will always be the date and time it was committed. So, when it comes to "preserving" dates you can only do so on the "Author Date". (For a longer explanation, see this excellent comment on a thread on StackOverflow:

In short, Git will preserve the commit date by default. However, if you want to set the commit date and author date to be the same, you can use the --ignore-date flag as follows:

$ git rebase --ignore-date

So, for example, the following commit would be the result of a normal rebase:

$ git log -1 --pretty=fuller
commit c78d2a46ff0187199ca940a8d97e3d681e0c6b95
Author: Joseph D. Purcell <>
AuthorDate: Thu Jan 3 18:13:32 2013 -0500
Commit: Joseph D. Purcell <>
CommitDate: Fri Jan 4 16:52:05 2013 -0500

    This is my example commit message.

That same commit would look like the following if the --ignore-date flag was used:

$ git log -1 --pretty=fuller
commit 17808d60428203752551896243c1978aa5e06c73
Author: Joseph D. Purcell <>
AuthorDate: Fri Jan 4 16:52:05 2013 -0500
Commit: Joseph D. Purcell <>
CommitDate: Fri Jan 4 16:52:05 2013 -0500

    This is my example commit message.

Alex Peattie has an excellent post, "Working with dates in Git", that explains more about Git and dates.

Resolving Conflicts

Resolving Conflicts During a Merge

Resolving Conflicts During a Rebase

There must be a way to rebase and squash any conflicts produced by "ours" or "theirs". To do it on a conflict-by-conflict basis you can do:

$ git checkout --theirs file.txt

At this point if you will open the file you will see there are no conflicts, but Git still thinks there are. Do a "git add file.txt" and then "rebase --continue".

Thank you Nathan Fox for you answer I found on StackOverflow.

Dealing with Merging Hotfixes



Pushing to a Checked Out Branch on a Non-Bare Repository

Git isn't designed to push to a branch that is checked out on a non-bare repository. However, with two tricks you can do so.

  1. Set the Git config of the repository to accept pushing to the branch that is checked out:
    $ git config receive.denyCurrentBranch ignore
  2. Add a post-receive hook to force update the working directory:
    $ vim .git/hooks/post-receive

    And make the contents the following:

    cd ..
    env -i git reset --hard

    Finally, you will need to make it executable:

    $ chmod +x .git/hooks/post-receive

Thanks to Ulrich Petri via a post by Felix Geisendörfer on "debuggable" for the post-receive hook tip, which I originally heard from Gerard Sychay, but forgot what it was.

To see an example of how to push to bare repositories that are also websites, check out Abhijit Menon-Sen's article.


Tracking Tags

Tags are not shared by default. Read this great StackOverflow thread for more details.

Option 1: tagopt config

Setting the "tagopt" flag via the following command will ensure you are tracking tags:

$ git config remote.origin.tagopt --tags

The problem with the "tagopt" flag is that it only works for pulling tags.

Option 2: pull config

I found this idea from Jakub Narębski on a StackOverflow comment. Run the following two commands to make sure Git will both push and pull your tags:

$ git config --add remote.origin.push +refs/heads/*:refs/heads/*
$ git config --add remote.origin.push +refs/tags/*:refs/tags/*

Mark Longair talks about the importance of namespacing these tags in another comment on StackOverflow, which is worth reading to know what is involved.

Warning! The push +refs/heads/* config will push all local branches to the remote! I haven't found a workaround yet.


Stage All Files

$ git add .

Unstage All Files

$ git reset HEAD

Note: if you have unmerged files they will now show under "Changes not staged for commit" instead of "Unmerged paths". If you are unaware of this, you could accidentally commit a file with all the merge conflict information within the file, i.e. all the "<<<<<<< Updated upstream" stuff.

Stage a File

$ git add path/to/file.php

Unstage a File (reference)

$ git reset path/to/file.php

Undo a Commit

This one is tricky. There are a couple ways to go about it and it depends on what the end state you are looking for is. If you are wanting to undo a commit in a cherry-pick fashion where you leave all commits made after it, see Option 3. Options 1 and 2 undo all commits up to the commit you want to undo.

Option 1: Undo a Commit by Rolling Back the History

This option will roll the history back to a previous state, which makes it more or less impossible to use in distributed repos. If you have multiple people using the repo you are working from you don't want to do this UNLESS you are undoing a commit you haven't pushed yet. If you are undoing a commit you haven't pushed, this is the option you want.

$ git reset --hard HEAD~

To go back more than one commit do:

$ git reset --hard HEAD~10

Replace 10 with the number of commits you want to go back, a 1 meaning "go back to the commit before the last commit you made." In other words, if you roll back 10 commits then commits 1-9 will also be undone. For cherry-picking see Option 3.

NOTE: this action can be undone by viewing the reflog (git reflog) and resetting the HEAD to the hash before the reset.

Option 2: Undo a Commit by Re-playing Changes

This is the safest bet. But, if you are undoing a commit you haven't pushed you probably want to roll back the history and completely remove the record of this action. This will undo the last commit by making an additional commit called a "revert commit" that re-plays the opposite of the changes you made, i.e. add a file if you deleted a file.

$ git revet HEAD

To undo a commit before that do:

$ git reset --hard HEAD~10

Replace 10 with the number of commits you want to undo. In other words, if you roll back 10 commits then commits 1-9 will also be undone. For cherry-picking see Option 3. Also, as a side note, I have never done a git revet on a revert commit, so I'm not sure what would happen.

NOTE: this action can be undone as well by viewing the reflog.

Option 3: Undo a Specific Commit

This is the "nicest" option in that no history is re-written. This is your only option if you are working on a branch that others will be using. The syntax is simple:

$ git revert b12c54423299ce185e79a1a07f0c5e9fe0786940


Option 4: Undo Part of a Commit

You can revert part of a commit by using the -n flag for no commit:

$ git revert -n b12c54423299ce185e79a1a07f0c5e9fe0786940

Then, reset all the files (conceptually, this means to un-stage them):

$ git reset HEAD .

Now, for each file you don't want to undo, run git checkout on the file:

$ git checkout file1.txt

Finally, you should now have only the files you want to undo all or part of what was changed. Simply open up the file and change it to your desired state, then run commit:

$ git commit


Option 5: Rebase

Rebasing is the most flexible option you have, and the most powerful. However, it involves rewriting the history which cannot happen if you are sharing the branch with others. The command is quite complex, so I will attempt to make it as simple as possible by breaking it into steps of a process that you can follow each time:

  1. Decide how many commits ago you will need to rewrite. This number will be N.
  2. Run rebase back to that commit:
    $ git rebase -i HEAD~N
  3. You will be prompted with a dialogue (similar to the commit dialogue) that asks you what actions you want to take on each commit. So, you should see a list similar to the following:
    pick 61171cd A commit message 6.
    pick 495341a A commit message 5.
    pick c906e78 A commit message 4.
    pick 4f2cd68 A commit message 3.
    pick b639073 A commit message 2.
    pick 175e7e4 A commit message 1.

    Take your time to consider each commit and what you want to do with it, starting with the oldest commit (i.e. 175e7e4)  and working your way to the newest (i.e. 61171cd). (The reason for this is because even though it will process the commits top to bottom, it is easier to think about what will happen by thinking of the forward progress of changes, that is, the progress from the oldest commit to the newest.)

    For each line you can do any one of the following:

    1. pick - will keep the commit as is
    2. reword - will use the commit, but will give you the commit dialogue to change the message
    3. edit - will use the commit, but will pause to allow you to change the working directory and will continue after you run git commit
    4. squash - will use the commit, but will combine it into the previous commit (previous being not in terms of chronology, but in terms of the order in the list of commits in the current dialogue you are editing)
    5. fixup - will use the commit and will combine it into the previous commit, but will discard this commits message and use the previous one
    6. exec - when this commit is reached the rebase will pause to run the command that comes after "exec" in a shell

    Additionally, you can re-order the commits or delete a commit, which will completely remove that commit from the history.

  4. Finally, save the file and close and git will run through each line from the rebase dialogue and execute the actions you specified, and will pause where an action by you is needed.
At any point you can abort the commit by running the following:
$ git rebase --abort


Clear the Working Directory

If you want to clear all changes you have made in your working directory (everything you see in git status), do this:

$ git reset --hard

NOTE: this action CANNOT be undone!!! (Yes, unfortunately I know this from experience.)

Avoiding Merge Commits

Read Linus Torvald's comments on rebasing and merging:

On Merge

Avoiding merge commits on a simple merge is straightforward by passing the 'no-commit' flag:

$ git merge --no-commit feature-branch

However, this will only come into play if there are conflicts, and if there are conflicts you probably want to keep track of when they happen. So, you probably will never want to use this.

On Pull

On the other hand, every time you run a git pull it will create a merge commit, even if there are no conflicts. There are a few cons to avoiding merge commits in this case and there are a number of ways to do this, as Levent Ali points out. However, the easiest way is to pull rebase:

$ git pull --rebase

A great post by Clinton Nixon shows how you can edit your git config to make this automatic. Simply do:

$ git config branch.master.rebase true

Now, anytime you run git pull it will rebase instead of merge. Simply replace 'master' with the branch you want to automatically rebase on pull. Or you can set the config for all branches in the repository:

$ git config branch.rebase true

Or, you can set it for all repositories on your system by adding it to the ~/.gitconfig which applies only to your user:

$ git config --global branch.rebase true

However, if you don't want to rebase, you can override with the no rebase flag:

$ git pull --no-rebase origin master

Supposedly, you can also configure git to automatically rebase for all remote tracking branches:

$ git config --global branch.autosetuprebase always


  • No "merge bubbles" (a photo describing this on Skitch is a must see: click here)
  • Cleaner commit history (type 'git hist' and you will see these "merge bubbles")


  • More difficult to undo a git pull
  • Less clean reflog

Pros and Cons Explained

As mentioned, the issue of merge commits are mainly a concern with pull requests, so we will focus on that. Normally, when you pull it will create a merge commit like this one:

commit 1fe5673d3f1e6e275fd19343a345b57a07e8de65
Merge: b01af47 b9eaac2
Author: Joseph D. Purcell
Date:   Mon Apr 23 08:30:15 2012 -0700

    Merge branch 'master' of into master

And, your reflog will look like:

$ git reflog
1fe5673 HEAD@{0}: pull origin master: Merge made by recursive.
b01af47 HEAD@{1}: <whatever your previous action was>

Then, to undo the pull you can simply do:

$ git reset --hard HEAD~

On the other hand, if you do a rebase pull you have no commit and your reflog will have a list of all the commits that you made that have not yet been pushed to the remote branch:

$ git reflog
41740ce HEAD@{0}: pull origin master: A commit I just made locally.
c286d20 HEAD@{1}: pull origin master: A commit before that one that I made.
b9eaac2 HEAD@{2}: checkout: moving from master to b9eaac2b4660c58ba51885897438916d1d02a679^0
b01af47 HEAD@{3}: <whatever your previous action was>

Now, if you want to undo your pull you will have to (correct me if I'm wrong) go to find whatever your previous action was in the reflog and then do a hard reset to it. You can find it by looking for the last 'checkout' and choosing the one before it. In our example above, it is 'b01af47':

$ git reset --hard b01af47

Perhaps there is an easier way, but not to my knowledge. So, as you can see, undoing pulls on rebases is a little more tricky because you have to know this process.



Recover a Deleted Stash

Refer to


Git Bisect

Git bisect is a great tool to help you find the commit that introduced a bug into your system. It's the kind of tool that you will never use, but once you encounter a situation in which it is needed, you feel as though you could never live without it. First, know that I am assuming the following four things:

  1. You are in the root directory of the project.
  2. HEAD points to the most recent commit, i.e. you have not done something like git reset --hard HEAD~10
  3. The latest commit is bad.
  4. 2fcc6e20a is the last known good commit.

Now, here is how it works:

$ git bisect start
$ git bisect bad
$ git bisect good 2fcc6e20a

Next, repeat git bisect good or git bisect bad until you get a message something like:

$ git bisect good
9bc751aa7f55f7b88310b4d9fc7b6a2e1c220458 is the first bad commit
commit 9bc751aa7f55f7b88310b4d9fc7b6a2e1c220458
Author: Mr. Person Man
Date:   Wed Jul 10 13:24:03 2012 -0400

    Break the code because I think that's funny.

:040000 040000 352bc21b262f079e8829b48034d30ba0c68653ff 3bbc6a60f8fd8554a533e914c5c835b22788b8eb M	core

And then reset bisect to reset HEAD back to the latest commit:

$ git bisect reset

Now, you have the fun task of finding a solution to fix the change that was done. For more information on git-bisect, read Larry Garfield's article "Using git bisect for fun and profit."

Show Commits Made Directly on a Branch

See Simply do:

$ git log --first-parent --no-merges develop ^master

Git Rebase Conflicts Prevent Progress

When you are trying to rebase and you type "git add ." and you get the following:

$ git status
# Not currently on any branch.
nothing to commit (working directory clean)

A StackOverflow thread suggests simply skipping it:

$ git rebase --skip

And it should continue fine. The reason this happens, from what I understand, is that the exact same changes have already been applied on the branch you are rebasing onto.

"You need to run this command from the toplevel of the working tree."

This just means you need to be in the root of the repository you are in. This always shows up when I try using git-bisect.

Git Blame Shows "00000000 (Not Committed Yet 2014-04-01 20:10:44 -0400  99)"

This could be for a couple of reasons:

  • make sure you are not trying to run git blame on a file that is symlinked from where you are (i.e. git blame htdocs/index.php where htdocs is a symlink to src)
  • some have reported this is due to issues with line endings (StackOverflow)
  • try specifying HEAD or the branch: git blame HEAD htdocs/index.php

Collaboration on Projects

Git Branching Models

Read this excellent thread on StackOverflow:


A quick way to send changes to someone else is to make a patch. Ariejan de Vroom wrote a good article on "How to create and apply a patch with Git", but here is the essence:

Create a patch:

$ git format-patch HEAD~1 --stdout > patch.txt

Apply a patch:

$ git apply patch.txt

Git Submodules

I haven't delved into submodules, but here are some relevant articles:

Further Reading

Older Articles »