Git branch split point

I am having issues cleaning up my git history tree..

Basically my git repository with 1 branch looks like:

  • Is it safe for multiple users to use a Git repo on a shared network drive?
  • Why does fatal: Not a git repository error show up when publishing site using sbt-ghpages for valid git project?
  • how to version files ONLY locally with git
  • Download specific files from github in command line, not clone the entire repo
  • How can I check whether two branches are “even”?
  • How can I make git tell curl to use openssl instead of gnutls without rebuilding the package?
  • A--B--C--D--E--F--G--H--I--Z--X--Y...   master
              \
               F--G--H--I--J--K--L...       branch1
    

    That commit E is some not-important changes and can be made redundant.
    The correct split point here should be I not D.
    That commit E can be either removed or added to both branches..

    Is there any way this can be achieved?

    —– Update

    Wow thank you very much for all your help!
    I have tried what torek suggested and it worked like a charm!

    Now.. I have another repository to clean but it is more complicated..

    It looks like:

    1--2--3--4--*A--5--6--7--8--*B--9--10--11--*C--*D--12--13--14            master
              \ 
               5--6--7--8--9--10--11--12--13--14                             branch1
    

    note1: the letters represent the content of the commit
    note2: * letter commits are only for master branch

    Is there a way to clean up a history tree like this?

    Also, what is the recommended git method for projects structured like this?
    i.e after branching, most of the commits will contain the same updates except some branch specific commits

  • Git svn clone: How to defer fetch of revision history
  • How to get modified files when I pull using NetBeans?
  • Git gives 'non-fast-forward updates' error even after 'git pull'
  • How do we verify commit messages for a push?
  • How do I properly set up hooks on a remote that is specified via the file:// protocol?
  • Rebase only part of a branch
  • 3 Solutions collect form web for “Git branch split point”

    First, +1 on the question for drawing the commit graph. 🙂 Second, not terribly important but you should probably think of this as two branches, master and branch1. There’s nothing special about master, it’s just a branch like any other.1

    Now, central to everything in git is the fact that you can never change a commit. This comes about from the way git actually names each commit,2 using the big ugly 40-character SHA-1: the SHA-1 is made by computing a cryptographic checksum of the contents of the commit. So if you (try to) change anything, the “true name” changes, and you have a new and different commit.

    Instead, what you can do is copy an old commit to a new one, making the desired changes along the way just before the commit gets finalized (and hence given its “true name”). What we’ll do here is make copies.

    You drew this graph:

    A--B--C--D--E--F--G--H--I--Z--X--Y...   master
              \
               F--G--H--I--J--K--L...       branch1
    

    but that can’t quite be right, because each commit points back to its parent commit,3 and here F points back to E when we look at master, but F points back to D when we look at branch1. (I’m assuming here that each single letter stands in for the SHA-1 “true name” of a given commit.) I’m going to assume that the ones currently on branch1 are the copies, though it doesn’t really matter which, as long as you’re happy with the trees associated with those commits (i.e., what you get when you git checkout one of them).

    What you want looks like this:

    A--B--C--D--E--F--G--H--I--Z--X--Y...   master
                             \
                              J'-K'-L'...   branch1
    

    Here the “prime” mark in J', K', and L' indicate that these are copies of commits J, K, and L.

    While I’ve been writing this answer, a few have proposed using git cherry-pick to make these copies. That works just fine—but in fact, we can use git rebase to do the trick, because where cherry-pick is a simple axe you can use to cut down one cherry tree, rebase is a fully automated chain-saw you can use to do them all in one swoop.

    We start by telling rebase to operate on branch1; the easy way is to git checkout branch1 (or we could just add branch1 as a final argument to git rebase, but I’ll use the “easy way”).

    Next, we need to know where we want the commits to branch off of, i.e., have some way to name commit I. From the above, we might be able to say master~3: count back four commits from master (which names commit Y) and you step through X, then Z, then reach I. Or you can do it by the true-name SHA-1, which always works but often requires cut-and-paste to get right. For the command below, I’ll just write I. We’ll tell rebase to base --onto that commit.

    Finally, we need to get rebase to pick commits J, K, and L to copy. There are a bunch of ways to do this as well. Perhaps the easiest is to use git rebase -i, which will offer to rebase everything on branch1, and then you can delete the pick lines for the first four commits. Or, we can tell rebase to exclude particular commits, but I’ll assume you’ll use the interactive method.

    Thus, the final command here is git rebase -i --onto I master (after doing the git checkout branch1 command). The -i makes this interactive, so that you can drop commits; the --onto selects the target for the new series of commits that rebase will cherry-pick; and the master part tells rebase which commits to exclude: specifically, any commits reachable from the name master. (This excludes commits A, B, C, and D, but not the copied versions F', G', H', and I' that appear on branch: you’ll just delete the “pick” lines for those).

    After git rebase finishes its series of git cherry-pick commands, the last thing it does is move the branch label for your current branch (branch1) to the tip-most new commit. So this does produce:

    A--B--C--D--E--F--G--H--I--Z--X--Y...   master
                             \
                              J'-K'-L'...   branch1
    

    provided that all goes well. (If it goes badly, you can git rebase --abort to stop the process and put everything back the way it was before you started.)


    We could be fancier and (assuming I' is branch1~3) do:

    git rebase --onto master~3 branch1~3 branch1
    

    without even any initial git checkout, but this assumes that counting back 3 is correct here (for both master and branch1). This is basically the same as before, with three modifications:

    1. we add branch1 on the end to make rebase check it out as a first step
    2. we use branch1~3 instead of master as the argument that says “exclude this stuff, only rebase stuff that’s not reachable from here”
    3. we drop the -i since we don’t need to edit away “pick” commands this time

    This requires a bit more careful counting of commits to make sure all the ~ expressions are right (or, again, you can work this by raw SHA-1 IDs).


    1Well, there are a few special things about master: for one, it’s the branch you’re put on when you do git init and it creates a new repository. The other is that git merge tweaks the merge message slightly. But both of these are pretty trivial.

    2This is actually true of all four of the kinds of objects found in a git repository (commit, tree, annotated tag, and “blob”—the last one stores file contents). Each is stored in the repository and then given a name that consists of its own cryptographic checksum.

    3More precisely, each commit has zero or more parent ... lines in it, with each parent giving the SHA-1 ID of the corresponding parent commit. The first parent is generally the “main line” of a branch, and any additional parents indicate that this is a merge commit. A commit with no parents, like A above, is a “root commit”.

    You could make a new branch at I and cherry pick all changes made in branch1 so basically

     git branch -b temp I
     git cherry-pick J..branch1
    

    You will then have a new branch temp split at I without the changes of E and containing all commits of branch1 starting with J if that worked correctly you can rename the two to make the temp branch your new branch1. Before cherry picking you can revert commit E if you don’t want it in branch1

    One way to do this, is to create a new branch as a copy of master and do git reset --hard I-commit and then cherry-pick the commits you want (hoping there are not too many)

    Git Baby is a git and github fan, let's start git clone.