How to Subversion rebase?
Asked Answered
C

6

51

I find this way easier to merge branches and less conflicts:

Copy trunk to a new branch, merge it with feature branch/s. When things done, merge the new branch back to the trunk. This technique is quite like the mercurial and git rebasing.

I used to merge whatever changes from trunk to feature branches. But later when I merged the feature branch back to trunk, some of the stuff from trunk would be merged back again to the trunk, which caused a lot of conflicts. There is a choice of reintegrate merge, but it didn't seem to work for me.

Does anyone do similar subversion rebasing? I just started doing this recently, and haven't seen any side effects. Would this cause any unforeseen problems?

Cashbox answered 5/7, 2010 at 4:12 Comment(2)
What you suggest in your question is the right solution ;-) It should work as expected, I can't see any side effects.Flabby
I am a vcs noob. I am curious: what kind of conflicts would these be? If you merge trunk@r1 through trunk@r2 into branch, and merge the result back into trunk, then there shouldn't be any changes due to trunk. Can you give an example?Unhelm
O
45

Generally speaking, rebasing is the act of incorporating upstream changes into a feature branch, before merging the feature branch back into the upstream branch.

In git, the process is even more sophisticated, because the changes that have been made since the branch was created are first taken off and buffered, the upstream changes are applied, then the buffered changes are applied. The takeaway here is merging trunk into a feature branch is not a rebase in git terms, there's more to it. The git approach has a number of advantages, but can't be implemented very cleanly in svn since all commits must be stored on the server (svn is not distributed), however it can be done in svn.

An 'svn rebase' (the git way) might look something like this

  1. svn cp trunk feature
  2. commits to feature & trunk
  3. svn cp trunk feature-rebase
  4. svn co feature-rebase
  5. cd feature-rebase
  6. svn merge feature
  7. svn commit
  8. svn rm feature
  9. svn mv feature-rebase feature
  10. (back on feature-rebase WC) svn switch feature

Then eventually on a working copy of trunk, svn merge --reintegrate feature

You see the difference from simply merging trunk to the feature branch? You start with the latest from upstream, trunk in this example, then merge the changes from feature onto that.

Imagine some of the commits on trunk could come from a merge of another feature branch into trunk, so I am not at all advocating committing directly to trunk.

Oraliaoralie answered 30/9, 2013 at 2:9 Comment(2)
The point-by-point explanation is a bit too minimalistic for a beginner to understand and the article that quickshiftin created is not available anymore. QQReimpression
The answer wasn't geared towards beginners, it was meant to be canonical. To your point, I've pulled down the link to the article.Oraliaoralie
B
9

I wish I had a clever trick to tell you on how to achieve rebasing in SVN but I've always avoided manual refreshing of a branch with trunk changes in SVN mainly because of the complications requiring manual cherry picking that jdehaan mentions.

What I generally do instead is follow the practice of merging changes from a branch to the trunk, deleting the branch, and then recreating the branch from the trunk. This allows me to refresh/rebase my feature branch but with the sometimes unfortunate side effect that any prior changes from that branch are now part of the trunk. For this reason I only follow this practice when a feature branch is at a stable and usable point yet I still wish to continue work on that feature in order to further complete some bigger objective.

What I would prefer is that refreshing a branch by merging trunk changes back into a branch not cause subsequent reintegration merges from that branch to pull those rebased revisions during the process. It should be possible to do this based on the merge-info properties but according to what jdehaan states and what I have feared is that doing this still requires cherry picking.

Note that proper rebasing implementation should also be able to take into consideration stair casing examples where a branch is made from another branch.

Update: According to the Subversion documentation it appears that when using the --reintegrate option that Subversion should be able to properly reintegrate work done in a branch in a way that minds any possible refresh merges that may have been done to bring base changes into the branch. Of course this is is technically a little different than rebasing but i think it is similar enough in usage that it could be referred to as rebasing.

Briton answered 9/2, 2011 at 18:28 Comment(1)
You have to use --reintegrate when merging upstream in subversion. This all came along w/ svn 1.5 merge tracking and made merging possible compared to the old headaches of tracking which changesets had been merged into a branch already. It's still pretty primitive an implementation compared to what hg / git offer and not really part the rebasing process. It's more for when you're done w/ a feature or release branch.Oraliaoralie
I
5

In my company we use following approach:

  1. for each task NK-$X in the issue tracker we have a separate branch branches/NK-$X
  2. we start work on a task by svn cp trunk branches/NK-$X
  3. we never commit changes directly to the trunk. For each schedulled update UPDNK-$X we have a separate branches/UPDNK-$X. We create it with svn cp trunk branches/UPDNK-$X just before the update.
  4. when the task NK-$X is schedulled for an update UPDNK-$Y we merge branches/NK-$X inot UPDNK-$Y. That is cd UPDNK-$Y; svn merge -r start:HEAD branches/NK-$X
  5. after UPDNK-$Y is ready, we merge it to trunk. That is cd trunk;svn merge -r start:HEAD branches/UPDNK-$Y

