73

I currently work on a project in which SVN is used as a repository. Locally, I do several "experiments" with the code that I don't want to commit to the repo. Therefore I use git locally on top of the SVN working directory and use multiple branches for different "experiments". So it looks e.g. like this.

C0 ---- C7 (master)
 \
  \---- C1 ---- C2 ---- C4 (exp1)
   \     \       \
    \     \       \---- C3 (exp2)
     \     \
      \     \---- C5 (exp3)
       \
        \---- C6 (exp4)

On branch master I want to have the untainted SVN repo, i.e. C0 is SVN Repo Revision x and C7 is SVN Repo Revision x + n.

Is it somehow easily possible to rebase all the exp branches onto C7 at once in such a way that the tree will look like the following diagram?

C0 ---- C7 (master)
         \
          \---- C1' ---- C2' ---- C4' (exp1)
           \     \         \
            \     \         \---- C3' (exp2)
             \     \
              \     \---- C5' (exp3)
               \
                \---- C6' (exp4)

I want to use rebase since I don't want to modify the state of my local SVN Checkout and want to have my changes built on top of the SVN revisions.

I'm also interested in solutions that work from within SourceTree without having to use the shell.

moktor
  • 1,011
  • 1
  • 8
  • 12
  • Possible duplicate of [Git: maintaining many topic branches on a frequently-moving base](https://stackoverflow.com/questions/9407234/git-maintaining-many-topic-branches-on-a-frequently-moving-base) – 0 _ Mar 05 '18 at 10:30
  • 1
    See also: https://github.com/nornagon/git-rebase-all – 0 _ Mar 05 '18 at 10:32
  • 1
    See also: https://github.com/goncalopp/git-utilities – 0 _ Mar 05 '18 at 10:35

5 Answers5

34

You can't get this in one step. At best you move each branch one by one; this would be the sequence:

git rebase --onto master exp1
git rebase --onto c2' c2 exp2
git rebase --onto c1' c1 exp3
git rebase --onto master exp4

The key is to rebase from each branch point in the old tree (e.g. c2 in the above) on to the new tree (e.g. c2' in the above).

GoZoner
  • 67,920
  • 20
  • 95
  • 145
  • 7
    There's a response submitted here [as an answer](https://stackoverflow.com/a/54206192/211160) suggesting the --onto isn't needed on the first or last lines...do you have an opinion and want to respond to their answer or edit yours? – HostileFork says dont trust SE Mar 21 '19 at 15:08
7

As GoZoner answered, there's no way of doing this using the bare git commands, but it's doable to support this kind of rebasing through a script that combines various git plumbing commands.

I wrote a working example, you can find it on github

loopbackbee
  • 21,962
  • 10
  • 62
  • 97
7

I just want to mention that GoZoner's sequence has a mistake in the first (and last) line. exp1 is not the upstream, but the branch you want to rebase.

In order to start the sequence, you need to have master as upstream, i.e. git rebase --onto master master exp1 or short, without --onto it would be git rebase master exp1. The total correct sequence for the example from the question would then be:

git rebase master exp1
git rebase --onto c2' c2 exp2
git rebase --onto c1' c1 exp3
git rebase master exp4
Coperator
  • 81
  • 1
  • 5
  • 5
    Please fix mistakes in the respective answer rather than submitting a new answer. Answers should be self-contained and distinct. Comments on the answer in question work too. – Kissaki Sep 09 '21 at 07:40
  • For reference: [git-rebase](https://git-scm.com/docs/git-rebase) `[--onto | --keep-base] [ []]` – Kissaki Sep 09 '21 at 07:51
6

You can rebase multiple branches / trees with a small trick.

  1. Create a DUMMY_BRANCH e.g. at location of master
  2. Merge all branches you like to move into DUMMY_BRANCH (Using a --no-ff Merge) - Update for comments from @kdb, @franckspike
  3. rebase DUMMY_BRANCH to your desired new point and add --preserve-merges option to your rebase-command
  4. After rebasing manually each of you branches to the rebased commits (e.g. for C4 git reset --hard [Commit-hash of C4' (exp1)]
  5. when you have moved all your branches, delete the rebased DUMMY_BRANCH
Radon8472
  • 4,285
  • 1
  • 33
  • 41
  • The merge in step 2 will foreach (branch) { squash all the commits into one; and then make the "branch" label point to this new commit; } If I'm thinking correctly, this would lose the commit history for each branches – franckspike Jan 24 '20 at 16:57
  • 1
    @franckspike The expected behavior is obtained when doing `--no-ff` merges. However, while the end result contains all the history as expected, the step of manually resetting the branches to the rebased history is error-prone. – kdb May 07 '20 at 14:13
  • Thx @kdb, I added your hint to my answer. – Radon8472 May 12 '20 at 08:46
2

This cannot be done directly in git, however it can be automated using git-assembler.

To quote my similar answer in https://stackoverflow.com/a/62835799/2792879, here's a direct link to the example in the documentation for rebasing local branches

tardis
  • 419
  • 3
  • 5