hg-port 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,12 +1,54 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'pathname'
3
+ require 'yaml'
3
4
  require 'tempfile'
4
-
5
5
  THIS_PATH = Pathname.new(__FILE__).realpath
6
6
  THIS_DIR = THIS_PATH.dirname
7
7
  THIS_BASE_NAME = THIS_PATH.basename
8
+ LIB_DIR = File.join(THIS_DIR, '..', 'lib', 'hg-port')
9
+ require File.join(LIB_DIR, 'gem_info')
10
+ require File.join(LIB_DIR, 'option_parser_with_required_args')
11
+ require File.join(LIB_DIR, 'version')
12
+ require File.join(LIB_DIR, 'version_template')
8
13
 
9
14
  module Helper
15
+ def self.option_parser
16
+ @option_parser ||= OptionParserWithRequiredArgs.new do |p|
17
+ p.banner = "Usage: #{THIS_BASE_NAME} [options]"
18
+
19
+ p.on_required(:merge_version, '--merge-version MERGE-VERSION', 'Merge version at which forward port will begin') do |options, value|
20
+ options.merge_version = value
21
+ end
22
+
23
+ p.on_tail '-h', '--help', 'Show help' do
24
+ puts p
25
+ exit
26
+ end
27
+
28
+ p.on_tail '--version', 'Show version' do
29
+ puts HGPort::GemInfo.version_string
30
+ exit
31
+ end
32
+ end
33
+ end
34
+
35
+ def self.read_config
36
+ config_file_name = nil
37
+ [
38
+ `hg root`.chomp,
39
+ ENV['HOME']
40
+ ].each do |path|
41
+ temp = File.join(path, '.hgportrc')
42
+ if File.exist?(temp)
43
+ config_file_name = temp
44
+ break
45
+ end
46
+ end
47
+ raise RuntimeError.new('No configuration file found') unless config_file_name
48
+
49
+ YAML.load(File.open(config_file_name))
50
+ end
51
+
10
52
  def self.fail(message, lines)
11
53
  display_output(lines)
12
54
  raise RuntimeError.new(message)
@@ -27,7 +69,7 @@ Switches:
27
69
  --merge-version <version-branch>
28
70
 
29
71
  Available versions:
30
- #{versions.collect { |version| " #{version}" }.join("\n")}
72
+ #{versions.reverse.collect { |version| " #{version}" }.join("\n")}
31
73
 
32
74
  EOS
33
75
  end
@@ -62,74 +104,8 @@ module HGHelper
62
104
  end
63
105
  end
64
106
 
65
-
66
- class Version
67
- include Comparable
68
-
69
- attr_reader :major, :minor
70
-
71
- def self.parse(str)
72
- return nil if str !~ /^v([0-9]+)\.([0-9]+)$/
73
- new(Integer($1), Integer($2))
74
- end
75
-
76
- def <=>(other)
77
- return 1 if other.nil?
78
-
79
- delta = major - other.major
80
- return 1 if delta > 0
81
- return -1 if delta < 0
82
-
83
- delta = minor - other.minor
84
- return 1 if delta > 0
85
- return -1 if delta < 0
86
- 0
87
- end
88
-
89
- def to_s
90
- "v#{major}.#{minor}"
91
- end
92
-
93
- private
94
-
95
- def initialize(major, minor)
96
- @major = major
97
- @minor = minor
98
- end
99
- end
100
-
101
- class Options
102
- attr_reader :merge_version
103
-
104
- def self.parse(args, versions)
105
- merge_version = nil
106
- while arg = args.shift
107
- case arg
108
- when '--merge-version' then
109
- temp = args.shift or raise RuntimeError.new('Value required for --merge-version')
110
- merge_version = Version.parse(temp) or raise RuntimeError.new("Invalid value \"#{temp}\" for --merge-version")
111
- else
112
- raise RuntimeError.new("Unsupported switch \"#{arg}\"")
113
- end
114
- end
115
-
116
- if !merge_version || !versions.include?(merge_version)
117
- Helper.display_usage(versions)
118
- return nil
119
- end
120
-
121
- new(merge_version)
122
- end
123
-
124
- private
125
-
126
- def initialize(merge_version)
127
- @merge_version = merge_version
128
- end
129
- end
130
-
131
- def get_versions(branches)
132
- branches.collect { |branch| Version.parse(branch)}.compact.sort
107
+ def get_versions(version_template, branches)
108
+ branches.collect { |branch| HGPort::Version.parse(version_template, branch)}.compact.sort
133
109
  end
