gitversion-controlbranchcommitgit-commit

Create new branch from specific commit onwards


I want to create a new branch, starting from a certain commit. However, I want all the commits that were commited afterwards to be part of that branch too.

Lets assume this is branch-D with all the commits.

> branch-D
commit-A ---- commit-B ---- commit-C ---- commit-D

I want to remove everything from commit-B to a new branch, lets name it branch-C, and only keep commit-A on branch-D

> branch-D
commit-A

> branch-C
commit-B ---- commit-C ---- commit-D

I haven't pushed anything to a remote repository yet. Is there a way to do this?


Solution

  • Update - I misread your diagrams and had mixed up the branch names as a result. Also as torek points out, a "more correct" reading of your diagrams includes some ambiguities. I'll keep my answer mostly as is (but with branch names straightened out) as I believe it conveys the main principles; and I'll at a bit about using rebase if you need to further edit branchC history; but for a more detailed answer, see torek's response.


    First let me get to the solution I think you're looking for; but I suggest reading beyond that, because there's conceptual confusion in this question that would be worth clearing up.

    So you have now

    A -- B -- C -- D <--(branchD)
    

    I think you want to end up with only A on branchD, and a new branch for B, C, and D. So step one is, with branchD (or D) checked out, create the new branch

    git branch branchC
    

    Then move the branchD branch back to A.

    git reset --hard HEAD~3
    

    (I used HEAD~3 because in this example, that would be one name for A. It depends on the number of commits being taken out of master's history. It would always be ok to use the commit ID (SHA) of A in place of HEAD~3.)

    After these steps, you have

    A <--(branchD)
     \
      B -- C -- D <--(branchC)
    

    which looks like what you described, but we didn't get there the way the description implied.

    I didn't move commits; I moved branches. This is much more straightforward in git, and it reflects that commits do not "belong to" branches in git the way they do in some systems. Consequently the statement "branch at a previous commit but include the following commits on that branch" doesn't really make sense in git.

    The ambiguities in the before-and-after diagrams in the question do leave open the possibility that I've misunderstood exactly what branchC should include in its history. If branchC is not meant to include A, then you have to rewrite B, C, and D - and you can most easily do that with git rebase -i. After creating branchC and moving branchD you could say

    git rebase -i branchD^ branchC
    

    Here branchD^ is a possible name for "the commit before A. If there is such a commit, it may have other names - perhaps master, or certainly its commit ID. If there is no such commit, then I suppose you could say

    git rebase -i --root branchC
    

    (but in that scenario, trying to remove A from the branchC history probably makes no sense, so I doubt that's the what's going on here).

    The rebase command will bring up a text editor with a TODO list with an entry for each commit. You can delete the entry for commit A from the list, then save and exit. This won't disturb branchD - it will still point at A - but it will create a new history for branchC by copying B, C, and D. so then you would have

    x -- B' -- C' -- D' <--(branchC)
      \
       A <--(branchD)
    

    In any event, to do what you want means removing B, C, and D from the history of branchD. You mentioned that these commits have not been pushed, so it should be no problem; but that is an important distinction to keep in mind. Any time you want to remove a commit from a branch's history, it's easier to do if the branch on the remote doesn't yet know about the commits in question.