submerge 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,3 @@
1
+ == 0.1 / 2007-12-27
2
+
3
+ * Initial release
data/Manifest.txt ADDED
@@ -0,0 +1,9 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ bin/submerge
6
+ lib/contrib/revision.rb
7
+ lib/submerge.rb
8
+ lib/svn_helper.rb
9
+ test/test_submerge.rb
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,8 @@
1
+ #!/usr/bin/env ruby
2
+ begin
3
+ require 'submerge'
4
+ rescue LoadError
5
+ require 'rubygems'
6
+ require 'submerge'
7
+ end
8
+ Submerge.new(ARGV).run
@@ -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: