I am attempting to clean up the git history in a repo I'm working on. Given a git history that looks like this:
        . -- .
       /       \
... - S   ...   T - ... H
       \       /
        . -- . 
That is:
- There is some arbitrary DAG behind point S.
- There is some arbitrary DAG after point T.
- There is some arbitrary DAG between point S and T.
- The graph before S is disjoint from the graph after T (i.e. removing T or S will disconnect the removed nodes predecessors from H).
I would like to rewrite the history between point S and T (e.g. squash or linearize), such that there is some new git history that ends with point T'.
... - S -- ... -- T'
The critical constraint is that the contents of the repo at point T and T' are exactly the same, even though the git commit is different and the way we got from S to T' might have changed.
This much I can do. What I would like to do after this (and i haven't had luck doing so yet) is to transplant the exact structure of the DAG inclusively between T and H to get:
... - S -- ... -- T' - ... H'
Of course the commit hashes will change, but what's important is that the graph structure, authors, and other meta data between T' and H' is the same.
I would have though I could do this with a cherry-pick:
git cherry-pick T^..H
but this seems to result in merge conflicts. I was looking for answers in this SO post: How to cherry-pick a range of commits and merge them into another branch? but either I'm invoking rebase --onto incorrectly, or these answers aren't a solution to my question.
To make this more concrete I have a MWE. Consider the following code:
The following code constructs this example:
    mkdir -p "$HOME/tmp/tmprepo"
    rm -rf   "$HOME/tmp/tmprepo"
    mkdir -p "$HOME/tmp/tmprepo"
    cd "$HOME"/tmp/tmprepo
    git init 
    git checkout -b main
    echo "state01" > state && git add state && git commit -m "Initial commit"
    echo "state02" > state && git add state && git commit -m "Modify state"
    git checkout -b branch1
    echo "state03" > state && git add state && git commit -m "Modify state"
    echo "state04" > state && git add state && git commit -m "Modify state"
    echo "state05" > state && git add state && git commit -m "Modify state"
    git checkout main
    git checkout -b branch2
    echo "state06" > state && git add state && git commit -m "Modify state"
    echo "state07" > state && git add state && git commit -m "Modify state"
    echo "state08" > state && git add state && git commit -m "Modify state"
    git checkout main
    git merge branch2 --no-ff -m "merge commit" 
    git merge branch1 -s ours --commit --no-edit --no-ff -m "merge commit" 
    git checkout -b branch3
    echo "state09" > state && git add state && git commit -m "Modify state - WANT TO SQUASH"
    git tag "Point1"
    echo "state10" > state && git add state && git commit -m "Modify state - WANT TO SQUASH"
    git checkout main
    git merge --no-ff -m "merge commit - WANT TO SQUASH" branch3
    git tag "Point2"
    git checkout main
    git checkout -b branch4
    echo "state11" > state && git add state && git commit -m "Modify state"
    echo "state12" > state && git add state && git commit -m "Modify state"
    echo "state13" > state && git add state && git commit -m "Modify state"
    git checkout main
    git checkout -b branch5
    echo "state14" > state && git add state && git commit -m "Modify state"
    echo "state15" > state && git add state && git commit -m "Modify state"
    echo "state16" > state && git add state && git commit -m "Modify state"
    git checkout main
    git checkout -b branch6
    echo "state17" > state && git add state && git commit -m "Modify state"
    echo "state18" > state && git add state && git commit -m "Modify state"
    echo "state19" > state && git add state && git commit -m "Modify state"
    git checkout branch5
    git merge branch6 -s ours --commit --no-edit --no-ff -m "merge commit" 
    git checkout main
    git merge branch5 -s ours --commit --no-edit --no-ff -m "merge commit" 
    git merge branch4 -s ours --commit --no-edit --no-ff -m "merge commit" 
This creates this git history:
As an example I want to squash the commits between Point1 and Point2, and then apply the rest of the history after Point2.
I can do the squash like this:
    # Squash all information between point1 and point2
    git checkout Point1
    git reset --hard Point2
    git reset --soft Point1^
    git commit -am "all changes between point1 and point2"
    git tag "Point2_prime"
which gives us this:
But I can't figure out how to get the rest of the history on top of it. This is what I've tried so far:
    # The state is now guarenteed to be the same as Point2, but the history has
    # been modified to our liking. Now we need to replay all the other commits
    # on top of this.
    # Based on answers in this SO post:
    # https://stackoverflow.com/questions/1994463/how-to-cherry-pick-a-range-of-commits-and-merge-them-into-another-branch
    COMMIT_A=$(git rev-list -n 1 Point2)
    COMMIT_B=$(git rev-list -n 1 main)
    echo "COMMIT_A = $COMMIT_A"
    echo "COMMIT_B = $COMMIT_B"
    # I've tried the following, but they do not seem to work.
    # Try with cherry pick
    git cherry-pick "${COMMIT_A}..${COMMIT_B}" 
    # Try with rebase onto
    git rebase "$COMMIT_A" "$COMMIT_B"~0 --onto HEAD
I would think because the state of the new commit is exactly the same as the state at Point2, there would be a way to do this non-interactively without merge errors. Is this possible?


 
     
    