loading...

Git – Branches

How to Install Intellij IDEA on Windows 10

A branch is the fundamental means of
launching a separate line of development within a software project. A branch
is a split from a kind of unified, primal state, allowing development to
continue in multiple directions simultaneously and, potentially, to produce
different versions of the project. Often, a branch is reconciled and merged
with other branches to reunite disparate efforts.

Git allows many branches and thus many different lines of development
within a repository. Git’s branching system is lightweight and simple.
Moreover, Git has first-rate support for merges. As a result, most Git users
make routine use of branches.

This chapter shows you how to select, create, view, and remove
branches. It also provides some best
practices, so your branches don’t twist into something akin to a manzanita.[17]

Reasons for Using Branches

A branch can be created for a countless number of technical,
philosophical, managerial, and even social reasons. Here is just a
smattering of common rationales.

  • A branch often represents an individual customer release. If you
    want to start version 1.1 of
    your project but you know that some of your customers want to stick
    with version 1.0, then keep the old version alive as a separate
    branch.

  • A branch can encapsulate a development phase, such as the
    prototype, beta, stable, or bleeding-edge release. You can think of
    the version 1.1 release as a separate phase, too; the maintenance
    release.

  • A branch can isolate the development of a single feature or
    research into a particularly complex bug. For example, you can
    introduce a branch for a well-defined and conceptually isolated task
    or to facilitate a merge of several branches prior to a
    release.

    It may seem like overkill to create a new branch just to fix one
    bug, but Git’s branching system encourages such small-scale
    use.

  • An individual branch can represent the work of an
    individual contributor. Another branch—the integration
    branch—can be used specifically to unify efforts.

Git refers to a branch like those just listed as a
topic branch or a development
branch
. The word topic simply indicates that
each branch in the repository has a particular purpose.

Git also has the notion of a tracking
branch
, or a branch to keep clones of a repository in sync.
Chapter 12 explains how to use a tracking
branch.

Branch or Tag?

A branch and a tag seem similar, perhaps even
interchangeable. So when should you use a tag name and when should you
use a branch name?

A tag and a branch serve different purposes. A tag is meant to be
a static name that does not change or move over time. Once applied, you
should leave it alone. It serves as a stake in the ground and reference
point. On the other hand, a branch is dynamic and moves with each commit
you make. The branch name is designed to follow your continuing
development.

Curiously, you can name a branch and a tag with the same name. If
you do, you will have to use their full ref names to distinguish them.
For example, you could use refs/tags/v1.0 and refs/heads/v1.0. You may want to use the same
name as a branch name during development and then convert it to a tag
name at the conclusion of your development.

Naming branches and tags is ultimately up to you and your project
policies. However, you should consider the key differentiating
characteristic: is this name static and immutable, or is it dynamic for
development? The former should be a tag and the latter a branch.

Finally, unless you have a compelling reason to do so, you should
simply avoid using the same name for both a branch and a tag.

Branch Names

The name you assign to a branch is essentially arbitrary,
though there are some limitations. The default branch in a repository is
named master and most developers keep
the repository’s most robust and dependable line of development on that
branch. There is nothing magic about the name master, except that Git introduces it during the
initialization of a repository. If you prefer, you can rename or even
delete the master branch, although it’s
probably best practice to leave it alone.

To support scalability and categorical organization, you can create
a hierarchical branch name that resembles a Unix pathname. For example,
suppose you are part of a development team that fixes a multitude of bugs.
It may be useful to place the development of each repair in a hierarchical
structure, under the branch name bug,
on separate branches named something like bug/pr-1023 and bug/pr-17. If you find you have many branches or
are just terminally overorganized, you can use this slash syntax to
introduce some structure to your branch names.

Tip

One reason to use hierarchical branch names is that Git, just like
the Unix shell, supports wildcards. For instance, given the naming
scheme bug/pr-1023 and bug/pr-17, you can select all bug branches at once with a clever and
familiar shorthand.

    git show-branch 'bug/*'

Dos and Don’ts in Branch Names

Branch names must conform to a few simple rules.

  • You can use the forward slash ( /) to create a hierarchical name scheme.
    However, the name cannot end with a slash.

  • The name cannot start with a minus sign ( -).

  • No slash-separated component can begin with a dot ( .). A branch name such as feature/.new is invalid.

  • The name cannot contain two consecutive dots ( ..) anywhere.

  • Further, the name cannot contain:

    • Any space or other whitespace character

    • A character that has special meaning to Git,
      including the tilde ( ~),
      caret ( ^), colon ( :), question mark ( ?), asterisk ( *), and open bracket ( [)

    • An ASCII control character, which is any byte with a value
      lower than \040 octal, or the DEL character (\177 octal)

These branch name rules are enforced by the git check-ref-format plumbing command, and they are designed to ensure
that each branch name is both easily typed and usable as a filename
within the .git directory and
scripts.

Using Branches

There may be many different branches within a repository at
any given time, but there is at most one active or current branch. The
active branch determines which files are checked out in the working
directory. Furthermore, the current branch is often an implicit operand in
Git commands, such as the target of the merge operation. By default,
master is the active branch, but you
can make any branch the current branch.

Tip

In Chapter 6, we presented commit graph
diagrams containing several branches. Keep this graph structure in mind
when you manipulate branches because it reinforces your understanding of
the elegant and simple object model underlying Git’s branches.

A branch allows the content of the repository to diverge in many
directions, one per branch. Once a repository forks at least one branch,
each commit is applied to one branch or the other, whichever is
active.

Each branch in a specific repository must have a unique name, and
the name always refers to the most recent revision committed on that
branch. The most recent commit on a branch is called the
tip or head of the
branch.

Git doesn’t keep information about where a branch originated.
Instead, the branch name moves incrementally forward as new commits are
made on the branch. Older commits must therefore be named by their hash or
via a relative name such as dev~5. If
you want to keep track of a particular commit—because it represents a
stable point in the project, say, or is a version you want to test—you can
explicitly assign it a lightweight tag name.

Because the original commit from which a branch was started is not
explicitly identified, that commit (or its equivalent) can be found
algorithmically using the name of the original branch from which the new
branch forked:

    $ git merge-base original-branch new-branch

A merge is the complement of a branch. When you merge, the
content of one or more branches is joined with an implicit target branch.
However, a merge does not eliminate any of the source branches or those
branches’ names. The rather complex process of merging branches is the
focus of Chapter 9.

You can think of a branch name as a pointer to a particular (albeit
evolving) commit. A branch includes the commits sufficient to rebuild the
entire history of the project along the branch from which it came, all the
way back to the very beginning of the project.

Figure 7-1. Commits reachable from dev

In Figure 7-1, the
dev branch name points to the head
commit, Z. If you wanted to rebuild the
repository state at Z, then all the
commits reachable from Z back to the
original commit, A, are needed. The
reachable portion of the graph is highlighted with wide lines and covers
every commit except ( S, G, H,
J, K, L).

Each of your branch names, as well as the committed content
on each branch, is local to your repository. However, when making your
repository available to others, you can publish or
elect to make one or any number of branches and the associated commits
available, too. Publishing a branch must be done explicitly. Also, if your
repository is cloned, your branch names and the development on those
branches will all be part of the newly cloned repository copy.

Creating Branches

A new branch is based upon an existing commit within the
repository. It is entirely up to you to determine and specify which commit
to use as the start of the new branch. Git supports an arbitrarily complex
branching structure, including branching branches and forking multiple
branches from the same commit.

The lifetime of a branch is, again, your decision. A branch may be
short lived or long lived. A given branch name may be added and deleted
multiple times over the lifetime of the repository.

Once you have identified the commit from which a branch
should start, simply use the git branch
command. Thus, to create a new branch off the HEAD of your current branch for the purposes of
fixing Problem Report #1138, you might use:

    $ git branch prs/pr-1138

The basic form of the command is

    git branch branch [starting-commit]

When no starting-commit is specified, the
default is the revision committed most recently on the current branch. In other
words, the default is to start a new branch at the point where you’re
working right now.

Note that the git branch command
merely introduces the name of a branch into the repository. It does not
change your working directory to use the new branch.
No working directory files change, no implicit branch context changes, and
no new commits are made. The command simply creates a named branch at the
given commit. You can’t actually start work on the branch until you switch
to it, as we show shortly in Checking out Branches.

Sometimes you want to specify a different commit as the start of a
branch. For instance, suppose that your project creates a new branch for
each reported bug and you hear about a bug in a certain release. It may be
convenient to use the starting-commit parameter as an alternative to switching
your working directory to the branch that represents the release.

Normally, your project establishes conventions that let you
specify a starting commit with certainty. For instance, to make a bug fix
on the Version 2.3 release of your software, you might specify a branch named
rel-2.3 as the starting
commit:

    $ git branch prs/pr-1138 rel-2.3

Note

The only commit name guaranteed to be unique is the hash ID. If
you know a hash ID, you can use it directly:

    $ git branch prs/pr-1138 db7de5feebef8bcd18c5356cb47c337236b50c13

Listing Branch Names

The git branch command
lists branch names found in the repository.

    $ git branch
      bug/pr-1
      dev
    * master

In this example, three topic branches are shown. The branch
currently checked out into your working tree is identified by the
asterisk. This example also shows two other branches,
bug/pr-1 and dev.

Without additional parameters, only topic branches in the repository
are listed. As you’ll see in Chapter 12,
there may be additional remote tracking branches in your repository. You
can list those with the -r option. You can list both
topic and remote branches with -a.

Viewing Branches

The git show-branch
command provides more detailed output than git
branch
, listing the commits that contribute to one or more
branches in roughly reverse chronological order. As with git branch, no options list the topic branches,
-r shows remote tracking branches, and
-a shows all branches.

Let’s look at an example.

    $ git show-branch
    ! [bug/pr-1] Fix Problem Report 1
     * [dev] Improve the new development
      ! [master] Added Bob's fixes.
    ---
     *  [dev] Improve the new development
     *  [dev^] Start some new development.
    +   [bug/pr-1] Fix Problem Report 1
    +*+ [master] Added Bob's fixes.

The git show-branch output is
broken down into two sections separated by a line of dashes. The section
above the separator lists the names of branches enclosed in square
brackets, one per line. Each branch name is associated with a single
column of output, identified by either an exclamation mark or—if it is
also the current branch—an asterisk.
In the example just shown, commits within the branch bug/pr-1 start in the first column,
commits within the current branch dev start in the second column, and
commits in the third branch master start in the third column. For
quick reference, each branch in the upper section is also listed with the
first line of the log message from the most recent commit on that
branch.

The lower section of output is a matrix stating which
commits are present in each branch. Again, each commit is listed with the
first log message line from that commit. A commit is present in a branch
if there a plus ( +), an
asterisk ( *), or a minus
( -) in that branch’s
column. The plus sign indicates the commit is in a branch; the asterisk
just highlights the commit as being present on the active branch. The
minus sign denotes a merge commit.

For example, both of the following commits are identified by
asterisks and are present in the dev branch:

    *  [dev] Improve the new development
    *  [dev^] Start some new development.

These two commits are not present in any other branch. They are
listed in reverse chronological order: The most recent commit is at the
top and the oldest commit at the bottom.

Enclosed within square brackets on each commit line, Git
also shows you a name for that commit. As already mentioned, Git assigns
the branch name to the most recent commit. Previous commits have the same
name with trailing caret ( ^)
characters. In Chapter 6, you saw master as the name for the most recent commit
and master^ as the name for the
penultimate commit. Similarly, dev and dev^ are the two most recent commits on
the branch dev.

Although the commits within a branch are ordered, branches
themselves are listed in an arbitrary order. This is because all branches
have equal status; there is no rule stating that one branch is more
important than another.

If the same commit is present in multiple branches, then it will
have a plus sign or an asterisk indicator for each branch. Thus, the last
commit shown in the previous output is present in all three
branches:

    +*+ [master] Added Bob's fixes.

The first plus sign means that the commit is in bug/pr-1, the asterisk means the same commit is
in the active branch dev, and the final
plus sign means the commit is also in the master branch.

When invoked, git show-branch
traverses through all the commits on all branches being shown, stopping
the listing on the most recent common commit present on all of them. In
this case, Git listed four commits before it found one common to all three
branches ( Added Bob's fixes.), at which
point it stopped.

Stopping at the first common commit is the default
heuristic for reasonable behavior. It is presumed that reaching such a
common point yields sufficient context to understand how the branches
relate to each other. If for some reason you actually want more commit
history, use the --more= num
option, specifying the number of additional commits you want to see going back in time
along the common branch.

The git show-branch
command accepts a set of branch names as parameters, allowing you to limit
the history shown to those branches. For example, if new branch named
bug/pr-2 is added starting at the master
commit, it would look like this:

    $ git branch bug/pr-2 master
    $ git show-branch
    ! [bug/pr-1] Fix Problem Report 1
     ! [bug/pr-2] Added Bob's fixes.
      * [dev] Improve the new development
       ! [master] Added Bob's fixes.
    ----
      *  [dev] Improve the new development
      *  [dev^] Start some new development.
    +    [bug/pr-1] Fix Problem Report 1
    ++*+ [bug/pr-2] Added Bob's fixes.

If you wanted to see the commit history for just the
bug/pr-1 and bug/pr-2 branches, you
could use

    $ git show-branch bug/pr-1 bug/pr-2

Although that might be fine for a few branches, if there were many
such branches, then naming them all would be quite tedious. Fortunately,
Git allows wildcard matching of branch names as well. The same results can
be achieved using the simpler bug/* branch wildcard
name:

    $ git show-branch bug/pr-1 bug/pr-2
    ! [bug/pr-1] Fix Problem Report 1
     ! [bug/pr-2] Added Bob's fixes.
    --
    +  [bug/pr-1] Fix Problem Report 1
    ++ [bug/pr-2] Added Bob's fixes.

    $ git show-branch bug/*
    ! [bug/pr-1] Fix Problem Report 1
     ! [bug/pr-2] Added Bob's fixes.
    --
    +  [bug/pr-1] Fix Problem Report 1
    ++ [bug/pr-2] Added Bob's fixes.

Checking out Branches

As mentioned earlier in this chapter, your working
directory can reflect only one branch at a time. To start working on a
different branch, issue the git
checkout
command. Given a branch name, git checkout makes the branch the new, current
working branch. It changes your working tree file and directory structure
to match the state of the given branch. However, as you’ll see, Git builds
in safeguards to keep you from losing data you haven’t yet
committed.

In addition, git checkout gives
you access to all states of the repository going back from the tip of the
branch to the beginning of the project. This is because, as you may recall
from Chapter 6, each commit captures a snapshot of
the complete repository state at a given moment in time.

A Basic Example of Checking out a Branch

Suppose you wanted to shift gears from the dev branch in the previous section’s
example and instead devote your attention to fixing the problem
associated with the bug/pr-1 branch. Let’s look at the
state of the working directory before and after git checkout:

    $ git branch
      bug/pr-1
      bug/pr-2
    * dev
      master

    $ git checkout bug/pr-1
    Switched to branch "bug/pr-1"

    $ git branch
    * bug/pr-1
      bug/pr-2
      dev
      master

The files and directory structure of your working tree have been
updated to reflect the state and contents of the new branch,
bug/pr-1. However, in
order to see that the files your working directory have changed to match
the state at the tip of that branch, you must use a regular Unix command
such as ls.

Selecting a new current branch might have dramatic effects
on your working tree files and directory structure. Naturally, the
extent of that change depends on the differences between your current
branch and the new, target branch that you would like to check out. The
effects of changing branches are:

  • Files and directories present in the branch being checked out
    but not in the current branch are checked out of the object store
    and placed into your working tree.

  • Files and directories present in your current branch but
    absent in the branch being checked out will be removed from your
    working tree.

  • Files common to both branches are modified to reflect the
    content present in the checked out branch.

Don’t be alarmed if it looks like the checkout appears to happen
almost instantaneously. A common newbie mistake is to think that the
checkout didn’t work because it returned instantly after supposedly
making huge changes. This is one of the features of Git that truly and
strongly differentiates it from many other VCSs. Git is good at
determining the minimum set of files and directories that actually need
to change during a checkout.

Checking out When You Have Uncommitted Changes

Git precludes the accidental removal or modification of
data in your local working tree without your explicit request. Files and
directories in your working directory that are not being tracked are
always left alone; Git won’t remove or modify them. However, if you have
local modifications to a file that are different from changes that are
present on the new branch, Git issues an error message such as the
following and refuses to check out the target branch:

    $ git branch
      bug/pr-1
      bug/pr-2
      dev
    * master

    $ git checkout dev
    error: Your local changes to the following files would be overwritten by checkout:
        NewStuff
    Please, commit your changes or stash them before you can switch branches.
    Aborting

In this case, a message warns that something has caused Git to
stop the checkout request. But
what? You can find out by inspecting the contents of the file NewStuff, as it is locally modified in the
current working directory, and the target dev branch:

    # Show what NewStuff looks like in the working directory
    $ cat NewStuff
    Something
    Something else

    # Show that the local version of the file has an extra line that
    # is not committed in the working directory's current branch (master)
    $ git diff NewStuff
    diff --git a/NewStuff b/NewStuff
    index 0f2416e..5e79566 100644
    --- a/NewStuff
    +++ b/NewStuff
    @@ -1 +1,2 @@
     Something
    +Something else

    # Show what the file looks like in the dev branch

    $ git show dev:NewStuff
    Something
    A Change

If Git brashly honored the request to check out the
dev branch, your local
modifications to NewStuff in your
working directory would be overwritten by the version from
dev. By default, Git
detects this potential loss and prevents it from happening.

Tip

If you really don’t care about losing changes in your working
directory and are willing to throw them away, you can force Git to
perform the checkout by using the -f option.

Seeing the error message might suggest that you update the file
within the index and then proceed with the checkout. However, this isn’t
quite sufficient. Using, say, git add
to update the new contents of NewStuff into the index only places the
contents of that file in the index; it won’t commit it to any branch.
Git still can’t check out the new branch without losing your change, so
it fails again.

    $ git add NewStuff
    $ git checkout dev
    error: Your local changes to the following files would be overwritten by checkout:
        NewStuff
    Please, commit your changes or stash them before you can switch branches.
    Aborting

Indeed, it would still be overwritten. Clearly, just adding it to
the index isn’t sufficient.

You could just issue git commit
at this point to commit your change into your current branch ( master). But suppose you want the change to be
made in the new dev branch instead.
You seem to be stuck: You can’t put your change into the dev branch until you check it out, and Git
won’t let you check it out because your change is present.

Luckily, there are ways out of this catch-22. One approach uses
the stash and is described in Chapter 11. Another approach is described in the next
section, Merging Changes into a Different Branch.

Merging Changes into a Different Branch

In the previous section, the current state of your working
directory conflicted with that of the branch you wanted to switch to.
What’s needed is a merge: The changes in your working directory must be
merged with the files being checked out.

If possible or if specifically requested with the
-m option, Git attempts to carry your local change into
the new working directory by performing a merge operation between your
local modifications and the target branch.

    $ git checkout -m dev
    M       NewStuff
    Switched to branch "dev"

Here, Git has modified the file NewStuff and checked out the dev branch successfully.

This merge operation occurs entirely in your working
directory. It does not introduce a merge commit on any branch. It is
somewhat analogous to the cvs update
command in that your local changes are merged with the target branch and
are left in your working directory.

You must be careful in these scenarios, however. Although it may
look like the merge was performed cleanly and all is well, Git has
simply modified the file and left the merge conflict indicators within
it. You must still resolve any conflicts that are present:

    $ cat NewStuff
    Something
    <<<<<<< dev:NewStuff
    A Change
    =======
    Something else
    >>>>>>> local:NewStuff

See Chapter 9 to learn more about merges and
helpful techniques to resolve merge conflicts.

If Git can check out a branch, change to it, and merge your local
modifications cleanly without any merge conflicts, then the checkout
request succeeds.

Suppose you’re on the master branch in your development
repository and you’ve made some changes to the NewStuff file. Moreover, you realize that the
changes you made really should be made on another branch, perhaps
because they fix Problem Report #1 and should be committed on the
bug/pr-1 branch.

Here is the setup. Start on the master branch. Make some changes to
some files, which are represented here by adding the text
Some bug fix to the
file NewStuff.

    $ git show-branch
    ! [bug/pr-1] Fix Problem Report 1
     ! [bug/pr-2] Added Bob's fixes.
      ! [dev] Started developing NewStuff
       * [master] Added Bob's fixes.
    ----
      +  [dev] Started developing NewStuff
      +  [dev^] Improve the new development
      +  [dev~2] Start some new development.
    +    [bug/pr-1] Fix Problem Report 1
    +++* [bug/pr-2] Added Bob's fixes.

    $ echo "Some bug fix" >> NewStuff

    $ cat NewStuff
    Something
    Some bug fix

At this point, you realize that all this work should be committed
on the bug/pr-1 branch and not the
master branch. For reference, here is what the
NewStuff file looks like in the
bug/pr-1 branch prior to the checkout in the next
step:

    $ git show bug/pr-1:NewStuff
    Something

To carry your changes into the desired branch, simply attempt to
check it out:

    $ git checkout -m bug/pr-1
    M       NewStuff
    Switched to branch "bug/pr-1"

    $ cat NewStuff
    Something
    Some bug fix

Here, Git was able to correctly merge the changes from your
working directories and the target branch and leave them in your new
working directory structure. You might want to verify that the merge
went according to your expectations by using git diff:

    $ git diff
    diff --git a/NewStuff b/NewStuff
    index 0f2416e..b4d8596 100644
    --- a/NewStuff
    +++ b/NewStuff
    @@ -1 +1,2 @@
     Something
    +Some bug fix

That one line addition is correct.

Creating and Checking out a New Branch

Another fairly common scenario happens when you want to
both create a new branch and simultaneously switch to it as well. Git
provides a shortcut for this with the -b
new-branch option.

Let’s start with the same setup as the previous example, except
now you must start a new branch instead of checking changes into an
existing branch. In other words, you are in the master branch, editing files, and
suddenly realize that you would like all of the changes to be committed
on an entirely new branch named bug/pr-3. The sequence is as
follows:

    $ git branch
      bug/pr-1
      bug/pr-2
      dev
    * master

    $ git checkout -b bug/pr-3
    M       NewStuff
    Switched to a new branch "bug/pr-3"

    $ git show-branch
    ! [bug/pr-1] Fix Problem Report 1
     ! [bug/pr-2] Added Bob's fixes.
      * [bug/pr-3] Added Bob's fixes.
       ! [dev] Started developing NewStuff
        ! [master] Added Bob's fixes.
    -----
       +  [dev] Started developing NewStuff
       +  [dev^] Improve the new development
       +  [dev~2] Start some new development.
    +     [bug/pr-1] Fix Problem Report 1
    ++*++ [bug/pr-2] Added Bob's fixes.

Unless some problem prevents a checkout command from completing,
the command:

    $ git checkout -b new-branch start-point

is exactly the same as the two-command sequence:

    $ git branch new-branch start-point
    $ git checkout new-branch

Detached HEAD Branches

Normally, it’s advisable to check out only the tip of a
branch by naming the branch directly. Thus, by default, git checkout changes to the tip of a desired
branch.

However, you can check out any commit. In such an instance, Git
creates a sort of anonymous branch for you called a detached
HEAD
. Git creates a detached HEAD when you:

  • Check out a commit that is not the head of a branch.

  • Check out a tracking branch. You might do this to explore
    changes recently brought into your repository from a remote
    repository.

  • Check out the commit referenced by a tag. You might do this to
    put together a release based on tagged versions of files.

  • Start a git bisect
    operation, described in Using git bisect
    of Chapter 6.

  • Use the git submodule
    update
    command.

In these cases, Git tells you that you have moved to a detached
HEAD:

    # I have a copy of the Git sources handy!
    $ cd git.git

    $ git checkout v1.6.0
    Note: moving to "v1.6.0" which isn't a local branch
    If you want to create a new branch from this checkout, you may do so
    (now or later) by using -b with the checkout command again. Example:
      git checkout -b <new_branch_name>
    HEAD is now at ea02eef... GIT 1.6.0

If, after finding yourself on a detached HEAD, you later decide that you need to make
new commits at that point and keep them, you must first create a new
branch:

    $ git checkout -b new_branch

This will give you a new, proper branch based on the commit where
the detached HEAD was. You can then
continue with normal development. Essentially, you named the branch that
was previously anonymous.

To find out if you are on a detached HEAD, just ask:

    $ git branch
    * (no branch)
      master

On the other hand, if you are finished with the detached
HEAD and want to simply abandon that
state, you can convert to a named branch by simply entering git checkout
branch.

    $ git checkout master
    Previous HEAD position was ea02eef... GIT 1.6.0
    Checking out files: 100% (608/608), done.
    Switched to branch "master"

    $ git branch
    * master

Deleting Branches

The command git branch -d
branch removes the named branch from a
repository. Git prevents you from removing the current branch:

    $ git branch -d bug/pr-3
    error: Cannot delete the branch 'bug/pr-3' which you are currently on.

Removing the current branch would leave Git unable to determine what
the resulting working directory tree should look like. Instead, you must
always name a noncurrent branch.

But there is another subtle issue. Git won’t allow you to delete a
branch that contains commits that are not also present on the current
branch. That is, Git prevents you from accidentally removing development
in commits that will be lost if the branch were to be deleted.

    $ git checkout master
    Switched to branch "master"

    $ git branch -d bug/pr-3
    error: The branch 'bug/pr-3' is not an ancestor of your current HEAD.
    If you are sure you want to delete it, run 'git branch -D bug/pr-3'.

In this git show-branch
output, the commit Added a bug fix for pr-3 is found only
on the bug/pr-3 branch. If that branch
were to be deleted, there would no longer be a way to access that
commit.

By stating that the bug/pr-3
branch is not an ancestor of your current HEAD, Git is telling you that the line of
development represented by the bug/pr-3
branch does not contribute to the development of the current branch,
master.

Git is not mandating that all branches be merged into the master branch before they can be deleted.
Remember, a branch is simply a name or pointer to a commit that has actual
content. Instead, Git is keeping you from accidentally losing content from
the branch to be deleted that is not merged into your
current branch.

If the content from the deleted branch is already present on another
branch, checking that branch out and then requesting
the branch deletion from that context would work. Another approach is to
merge the content from the branch you want to delete into your current
branch (see Chapter 9). Then the other branch can be
safely deleted.

    $ git merge bug/pr-3
    Updating 7933438..401b78d
    Fast forward
     NewStuff |    1 +
     1 files changed, 1 insertions(+), 0 deletions(-)

    $ git show-branch
    ! [bug/pr-1] Fix Problem Report 1
     ! [bug/pr-2] Added Bob's fixes.
      ! [bug/pr-3] Added a bug fix for pr-3.
       ! [dev] Started developing NewStuff
        * [master] Added a bug fix for pr-3.
    -----
      + * [bug/pr-3] Added a bug fix for pr-3.
       +  [dev] Started developing NewStuff
       +  [dev^] Improve the new development
       +  [dev~2] Start some new development.
    +     [bug/pr-1] Fix Problem Report 1
    ++++* [bug/pr-2] Added Bob's fixes.

    $ git branch -d bug/pr-3
    Deleted branch bug/pr-3.

    $ git show-branch
    ! [bug/pr-1] Fix Problem Report 1
     ! [bug/pr-2] Added Bob's fixes.
      ! [dev] Started developing NewStuff
       * [master] Added a bug fix for pr-3.
    ----
       * [master] Added a bug fix for pr-3.
      +  [dev] Started developing NewStuff
      +  [dev^] Improve the new development
      +  [dev~2] Start some new development.
    +    [bug/pr-1] Fix Problem Report 1
    +++* [bug/pr-2] Added Bob's fixes.

Finally, as the error message suggests, you can override Git’s
safety check by using -D instead of -d.
Do this if you are certain you don’t want the extra content in that
branch.

Git does not maintain any form of historical record of branch
names being created, moved, manipulated, merged, or
deleted. Once a branch name has been removed, it is gone.

The commit history on that branch, however, is a separate question.
Git will eventually prune away commits that are no longer referenced and
reachable from some named ref such as a branch or tag name. If you want to
keep those commits, you must either merge them into a different branch,
make a branch for them, or point a tag reference to them. Otherwise,
without a reference to them, commits and blobs are unreachable and will
eventually be collected as garbage by the git
gc
tool.

Tip

After accidentally removing a branch or other ref, you
can recover it by using the git
reflog
command. Other commands such as git fsck and configuration options such as
gc.reflogExpire and gc.pruneExpire can also help recover lost
commits, files, and branch heads.


[17] OK, OK. It’s a small, bushy tree, a highly branched shrub thing.
Perhaps a better analogy is a banyan tree.

Comments are closed.

loading...