Nexcess
Nexcess Blog Logo
August 26, 2020

Advanced Git Usage & Workflows

Recently we looked at the basics of getting started with using Git for source control in your projects. While that’s a great starting point, there are a bunch of other commands and Git workflows that will help you wrap your head around using Git in your daily coding work.

Git Workflows

When I started using Git, I figured I knew how to use it properly. My ideal approach was to make all changes in one spot without branches and then commit them to the repository and keep working.

While it was better than not using version control, it took me a while to realize that I wasn’t using most of the power that Git provided. To get Git working for me, I needed to have a strategy to branch and merge my changes.

Then git-flow came out and I adopted it as my strategy. I still remember feeling like I was peeking behind some curtain to where the amazing developers were. I now had insight into how they worked and could start to become one of them.

But git-flow doesn’t fit every scenario, so while we’re going to look at it we’ll also take a look at a few other methods of keeping your Git projects organized including how I manage my projects as a lone developer.

git-flow

Looking at git-flow now, I acknowledge that the software landscape has changed greatly in 10 years and git-flow may not be the best option for your team. When git-flow was written, it was rare to deploy an application many times in a day. Instead, you probably did a major version number and release every few months or weeks if you were on a particularly agile team.

Let’s take a look at what git-flow is.

If you want to see the full deep explanation with charts and Git commands for Git Flow, you should read this post.

In git-flow, two branches have an infinite lifetime. First, main which should reflect code that is ready to be deployed to your live/production environment.

Second, we have our develop branch. This branch should have the latest changes that are ready for the next release of our software. When the changes in develop are ready to be deployed to our live application, we merge them into the main branch and tag them with the version number that corresponds with the release number.

Outside of the two major branches, there are three types of supporting branches.

1. Feature

A feature branch may be made from the develop branch only. It must be merged back into the develop branch. Naming can be anything descriptive of the feature you’re working on.

When the work is ready for the next release it gets merged back into the develop branch where it waits for release time.

2. Release

Release branches are made from the develop branch and must merge back into both develop and main. You name a release branch with the release-x convention. In practice that usually means you’d name a branch with the release number you’re planning to use like this: release-2.2

You use a release branch as a way to do the final prep to release your software. This may include bumping the version number of files, making sure that your translations are done, or final code checks.

3. Hotfix

The hotfix branch is made from the main branch and is used to contain changes that need to be dealt with in the live application right away. This may be a bug that has to be fixed or a security issue that needs to be dealt with.

Once the problem is fixed and deployed to your main branch you’d tag your code with the proper version number.

The biggest reason that teams don’t use git-flow now is that the way we release software has changed. Instead of larger releases less often, you may release a change to an application a few times in a day. I know that I push work to my client’s sites many times a week as soon as it’s ready. We don’t do version numbers of the site, we just keep improving it.

Standard git-flow isn’t meant to accommodate this.

Github Flow

The second option that many people use is Github Flow.

The one big rule of Github Flow is that whatever code is on the main branch can be deployed at any time because it’s production-ready.

All branches are created off of the main branch with a descriptive name for whatever you’re doing.

Once you have your changes ready you create a pull request.

Pull requests tell others working on the same code that the work you’re doing is ready to be reviewed before those changes are merged into the main code.

Once you have a pull request submitted, the team you’re working with can review the changes and provide feedback. If the pull request is deemed ready to merge, then it’s merged into the main branch for your project.

One drawback to Github flow for a single developer or very small team is that the administration of a pull request can create extra overhead in managing the project. This is why I don’t use them in my work.

How I Use Git with Client Projects

In my client work, I’m usually the only one writing code daily for a project. My client may update WordPress plugins or change some CSS, but they don’t do any major coding work. That means if I went with Github flow I’d be reviewing my pull requests which only create extra management headaches. Let’s look at the system I use, which bears some resemblance to both git-flow and Github flow.

I have two main branches called main and staging. The main branch tracks with whatever code is currently running on the production site. The staging branch tracks with whatever is being tested on the staging site we use to test changes before we push them to the live site.

Every branch is created from the main branch. New features are given a name like this: feature/32-new-feature. In this context, the number 32 corresponds to the ticket number in our project management system and the words after it are a short description of what’s being worked on. Bug fixes get named similarly, bug/20-bug-name.

Every branch created gets merged into our staging branch first, and then once approved by the client or tested by myself gets merged into the master branch. That workflow may look like this.

# merge feature into staging branch

git merge feature/32-new-feature

# deploy and test the feature

git checkout main

git merge feature/32-new-feature

# deploy feature to the live site

In my projects, I have continuous deployment set up which means any time I push code to main it gets pushed to the live site automatically. The same process is set up for the staging branch.

I’ve already written about continuous deployment with Deploybot.

If I was working with a team that could check my code in a pull request workflow, then I’d use this system because it works well in a team. For a developer mostly working on their own, it’s simply management overhead that’s going to slow down your workflow.

Advanced Git Commands I Use

Now that we have some idea of how we can use Git in a practical workflow, let’s take a look at more advanced commands that will be needed to make this happen. I use each of these commands at least a few times a week as I work with my customer’s code.

Even if you’re going to use a GUI application, (I mentioned some good ones in my last post on Git) it’s still important to have an understanding of what is happening in the background. Many times I’ve had to work via terminal to fix an issue that was created by a Git GUI application. 

Adding Changes by Line

The one command that made Git usage via Terminal click for me was git add -p. Until I learned this command I used GUI applications because I’d make changes in my code but want to break up specific lines into different commits so that I could explain why I had made a change. To do this I used a GUI to select the lines, but git add -p walks you through an interactive interface to add changes in chunks.

I use this many times every day.

Track Remote Git Branch

If you’re pulling down an existing repository and have branches like main and staging that you need to keep track of but already exist, you need to tell your local versions of the branches to track those remote versions of the branch.

There are a few ways to do this.

# Set upstream when pushing to remote

git push -u origin staging

# Set upstream

# assumes you’re on the branch you want to currently track with remote

git branch -u origin/staging

git branch --set-upstream-to=origin/staging

# If you’re not on the branch you want to track so you specify the branch at the end

git branch --set-upstream-to=origin/staging staging

Save Changes without Committing Them

Sometimes you’ll be in the middle of some work that’s not ready to be committed yet, but you need to save its state. That’s where git stash is useful. This command stashes changes away for you by removing the changes. You can get them back by using git stash pop. There are a few more commands to make stash useful, but those are the two I use regularly.

Pull a Specific Git Commit

Sometimes you need to pull a specific commit into your current work. With a clean HEAD (you haven’t made any changes yet) you can pull in a specific commit with git cherry-pick . You can find the full documentation on git cherry-pick here.

For each commit Git builds a long sequence of letters and numbers which is called a Git Object, and commonly referred to as a SHA. Since each commit gets one you can reference a commit with its SHA value. Read more about Git Objects.

Throw Away Changes You Don’t Need

At some point in any project, we’re going to make changes and then realize that it’s not working and we need to simply scrap them and start over. Instead of just trying to undo until we’re back where we want to be we can use the following Git command to remove any changes that have not been tracked yet.

git reset --hard

The command above will reset your code back to the most recent commit on the branch you’re currently working on. We could also use a <#commitSHA> with this command to reset to a specific commit if we wanted to get back to a state before the latest commit. Maybe you’d use this to reset to the initial branch checkout because the entire branch worth of work isn’t something you want to keep, but you had already tracked some of the work.

To take it one step further, we can throw away any files or directories that have not been tracked in git yet with the git clean command.

git clean -fd: the flags f and d tell git to throw away files and directories that are untracked.

Remove Branches

Every week or two I look at the results of a git status command and find I have way too many branches to reasonably understand what’s going on in my repository. That means I can remove any branches that correspond to tickets that have been resolved with the following commands.

# removes the local version

git branch -d $branchname

#removes the branch on my remote

git push $remotename --delete $branchname

Use Version Control

While you may not be an expert at all the Git commands you’ll use, one important thing to remember is that you should be using version control. Even if you’re the only person working on a project, using Git and a Git workflow will help you keep your projects organized. You won’t need to press CTRL + Z until you’ve reset your work after writing code that didn’t work.

You’ll be able to trust your system and keep producing work for your projects.

Avatar for Curtis McHale
Curtis McHale
Curtis is a husband, father, developer and business coach. He specializes in helping people build a business that lets them spend time with their family instead of working all the time.