Does this sound familiar?
It’s 5:30 pm on a Friday, and you’re glued to the screen. Your team has about a dozen pull requests to deal with, and the client is expecting a demo on Monday. Despite your best efforts, there’s still a ton of work to finish. How did this happen?
If you find yourself in this situation over and over again, you might not be using version management to the fullest. For developers with more passion than time, these tips, tricks, and aliases can act as a life preserver when you’re drowning in code.
Too busy to read? We understand. Go straight to the TL;DR section, complete with a list of all aliases mentioned.
For Repository Managers
Painless Pull Requests
If you’re the one managing a team’s central repository, you know how important it is to keep it production-ready. These strategies can help keep your central repository clean and lighten the load when merging pull requests.
Merge with Style
Merging branches and handling pull requests can be time-consuming. If git diff
isn’t quite enough, you can use git mergetool
to bring up an editor that will help you sort changes. There are also merge GUIs like Meld that can help you breeze past the inevitable merge conflict.
A relatively basic fix that many developers overlook is the use of git merge --no-ff
. If you use the following alias, Git will maintain a history of merging in branches so that you can look back and see past merges.
alias merge = merge --no-ff
Cherry-pick prior commits
Sometimes, you need to pull in changes to a branch without merging all the commits before and after it. For example, someone made a bug-fix several commits ago that you want to merge. How can you merge only one commit while ignoring the rest?
The answer is git cherry-pick
. Cherry-picking can ensure that you only merge clean commits into your central repository, and there are only a few steps necessary:
- Create a new branch off of your
master
branch. - Find the commit hash (the alphanumeric id for the commit) of the commit that you want to merge in by entering the
git log
command.
- Use
git cherry-pick
followed by the commit hash.
To cherry-pick a range of commits, use the following syntax:
git cherry-pick f1b62817d9^..98d2bba6d0
This stages commits f1b62817d9
through 98d2bba6d0
. The ^
indicates that the first commit is included. Otherwise, only the commits after the first one will be included.
Completing Pull Requests Locally Using Bitbucket
You’ll sometimes want to pull down a fork locally before merging with your central repository. This allows you to test thoroughly in a local branch without the chance of contaminating your code in production.
Note that this tutorial covers the use of Bitbucket. If you’re using Github, there is a slightly different method that is already well-documented.
We’ll use a pretend repository for this tutorial: the ‘cuttle-repo’ from Cuttlesoft that has been forked by user emilyemorehouse. When following along, you can swap out the URL of the repository with your own.
Since we use Bitbucket, the URL of the base repository is:https://bitbucket.org/cuttlesoft/cuttle-repo
and the forked repository is https://bitbucket.org/emilyemorehouse/cuttle-repo
.
A few key pieces of information:
- Pull requests will merge in the entire branch (making branch standards VERY important)
- New commits to a branch with an existing open pull request will also be included
- If accepted, a new pull request on the same branch will pull in all commits from that branch, not just existing ones
Now for a step-by-step guide:
Acquire New Changes
- Address any outstanding changes after using
git status
to find them. - Fetch changes from the branch you want – this will likely be a feature branch, as we encourage users to utilize standard branching structure.
Using SSH:
git fetch git@bitbucket.org:emilyemorehouse/cuttle-repo.git feature/new-thing
Using HTTPS:
git fetch https://bitbucket.org/emilyemorehouse/cuttle-repo feature/new-thing
View and Test Changes
- To view the changes that you’re about to merge, run:
git diff master FETCH_HEAD
The master
refers to the local branch we will be merging into. FETCH_HEAD
contains all changes that were brought down from the forked repository, (they won’t be visible using git status
quite yet).
- To see your new code before merging, run
git checkout FETCH_HEAD
. This will detach your HEAD, allowing you to test new changes in a clean environment, you should now see fetched changes when usinggit log
.
Merge Your Pull Request/Branch
- Next, checkout a new branch for your changes. Don’t push this branch, but use correct naming convention:
PR/new-thing
. Rungit checkout -b PR/new-thing
. - Now that your changes are populated to a new branch, merge it into your dedicated branch (or
master
).
You should also squash changes into a single commit using:
git checkout master
git merge --squash PR/new-thing
- Now it’s time to test. Make sure nothing breaks in your local repository. Test your code manually, run a test suite, and check for console errors.
Once done, commit and push:
git commit
git push
Alternative Steps
Steps 4 and 5 can be replaced, as it’s not absolutely necessary to checkout changes to a branch before merging. To merge into master
you use the following commands:
git checkout master
git merge --squash FETCH_HEAD
Test thoroughly, then:
git commit
git push
Make changes to a pull request before merging
Sometimes you need to quick-fix a pull request before merging. It could be a new bug, an API key accidentally committed, or incomplete documentation. Either way, you can make fixes before merging to keep your repository clean.
Method 1: Keep Changes to the Forked Repository
Use this method to fix an error immediately.
Fetch and checkout new commits normally.
Using SSH:
git fetch git@bitbucket.org:emilyemorehouse/cuttle-repo.git feature/new-thing
Using HTTPS:
git fetch https://bitbucket.org/emilyemorehouse/cuttle-repo feature/new-thing
Then:
git checkout FETCH_HEAD
Make any changes then commit and note the commit ID:
git commit -m "[#XXXXXX] fixed the thing"<br>[detached HEAD 8bb13a2] [#XXXXXX] fixed the thing<br>1 file changed, 1 insertion(+)
git checkout master
git merge 8bb13a2
git push
Method 2: Make the changes mid-merge
Another option is to make changes while you test your merge in Step 6. Be wary, changes made here will not be made in their own commit and won’t be included in the automatic merge commit message. This will make them harder to find later on, so make sure everything is exactly right before using this option.
References:
For Developers
When pull requests go wrong, it’s usually the repository manager who feels the burn. Here are some strategies that will make life easier for your lead developer.
Branching-Off
There are several different branching strategies that teams can employ.
- Release Branching keeps each release inside it’s own branch.
- Feature Branching keeps work for a single feature inside a single branch
- Task Branching is even more specific, and involves creating a new branch for every task, usually tied to a project management software like Jira or Pivotal Tracker.
In practice, most teams use a mix of these strategies, but either way, it’s important that everyone on your team is on the same page as far as what should be done in a branch. Generally, any experimental work, new features, or unstable code should be kept in a branch.
If you forgot to checkout a new branch before getting to work, you can use git checkout -b
to create a new branch and check it out immediately without losing work.
Commit Constantly
Making frequent, focused commits is one of the most important things you can do to keep your repository in good shape. One logical code change should equal one commit. That way, bugs and merge conflicts are easier to find. Lumping a bunch of changes in a single commit is a great way to get yourself in hot water.
Also, you should always use git diff
before committing to make sure you aren’t including any changes by accident, (a diff GUI like diff-so-fancy can help you see changes in greater detail).
Keep Messages On Point
Commit messages make your changes easier to navigate. Make sure that your commit messages indicate what parts of the code were changed, and why.
If you make a mistake in a commit message, use git commit --amend
to open up your editor to correct it. Be sure to never amend a commit that has already been pushed up to a shared repository, though.
Fix Mistakes
If you get off-track while using Git, always correct course before pushing or merging with others.
Add Patch Mode
Did you dump too many changes into a single commit? Fear not. You can use “add patch mode” to select chunks of code for their own commits. Enter git add -p
to sift through your changes and pick out individual lines for commits. Watch this video tutorial for add patch mode if you need help.
Stash
To switch tasks without committing (maybe to fix an urgent bug), you can use git stash
to “stash” a snapshot of your files. You can then switch to another branch, make a commit, switch back, and use git stash apply
to return your files to the “stashed” state.
Bisect
If you’re encountering a tricky bug and can’t identify the specific commit which caused it, you can use git bisect
to pinpoint that guilty commit. We’d describe it here, but The Practical Dev already has a great tutorial that we will refer you to.
Aliases for Better Commits
amend
# Command
git amend
# Alias
amend = commit --amend -C HEAD
- What it does: Commits staged changes as an amendment to your previous commit while keeping the same message as your previous commit.
- When to use it: When you need to fix a small mistake or typo without rolling back changes or creating a new commit just for a minor edit.
addmend
# Command
$ git addmend
# Alias
addmend = "!f() { git add --all && git amend; }; f"
# Note: uses amend alias
- What it does: Adds all changes and commits them as amendment to your previous commit, keeping the same message as your previous commit.
- When to use it: When you find yourself making corrections with ‘git amend’ and want to save a little bit of time/effort each time you do.
diffs
# Command
$ git diffs
# Alias
diffs = diff --staged
- What it does: Allows you to
diff
staged files (using the--staged
flag). - When to use it: When you want to view changes to staged files before committing. (Basically it just saves you some typing).
Aliases for Reviewing
hist
# Command
$ git hist
# Alias
hist = log --pretty=format:\"%h %ad | %s%d [%an]\" --graph --date=short
- What it does: Shows a list of past commits, formatted in a human-friendly manner.
- When to use it: When merging pull requests or cherry-picking commits, or when you want to easily see what commits are present in a branch.
yesterday
# Command
$ git yesterday
# Alias
yesterday = "!f() { git --no-pager hist --since=yesterday.midnight --until=today.midnight; }; f"
- What it does: Shows changes from previous day.
- When to use it: Daily meetings, billing, and reporting.
today
# Command
$ git today
# Alias
today = "!f() { git --no-pager hist --since=today.midnight; }; f"
# Note: uses hist alias
- What it does: Shows list of commands made today (since 12:00 am).
- When to use it: Reporting, wrapping up work for the day, or preparing for the weekend.
latest
# Command
$ git latest
# Alias
latest = diff HEAD~1 HEAD
- What it does: Shows you the git diff of the last commit that was made.
- When to use it: When you want to review the last changes you committed, especially when returning to work first thing Monday morning.
distance
# Command
$ git distance origin
$ git distance upstream master
# Alias
distance = "!f() { git --no-pager log --oneline --graph --first-parent --left-right --no-decorate HEAD...$1/${2:-$(git rev-parse --abbrev-ref HEAD)}; }; f"
- What it does: Shows you how far your current branch is from the corresponding (or specified) branch on the given remote.
- When to use it: When you want to see which commits you haven’t pushed up or pulled down yet.
compare
# Command
$ git compare branch1 branch2
# Alias
compare = "!f() { git log --oneline --graph --first-parent --left-right --decorate $1...$2; }; f"
- What it does: Shows which commits differ between the provided branches.
- When to use it: When you want to see how two branches differ, based on commits (for example, to check if all the changes in one branch are already in the other, or which ones are not).
TL;DR
- Cherry-pick only the commits you want
- Pull and test locally
- Make frequent, focused commits
- Use branches intelligently and consistently
- Write informative commit messages
- Fix mistakes before merging your code with others’
- Use aliases to speed up and simplify development
# Git Aliases: copy and paste the following code into your .gitconfig file under the [alias] heading.
[alias]
# amend
amend = commit --amend -C HEAD
# addmend # Note: uses "amend" alias
addmend = "!f() { git add --all && git amend; }; f"
# diffs
diffs = diff --staged
# hist
hist = log --pretty=format:\"%h %ad | %s%d [%an]\" --graph --date=short
# yesterday # Note: uses "hist" alias to format output
yesterday = hist --since=yesterday.midnight --until=today.midnight
# today # Note: uses "hist" alias to format output
today = hist --since=today.midnight
# latest
latest = diff HEAD~1 HEAD
# distance
distance = "!f() { git --no-pager log --oneline --graph --first-parent --left-right --no-decorate HEAD...$1/${2:-$(git rev-parse --abbrev-ref HEAD)}; }; f"
# compare
compare = "!f() { git log --oneline --graph --first-parent --left-right --decorate $1...$2; }; f"
# merge
merge = merge --no-ff
You don’t necessarily need to use all of these to use Git effectively. More important than any one tip or trick is good communication between team members. If everyone is on the same page as far as when to branch, when to commit, and how to make pull requests, you’ll (hopefully) be able to go home on time.
Git can’t fix every problem, but the strategies mentioned here can at least make sure that it doesn’t cause new ones. If you have questions about Git or any of the topics mentioned earlier, please leave a comment below.
In a source control rut? Feel free to contact us.