How to duplicate branch

10,038

Solution 1

If you have used other version control systems—especially Mercurial, which really is a lot like Git but with some key differences including this very one here—you might expect commits to remember which branch they were made on.

In Mercurial, they do.

In Git, they don't.

A branch name is nothing more than a label pointing to one commit

In Git, think of a branch name as a sort of yellow sticky note. The note has the name of the branch on it, but it's just loosely attached to the commit itself:

<--o   <--branch

The commit has its own outgoing arrow, which points to the commit's parent commit. This builds up a backwards chain of commits. If we give each commit a single letter name and draw arrows saying who points to whom, we get:

A <- B <- C   <-- branch

Here A is the very first commit ever. It has no parent—it can't have one, so it just doesn't. B is the second commit, and its parent is A, so B points to A. (Literally, B has A's hash ID inside it, as part of B itself.) Likewise, C points to B. The name branch remembers the big ugly hash ID of commit C, so that we don't have to.

To make a new commit:

  1. Git collects a log message from you.
  2. Git writes out the index (aka staging area): whatever you have updated with git add is new, everything else is the same as before.
  3. Git writes out a new commit. The new commit's parent is the current commit (its hash ID). The new commit's log message is the one you wrote; the author and committer of the new commit is you; and the saved tree (source code) for the new commit is the result of writing out the index.
  4. This new commit has a new, unique hash ID, different from every other commit ever made.
  5. Here is the secret of branches: Whatever branch you are on right now, Git puts the new ID into the branch name. So now branch remembers the new commit D we just made:

    A <- B <- C <- D   <-- branch
    

    In effect, Git peels the sticky-note off the old commit and pastes it on the new one.

If you attach multiple names to one commit, and then make a new commit, Git only peels off one sticky-note. So if you make a new name that points to C, and then make new commit D, only the current branch name moves to D, the old one stays with C:

A--B--C     <-- newname
       \
        D   <-- branch

You should now ask how Git knows which label is the current branch!

The answer is HEAD. This is why, if you use git log --decorate, you will see things like:

commit e0688e9... (HEAD -> master, origin/master, origin/HEAD)

This tells me that Git believes the current branch is master, and that there are three names for the current commit e0688e9...: master, origin/master, and origin/HEAD.

Solution 2

Branching from testing to create a testing2 branch is equivalent to creating a branch from master that is the same as testing.

For example:

                              ___testing2__
                ___testing___/
               /
------master--------

is logically equivalent to:

                ___testing___
               /
------master--------
               \___testing2__

with the same changes in the testing and testing2 branches. It may not show like that in the log/graph, but it is the same.

Think about it more like:

                X
               /
------master--------

where the commit at X has both branch labels of testing and testing2.

I've just created this structure to give a better view. When creating the testing2 branch, you will get:

starting point

Once you've made some commits on each branch, you will get:

after commits to branches

Solution 3

Merge with master then:

git checkout master
git branch testing2
git pull origin master
Share:
10,038
TheRedCameron
Author by

TheRedCameron

Updated on June 25, 2022

Comments

  • TheRedCameron
    TheRedCameron almost 2 years

    I have two branches. 'master' and 'testing'.

                    ___testing____  
                   /
    ------master--------
    

    I want to continue with the testing branch to add features, but before that, I need to merge the testing branch to master. I want to create a third branch called 'testing2' that is exactly the same as 'testing', but comes from the 'master' branch instead of being a branch off of 'testing'.

                       _____testing___
                      /
    ---------master----------
                      \____testing2____
    

    As an alternative, I know I can merge the 'testing' branch to 'master' and create two new branches and do my work that way,

                    _____testing_____        ____newtest___
                   /                 \      /                 
    ---master-------------------------------------------------
                                            \____continuetest_____
    

    but is there not a way to simply duplicate the branch 'testing'?

  • TheRedCameron
    TheRedCameron about 7 years
    Wouldn't 'testing2' then have 'testing' as it's parent rather than 'master'?
  • torek
    torek about 7 years
    @TheRedCameron: branches don't have parents (which is what adrianbanks is telling you as well). Only commits have parents. Commits are named by their hash IDs (face0ff and so on, usually unpronounceable and impossible to remember). So Git has tags and branch-names that remember those hash IDs for you. That's almost all they do: remember a hash ID.
  • TheRedCameron
    TheRedCameron about 7 years
    So then if we merge 'testing' to 'master', 'testing2' is going to be from 'master' anyway, correct?
  • adrianbanks
    adrianbanks about 7 years
    Yes, all of testing will then be in master, and some of testing2 (the bits that came from testing) will be also.
  • TheRedCameron
    TheRedCameron about 7 years
    But the specific things about testing2 will remain in testing2 and outside of master, correct?