134
110
 
135
111
  def forward_port(dir, merge_version, version)
@@ -138,7 +114,6 @@ def forward_port(dir, merge_version, version)
138
114
 
139
115
  # Build commit message from preview
140
116
  status, lines = HGHelper.run_hg(dir, "hg merge --preview #{merge_version}")
141
- puts "LINES: #{lines.inspect}"
142
117
  commit_message = <<-EOS
143
118
  Automatic forward port from #{merge_version} to #{version}
144
119
 
@@ -188,15 +163,30 @@ end
188
163
 
189
164
  dir = '.'
190
165
 
191
- versions = get_versions(HGHelper.get_branches(dir))
192
- options = Options.parse(ARGV.clone, versions)
193
- exit 1 if options.nil?
166
+ config = Helper.read_config
167
+ raise RuntimeError.new('No version template specified') unless config.include?('version_template')
168
+
169
+ version_template = HGPort::VersionTemplate.new(config['version_template'])
170
+ branches = HGHelper.get_branches(dir)
171
+ versions = get_versions(version_template, branches)
172
+
173
+ options = nil
174
+ begin
175
+ options, free_args = Helper.option_parser.run(ARGV)
176
+ rescue OptionParserWithRequiredArgs::RunError => e
177
+ $stderr.puts e.message
178
+ exit 1
179
+ end
180
+
181
+ exit 0 if ENV.include?('HG_PORT_TEST')
194
182
 
195
- unless options.merge_version.to_s == HGHelper.current_branch(dir)
183
+ merge_version = HGPort::Version.parse(version_template, options.merge_version) or raise RuntimeError.new("Invalid merge version \"#{options.merge_version}\"")
184
+ current_branch_version = HGPort::Version.parse(version_template, HGHelper.current_branch(dir))
185
+ unless current_branch_version && merge_version == current_branch_version
196
186
  raise RuntimeError.new("Check out merge version \"#{options.merge_version}\" before attempting a forward port")
197
187
  end
198
188
 
199
- merge_and_later_versions = versions.select { |version| version >= options.merge_version }
189
+ merge_and_later_versions = versions.select { |version| version >= merge_version }
200
190
  merge_and_later_versions.each_with_index do |version, index|
201
191
  next if index == 0
202
192
  unless forward_port(dir, merge_and_later_versions[index - 1], version)
