My git workflow for teams

This is a guide I wrote a couple years ago. I thought it would be useful for those beginning to use Git for themselves or in a team. Sorry for passive voice ;)

Working in branches on the main repo

First, a note about git stash. Only stash when there are code changes that don't need to be committed. Stashes are only stored locally and ignored by the git push command. Stashes stick around until they are popped or cleared (more on this in a moment). They are not a permanent solution for managing work in progress. For WIP, you might want to consider using a personal branch. Let's get started.

Secondly, always run git status before entering any of the steps in this guide. Understand what everything in it means.

On stashing

Stashes are a great way to temporarily store work in progress. Reckless use of these commands can result in lost work. So be careful.

  • git stash removes all changes from working directory and temporarily stores them in a stash.
  • git stash save 'some description' does the same thing, but with a description of your choosing.
  • git stash pop reapplies changes in the most recent stash and then deletes that stash.
  • git stash apply does the same thing but retains the stash.
  • git stash drop deletes the most recent stash
  • git stash list lists all your stashes, numbers them and orders them from newest to oldest.
  • git stash clear permanently deletes all your stashes (tread carefully)

Using pop, apply, drop with a specific stash is simple. Use one of the following commands as necessary. Use git stash list to learn the number of a specific stash before doing so.

$ git stash pop stash@{0}
$ git stash apply stash@{0}
$ git stash drop stash@{0}

I use stashes to manage my work in progress while staying in sync or working with multiple branches. There are more advanced guides on the git stash command. Google it.

Creating new topic branches

Rule #1: Never commit directly to the master branch. It's unsafe and causes problems for other team members. The right way to safely commit work is by creating branches that are focused on a specific topic, feature, bug, or what have you. Creating a branch is pretty simple. Let's first make sure local master is in sync with origin master:

$ git checkout master 
$ git pull 

Git may complain that the working directory isn't clean. I get around this by running:

$ git stash save "Description of my work in progress"

Running the basic git stash command works too. But it will use the last commit message as it's description which may prove unhelpful.

Run those first 2 commands again and now local master should be in sync with origin master. Lets go ahead and create a new branch:

$ git branch feature_xyz
$ git checkout feature_xyz

Those 2 commands create the branch locally and checks it out. Your branch name should be a short 3 to 5 words and describe what your changes will focus on. Now the magic begins. Code is written. Changes are made. Master of domains and all that.

Protip: It is possible to branch and checkout in one command: git checkout -b feature_xyz.

If you have any stashed code, you retrieve it with:

$ git stash pop

Updating, committing to, and pushing to a topic branch

First. origin describes the "remote" version of all git branches, including master.

Let's get up-to-date with master. While in a topic branch, stash any code changes with: git stash and then run the following:

$ git pull origin master

The above command will pull any changes made to origin master and then stage and commit those changes to the topic branch. This is important because the "merge" becomes commit that can be reverted and seen in commit logs. At this point stashes can be popped or applied.

If busy work is preferred, running the following works:

$ git stash <if needed>
$ git checkout master
$ git pull

Then merge the changes:

$ git checkout feature_xyz
$ git merge master

It's time to commit and push some new code. But first lets see what is going to be committed by running $ git status:

On branch feature_xyz
Your branch is up-to-date with 'origin/feature_xyz'.
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:   path/to/file.ext
        modified:   path/to/another_file.ext

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

Depending on the situation, there may be more files. Some tracked and untracked. Nevertheless, to stage these files:

$ git add path/to/file.ext path/to/another_file.ext

Create a commit with:

$ git commit -m "Describe the new code you're committing"

Push commits to the remote branch.

$ git push origin feature_xyz

When another team member pulls down the feature_xyz branch, they will see all of that new code. If someone pushes changes to the same branch, the local copy needs to pull in those updates. Remember to stash any work in progress to prevent conflicts while updating a branch.

$ git pull origin feature_xyz

Now the feature_xyz branch is up-to-date with changes others have made.

Protip: Sometimes conflicts will occur after popping/applying a stash or pulling changes from origin master. This happens often because hours, days, and weeks are allowed to pass between changes or those changes a large and sweeping and can't be merged automatically. For this reason, always keep any branches in sync with origin master as often as necessary to avoid conflicts.

Working in forks of the main repo

The workflow for a fork is a little different. All instructions in this section assumes the main repo has been successfully forked, cloned and the main repo added as an 'upstream' remote. Don't know how? GitHub has a simple guide on configuring forks.

Updating, committing and pushing code to your fork

The process is almost identical to what was talked about earlier. But instead of origin we use upstream.

$ git fetch upstream master

Just like in a branch, it pulls changes from upstream master, stages and commits them to your fork. Assuming any work in progress has been stashed, let's merge any changes.

$ git merge upstream/master

The next step is to push these merged changes to origin master.

$ git commit -am "Merging upstream master"
$ git push origin master

Now we can pop our stash from earlier. Resolve conflicts if there are any and push our work for a pull request later.

$ git stash pop
$ git commit -am "Some work I did"
$ git push origin master

Protip: You can use the branch workflow for committing and pushing code in your fork.

Conflict resolution basics

A personal rule of mine to keep conflict resolutions as separate commits as it makes reverting them a little easier. There is a simple guide on resolving conflicts manually. Go read it. But here is the short; resolve the conflict, stage the changes, commit and push.

$ git add path/to/conflicted/file
$ git commit -m "Describe what changed here"
$ git push origin feature_xyz

Commit and push all other changes with the following commands:

$ git add path/to/changed/file
$ git commit -m "Describ your changes"
$ git push origin feature_xyz

Protip: Adding each file one by one is great. But the laziness is real, my friends. git add --all will stage everything including untracked files. git commit -am 'Describe your commit' will stage and commit only the modified files, leaving untracked files untouched.


Rinse and repeat.

May 29, 2015