Branching
Introduction
Source control affords us several options for mapping our serverside structure however for clarity we are using a one to one mapping between the server and client folders. Thus this guide does not cover using one working directory and svn switching it between various branches and trunk. Due the limitations of the current version of SVN as regards merging we will be using a utility called SvnMerge that takes away much of the pain and manual process
Key Concepts
The key concepts here are;
- Creating a branch from trunk (or another branch)
- Merging up from the source (i.e. trunk) throughout the lifetime of the branch
- Collapsing the branch back into the source
Server side folders
projects
|-$project
|-trunk
|-tags
|-branches
|-$BranchName1
|-$BranchName2
Local side mapping
c:\code\$project |-trunk |-tags |-branches |-$BranchName1 |-$BranchName2
Note $ merely denotes a given arbitary name (such as MyProject or !MyChangesBranch).
The Branch lifecycle
Quick example
To quickly illustrate the process of creating, using, merging and collapsing a branch we'll use the PowerShell functions (available below). Please note below this example is a reference to manually doing this actions via a standard command prompt. It is also worth mentioning that SVN is case sensitive; be sure to always use correct casing for project and branch names.
- First we wish to create a branch called !MyBranch off of the MyProject project trunk
PS > Create-Branch MyProject MyBranch
- Note you can use '''Help-Branching''' for some quick reminders as to functions
- Make some changes to the !MyBranch
- Now you wish to Merge Up from the Trunk to stay in sync
PS > MergeUp-Branch MyProject MyBranch
- At this point you have uncommited changes wih potential conflicts; resolve these if necessary then
PS > CommitUp-Branch MyProject MyBranch
- Continue to repeat the last two steps throughout your development; you're less likely to face tricky conflicts if you do
- If you need to merge down to the trunk
PS > MergeDown-Branch MyProject MyBranch
- As with a merge up operation a similar merge down commit is required once any conflicts are resolved
PS > CommitDown-Branch MyProject MyBranch
- When you've finished development and are ready to collapse the branch; first ensure you have Merged down any changes (as per last two steps) then
PS > Close-Branch MyProject MyBranch Are you sure(y|n)? this will delete the specified branch (Close-Branch -? for more) y
Branch Creation
Create via Powershell (See below)
Create-Branch MyProject MyBranch
Create via Command batch file (See below)
CreateBranch.bat MyProject MyBranch
Create Manually
rem Get latest reverting any uneeded changes cd c:\code\MyProject\trunk\ svn revert . -R svn up rem : Create A Branch from Trunk On Server svn copy https://svn.example.com/projects/MyProject/trunk https://svn.example.com/projects/MyProject/branches/MyBranch -m "Created MyBranch" cd c:\code\MyProject\branches\ svn up MyBranch rem Initialize merge up support cd c:\code\MyProject\branches\MyBranch svnmerge init svn commit -F svnmerge-commit-message.txt del svnmerge-commit-message.txt rem Initialise merge down support cd c:\code\MyProject\trunk\ svn revert . -R svnmerge init https://svn.example.com/projects/MyProject/branches/MyBranch svn commit -F svnmerge-commit-message.txt del svnmerge-commit-message.txt rem Get the new branch locally cd c:\code\MyProject\branches\ svn up https://svn.example.com/projects/MyProject/branches/MyBranch MyBranch
Merging
Merging Up
Merging up Via PowerShell
MergeUp-Branch MyProject MyBranch
- '''You must identify, fix and resolve merge conflicts here'''
CommitUp-Branch MyProject MyBranch
Merge up Manually
cd c:\code\MyProject\branches\MyBranch svnmerge merge --bidirectional -S https://svn.example.com/projects/MyProject/trunk
- '''You must identify, fix and resolve merge conflicts here'''
cd c:\code\MyProject\branches\MyBranch svn commit -F svnmerge-commit-message.txt del svnmerge-commit-message.txt
Merging Down
- If you have work that needs to go back into the source follow this process
Merge down via PowerShell
MergeDown-Branch MyProject MyBranch
- '''You must identify, fix and resolve merge conflicts here'''
CommitDown-Branch MyProject MyBranch
Merge Down manually
cd c:\code\MyProject\trunk svnmerge merge --bidirectional -S https://svn.example.com/projects/MyProject/branches/MyBranch
- '''You must identify, fix and resolve merge conflicts here'''
cd c:\code\MyProject\trunk svn commit -F svnmerge-commit-message.txt del svnmerge-commit-message.txt
Closing A Branch
Do a final merge up of the latest trunk changes to the branch. When that's done, the latest versions of branch and trunk will be absolutely identical except for your branch changes.
Close A Branch via PowerShell
Close-Branch MyProject MyBranch
Close A Branch Manually
cd c:\code\MyProject\branches\MyBranch svnmerge uninit -S https://svn.example.com/projects/MyProject/trunk svn commit -m "Removing trunk merge integration for MyBranch" del svnmerge-commit-message.txt cd c:\code\MyProject\trunk\ svnmerge uninit -S https://svn.example.com/projects/MyProject/branches/MyBranch del svnmerge-commit-message.txt svn remove https://svn.example.com/projects/MyProject/branches/MyBranch -m "MyBranch is now closed" cd c:\code\MyProject\branches\ svn up MyBranch
Automation Scripts
Command Batch files
!CreateBranch.bat
- '''Usage:''' [[code type="javascript"]]CreateBranch.bat %project %DesiredBranchName[[/code]]
- '''Example:''' [[code type="javascript"]]CreateBranch.bat MyProject MyMyProjectBranch[[/code]]
cd c:\code\%1\trunk\ svn revert . -R svn up svn copy https://svn.example.com/projects/%1/trunk https://svn.example.com/projects/%1/branches/%2 -m "Created %2 merge from this point" cd c:\code\%1\branches\ svn up %2 CD %2 svnmerge init svn commit -F svnmerge-commit-message.txt del svnmerge-commit-message.txt cd c:\code\%1\trunk\ svnmerge init https://svn.example.com/projects/%1/branches/%2 svn commit -F svnmerge-commit-message.txt del svnmerge-commit-message.txt echo Merging enabled via calling SvnMerge while in branch root folder echo for more information please read: echo https://svn.example.com/cgi-bin/trac.cgi/wiki/BranchingGuidance#MergeUpChanges"
PowerShell Functions
- Help-Branching
- Create-Branch $projectName $branchName
- MergeUp-Branch $projectName $branchName
- CommitUp-Branch $projectName $branchName
- MergeDown-Branch $projectName $branchName
- CommitDown-Branch $projectName $branchName
- Close-Branch $projectName $branchName
Adding functions to your PowerShell
- See PowerShell page for more details on setting up PowerShell
- Simply add these scripts to the end of your $profile
Function Script
########### Branching functions ############# #Help-Branching function Help-Branching() { echo "Skinkers Branching Guidance" "Commands:" echo "Create-Branch [projectName] [branchName] - Branch from $projectName trunk in c:\code\" echo "MergeUp-Branch [projectName] [branchName] - Updates a branch with latest from trunk" echo "CommitUp-Branch [projectName] [branchName] - Commits latest changes from trunk" echo "MergeDown-Branch [projectName] [branchName] - Updates a trunk with latest changes from branch" echo "CommitDown-Branch [projectName] [branchName] - Commits latest changes from branch" echo "Close-Branch [projectName] [branchName] - Deletes a trunk be sure to merge down first" echo "Notes:" echo "All merge commands require the user to ensure that all conflicts are resolved before commiting" echo "Do not close a branch until you have merged down and committed" } #Create-Branch function Create-Branch ([string] $projectName, [string] $branchName) { if(($projectName -eq "-?") -or ($projectName -match "help")) { echo "Usage: Create-Branch [string]$projectName [string]$branchName" echo "Example: Create-Branch MyProject MyNewBranch" "For more details please read:" echo "https://svn.example.com/cgi-bin/trac.cgi/wiki/BranchingGuidance#branchcreation" } elseif (([String]::IsNullOrEmpty($projectName)) -or ([String]::IsNullOrEmpty($branchName))) { echo "You must provide a projectName and a branch name for this command; type Create-Branch help for more information " } else { $startLocation = Get-Location cd c:\code\$projectName\trunk\ svn revert . -R svn up $projectRoot = "https://svn.example.com/projects/" $commitMessage = "Created $branchName merge from this point" svn copy $projectRoot$projectName/trunk $projectRoot$projectName/branches/$branchName -m $commitMessage cd c:\code\$projectName\branches\ svn up $branchName CD $branchName svnmerge init svn commit -F svnmerge-commit-message.txt del svnmerge-commit-message.txt cd c:\code\$projectName\trunk\ svnmerge init https://svn.example.com/projects/$projectName/branches/$branchName svn commit -F svnmerge-commit-message.txt del svnmerge-commit-message.txt echo "Merging enabled via SvnMerge or MergeUp-Branch $projectName $branchName" "for more information please read:" "https://svn.example.com/cgi-bin/trac.cgi/wiki/BranchingGuidance#MergeUpChanges" cd $startLocation } } # MergeUp-Branch function MergeUp-Branch([string] $projectName, [string] $branchName) { if(($projectName -eq "-?") -or ($projectName -match "help")) { echo "" "Usage: MergeUp-Branch [string]$projectName [string]$branchName" "Examples:" echo "MergeUp-Branch MyProject MyNewBranch" "Notes:" echo "Reverts all curent changes in branch, updates your branch then merges changes from trunk" echo "For more details" "please read https://svn.example.com/cgi-bin/trac.cgi/wiki/BranchingGuidance#MergingUp" } elseif (([String]::IsNullOrEmpty($projectName)) -or ([String]::IsNullOrEmpty($branchName))) { echo "You must provide a projectName and a branch name for this command; type MergeUp-Branch help for more information " } else { cd c:\code\$projectName\branches\$branchName svnmerge merge --bidirectional -S https://svn.example.com/projects/$projectName/trunk echo "When you have resolved any conflicts commit the merge with CommitUp-Branch" echo "Do not delete svnmerge-commit-message.txt" } } #CommitUp-Branch function CommitUp-Branch([string] $projectName, [string] $branchName) { if(($projectName -eq "-?") -or ($projectName -match "help")) { echo "" "Usage: CommitDown-Branch [string]$projectName [string]$branchName" "Examples:" echo "CommitUp-Branch MyProject MyNewBranch" "Notes:" echo "Commits all merge changes and resolutions to branch" echo "For more details please read" "https://svn.example.com/cgi-bin/trac.cgi/wiki/BranchingGuidance#MergingUp" } elseif (([String]::IsNullOrEmpty($projectName)) -or ([String]::IsNullOrEmpty($branchName))) { echo "You must provide a projectName and a branch name for this command; type CommitUp-Branch help for more information " } else { cd c:\code\$projectName\branches\$branchName svn commit -F svnmerge-commit-message.txt del svnmerge-commit-message.txt } } #MergeDown-Branch function MergeDown-Branch([string] $projectName, [string] $branchName) { if(($projectName -eq "-?") -or ($projectName -match "help")) { echo "" "Usage: MergeDown-Branch [string]$projectName [string]$branchName" "Examples:" echo "MergeDown-Branch MyProject MyNewBranch" "Notes:" echo "Reverts all curent changes in branch, updates your branch then merges changes from trunk" echo "For more details please read" "https://svn.example.com/cgi-bin/trac.cgi/wiki/BranchingGuidance#MergingDown" } elseif (([String]::IsNullOrEmpty($projectName)) -or ([String]::IsNullOrEmpty($branchName))) { echo "You must provide a projectName and a branch name for this command; type MergeDown-Branch help for more information " } else { cd c:\code\$projectName\trunk svn revert . -R svn up svnmerge merge --bidirectional -S https://svn.example.com/projects/$projectName/branches/$branchName echo "When you have resolved any conflicts commit the merge with CommitDown-Branch" echo "Do not delete svnmerge-commit-message.txt" } } #CommitDown-Branch function CommitDown-Branch([string] $projectName, [string] $branchName) { if(($projectName -eq "-?") -or ($projectName -match "help")) { echo "" "Usage: CommitDown-Branch [string]$projectName [string]$branchName" "Examples:" echo "CommitDown-Branch MyProject MyNewBranch" "Notes:" echo "Commits all merge changes and resolutions to trunk" echo "For more details please read" "https://svn.example.com/cgi-bin/trac.cgi/wiki/BranchingGuidance#MergingDown" } elseif (([String]::IsNullOrEmpty($projectName)) -or ([String]::IsNullOrEmpty($branchName))) { echo "You must provide a projectName and a branch name for this command; type CommitDown-Branch help for more information " } else { cd c:\code\$projectName\trunk svn commit -F svnmerge-commit-message.txt del svnmerge-commit-message.txt } } # Close-Branch function Close-Branch ([string] $projectName, [string] $branchName) { if(($projectName -eq "-?") -or ($projectName -match "help")) { echo "" "Usage: Close-Branch [string]$projectName [string]$branchName" echo "Examples:" "Close-Branch MyProject MyNewBranch" "Notes:" echo "Please read https://svn.example.com/cgi-bin/trac.cgi/wiki/BranchingGuidance#ClosingABranch" } elseif (([String]::IsNullOrEmpty($projectName)) -or ([String]::IsNullOrEmpty($branchName))) { echo "You must provide a projectName and a branch name for this command; type Close-Branch help for more information " } else { echo "Are you sure(y|n)? this will delete the specified branch (Close-Branch help for more)" $confirm = [System.Console]::ReadLine() if($confirm -match "y") { cd c:\code\$projectName\branches\$branchName svnmerge uninit -S https://svn.example.com/projects/$projectName/trunk svn commit -m "Removing trunk merge integration for $branchName" del svnmerge-commit-message.txt cd c:\code\$projectName\trunk\ svnmerge uninit -S https://svn.example.com/projects/$projectName/branches/$branchName svn commit -m "Removing $branchName merge integration for trunk" del svnmerge-commit-message.txt svn remove https://svn.example.com/projects/$projectName/branches/$branchName -m "$branchName is now closed" cd c:\code\$projectName\branches\ svn up $branchName } else { echo "Close cancelled; remember to merge down all required changes before closing a branch" } } }