Git branches

$ git branch [-a] [-l "GLOB_PATTERN"]

shows all local branches in a repository.

-a

also shows all removed branches.

-l

restricts the branches to those that correspond to a specific pattern.

$ git branch --sort=-committerdate

sorts the branches according to the commit date.

You can also use git config --global branch.sort -committerdate to make this setting your default setting.

$ git branch BRANCH_NAME

creates a new branch based on the current HEAD.

$ git switch [-c] BRANCH_NAME

switches between branches.

-c

creates the branch to switch to.

$ git switch -

alternates between the last two branches. This makes it much easier to jump back and forth between branches.

Note

In Git < 2.23, git switch is not yet available. In this case you still need to use git checkout:

$ git checkout [-b] [BRANCH_NAME]

changes the working directory to the specified branch.

-b

creates the specified branch if it does not already exist.

$ git merge FROM_BRANCH_NAME

connects the specified branch with the branch you are currently in, for example:

$ git switch main
$ git merge hotfix
Updating f42c576..3a0874c
Fast forward
 setup.py |    1 -
 1 files changed, 0 insertions(+), 1 deletions(-)
Fast forward

means that the new commit immediately followed the original commit and so the branch pointer only had to be continued.

In other cases the output can look like this:

$ git switch main
$ git merge 'my-feature'
Merge made by recursive.
 setup.py |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)
recursive

is a merge strategy that is used when the merge is only to be done to HEAD.

Merge conflicts

Occasionally, however, Git runs into issues with merging, such as:

$ git merge 'my-feature'
Auto-merging setup.py
CONFLICT (content): Merge conflict in setup.py
Automatic merge failed; fix conflicts and then commit the result.

The history can then look like this, for example:

*   49770a2 (HEAD -> main) Fix merge conflict with my-feature
|\
| * 9412467 (my-feature) My feature
* | 46ab1a2 Hotfix directly in main
|/
* 0c65f04 Initial commit

Improved conflict display with zdiff3

Git normally displays merge conflicts as follows:

<<<<<<< HEAD
This line has been changed by feature one.
This line has also been changed by feature one.
This line will be changed by feature two.
=======
This line is changed by feature one.
This line has been changed by feature two.
This line has also been changed by feature two.
>>>>>>> feature_two

The lines of the merge target are located between the markers <<<<<<< and =======. The lines between the markers ======= and >>>>>>> are the lines of the merge source. The labels after the arrow markers name the commit references that are merged.

This is often sufficient to resolve a conflict. But it can also be unnecessarily challenging because the original lines from which both sides started are missing. The common ground from which both sides started creates clarity about the context in which both changes arose.

If you set merge.conflictStyle to zdiff3, you can also display the common basis:

$ git config --global merge.conflictStyle zdiff3

Here is the same merge with this style:

<<<<<<< HEAD
This line has been changed by feature one.
This line has also been changed by feature one.
This line will be changed by feature two.
||||||| 45d92bd
This line is changed by feature one.
This line will be changed by feature one and feature two.
This line will be changed by feature two.
=======
This line is changed by feature one.
This line has been changed by feature two.
This line has also been changed by feature two.

The common base is now displayed between the markers ||||||| and ======= with the SHA value of the common base. This additional context is often useful for resolving a conflict.

rerere to reuse recorded conflict resolutions

rerere makes it easier for you to have to resolve the same merge conflicts again and again. This can happen, for example, if you merge a commit into several branches or if you have to rebase a branch repeatedly. Resolving merge conflicts requires concentration and energy, and it is a waste to resolve the same conflict again and again. git rerere is rarely called directly, however, but is usually activated globally. It is then automatically used by git merge, git rebase and git commit. Its most important effect is that it adds some messages to the output of these commands. You can activate it with:

$ git config --global rerere.enabled true

Let’s look at an example of git rerere in action. Suppose you attempt a merge and run into conflicts:

% git merge rerere-example
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Recorded preimage for 'README.md'
Automatic merge failed; fix conflicts and then commit the result.

git rerere wrote the third line, Preimage for 'README.md', meaning that the conflict was recorded before we fixed it. If we fix the conflict now, we can proceed with the merge, in our example with:

$ git add README.md
$ git merge --continue
Recorded resolution for 'README.md'.
[main 5935d00] Merge branch 'rerere-example'

git rerere now reports conflict resolution recorded for 'README.md'., meaning that it has saved how we resolved the conflicts in this file. Suppose you undo this merge because you realise that it was not finished:

$ git reset --keep @~

Later you repeat the merging process:

$ git merge rerere-example
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Resolved 'README.md' using previous resolution.
Automatic merge failed; fix conflicts and then commit the result.
When finished, apply stashed changes with `git stash pop`

git rerere solved the conflict using the previous solution, which means it reused your previous merge. Now check that the file is correct and then continue:

$ git add README.md
$ git merge --continue
[main c922b21] Merge branch 'rerere-example'

git rerere saves its data within the .git directory of your Git repository in an rr-cache directory. You should note two things here:

  1. The rerere cache is local. It is not shared when you perform a git push, so your team colleagues cannot reuse the merges you have performed.

  2. Git’s automatic garbage collection deletes entries from the rr-cache. It is controlled by two configuration options:

    gc.rerereResolved

    determines how long entries for resolved conflicts are kept. The default value is 60 days. And with git config gc.rerereResolved you can change the default values for your project.

    gc.rerereUnresolved

    determines how long entries for unresolved conflicts are kept. The default value is 15 days.

Aliases for faster resolution of merge conflicts

Resolving conflicts when merging can be time-consuming, but aliases can speed up the process.

You can use the –diff-filter option for git diff to display only the files that are not merged:

$ git diff --name-only --diff-filter U

You can also create a Git command alias:

$ git config --global alias.list-unmerged '!git diff --name-only --diff-filter U'

And to edit the non-merged files, you can create a Git command alias:

$ git config --global alias.edit-unmerged '!git diff --name-only --diff-filter U | xargs -r $(git var GIT_EDITOR)'

Now you can edit all unmerged files with git edit-unmerged and then add all files to the staging area with git add -u.

See also

I took the editor variable from the gitalias project. And maybe you’ll find more ideas for your alias there.

Delete branches

$ git branch -d [BRANCH_NAME]

deletes the selected branch if it has already been transferred to another.

-D instead of -d forcing the deletion.

Remote branches

So far, these examples have all shown local branches. However, the git branch command also works with remote branches. To work with remote branches, a remote repository must first be configured and added to the local repository configuration:

$ git remote add origin https://ce.cusy.io/veit/NEWREPO.git

Add remote branches

Now the branch can also be added to the remote repository:

$ git push origin [BRANCH_NAME]

With git branch -d you delete the branches locally only. To delete them on the remote server as well, you can type the following:

$ git push --set-upstream origin [BRANCH_NAME]

If you want to add all branches of a local repository to the remote repo, you can do this with:

$ git push --set-upstream origin --all

You can configure the following so that this happens automatically for branches without a tracking upstream:

$ git config --global push.autoSetupRemote true

Delete remote branches

To remove remote branches locally, you can run git fetch with the --prune or -p option. You can also make this the default behaviour by enabling fetch.prune:

$ git config --global fetch.prune true

See also

PRUNING

Rename branches

You can rename branches, for example with

$ git branch --move master main

This changes your local master branch to main. In order for others to see the new branch, you must push it to the remote server. This will make the main branch available on the remote server:

$ git push origin main

The current state of your repository may now look like this:

$ git branch -a
* main
  remotes/origin/HEAD -> origin/master
  remotes/origin/main
  remotes/origin/master
  • Your local master branch has disappeared because it has been replaced by the main branch.

  • The main branch is also present on the remote computer.

  • However, the master branch is also still present on the remote server. So presumably others will continue to use the the master branch for their work until you make the following changes:

    • For all projects that depend on this project, the code and/or configuration must be updated.

    • The test-runner configuration files may need to be updated.

    • Build and release scripts need to be adjusted.

    • The settings on your repository server, such as the default branch of the repository, merge rules and others, need to be adjusted.

    • References to the old branch in the documentation need to be updated.

    • Any pull or merge requests that target the master branch should be closed.

After you have done all these tasks and are sure that the main branch works the same as the master branch, you can delete the master branch:

$ git push origin --delete master

Team members can delete their locally still existing references to the master branch with

$ git fetch origin --prune

Tip

With git symbolic-ref you can create aliases, for example:

$ git symbolic-ref refs/heads/main refs/heads/master
$ git symbolic-ref refs/remotes/origin/main refs/remotes/origin/master