Skip to content
Eric Bouchut edited this page Jul 12, 2022 · 127 revisions

Create a Branch

Here is how to create a branch out from the tip of the current branch (HEAD).

git branch my_branch

Create a Branch from an existing commit

Here is how to create the branch named my_branch that starts from the commit with the SHA-1 1234567.

git branch my_branch 123467

Instead of a SHA-1, you can use any other commit reference for the starting point. You can use for instance another branch name (origin/master), or relative reference (HEAD~1).

Remove a Local Branch

To remove a local branch (say my_branch) from my local git repository:

git branch -d my_branch

Git refuses to delete a local branch which has not been merged into another branch.
To remove such a branch use the -D option to bypass this safeguard.

git branch -D my_branch

Remove a Remote Branch

To remove a branch from the remote repository. Say, for instance, I want to remove my_branch from the remote repository named origin:

git push --delete origin my_branch  # if git version >= 1.7.0

If you are using git version < 1.7.0, then the previous command should not have worked saying the --delete option is not supported, use the following command instead.

git push origin :my_branch  # if git version < 1.7.0

Please note the colon : preceding the name of the branch. The colon acts as a separator in between the name of the local branch to push and the name of the remote branch where to push it to. In the example above pushing nothing (no local branch) to the remote branch my_branch has the effect of removing the remote branch my_branch.

List Local Branches

The below command list the local branches and gives you a hint about the branch you are currently sitting on.

git branch
 * master
   my_branch

There are 2 local branches masterand my_branch. The star (*) next to a branch (master in this example) indicates the current branch.

What is the current Branch?

git branch Cf. section List the Local Branches. or

git rev-parse --abbrev-ref HEAD

List Local Branches without the star prefix

The below command list the local branches without the star (*) in front of the default branch.

git branch --format'%(refname:short)'
master
my_branch

This is useful to test if local branch is present like so:

git branch --format='%(refname:short)' | grep '^rainbow'

List Remote Branches

git branch -r
  origin/batch-to-cassandra
  origin/json-serialization-tests
  origin/master

Each remote branch is prefixed with the remote name followed by a slash ('/').

List Local and Remote Branches

git branch -a
 * master
   my_branch
   origin/batch-to-cassandra
   origin/json-serialization-tests
   origin/master

Jump on a Branch

To change the current branch and switch to the local and existing branch my_branch:

git checkout my_branch

To go to the branch you were on previously (the branch before last you checked out).

git checkout  -

This is in the same vein as cd - in bash / zsh.

git checkout master
git checkout featureX

git checkout -
# You are now on the master branch

Checkout a remote branch

Here is how to create a local branch named my_branch out of the remote one origin/my_branch, configure it to track the remote, then jumps on the local branch. Things are different depending on the version of git you are using.

  • When using git version < 1.6.6 (Option 1)
git fetch
git checkout -b my_branch --track origin/my_branch
  • When using git version >= 1.6.6 (Option 2)

If there is single remote with a branch named my_branch:

git fetch
git checkout my_branch

Branch my_branch set up to track remote branch my_branch from origin.
Switched to a new branch 'my_branch'

If there are multiple remotes with a branch named my_branch, git cannot guess the one you are talking about. In this case, it will probably checkout the last commit of the remote branch my_branch and won't create a local branch.
You will end-up in a detached HEAD state, ie on a specific commit instead of being on a newly created local branch. Here is a 2 step fix, to leave the detached HEAD state and create the local branch. First-off jump on another branch like master for instance. Then remove the ambiguity and specify the name of the local and remote branch so that git knows exactly where to check out the branch from and where to create it.

