Advertisement
Tools & Tips

Git Tips From the Pros

by

You're already using source control for managing your code, right? You might even be using your SCM as the central piece of your workflow, like we do at New Relic.

In this article, we're not going to review the basics of source control management, regardless of which one you use. Let's just assume that you already know how to get around. What we are going to cover is how the pros use git. We'll take a look at some of the advanced features and workflows that you might not already be familiar with. Hopefully, you'll walk away with your mouth agape at the sheer possibilities that git provides!

If you are anything like me, you love to explore how other developers work.

For the uninitiated, or those coming from another SCM, Git is a distributed version control system. It is free and open source, has a tiny footprint, and can fit within the workflow that suits you best. Generally, it doesn't force you to work in a particular way, which means there are many different methodologies on how to use its features, like staging areas, branching and tagging releases. If you are anything like me, you love to explore how other developers work. So get ready to start modifying your .gitconfig, because you're in for a treat. Let's see how the pros use git.


Stage Your Changes in Hunks

You are likely familiar with accidentally modifying a single file for two different reasons without committing in between.

You are certainly familiar with adding files to the staging area with the appropriately named add command. And you are likely familiar with accidentally modifying a single file for two different reasons without committing in between. So you will sure have a git log filled with messages like "Edit X and change unrelated Y". If this sounds like your workflow, then interactive adding is your new best friend.

Interactive adding, or adding a patch, walks you through your changes one hunk at a time. When you add a file with the -p command, you will be prompted at each logical change (i.e., successively edited lines will be grouped together). There are a number of choices you can make on each hunk, from splitting the current hunk into smaller ones, skipping a hunk, or even manually editing it. Use the ? option to see a complete list of commands.

Getting started with staging hunks is as simple as:

git add -p <FILE>

Checkout Your Last Branch

As a good coding citizen, when you come across something that needs a quick fix or cleanup, you should probably take a moment to change it. But if you are using a heavy feature-branch workflow, then you don't want that unrelated fix in your feature branch. This means you'll need to stash your current changes, change to your master branch and then make the fix there. Bouncing around between branches can be tedious, but, luckily, there is a quick shortcut for switching to your last branch. (via Zach Holman)

git checkout -

This syntax should look pretty familiar to *NIX users. The cd command has a similar shortcut (cd -) that will jump to the last directory you were in. You'll never have to remember what you named that feature branch when you need to switch back; just git checkout -.


Show Which Branches are Merged (or not)

When working with feature branches, you can quickly create so many that clutter up the output of git branch --list. Every now and again you want to get rid of the branches that have made it into master. But you probably have a quick pause before you git branch -d <BRANCH>, but with the below commands you can confidently delete them without a second thought. (via Zach Holman)

If you want to see which local branches you have that are merged into the branch you are currently on, then all you need is:

git branch --merged

The reverse is also available. Show which branches haven't been merged into the currently selected branch with:

git branch --no-merged

Mash this up with a couple easy UNIX tools and you can quickly delete everything that has already been merged:

git branch --merged | xargs git branch -d

Grab a File from Another Branch without Switching Branches

Let's say that you're experimenting with some refactorings, and you have a few branches that have various changes you've made. If you have changes in a file in some distant branch that you want to bring into your current working branch, then you could do any number of steps. Without the below tip, you'd probably stash your current changes, switch branches and grab the file contents you want to change, switch back (with git checkout - of course) and make your edits. Or you could simply checkout just that file which will merge it into your current branch (via Zach Holman):

git checkout <BRANCH> -- path/to/file.rb

Git Branches Sorted by Last Commit

So you've got the cluttered branch list that we talked about before; some of those you've cleaned up with the --merged flag. But what about all those other branches? How do you know which ones are useful or entirely out of date? The for-each-ref command will output a list for each branch and show the reference information for the last commit. We can customize the output to include some useful information, but, more importantly, we can sort the list by date. This command will give us a list of branches with the last commit message and committer, sorted in descending date order. (via Rein Henrichs)

git for-each-ref --sort=-committerdate --format='%(committerdate:short) %(refname:short) [%(committername)]'

