Why compare branches?
If you have a solid branch process, you know the importance of comparing them before a merge. Whether you’re working on a new feature or bug fix, having the ability to see how branches differ can provide a ton of insight. Beanstalk allows you to do this with ease by using the Compare View feature. But if you’re using the terminal to get things done, Git offers a few handy commands for comparing branches.
This guide will help you understand how comparing branches can help your development process. First, we’ll discuss how to compare branches on the command line. Then we’ll talk about Git merge tools and branch workflows later.
git status does well at informing you of what has changed at a high level, but there are other commands that can help you identify changes in detail. Before we get into the specific commands, let’s discuss a common scenario where comparing branches could be useful.
Let’s say you’ve been working on a feature for a few weeks. You’re almost ready to merge your code, but you want to compare the files on your
feature branch with the
master to see if anything has changed. You can use the
git diff command to compare branches.
git diff command allows you to view the differences of two versions. For this example, let’s compare the local
master branch with the remote
master branch. To do this, run the
git fetch command so that you’ll have the latest changes from your remote branch, and then run:
git diff (local-branch) (remote-branch)
If you’d like to get more specific, you can compare two different revisions. Run:
git diff 0023cdd..fcd6199
Reading the output
Reading the output of the
git diff can intimidating at first, but let’s break this down in detail. Let’s say we want to compare two branches where the about.html file were changed. After running the
git diff command, here’s the output:
diff --git a/about.html b/about.html index db35ae1..6a0d181 100755 --- a/about.html +++ b/about.html @@ -8,8 +8,8 @@ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"> <meta content="black" name="apple-mobile-web-app-status-bar-style"> - - <title>Selfy </title> + + <title Selfyness </title> <meta name="description" content=""> <link rel="stylesheet" type="text/css" href="css/normalize.min.css">
At the top, Git tells you the two files that have changed. In this case, here are the files we’re comparing: a/about.html b/about.html. These are the compared files. There is an A version, and a B version. There are instances where version A and B are two different files, but in most cases they are the same. To be sure, Git will always define what file represents item A or B. The symbols to the left of the about.html are file markers. These markers help you to identify which file the changes belong to. In this example
- represents the A version of the about.html. Each time there is a
- mark next to a set of changes, then that means those modifications belong to the A version of the about.html file. The
+ represents the b/about.html file.
Chunks and changes
Chunks are a set of related changes. When you see the
@@ symbol, that means you’re at the beginning of a new chunk or set of modifications. In this example, the a/about.html version is showing the title to be:
<title> selfy </title>
But the B version (b/about.html) shows that the title is <title> Selfyness </title>. Remember, we know that this change relates to the B version because of the + symbol.
Note: The diff command shows modified changes, not the entire file itself. This allows you focus on comparing changes, without having to sift through several hundred lines of code to locate them.
There are scenarios where your output could look different. What if your output looks like this:
diff --git a/about.html b/about.html index db35ae1..6a0d181 100755 --- a/about.html +++ b/about.html @@ -16,5 +21,6 @@ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"> <meta content="black" name="apple-mobile-web-app-status-bar-style"> + <p>I graduated from college in 2001</p> + <p>After college, I moved to France.</p> <meta name="description" content=""> <link rel="stylesheet" type="text/css" href="css/normalize.min.css">
It looks like the changes were only made to one version of the file. But there is more to what we’re seeing here. The
+ symbol indicates that there were lines of code added to version B. Since the
- symbol is missing, this indicates that the code does not exist, at least on version A. This means that the code represented by the
+ was added to the file and does not exist on version A.
If the scenario was reversed, and a line has been deleted, the output may look like this:
diff --git a/about.html b/about.html index db35ae1..6a0d181 100755 --- a/about.html +++ b/about.html @@ -16,5 +21,6 @@ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"> <meta content="black" name="apple-mobile-web-app-status-bar-style"> - <p>I graduated from college in 2001</p> - <p>After college, I moved to France.</p> <meta name="description" content=""> <link rel="stylesheet" type="text/css" href="css/normalize.min.css">
The changes in a/about.html were not found in the b/about.html because the lines were deleted.
Comparing with Beanstalk
You can also compare branches within Beanstalk itself. The compare-view feature allows you to compare branches, and specific revisions in just a few clicks.
- View the latest changes without using the command line, or a diff tool.
- Search for specific commits within a branch, and compare them to another.
- Filter commits based on the user to see who made what changes.
- Assign code reviews while comparing branches to review and collaborate with your team.
In the brief video below, we demonstrate how using the compare-view feature can save you time, and minimize your toolset.
Using a diff tool
If you’re looking for an easier way to read Git diff output, then you have a few options. You can use a Git diff merge tool to help you see changes with ease. A diff tool is an application that allows you to view comparisons in a clear, and understandable format. There are several different tools available, but they all share the same purpose. Here are a list of a few diff tools that can help you inspect differences and change code if needed.
Now that you know how to compare the differences in branches. Let’s talk about how the types of branches that exist and how to use them in your workflow.
In the ideal world, branches should only last a few days. But if you’re working with a large team, there are scenarios where you may need a longer running branch. In this section, we’ll discuss the difference between short and long lived branches.
Short lived branches focus on specific topics like bug fixes, features, or experiments. These branches are normally safe to delete after merging into the
master branch. Once a short lived branch is complete it can be integrated into the
master branch depending on your workflow.
Short lived branches are also known as topic branches, and can be useful in any project. It’s important to know that all branches are created equal. There are no specific command you can use to create a short or long lived branch. But you can decide the purpose of a branch. If you want to use a branch to fix a bug and merge your changes, then you have a short lived branch. Here's the main takeaway when working with short lived branches.
- Short lived branches can usually be deleted at some point after completion. Remember, branches are pointers to a single commit. Deleting the branch does not undo all your hard work as long as it was merged to stable.
Long lived branches are on the opposite end of the totem pole. These branches are stable and often have a permanent place in your repository. One example of a long lived branch is the
master branch. The
master branch is for production ready code.
Depending on your workflow, the
develop branch can be used in a similar way. It can be used for testing features or bug fixes before they go into production. For example, once you complete a feature you can merge it into your
develop branch. Once the code is reviewed and tested, then it's merged into the master. Are you noticing the pattern here?
Long lived branches are rarely (if ever) deleted because they represent different points in your project’s lifecycle. Here are a few general rules to keep in mind when working with them, but none of these points are set in stone.
- Short lived branches are often integrated into long lived branches. It’s rare that you’ll need to work on a long lived branch directly.
- Set clear rules on how to create and use long lived branches. If there is confusion over where feature branches should be merged, problems will exist.
Now that we know the beauty of short and long lived branches, how are they used? There are so many workflows that exist, how do you choose what’s best for your team? Since each team is unique, this will not be a one size fits all scenario. But there is a basic workflow that could be helpful to a team starting out.
Things to keep in mind
- Always create a branch if you’re working on a new feature, bugfix, or experiment. This is true even if the bugfix or feature you’re adding seems insignificant or hardly noticeable. Try your best to avoid committing directly on the
- When creating a topic branch, always base from the
- After completing a topic branch, merge your changes into your
developbranch for testing. Remember, the
developbranch is often a long lived branch. This branch is for testing features and bug fixes before pushing to
- Review and test all code before merging into the
master. We recommend using some form of quality assurance. For example, code reviews are helpful for double checking work, and mentoring inexperienced developers.
masterbranch should always be stable.
Sometimes when working on a feature, the
master branch gets updated before you’ve completed your task. This can cause confusion, and even worst, merge conflicts. We’ve written articles on how to deal with merge conflicts. In this guide though, we’ll focus on one way to conflicts as a whole.
Keep changes in sync
Let’s go back to our example above. You’re working on a
feature branch and your colleague just pushed his new bugfix to production. Now you’re out of sync with the
master, and you’ve already started working on your new feature. For this reason, we recommend merging new changes from the
develop branch as often as possible. This keeps changes stable, and avoids conflicts when your code is ready to go into production.
Another way to prevent conflicts and confusion is to be consistent when naming branches. For example, let’s say you’re trying to fix a bug on your client’s website. You create a new branch and name it “bug fix”. While this name describes the purpose of the branch, it doesn’t do much else. Does the bug fix have a reference number? If so, include it when naming the branch. If you’ve named a branch “bug-fix-223” on your local environment, be consistent by giving it the same name in your remote repository.
As we’ve discussed, there are a lot of options when it comes to branching workflows. One popular workflow is Git flow. Git flow is often used by larger teams that have more specific needs. It’s a strict branching model that is useful for managing larger projects. You can find several different workflows online, but here are a few others that are well known.
What is rebasing?
In simple terms, rebasing is the process of moving one branch from one place to another. Rebasing is another way to integrate branches. The
git merge command is the easiest way to integrate branches, but there are scenarios where some prefer the rebase method. Let’s consider how rebasing works and why some teams use this approach.
Why some teams rebase
If you’ve ever merged one branch into another, you’ve seen a merge commit. A merge commit is a commit that’s made by Git (not a developer) to tie two branches together. For example, let’s say you’re working on a
feature branch and you’re ready to merge it into your
When you merge, Git automatically creates a new commit that joins both branches together.
This single, merge commit includes all the code in your
feature branch and adds it to the
develop branch. In a sense, it’s a knot that joins two branches together.
This process is completely acceptable to some developers. But there are instances where you may prefer to go without an automatic merge commit. Why? Not having merge commits can keep your repository history clean.
Some teams prefer to have their history look as if it was not composed of multiple branches at some point. If you want to avoid merge commits you can fix this by rebasing. But there are some risks involved when taking this approach.
What we recommend
Rebasing does have drawbacks. One common misconception about rebasing is that you’re simply moving commits around without changing the history. This is not correct as rebasing will rewrite your history. In essence, when you rebase you change all the commits made since splitting from the last branch. Why? In Git, commits are immutable and can’t be moved.
Each commit has its own unique parent and properties. If you could (hypothetically) move a commit, you’d be changing it’s parent commit. This simply is not possible with Git, hence, why when you rebase you’re making new commits. This can easily become a problem if the commits that have been rebased are published. Another developer may have already produced work referencing the old commits, before the rebase occurred.
In this light, we do not recommend rebasing. As stated earlier, rebasing will rewrite your history and is often too much risk for very little gain. It can affect the workflow of your entire team. If there is one gap of miscommunication, and a developer rebases commits that have been published, you could find yourself in a lot of trouble.
There are many different workflows, and tools available when working with Git. Understanding how to compare branches, and use merge tools will solidify your process and help you write better code. Knowing the difference between merging, rebasing, and it’s drawbacks will keep you from making trivial mistakes, and save you loads of time.