git checkout master`
git checkout -b my_branch --track origin/my_branch

Stale Branches

When a remote branch that you previously pulled off, has been removed from the remote repository (say origin in this example), your local remote cache contains a stale reference to a branch that no longer exists on the remote.

List the Stale Branches

The branches patch-2 and 1.1.2 are stale because they no longer exist on the remote origin.

git remote show origin

* remote origin
  Fetch URL: git://github.com/ebouchut/rickshaw_rails.git
  Push  URL: git://github.com/ebouchut/rickshaw_rails.git
  HEAD branch: master
  Remote branches:
    master                      tracked
    patch-1                     tracked
    refs/remotes/origin/1.1.2   stale (use 'git remote prune' to remove)
    refs/remotes/origin/patch-2 stale (use 'git remote prune' to remove)
    typo-fix                    tracked
  Local branch configured for 'git pull':
    master merges with remote master
  Local ref configured for 'git push':
    master pushes to master (up to date)

Remove Stale Branches

To clean things up and remove all (local) stale branches for the remote origin, use this:

git remote prune origin

You can also kill 2 birds with one stone, asking git to prune stale branches while fetching from a remote.

fetch -p origin

Rename a Local Branch

To rename the local branch old to new, proceed as follows:

git branch -m old new

Rename both a Local and Remote Branch

Say you want to rename the branch old to new. This branch also exists on the remote repository named origin.

  • Rename the local branch (old ➞ new)
git branch -m old new
  • Delete the remote branch (old)
git push origin :old          # if git version < 1.7.0
git push origin --delete old  # if git version >= 1.7.0

No need to say that as this branch is public, this may cause problem if someone has cloned the repository and is working on this branch. Coordinate in advance with users working with this branch to reduce the impact"

  • Push the renamed branch (new)
git push -u origin new

Rename the master branch to main

git checkout master

# Rename master to main
git branch -m master main

# Push main to origin/master
git push origin HEAD:master

# Push main to origin/main
git push origin HEAD

# Update all of your CI pipelines referencing master

# Rename the **default** branch main here:
#   https://github.com/YOU/YOUR_PROJECT/settings/branches

# Remove the local branch master
git checkout main
git branch -D master

# Unset the removed upstream branch origin/master
git branch --unset-upstream

# Set the new upstream branch as origin/main
git push --set-upstream origin main

# Delete origin/master
git push origin --delete master

Create a Branch after the fact

Sometimes you forgot to create a branch, and decide later on to create it after the fact.

  • First thing is to spot the commit you want to create the branch from and use its SHA-1 (say a123456 for the sake of example) in the command below.

  • Then create the branch

git branch my-branch
  • And restore the state of the working directory and the staging area as of commit a123456 (where you want the branch my-branch to start from)
git reset --hard a123456

Rebase a Branch

Say I want to rebase the branch master onto my_branch.

# Jump on the (destination) branch you want to rebase onto
git checkout my_branch

# rebase the source branch (master) onto the current branch (my_branch)
git rebase master 

There is another variant to do the same thing that works whatever the current branch.

git rebase --onto=my_branch  master 

Sources:

Interactive Rebase

When doing an interactive rebase of a feature branch you usually count how many commits before HEAD where to start the interactive rebase. For instance assuming you need to perform an interactive on the 4 most recent commits, you will do this:

git rebase -i HEAD~5    # interactive rebase on ] HEAD~4, HEAD]

This will rebase starting from commit HEAD~5not included (ie. HEAD~4) up to and including HEAD.

Instead of counting each and every time the commits on which to perform an interactive rebase you can use instead this trick which always works whatever the number of commits.

If you are using the Git Flow workflow then your feature branch branched of the develop branch

# Assuming, I'm on the feature "feature/XXX" (branched off develop like all git flow feature branches are)
git rebase -i develop

If you are using the Github flow instead, ie. you are on the feature branch named say XXX branched off master:

# Assuming I'm on the feature "XXX" (branched off master)
git rebase -i master

Keep merge commits when doing a git pull --rebase

Say you are on master and merge in the feature branch my_branch, like so:

git checkout master
git merge --no-ff my_branch

The commit at the tip of the master branch is a merge commit saying "Merge branch my_branch into master".

You now want to push your changes upstream (ie. push master up to origin/master). But origin/master has moved since the last time you pulled it off. To be able to push, you first need to pull origin/master.

But, if you do git pull --rebase, you will notice the merge commit(s) gets removed. This default behaviour is a bit annoying at first, here is the workaround to keep the merge commits.

Instead of git pull --rebase you need to do this 2 steps process:

  1. fetch (origin/master)
  2. rebase master (against origin/master) using the magic incantation -p asking git to preserve the merge commit(s)
# git checkout master
git fetch
git rebase -p origin/master

As far as I know, there is no shorter way to do this.

Source

Track a Remote Branch

Here is how to make the existing local branch my_branch to track the remote branch origin/my_branch, depending on the version of git you are using:

  • git version >= 1.8
# This is the shorter form
git branch -u origin/my_branch my_branch

# or the longer and more verbose form (which does the same thing)
git branch --set-upstream-to=origin/my_branch my_branch
  • git version >= 1.7.0
git branch --set-upstream my_branch origin/my_branch

List Tracked Branches

git branch -lvv    # "lvv" as in  list verbose verbose

* my_branch 479916b [origin/my_branch] Fix typo
  master    6209803                    

The branch my_branch is configured to track origin/my_branch.

Source

Stop Tracking a Remote Branch

Assuming you no longer want to track the branch my_branch from the remote origin. Here is how to do this depending on the version of git you are using.

# git version < 1.8

git config --unset branch.my_branch.remote
git config --unset branch.my_branch.merge
# git version >= 1.8
git branch --unset-upstream my_branch

You may also want to remove your local cache of the remote branch. This does not delete my_branch on the remote origin.

git branch -d -r origin/my_branch

List Branches merged into origin/master

List the local branches that have been merged into origin/master.

git branch --merged origin/master

List the remote branches that have been merged into origin/master.

git branch -r --merged origin/master

List Branches not merged into origin/master

List the local branches that have not been merged into origin/master.

git branch --no-merged origin/master

List the remote branches that have not been merged into origin/master.

git branch -r --no-merged origin/master

Compare Branches across Repositories

When you want to compare branches from different github repositories none of the repository being a fork of the other. I did not manage to find a way to do this using Github's UI. I needed to go back to git's CLI to do this and here is my recipe.

Say I need to compare the differences between branch branch1 from user1/repo1 and branch2from user2/repo2.

# Clone the user1/repo1 (assuming we have write permission on this one)
git clone [email protected]:user1/repo1.git

cd repo1

# Add a remote named repo2 pointing to user2/repo2
git remote add user2 https://github.com/user2/repo2.git

# Populate user2 the the local cache of the remote repository user2/repo2
git fetch user2

# Compare branches now.

# Changes in branch2 that are not in branch1
git log --oneline --decorate --graph origin/branch1..user2/branch2

# Changes in branch1 that are not in branch2
git log --oneline --decorate --graph origin/branch2..user2/branch1

FAQ Error: "refname origin/XXX is ambiguous"

You may end up with this error after attempting to create a local from a remote branch. However if you use the wrong syntax, git branch origin/my_branch instead of git branch my_branch origin/my_branch.

# We intend to create a local branch "my_branch" out of the remote "origin/my_branch"
# But... we use the wrong syntax
git branch origin/my_branch 

We end up with a local branch named origin/my_branch.

From now on each time you interact with the remote (git push, git pull) you will see this error. Git finds a local branch named origin/my_branch which is an ambiguous name reference because of the remote branch with the same name origin/my_branch.

What is the Problem?

I found 2 ways to find to spot the problem:

  • branch -lvv
  • git show-ref
git branch -lvv  # Output: branch_name SHA-1 [tracked_remote_branch] latest_commit_message 

* master           bf3d207 [origin/master] Add postgres support
  origin/my_branch abc1234                 Fix Bug #12345

You can see the misnamed local branch origin/my_branch does not track a remote branch (because there is no remote branch name in between square brackets).

You can also list all the references that contains origin/my_branch.

git show-ref  origin/my_branch

abc123493885415d89d70dcad55e561ccd0df417 commit	refs/heads/origin/my_branch
aab15e599727918323b7fd7604df536cae316b2c commit	refs/remotes/origin/my_branch

References of a:

  • local branch starts with refs/heads/
  • remote branch (from the remote origin) starts with refs/remotes/origin

Most of the time we do not use the full ref name of a branch refs/heads/master but its shorter form master. This works as long as there is no ambiguity!

The shortcut origin/my_branch is ambiguous here because it can refer to 2 full ref names:

  • refs/heads/origin/my_branch
  • refs/remotes/origin/my_branch.

Fix the problem

Using the full ref name of the branch like refs/remotes/origin/my_branch is possible to remove ambiguity but is not a long term solution. I see two options:

  • You do not have modifications on this local branch:

In this case, remove it and recreate the branch this time using the correct syntax, like so:

git checkout master

git branch -D origin/my_branch
git branch my_branch origin/my_branch
  • You have commits on the branch that are not yet pushed to origin

In this case, rename the branch using its non ambiguous full name (the long ref name: refs/heads/origin/my_branch)

git branch -m refs/heads/origin/my_branch  my_branch

Run a Dry-Run Merge

Sometimes you need to check what a merge would do without really doing it. As git merge does not provide the usual --dry-run option, here is an alternative.

git merge --no-commit --no-ff my_branch

# Now, check things are as expected
git diff --cached

# Abort the merge
git merge --abort

Merge a branch without doing a git checkout beforehand

This trick allows you to merge a branch you are not on, so that you do not need to jump on the branch you want to merge.

Say for instance:

  • you are on a feature branch (featureX), you do not want to checkout any other branch
  • and you want to merge another branch, origin/master onto master but without doing the usual git checkout master beforehand.

There are 2 possible options depending on whether merging origin/master onto master requires a fast forward merge or not.

  • Fast Forward Merge
# When not on the master branch
git fetch origin master:master
  • Non Fast Forward Merge
# When not on the master branch
git fetch origin +master:master

Note the plus sign (+) before the remote branch name

Source

Clean the working tree

To remove files -f and directories -d git does not track:

git clean -d -f

You can also use the -n to let git make a dry-run where it says what it will remove without doing it.

git clean -d -f -n

Force Pull a Branch

First off, make sure you do successfully set aside your stuff in the current working directory using stash for instance. To do the equivalent of a force pull for the current branch which AFAIK does not exist. This will first-off clear the working directory and the cache/index then move the branch pointer to its remote counter part.

git fetch

git checkout my_branch

# Assuming you are on the branch my_branch
git reset --hard  origin/my_branch

Show the Content of a File on a given Branch

Here is how to show the content of the file CHANGELOG.md on the branch origin/master.

git show origin/master:CHANGELOG.md

Please do note:

  • there is a colon (:) in between the branch name and the file path
  • the file path must be relative to the project's root directory

For instance to view the content of the file src/main/resources/config.properties on the develop branch: git show develop:src/main/resources/config.properties

Is a branch is up to date with origin

Here is how to test if a branch is up to date with its remote counterpart in a shell script. Here we use bash to test if develop is up to date with origin/develop. The trick is to retrieve and compare the SHA1 of the tip of both the local and remote develop branches. If the SHA1s are different then the branch is not up to date.

git fetch 

if [ $(git rev-parse develop) != $(git rev-parse develop@{u}) ]; then
  # Branch develop is not up to date with origin.
fi

The shorthand reference develop@{u}denotes the develop branch on the upstream repository. If develop has been checked out from origin, then this is equivalent to origin/develop.

List Git Flow Release Branches not finished yet

The command git flow release start does not check if a remote release branch already exists before creating a local one. It only checks if one exists locally which is a pity! Because of this you may end up with more than one release branch and this something that I want to avoid as it may prove very difficult to handle.

ℹ️Here comes git to the rescue with a handy git alias to list (local and remote) git flow release branch(es) not finished yet (ie. the ones not yet merged into master).

git config --global alias.releases '!git fetch ; git branch -a --no-merged origin/master | grep "$(git config --local --get gitflow.prefix.release)" # List git flow releases not finished yet'

Once created, I can use the releases git alias to see if there is a release branch underway so that I do not create a new one as long as the existing release branch is not finished, like so:

git releases

  remotes/origin/release/1.10.0

In this example release 1.10.0 exists on origin and is not finished. I therefore now know that I must not create a new release up until this one is finished.

List Git Flow Hotfix Branches not finished yet

The command git flow hotfix start does not check if a remote release branch already exists before creating a local one. Because of this you may end up with more than one hotfix branches.

ℹ️Here comes git to the rescue with a handy git alias to list (local and remote) git flow hotfix branch(es) not finished yet (ie. the ones not yet merged into master).

git config --global alias.hotfixes '!git fetch ; git branch -a --no-merged origin/master | grep "$(git config --local --get gitflow.prefix.hotfix)" # List git flow hotfixes not finished yet'

Once created, I can use the hotfixes git alias to see if there is a hotfix branch underway so that I do not create a new one as long as the existing one not finished, like so:

git hotfixes

  remotes/origin/hotfix/1.10.1

In this example hotfix 1.10.1 exists on origin and is not finished. Now I know that I must not create a new hotfix up until this one is finished.

Clone this wiki locally