Suppose you are creating a branch from a trunk and you need to merge the commits from the trunk to the branch. Unlike Git, SVN does not really have a concept of rebasing. To do that in SVN, it requires quite a bit of work. To simplify the tasks of rebasing in SVN, I created small tools, one in Go and the other one in Python.
svnrebase.go
package main
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
"os"
"os/exec"
"strings"
)
const BaseRevFile = "base_rev.txt"
func getLatestBaseSVNRev(svnBaseURL string) (string, error) {
out, e1 := exec.Command("svn", "info", svnBaseURL).Output()
if e1 != nil {
return "", errors.New("Unable to execute svn info " +
svnBaseURL)
}
r := bufio.NewReader(bytes.NewReader(out))
for {
l, _, e2 := r.ReadLine()
if e2 == io.EOF {
break
}
s := string(l)
if strings.Contains(s, "Revision:") {
svnRev := strings.TrimSpace(strings.Split(s, ":")[1])
return svnRev, nil
}
}
return "", errors.New("Unable to get the SVN revision number")
}
func getOldBaseSVNRev() (string, error) {
f, e := os.Open(BaseRevFile)
if e != nil {
return "", errors.New(BaseRevFile + " does not exist. " +
"You need to create this file initially!")
}
defer f.Close()
r := bufio.NewReader(f)
l, _, _ := r.ReadLine()
return strings.TrimSpace(string(l)), nil
}
func updateBaseSVNFile(newBaseRev string) error {
f, e := os.OpenFile(BaseRevFile, os.O_WRONLY, 0666)
if e != nil {
return e
}
defer f.Close()
f.Write([]byte(newBaseRev))
return nil
}
func SVNRebase(svnBaseURL string, dryRun bool) error {
oldBaseRev, e1 := getOldBaseSVNRev()
if e1 != nil {
return e1
}
newBaseRev, e2 := getLatestBaseSVNRev(svnBaseURL)
if e2 != nil {
return e2
}
if oldBaseRev == newBaseRev {
fmt.Println("Your repo has already had the latest revision")
return nil
}
cmd := "svn"
args := []string{"merge"}
if dryRun {
args = append(args, "--dry-run")
}
args = append(args, svnBaseURL + "@" + oldBaseRev)
args = append(args, svnBaseURL + "@" + newBaseRev)
fmt.Println("Command:", cmd + " " + strings.Join(args, " "))
c := exec.Command(cmd, args...)
c.Stdout = os.Stdout
c.Stdin = os.Stdout
c.Stderr = os.Stderr
e3 := c.Run()
if e3 != nil {
return e3
}
if !dryRun {
updateBaseSVNFile(newBaseRev)
}
return nil
}
func printUsage() {
fmt.Println("Usage:", os.Args[0], "<svn_base_url> [--dry-run]")
}
func validateArgs() {
if len(os.Args) == 1 || len(os.Args) > 3 {
printUsage()
os.Exit(1)
}
if len(os.Args) == 3 {
if os.Args[2] != "--dry-run" {
fmt.Println("Error: Invalid option:", os.Args[2])
os.Exit(1)
}
}
}
func main() {
validateArgs()
baseSVNURL := os.Args[1]
dryRun := false
if len(os.Args) == 3 {
dryRun = true
}
if e := SVNRebase(baseSVNURL, dryRun); e != nil {
fmt.Println("Error:", e)
}
}
Usage: ./svnrebase [--dry-run]
svnrebase.py
#!/usr/bin/env python
import sys, subprocess, os
BASE_REV_FILE = 'base_rev.txt'
def get_latest_base_svn_rev(svn_base_url):
p = subprocess.Popen(['svn', 'info', svn_base_url], stdout=subprocess.PIPE)
for line in p.communicate()[0].split(os.linesep):
if line.startswith('Revision:'):
return line.split(":")[1].strip()
return None
def get_old_base_svn_rev():
if not os.path.exists(BASE_REV_FILE):
raise Exception(BASE_REV_FILE + ' does not exist. ' +
'You need to create this file initially!')
f = open(BASE_REV_FILE, 'r')
return f.read().strip()
def update_base_svn_file(new_base_rev):
f = open(BASE_REV_FILE, 'w')
f.write(new_base_rev)
f.close()
def svn_rebase(svn_base_url, dry_run):
old_base_rev = get_old_base_svn_rev()
new_base_rev = get_latest_base_svn_rev(svn_base_url)
if old_base_rev == new_base_rev:
print 'Your repo has already had the latest revision'
return
cmd = ['svn', 'merge']
if dry_run:
cmd.append('--dry-run')
cmd.append(svn_base_url + '@' + old_base_rev)
cmd.append(svn_base_url + '@' + new_base_rev)
print 'Command:', ' '.join(cmd)
subprocess.call(cmd)
if not dry_run:
update_base_svn_file(new_base_rev)
def print_usage():
print 'Usage:', sys.argv[0], '<svn_base_url> [--dry-run]'
def validate_args():
if len(sys.argv) == 1 or len(sys.argv) > 3:
print_usage()
sys.exit(1)
if len(sys.argv) == 3:
if sys.argv[2] != '--dry-run':
print 'Error: Invalid option:', sys.argv[2]
sys.exit(1)
if __name__ == '__main__':
validate_args()
base_svn_url = sys.argv[1]
dry_run = True if len(sys.argv) == 3 else False
try:
svn_rebase(base_svn_url, dry_run)
except Exception, e:
print 'Error:', e
Usage: ./svnrebase.py [--dry-run]
Initially, you need to create base_rev.txt that specifies which revision your branch was created. Subsequently, the base_rev.txt will be automatically updated by the tool.