If it happens that task NK-$X lasts longer than one iteration cycle, and therefore needs refreshing, we never, ever, NEVER merge trunk to NK-$X. We have a rule that you commit to your branch only things that you wrote yourself, which makes everything easier. Instead we do:

cd NK-$X
svn log
//let L = the number of the last changeset to this branch changeset
//let F = the number of the first changeset to this branch
svn rm branches/NK-$X 
svn cp trunk branches/NK-$X 
svn up
svn merge -r F:L branches/NK-$X@L 
svn ci -m 'refereshed'

This way, whenever you look at the changelog of branches/NK-$X you see only changes actually performed by the developer.

Update: Since the above workflow can be automated, I've started a project on github: svn rebase.

Ish answered 22/11, 2012 at 7:17 Comment(0)
P
2

use git svn:

git svn clone -s <link to svn trunk/branches/tags parent>

then feel free to use git rebase command

Polonaise answered 21/6, 2019 at 9:49 Comment(0)
H
0

I am using this approach:

With rebasing you have to take care not to take the rebased revisions over when you merge again. When it comes to merging, do a cherry picking: select only the revisions on the feature branch that implement something new, not the rebasing changesets. Then it should work fine. COMMENT: I cannot ever remember having used the reintegrate branch for something. I think it is intended for very simple use-cases only.

In your new approach, it is not clear from the description how you handle the rebase from trunk to your feature branches if you need to. Do you want to completely disallow rebasing? As branching in svn is a cheap operation this could be an option too.

Hagiarchy answered 5/7, 2010 at 5:13 Comment(1)
below is my procedure: 1. trunk->f1,f2(feature branches) 2. when f1,f2 done, trunk->version branch(for integration) 3. merge f1,f2 to version branch (VB) 4. test VB; merge fixs from f1,f2 to VB; keep this step short, otherwise repeat 2 to 3 5. when VB done, merge it back to the trunk, release it.Cashbox
P
0

I use a script that does a git like rebase for svn:

#!/bin/bash

set_safe()
{
    set -eo pipefail
}

validate_number()
(
    if (! [ "$1" -eq "$1" ] 2>/dev/null ) then
    {
        echo "$1 is not a number"
        return 1
    }
    fi
)

get_line()
(
    set_safe
    #head -n "$1" | tail -n 1
    sed -n "${1}p;$(($1+1))q"
)

split_trim()
(
    set_safe
    tr "$1" '\n' | sed -e 's/^\s*//;' -e 's/\s*$//'
)

run_svn()
(
    set +x
    #echo "svn $*" 1>&2
    svn --non-interactive --password "$svn_password" "$@"
)

rebase()
(
    set_safe

    url="$1"
    cur="$2"
    end="$3"

    validate_number "$cur"
    if ([ -z "$end" ] || [ "$end" = "HEAD" ]) then
    {
        end="$(run_svn info "$url" | grep "Last Changed Rev" | cut -d' ' -f4)"
        echo "end: $end"
    }
    else
    {
        validate_number "$end";
    }
    fi

    while (true) do
    {
        log="$(run_svn log "$url" -l1 -r "$cur:HEAD")"
        meta="$(echo -n "$log" | get_line 2 | split_trim '|')"
        next="$(echo -n "$meta" | get_line 1 | tail -c +2)"
        author="$(echo -n "$meta" | get_line 2)"
        date="$(echo -n "$meta" | get_line 3 | awk '{print $1, $2, $3}')"
        msg="$(echo -n "$log" | tail -n +4 | head -n -1)"
        cur=$next

        if ([ -z $cur ] || [ $cur -gt $end ]) then { break; } fi

        echo "$msg" > .msg

        echo "Merging revision $cur:"
        echo "========"
        cat .msg
        echo "========"

        run_svn merge "$url" -c $cur
        run_svn commit -F .msg
        rm -f .msg
        run_svn update

        echo "Success"
        echo

        cur=$(($cur + 1))
    }
    done
)

if ([ -z "$1" ]) then
{
    echo "Usage:"
    echo "    svn-rebase.sh <url> <begin revision> [end revision]"
    exit
}
fi

echo -n "svn password: "
read -s svn_password
echo

rebase "$1" "$2" "$3"
err=$?
if ([ $err -ne 0 ]) then { echo "rebase failed: $err"; } fi
exit $err

It merges revisions from other branch one by one.

Paunchy answered 8/5, 2018 at 11:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.