@@ -0,0 +1,12 @@
1
+ require 'pathname'
2
+
3
+ module HGPort
4
+ autoload :Version, 'hg-port/version'
5
+
6
+ extend self
7
+ end
8
+
9
+ Dir[File.join(Pathname.new(__FILE__).realpath.dirname, 'hg-port', '*.rb')].each do |path|
10
+ require path
11
+ end
12
+
@@ -0,0 +1,12 @@
1
+ module HGPort
2
+ module GemInfo
3
+ MAJOR_VERSION = 0
4
+ MINOR_VERSION = 0
5
+ PATCH_VERSION = 1
6
+
7
+ def self.version_string
8
+ [MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION].join('.')
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,48 @@
1
+ require 'optparse'
2
+ require 'ostruct'
3
+
4
+ class OptionParserWithRequiredArgs < OptionParser
5
+ class RunError < Exception; end
6
+
7
+ def initialize
8
+ @required_arg_names = []
9
+ super
10
+ end
11
+
12
+ def on_required(arg_sym, *args, &block)
13
+ on *args, &block
14
+ @required_arg_names << arg_sym
15
+ end
16
+
17
+ def on(*args, &block)
18
+ super *args do |value|
19
+ block.call(@options, value)
20
+ end
21
+ end
22
+
23
+ def on_tail(*args, &block)
24
+ super *args do
25
+ block.call(@options)
26
+ end
27
+ end
28
+
29
+ def run(args)
30
+ @options = OpenStruct.new
31
+ cloned_args = args.clone
32
+ begin
33
+ parse!(cloned_args)
34
+ rescue OptionParser::InvalidOption, OptionParser::MissingArgument
35
+ message = $!.to_s + "\n\n" + self.to_s
36
+ raise RunError.new(message)
37
+ end
38
+
39
+ missing_arg_names = @required_arg_names.select { |arg_name| @options.send(arg_name).nil? }
40
+ if !missing_arg_names.empty?
41
+ message = "missing required option: #{missing_arg_names.join(', ')}\n\n" + self.to_s
42
+ raise RunError.new(message)
43
+ end
44
+
45
+ [@options, cloned_args]
46
+ end
47
+ end
48
+
@@ -0,0 +1,54 @@
1
+ module HGPort
2
+ class Version
3
+ include Comparable
4
+
5
+ attr_reader :major, :minor, :patch
6
+
7
+ def self.parse(template, str)
8
+ values = template.parse(str)
9
+ return nil if values.nil?
10
+ major = values[:major]
11
+ minor = values[:minor]
12
+ patch = values[:patch]
13
+ return nil if major.nil? || minor.nil? || patch.nil?
14
+
15
+ new(template, major, minor, patch)
16
+ end
17
+
18
+ def <=>(other)
19
+ return 1 if other.nil?
20
+
21
+ delta = major - other.major
22
+ return 1 if delta > 0
23
+ return -1 if delta < 0
24
+
25
+ delta = minor - other.minor
26
+ return 1 if delta > 0
27
+ return -1 if delta < 0
28
+
29
+ delta = patch - other.patch
30
+ return 1 if delta > 0
31
+ return -1 if delta < 0
32
+
33
+ 0
34
+ end
35
+
36
+ def to_s
37
+ @template.expand(
38
+ :major => major,
39
+ :minor => minor,
40
+ :patch => patch
41
+ )
42
+ end
43
+
44
+ private
45
+
46
+ def initialize(template, major, minor, patch)
47
+ @template = template
48
+ @major = major
49
+ @minor = minor
50
+ @patch = patch
51
+ end
52
+ end
53
+ end
54
+
@@ -0,0 +1,29 @@
1
+ module HGPort
2
+ class VersionTemplate
3
+ def initialize(template_str)
4
+ @template_str = template_str
5
+
6
+ @keys = []
7
+ pattern = Regexp.escape(@template_str).gsub(/(:\w+)/) do |colon_key|
8
+ key = colon_key[1..-1]
9
+ @keys << key.to_sym
10
+ "(?<#{key}>[0-9]+)"
11
+ end
12
+
13
+ @regexp = Regexp.new("^#{pattern}$")
14
+ end
15
+
16
+ def expand(values)
17
+ @template_str.gsub(/(:\w+)/) do |colon_key|
18
+ key_sym = colon_key[1..-1].to_sym
19
+ values[key_sym] || $1
20
+ end
21
+ end
22
+
23
+ def parse(str)
24
+ match = @regexp.match(str)
25
+ match ? @keys.inject({}) { |memo, key| memo[key] = Integer(match[key]); memo } : nil
26
+ end
27
+ end
28
+ end
29
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hg-port
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 0.0.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-07-23 00:00:00.000000000 Z
12
+ date: 2013-07-26 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Script to automate forward porting in Mercurial
15
15
  email: rcook@rcook.org
@@ -19,6 +19,11 @@ extensions: []
19
19
  extra_rdoc_files: []
20
20
  files:
21
21
  - MIT-LICENSE.txt
22
+ - lib/hg-port/option_parser_with_required_args.rb
23
+ - lib/hg-port/gem_info.rb
24
+ - lib/hg-port/version_template.rb
25
+ - lib/hg-port/version.rb
26
+ - lib/hg-port.rb
22
27
  - bin/hg-port
23
28
  homepage: https://bitbucket.org/rcook/hg-port
24
29
  licenses: