Vic
Vic

Reputation: 8961

git force checkout of duplicate branch?

If I want to create a new branch, I would do:

git checkout -b new-branch

However, sometimes this branch already exists, eg:

fatal: A branch named 'new-branch' already exists.

is there an easier way than doing the following?

git branch -D new-branch
git checkout -b new-branch

I tried this but it doesn't work:

git checkout -b new-branch --force

Upvotes: 3

Views: 3035

Answers (1)

torek
torek

Reputation: 487775

What you want here is git checkout -B name. If name does not name an existing branch, this creates the new branch, pointing to the current commit, as if by regular git checkout -b.

If name does name an existing branch, what happens instead is that Git forcibly re-points the branch name to the current commit. This is a lot like a git reset --soft. A branch name is really just a human-readable name for some Git hash ID, and a soft reset changes the hash ID attached to the branch name, without touching the index or work-tree. In the same way, git checkout -B will change the ID attached to this name, without touching the index or work-tree.

(The main answer ends here, the rest is just an aside on all the various success vs failure modes.)

There is a big difference between -B and --force

There's an important couple of distinctions to make here.

The general form of this particular kind of checkout is:

git checkout [-b | -B] name [target-commit-specifier]

such as, e.g., git checkout -b newbr a234567.

The difference between -b and -B at this point is obvious: if the name part names a branch that already exists, git checkout -b fails immediately. Git doesn't have to try the checkout: the name exists; error. If the name doesn't exist, or you used -B, though, there is more to try.

If you leave out the target-specifier, as we are doing above and in the original question, a whole class of problems just goes away. This form, without a target commit, means: Leave the current commit in place. Don't touch the index or work-tree at all. Doing nothing is easy, and never fails, so this part never fails.

When you use the form git checkout -B name target-commit-specifier, though, the command tries to git checkout the target commit a234567. This step requires updating the index and/or work-tree, and this may fail with, e.g.:

error: The following ... files would be overwritten ...

In this case, the git checkout -b or git checkout -B is aborted and no branch is created or re-set. (See also Checkout another branch when there are uncommitted changes on the current branch.)

The --force option tells Git that if this checkout step is about to fail, Git should go ahead and do the checkout anyway, overwriting or removing files that are not safely saved away in the repository. This can make all kinds of changes to the index and/or work-tree, and any file-data that are not permanently, safely stored away in a commit could be lost here. So --force remains dangerous (in that it can lose file data), but is not related to -b vs -B: it only affects this step, of moving from your current commit, whatever it is, to this other target commit.

If Git gets this far—the target commit is now installed as the current commit, with the index and work-tree changed; or the target commit is the current commit, so the index and work-tree are left alone—then at this point the whole thing is destined to succeed. We've already checked whether the branch name exists for -b. All Git has to do now is write the hash ID of the current commit into the branch name (so that name points to a234567, for instance) and write ref: refs/heads/name into .git/HEAD, so that you're now on branch name.

Upvotes: 6

Related Questions