submerge 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +3 -0
- data/Manifest.txt +9 -0
- data/README.txt +77 -0
- data/Rakefile +18 -0
- data/bin/submerge +8 -0
- data/lib/contrib/revision.rb +85 -0
- data/lib/submerge.rb +157 -0
- data/lib/svn_helper.rb +71 -0
- data/test/test_submerge.rb +0 -0
- metadata +65 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
submerge
|
2
|
+
by Aaron Suggs
|
3
|
+
http://ktheory.com/
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Submerge make it easy to perform common merge tasks when working with trunk
|
8
|
+
and branches in the subversion SCM.
|
9
|
+
|
10
|
+
Submerge requires that your present working directory is a subversion
|
11
|
+
checkout in order to compute the svn URL of your current checkout.
|
12
|
+
|
13
|
+
submerge assumes that your project follows the convention of keeping the
|
14
|
+
latest changes in
|
15
|
+
your-svn-project-url/trunk
|
16
|
+
and branches in
|
17
|
+
your-svn-project-url/branches/your-branch-name
|
18
|
+
and moreover that release branches are named like "RB-/\d+(\.\d+)*/";
|
19
|
+
e.g. the release 1.0 branch would have the URL
|
20
|
+
your-svn-project-url/branches/RB-1.0
|
21
|
+
|
22
|
+
== FEATURES/PROBLEMS:
|
23
|
+
|
24
|
+
== SYNOPSIS:
|
25
|
+
|
26
|
+
$ submerge [CHANGESET...] [-b BRANCH]
|
27
|
+
|
28
|
+
CHANGESET may either be a single number, like "2", or a range changesets,
|
29
|
+
like "M:N" where M and N are numbers. If no CHANGESET is specified, defaults
|
30
|
+
to a single number HEAD. If M or N is not specified in range form, they
|
31
|
+
default to HEAD.
|
32
|
+
So the following are equivalent:
|
33
|
+
submerge
|
34
|
+
submerge HEAD
|
35
|
+
and
|
36
|
+
submerge 2:
|
37
|
+
submerge 2:HEAD
|
38
|
+
and
|
39
|
+
submerge :999
|
40
|
+
submerge HEAD:999
|
41
|
+
|
42
|
+
If BRANCH is not set, defaults to:
|
43
|
+
* latest release branch if pwd is a trunk checkout
|
44
|
+
* trunk otherwise
|
45
|
+
|
46
|
+
The -u or --undor option may be used for in
|
47
|
+
|
48
|
+
== REQUIREMENTS:
|
49
|
+
|
50
|
+
== INSTALL:
|
51
|
+
|
52
|
+
gem install submerge
|
53
|
+
|
54
|
+
== LICENSE:
|
55
|
+
|
56
|
+
(The MIT License)
|
57
|
+
|
58
|
+
Copyright (c) 2007 FIX
|
59
|
+
|
60
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
61
|
+
a copy of this software and associated documentation files (the
|
62
|
+
'Software'), to deal in the Software without restriction, including
|
63
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
64
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
65
|
+
permit persons to whom the Software is furnished to do so, subject to
|
66
|
+
the following conditions:
|
67
|
+
|
68
|
+
The above copyright notice and this permission notice shall be
|
69
|
+
included in all copies or substantial portions of the Software.
|
70
|
+
|
71
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
72
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
73
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
74
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
75
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
76
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
77
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
$:.unshift(File.dirname(__FILE__) + "/lib")
|
6
|
+
require 'submerge'
|
7
|
+
|
8
|
+
Hoe.new('submerge', Submerge::VERSION) do |p|
|
9
|
+
p.rubyforge_name = 'submerge'
|
10
|
+
# p.author = 'Aaron Suggs'
|
11
|
+
# p.email = 'aaron@ktheory.om'
|
12
|
+
# p.summary = 'A tool for helping merge changesets between branches in the Subversion SCM'
|
13
|
+
# p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
|
14
|
+
# p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
|
15
|
+
p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
|
16
|
+
end
|
17
|
+
|
18
|
+
# vim: syntax=Ruby
|
data/bin/submerge
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# Revision class
|
2
|
+
# Designed to function in version comparisons, where the
|
3
|
+
# version number matches /\d+(\.\d+)*/
|
4
|
+
|
5
|
+
# Author: Hugh Sasse, De Montfort University <hgs / dmu.ac.uk>
|
6
|
+
|
7
|
+
# Created: 06-OCT-2000
|
8
|
+
# Last Modified: 19-OCT-2005
|
9
|
+
# $Id: revision.rb,v 1.3 2000-10-09 10:40:39+01 hgs Exp hgs $
|
10
|
+
|
11
|
+
class RevisionComparisonError < RuntimeError; end
|
12
|
+
|
13
|
+
class Revision
|
14
|
+
include Comparable
|
15
|
+
|
16
|
+
DEBUG = false
|
17
|
+
|
18
|
+
attr :contents
|
19
|
+
|
20
|
+
def to_i
|
21
|
+
@contents.join('').to_i
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
astring = @contents.collect { |j| j.to_s }.join(".")
|
26
|
+
end
|
27
|
+
|
28
|
+
# Convert the Major.Minor to float. MinorMinor version number
|
29
|
+
# is difficult to handle because it depends on the size of Minor
|
30
|
+
# to determine how far right to shift it. Bacwards
|
31
|
+
# compatibility would necessitate 1 decimal place per revision,
|
32
|
+
# but if Ruby reaches 1.10.1 this will break. Therefore float
|
33
|
+
# comparisons should be deprecated.
|
34
|
+
def to_f
|
35
|
+
(@contents[0].to_s + '.' + contents[1]..to_s).to_f
|
36
|
+
end
|
37
|
+
|
38
|
+
def [](i)
|
39
|
+
return @contents[i]
|
40
|
+
end
|
41
|
+
|
42
|
+
def <=>(x)
|
43
|
+
puts "Revision(#{self}).<=> called with (#{x})" if DEBUG
|
44
|
+
case x
|
45
|
+
when String
|
46
|
+
result = self <=> Revision.new(x)
|
47
|
+
when Float
|
48
|
+
f = self.to_f
|
49
|
+
puts "#{f} <=> #{x}" if DEBUG
|
50
|
+
result = self.to_f <=> x
|
51
|
+
when Revision
|
52
|
+
len = [@contents.size, x.contents.size].max
|
53
|
+
puts "len is #{len}" if DEBUG
|
54
|
+
result = 0
|
55
|
+
(0..(len-1)).each do |index|
|
56
|
+
puts "result is #{result}" if DEBUG
|
57
|
+
if result == 0
|
58
|
+
result = ((@contents[index] || 0) <=> (x.contents[index] || 0))
|
59
|
+
else
|
60
|
+
break
|
61
|
+
end
|
62
|
+
end
|
63
|
+
puts " finally result is #{result}" if DEBUG
|
64
|
+
result
|
65
|
+
else
|
66
|
+
raise RevisionComparisonError.new("self is #{self}, other is #{x}")
|
67
|
+
end
|
68
|
+
result
|
69
|
+
end
|
70
|
+
|
71
|
+
def initialize(thing)
|
72
|
+
if thing.class == Revision
|
73
|
+
@contents = thing.contents.dup
|
74
|
+
# dup so we get a new copy, not a pointer to it.
|
75
|
+
elsif thing.class == String
|
76
|
+
result = thing.split(".").collect {|j| j.to_i}
|
77
|
+
@contents = result
|
78
|
+
elsif thing.class == Float
|
79
|
+
@contents = thing.to_s.split(".").collect {|j| j.to_i}
|
80
|
+
else
|
81
|
+
raise "cannot initialise to #{thing}"
|
82
|
+
end
|
83
|
+
puts "initialize(#{thing}) => @contents is #{@contents}" if DEBUG
|
84
|
+
end
|
85
|
+
end
|
data/lib/submerge.rb
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
# == Author
|
2
|
+
# Aaron Suggs (aaron@zenbe.com)
|
3
|
+
#
|
4
|
+
# == Copyright
|
5
|
+
# Copyright (c) 2007 Zenbe.
|
6
|
+
require 'optparse'
|
7
|
+
require 'ostruct'
|
8
|
+
require 'rdoc/usage'
|
9
|
+
|
10
|
+
#$:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
11
|
+
begin
|
12
|
+
require 'contrib/revision'
|
13
|
+
require 'svn_helper'
|
14
|
+
rescue
|
15
|
+
|
16
|
+
require 'rubygems'
|
17
|
+
gem 'submerge'
|
18
|
+
end
|
19
|
+
|
20
|
+
class Submerge
|
21
|
+
VERSION = '0.1'
|
22
|
+
|
23
|
+
attr_reader :options
|
24
|
+
|
25
|
+
def initialize(args)
|
26
|
+
# Set default options
|
27
|
+
@options = OpenStruct.new
|
28
|
+
@options.quiet = false
|
29
|
+
@options.verbose = false
|
30
|
+
|
31
|
+
# Parse options
|
32
|
+
opts = OptionParser.new
|
33
|
+
opts.on('-v', '--version') { output_version; exit 0 }
|
34
|
+
opts.on('-h', '--help', '-?') { output_help; exit 0 }
|
35
|
+
opts.on('-V', '--verbose') { @options.verbose = true }
|
36
|
+
opts.on('-q', '--quiet') { @options.quiet = true }
|
37
|
+
opts.on('-u', '--undo') { @options.undo = true}
|
38
|
+
opts.on("-b", "--branch BRANCH", "merge from branch BRANCH") {|branch| @branch = branch}
|
39
|
+
opts.parse!(args)
|
40
|
+
|
41
|
+
begin
|
42
|
+
@svn = SvnHelper.new
|
43
|
+
rescue => e
|
44
|
+
puts e.message
|
45
|
+
exit 1
|
46
|
+
end
|
47
|
+
|
48
|
+
@changesets = opts.default_argv
|
49
|
+
if @changesets.empty?
|
50
|
+
puts "No changesets specified, using HEAD" if @options.verbose
|
51
|
+
@changesets << 'HEAD'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def run
|
56
|
+
parse_changesets
|
57
|
+
|
58
|
+
@changesets.each do |changeset|
|
59
|
+
cmd = "svn merge -r#{changeset} #{source_url}"
|
60
|
+
puts cmd unless @options.quiet
|
61
|
+
system(cmd)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def source_url
|
66
|
+
@source_url ||= compute_source_url
|
67
|
+
end
|
68
|
+
|
69
|
+
def compute_source_url
|
70
|
+
# Compute the source_url from which to merge
|
71
|
+
if @branch
|
72
|
+
unless @svn.valid_branch?(@branch)
|
73
|
+
puts "Invalid branch #{@branch}"
|
74
|
+
exit 1
|
75
|
+
end
|
76
|
+
url = @svn.branch_url(@branch)
|
77
|
+
else
|
78
|
+
url = @svn.trunk? ? @svn.latest_release_branch_url : @svn.trunk_url
|
79
|
+
end
|
80
|
+
puts "Merging from #{url}" unless @options.quiet
|
81
|
+
url
|
82
|
+
end
|
83
|
+
|
84
|
+
# Convert all changesets to ranges by merging from prevision changeset.
|
85
|
+
# E.g.,
|
86
|
+
# "5" => "4:5";
|
87
|
+
# "5:" => "5:HEAD"
|
88
|
+
def parse_changesets
|
89
|
+
@changesets.map! do |changeset|
|
90
|
+
if changeset.include?(':')
|
91
|
+
# Changeset range
|
92
|
+
s,e = changeset.split(':')
|
93
|
+
[s,e].each{|rev| rev = (rev.to_i == 0) ? @svn.head_revision : rev}
|
94
|
+
"#{s}:#{e}"
|
95
|
+
else
|
96
|
+
# Single changeset
|
97
|
+
rev = (changeset.to_i == 0) ? @svn.head_revision.to_i : changeset.to_i
|
98
|
+
@options.undo ? "#{rev}:#{rev-1}" : "#{rev-1}:#{rev}"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
protected
|
104
|
+
|
105
|
+
def output_help
|
106
|
+
output_version
|
107
|
+
|
108
|
+
puts %Q{
|
109
|
+
USAGE:
|
110
|
+
$ submerge [CHANGESET...] [-b BRANCH]
|
111
|
+
|
112
|
+
CHANGESET may either be a single number, like "2", or a range changesets,
|
113
|
+
like "M:N" where M and N are numbers. If no CHANGESET is specified, defaults
|
114
|
+
to a single number HEAD. If M or N is not specified in range form, they
|
115
|
+
default to HEAD.
|
116
|
+
So the following are equivalent:
|
117
|
+
submerge
|
118
|
+
submerge HEAD
|
119
|
+
and
|
120
|
+
submerge 2:
|
121
|
+
submerge 2:HEAD
|
122
|
+
and
|
123
|
+
submerge :999
|
124
|
+
submerge HEAD:999
|
125
|
+
|
126
|
+
If BRANCH is not set, defaults to:
|
127
|
+
* latest release branch if pwd is a trunk checkout
|
128
|
+
* trunk otherwise
|
129
|
+
|
130
|
+
OPTIONS:
|
131
|
+
-h, --help Display help message
|
132
|
+
-b, --branch Specify a branch from which to merge
|
133
|
+
-q, --quiet Silence output
|
134
|
+
-u, --undo Invert merge order for single
|
135
|
+
|
136
|
+
EXAMPLES:
|
137
|
+
If your pwd is a trunk checkout:
|
138
|
+
The following command would merge the difference from changeset 999
|
139
|
+
from the latest release branch to your pwd:
|
140
|
+
submerge 999
|
141
|
+
This is equivalent to running
|
142
|
+
svn merge -r998:999 your-svn-project-url/branches/latest-release-branch
|
143
|
+
The following merges changeset 999 from my-branch:
|
144
|
+
submerge 999 -b my-branch
|
145
|
+
submerge 2
|
146
|
+
Other examples:
|
147
|
+
submerge 2:HEAD
|
148
|
+
submerge 2 3 100
|
149
|
+
submerge 2 -b BRANCH
|
150
|
+
}
|
151
|
+
exit
|
152
|
+
end
|
153
|
+
|
154
|
+
def output_version
|
155
|
+
puts "#{File.basename(__FILE__, '.rb')} version #{VERSION}"
|
156
|
+
end
|
157
|
+
end
|
data/lib/svn_helper.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# Worlds dumbest svn wrapper
|
2
|
+
class SvnNotFound < Exception; end
|
3
|
+
class PwdNotSvn < Exception; end
|
4
|
+
class UnrecognizedSvnURL < Exception; end
|
5
|
+
|
6
|
+
|
7
|
+
class SvnHelper
|
8
|
+
TRUNK_REGEXP = /\/trunk$/ # matches a trunk checkout
|
9
|
+
BRANCH_REGEXP = /\/branches\/\S+$/ # matches a branch checkout
|
10
|
+
RELEASE_BRANCH_REGEXP = /^RB-([0-9.]+)/ # matches release branch names
|
11
|
+
|
12
|
+
attr_reader :url
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
begin
|
16
|
+
@svn_path = `which svn`
|
17
|
+
raise SvnNotFound, "svn not found in path" if @svn_path.empty?
|
18
|
+
svn_info = `svn info`
|
19
|
+
raise PwdNotSvn, "Current directory does not appear to be under SVN" unless $? == 0
|
20
|
+
svn_info.detect{|line| line =~ /^URL: (.*)$/}
|
21
|
+
@url = $1 # The current url
|
22
|
+
raise UnrecognizedSvnUrl, "SVN url is neither trunk nor a branch: #{@url}" unless trunk? || branch?
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Is the current directory a trunk checkout?
|
27
|
+
def trunk?
|
28
|
+
@url =~ TRUNK_REGEXP
|
29
|
+
end
|
30
|
+
|
31
|
+
# Is the current directory a branch checkout?
|
32
|
+
def branch?
|
33
|
+
@url =~ BRANCH_REGEXP
|
34
|
+
end
|
35
|
+
|
36
|
+
def base_url
|
37
|
+
@base_url ||= @url.gsub(trunk? ? TRUNK_REGEXP : BRANCH_REGEXP, '')
|
38
|
+
end
|
39
|
+
|
40
|
+
def branches
|
41
|
+
@branches ||= `svn ls #{base_url}/branches`.split("\n").map{|name| name.sub(/\/$/, '')}
|
42
|
+
end
|
43
|
+
|
44
|
+
def valid_branch?(branch)
|
45
|
+
branches.include? branch
|
46
|
+
end
|
47
|
+
|
48
|
+
def latest_release_branch
|
49
|
+
begin
|
50
|
+
@latest_release ||= branches.inject(Revision.new('0')){|rev, branch| branch =~ RELEASE_BRANCH_REGEXP ? [rev, Revision.new($1)].max : rev}
|
51
|
+
raise LatestReleaseBranchNotFound, "could not find a latest release branch\ntry specifying a source with '-b BRANCH' instead" if @latest_release == Revision.new('0')
|
52
|
+
@latest_release_branch ||= "RB-#{@latest_release}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def head_revision
|
57
|
+
# TODO: there must be a better way to compute this
|
58
|
+
@head_revision ||= `svn log --limit 1 -q`.split[1].sub(/^r/, '')
|
59
|
+
end
|
60
|
+
def trunk_url
|
61
|
+
"#{base_url}/trunk"
|
62
|
+
end
|
63
|
+
|
64
|
+
def branch_url(branch)
|
65
|
+
"#{base_url}/branches/#{branch}"
|
66
|
+
end
|
67
|
+
|
68
|
+
def latest_release_branch_url
|
69
|
+
branch_url(latest_release_branch)
|
70
|
+
end
|
71
|
+
end
|
File without changes
|
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.4
|
3
|
+
specification_version: 1
|
4
|
+
name: submerge
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: "0.1"
|
7
|
+
date: 2007-12-27 00:00:00 -05:00
|
8
|
+
summary: The author was too lazy to write a summary
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: ryand-ruby@zenspider.com
|
12
|
+
homepage: http://www.zenspider.com/ZSS/Products/submerge/
|
13
|
+
rubyforge_project: submerge
|
14
|
+
description: The author was too lazy to write a description
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Ryan Davis
|
31
|
+
files:
|
32
|
+
- History.txt
|
33
|
+
- Manifest.txt
|
34
|
+
- README.txt
|
35
|
+
- Rakefile
|
36
|
+
- bin/submerge
|
37
|
+
- lib/contrib/revision.rb
|
38
|
+
- lib/submerge.rb
|
39
|
+
- lib/svn_helper.rb
|
40
|
+
- test/test_submerge.rb
|
41
|
+
test_files:
|
42
|
+
- test/test_submerge.rb
|
43
|
+
rdoc_options:
|
44
|
+
- --main
|
45
|
+
- README.txt
|
46
|
+
extra_rdoc_files:
|
47
|
+
- History.txt
|
48
|
+
- Manifest.txt
|
49
|
+
- README.txt
|
50
|
+
executables:
|
51
|
+
- submerge
|
52
|
+
extensions: []
|
53
|
+
|
54
|
+
requirements: []
|
55
|
+
|
56
|
+
dependencies:
|
57
|
+
- !ruby/object:Gem::Dependency
|
58
|
+
name: hoe
|
59
|
+
version_requirement:
|
60
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: 1.4.0
|
65
|
+
version:
|