While you could type this command each time, I highly recommend making it an alias and save yourself some serious headaches.

git config --global alias.latest "for-each-ref --sort=-committerdate --format='%(committerdate:short) %(refname:short) [%(committername)]'"

People in Glass Houses Shouldn't Use Git Blame

Or at least they shouldn't use git blame without one of the options flags below. Git blame is powerful; it's basically like using science to prove you're right. But be careful, many changes are superficial and to find the real source of the in-question code takes a bit more hunting. Things like removing white space, moving text to new lines, or even moving text from another file can be ignored to get to the original author of the code much easier.

Before you git blame someone, make sure you check one of these:

git blame -w  # ignores white space
git blame -M  # ignores moving text
git blame -C  # ignores moving text into other files

Find a String in the Entire Git History (and Remove It)

From time to time, you need to hunt down a line of code you know you wrote but just can't find. It could be stuck in some distant branch, deleted a long long time ago, or hiding in plain site; but either way you can find any string in your entire git history by mashing up a few commands. First, we're going to get a list of all commits, and then grep each of them for our string.

git rev-list --all | xargs git grep -F '<YOUR STRING>'

You probably have a friend who has accidentally committed sensitive data to a repo: access keys, passwords, your grandmother's secret marinara recipe. The first thing they should do is change their passwords and revoke access with those keys (and apologize to your grandmother). Next, you'll want to hunt down the offending file and remove it from the entire git history, which sounds far easier than it actually is. After this process is complete, anyone that pulls in the cleaned changes will have the sensitive data removed as well. Forks of your repo that do not merge your upstream changes will still contain the compromised files (so don't skip changing passwords and revoking access keys).

First, we'll rewrite the git history for each branch, removing the file with the sensitive data.

git filter-branch --index-filter 'git rm --cached --ignore-unmatch <FILENAME>' --prune-empty --tag-name-filter cat -- --all

Add the file to .gitignore and commit to update .gitignore.

echo <FILENAME> >> .gitignore
git add .gitignore
git commit -m "Add sensitive <FILENAME> file to gitignore"

Since we are rewriting history, you'll need to force push the changes to your remote.

git push origin master --force

The compromised files still exist in your local repo, so you'll need to do a few clean-up tasks to purge them entirely.

rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

Your friend's repo should be free of sensitive data and you'll be the hero for helping them with your pro git knowledge. (via StackOverflow and GitHub)


Ignore Changes in a Tracked File

Working with someone else's code in your environment can mean that you need to make any number of config changes to get the application running. It is all too easy to accidentally commit a change to those configs that were meant exclusively for your environment. So, instead of always watching out for those files and having them linger in the "modified" staging area, you can simply tell the git index to ignore changes to that file. You can think of this somewhat like a git ignored file that stays with the repo. (via Arnaud Coomans)

git update-index --assume-unchanged <FILENAME>

Zero Out a Branch's History

Sometimes starting from scratch is exactly what you need to do, for any number of reasons. Maybe you've inherited a codebase that you can't ensure is safe to open source, maybe you're just going to try something entirely new, or maybe you're adding a branch that serves a separate purpose that you want maintained with the repo (like GitHub Pages). For this case, there is a very simple way to create a new branch in your repo that essentially has no history. (via Nicola Paolucci)

git checkout --orphan <NEWBRANCH>

Aliases You Can't Live Without

Stop wasting time typing long commands and make yourself a few useful aliases.

No discussion of git would be complete without talking about various aliases that will literally save you minutes a year in saved keystrokes. Stop wasting time typing long commands and make yourself a few useful aliases. Aliases can be made by adding them to your .gitconfig file or using the command-line git config --global alias.<NAME> "<COMMAND>". Below are just a sample of alias that you can use as a springboard for ideas.

co: with a feature branch workflow, you'll be moving between branches regularly. Save yourself six characters every time.

co = checkout

ds: it is always best practice to review the changes you're going to commit before making the actual commit. This allows you to catch typos, accidental inclusion of sensitive data and grouping code into logical groups. Stage your changes and then use git ds to see the diff of those changes.

ds = diff --staged

st: you should be pretty familiar with the verbose output of git status. At some point you'll want to skip all the formality and get down to business. This alias shows the short form of status and includes the branch details.

st = status -sb

amend: did you forget to include a file with your last commit, or maybe you had one tweak you needed to make? Amend the staged changes to your last commit.

amend = commit --amend -C HEAD

undo: sometimes, amending your last commit isn't enough and you'll need to undo it instead. This alias will step back one commit and leave the changes from that commit staged. Now you can make additional changes, or recommit with a new message.

undo = reset --soft HEAD^

ls: working on a codebase with a group of developers means trying to keep up with what people are working on. This alias will provide a one line git log including date and committer name.

ls = log --pretty=format:"%C(yellow)%h %C(blue)%ad%C(red)%d %C(reset)%s%C(green) [%cn]" --decorate --date=short

standup: this alias is great for reviewing what you worked on yesterday for any type of daily standup, or just to refresh your memory in the morning.

standup = log --since '1 day ago' --oneline --author <YOUREMAIL>

graph: a complex git history can be difficult to review in a straight line. Using the graph flag shows you how and when commits were added to the current branch.

graph = log --graph --pretty=format':%C(yellow)%h%Cblue%d%Creset %s %C(white) %an, %ar%Creset'

In Closing

Git can be both amazingly simple and mind-blowingly complex. You can start with the basics and work yourself into more complex graph manipulation over time. There's no need to grok all of it before you can use it. The command that will be most powerful as you learn is man git-<command>-<name>. Try using it before you refer to Google for an answer.

You can learn more about how the company I work for you, New Relic, uses git on our blog, or give New Relic Pro a try for free. Thanks for reading! If you have any questions, let us know below!

Related Posts
  • Code
    Tools & Tips
    How to Collaborate On GitHubGithub collab retina preview
    If you don't already know, GitHub is an incredibly effective way to collaborate on development projects. Providing a place for anyone with an internet connection to have an avenue where they can share code with the world for free (not to mention the robust supporting tools for source inspection and easy viewing of commit histories). GitHub has been adopted by many large open-source projects as their primary home for collaboration and contribution. But how do you join in and contribute to a project? Sure, you know how to use Git to track changes to files and push those files to a server. But there are major benefits to getting involved in larger open-source projects, and GitHub is arguably the best place to start. Today, we will discuss a few rules of the road for collaborating on open source projects, and give you the knowledge and intuition you will need to get involved.Read More…
  • Web Design
    Applications and Tools
    Getting to Know GitHub Pages: Static Project Pages, FastGithub pages preview retina
    Let's talk about GitHub Pages; what they are and how you can use the service in minutes to deploy static pages for free. Let's get started!Read More…
  • Code
    General
    Team Collaboration With GitHubGithub team preview
    GitHub has become the corner stone for all things open source software. Developers love it, collaborate on it and are constantly building awesome projects through it. Apart from hosting our code, GitHub's main attraction is using it as a collaborative tool. In this tutorial, let's explore some of the most useful GitHub features, especially for working in teams, making it all the more efficient, productive and, most importantly, fun!Read More…
  • Code
    Tools & Tips
    Git Succinctly: Undoing ChangesGit succinctly cover 400
    The whole point of maintaining "safe" copies of a software project is peace of mind: should your project suddenly break, you'll know that you have easy access to a functional version, and you'll be able to pinpoint precisely where the problem was introduced. To this end, recording commits is useless without the ability to undo changes. However, since Git has so many components, "undoing" can take on many different meanings. For example, you can:Read More…
  • Code
    Tools & Tips
    Git Succinctly: BranchesGit succinctly cover 400
    Branches multiply the basic functionality offered by commits by allowing users to fork their history. Creating a new branch is akin to requesting a new development environment, complete with an isolated working directory, staging area, and project history.Read More…
  • Code
    Tools
    Git for DesignersCode
    You're likely familiar with tools, like Git and Subversion. But, as web designers, it's quite possible that, though you might say you leverage version control in your projects, the truth is that, more often than not, you simply don't.Read More…