hglib 0.2.0 → 0.3.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f6d1d2540e89d71dd245cd203f0bfac51901c85ecc2e15ad9813566780accb85
4
- data.tar.gz: 921a4beab0aea45f0762a54e010e25d2385534dc10b909a8f0b3d651c1b12e25
3
+ metadata.gz: 609e12ea2bd06f9e02b74b8f9c44502ebfe9bf8eb3d76cde39229719f25296be
4
+ data.tar.gz: e2100c0d719f3ae437e8d44120b881bc561f488b81ba84ba77745a2fde8db020
5
5
  SHA512:
6
- metadata.gz: 0642c4fa43ba0f1dba6962e12ef566c3123079c78db53d3d9ffb4a73e1d9a9504a7f66b3966a622ba205fbb22f41ffbcd25df81044e48f0bf4a8f3b6624f8ec9
7
- data.tar.gz: b9838d26c1c4bc7820554463737eb9d3d57f09c934ff0865f21e92597cba2650e4ae791caadad5011e3d6437f7b79074618dc4986469770a401fc98abd512b16
6
+ metadata.gz: df2e978345babfd706e31e90214042bf79cb4ec0f2a945117e23466f15a56ed6cb2a1fccd3b0cad4e4722a43b6060e15f83cb8e8a2a688855acf9894195ff75d
7
+ data.tar.gz: 9fc6c4485ec905578ae2928a83f61e6e30a9e9ec80ef1a81e4abd1fbd5067d24471cb69c4493a362ba2aad8a22b69465573e835014e1324c976b7891fe212b76
checksums.yaml.gz.sig CHANGED
Binary file
data.tar.gz.sig CHANGED
Binary file
data/History.md CHANGED
@@ -1,3 +1,30 @@
1
+ # Release History for hglib
2
+
3
+ ---
4
+
5
+ ## v0.3.0 [2019-10-02] Michael Granger <ged@FaerieMUD.org>
6
+
7
+ Changes:
8
+
9
+ - Changed Repo#status to return Repo::StatusEntry objects
10
+ - Change Repo#id -> #identity with alias
11
+
12
+ Improvements:
13
+
14
+ - Handle multi-part errors
15
+ - Handle numeric option args
16
+ - Added implementations of:
17
+ - config
18
+ - log
19
+ - tag
20
+ - bookmark
21
+ - diff
22
+ - push
23
+ - paths
24
+ - version
25
+ - sign
26
+
27
+
1
28
  ## v0.2.0 [2019-04-03] Michael Granger <ged@FaerieMUD.org>
2
29
 
3
30
  Improvements:
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # hglib
2
2
 
3
3
  home
4
- : http://bitbucket.org/ged/ruby-hglib
4
+ : https://hg.sr.ht/~ged/hglib
5
5
 
6
6
  code
7
- : http://bitbucket.org/ged/ruby-hglib
7
+ : https://hg.sr.ht/~ged/hglib
8
8
 
9
9
  github
10
10
  : https://github.com/ged/ruby-hglib
@@ -21,6 +21,10 @@ that uses the [Command Server][cmdserver] for efficiency.
21
21
 
22
22
  ### Examples
23
23
 
24
+ require 'hglib'
25
+
26
+ repo = Hglib.clone( 'https://https://hg.sr.ht/~ged/hglib' )
27
+ # => #<Hglib::Repo:0x00007fae3880ec90 @path=#<Pathname:/Users/ged/temp/hglib>, @server=nil>
24
28
 
25
29
 
26
30
  ## Prerequisites
@@ -36,20 +40,24 @@ that uses the [Command Server][cmdserver] for efficiency.
36
40
  ## Contributing
37
41
 
38
42
  You can check out the current development source with Mercurial via its
39
- [project page](http://bitbucket.org/ged/ruby-hglib). Or if you prefer Git, via
43
+ [project page](https://hg.sr.ht/~ged/hglib). Or if you prefer Git, via
40
44
  [its Github mirror](https://github.com/ged/ruby-hglib).
41
45
 
42
46
  After checking out the source, run:
43
47
 
44
- $ rake newb
48
+ $ gem install -Ng
49
+
50
+ This will install any missing dependencies.
51
+
52
+
53
+ ## Authors
45
54
 
46
- This task will install any missing dependencies, run the tests/specs,
47
- and generate the API documentation.
55
+ - Michael Granger <ged@FaerieMUD.org>
48
56
 
49
57
 
50
58
  ## License
51
59
 
52
- Copyright (c) 2018, Michael Granger
60
+ Copyright (c) 2018-2019, Michael Granger
53
61
  All rights reserved.
54
62
 
55
63
  Redistribution and use in source and binary forms, with or without
data/lib/hglib.rb CHANGED
@@ -12,7 +12,7 @@ module Hglib
12
12
  Exception2MessageMapper
13
13
 
14
14
  # Package version
15
- VERSION = '0.2.0'
15
+ VERSION = '0.3.0'
16
16
 
17
17
  # Version control revision
18
18
  REVISION = %q$Revision$
@@ -29,7 +29,51 @@ module Hglib
29
29
 
30
30
  # Base exception class for errors raised by this library
31
31
  def_exception :Error, "hglib error"
32
- def_exception :CommandError, "error in hg command", Hglib::Error
32
+
33
+
34
+ class CommandError < Hglib::Error
35
+
36
+ def initialize( args )
37
+ @command = args.shift
38
+ @messages = args.flatten.map( &:chomp )
39
+ @messages << "error in hg command" if @messages.empty?
40
+ end
41
+
42
+ ##
43
+ # The command that resulted in an error
44
+ attr_reader :command
45
+
46
+ ##
47
+ # The Array of error messages generated by the command
48
+ attr_reader :messages
49
+
50
+
51
+ ### Returns +true+ if the command resulted in more than one error message.
52
+ def multiple?
53
+ return self.messages.length > 1
54
+ end
55
+
56
+
57
+ ### Overridden to for multi-message errors.
58
+ def message
59
+ msg = String.new( encoding: 'utf-8' )
60
+
61
+ msg << "`%s`: " % [ self.command ]
62
+
63
+ if self.multiple?
64
+ self.messages.each do |errmsg|
65
+ msg << "\n" << ' - ' << errmsg
66
+ end
67
+ msg << "\n"
68
+ else
69
+ msg << self.messages.first
70
+ end
71
+
72
+ return msg
73
+ end
74
+
75
+ end # class CommandError
76
+
33
77
 
34
78
  # Loggability API -- set up a Logger for Hglib objects
35
79
  log_as :hglib
@@ -48,6 +92,7 @@ module Hglib
48
92
 
49
93
 
50
94
  # Set up automatic loading of submodules
95
+ autoload :Config, 'hglib/config'
51
96
  autoload :Server, 'hglib/server'
52
97
  autoload :Repo, 'hglib/repo'
53
98
 
@@ -0,0 +1,60 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'loggability'
5
+ require 'hglib' unless defined?( Hglib )
6
+
7
+
8
+ class Hglib::Config
9
+ extend Loggability
10
+ include Enumerable
11
+
12
+
13
+ # Config item type
14
+ Item = Struct.new( :value, :source )
15
+
16
+
17
+ # Log to the Hglib logger
18
+ log_to :hglib
19
+
20
+
21
+ ### Create a new Config object from the given +config_items+.
22
+ def initialize( *config_items )
23
+ @items = self.expand_config_items( config_items )
24
+ end
25
+
26
+
27
+ ######
28
+ public
29
+ ######
30
+
31
+ ##
32
+ # The Hash of Hglib::Config::Items from the config, keyed by name.
33
+ attr_reader :items
34
+
35
+
36
+ ### Fetch the value of the config item +key+.
37
+ def []( key )
38
+ return self.items[ key ]&.value
39
+ end
40
+
41
+
42
+ ### Call the block once for each config item, yielding the key and the Item.
43
+ def each( &block )
44
+ return self.items.each( &block )
45
+ end
46
+
47
+
48
+ ### Expand the Array of configuration +items+ such as that returned by the JSON
49
+ ### template of `hg showconfig` and return a hierarchical Hash.
50
+ def expand_config_items( items )
51
+ return items.flatten.each_with_object( {} ) do |item, hash|
52
+ self.log.debug "Expanding %p" % [ item ]
53
+ hash[ item[:name] ] = Item.new( item[:value], item[:source] )
54
+ end
55
+ end
56
+
57
+
58
+ end # class Hglib::Config
59
+
60
+
@@ -0,0 +1,77 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'hglib' unless defined?( Hglib )
5
+
6
+
7
+ module Hglib
8
+
9
+ # A collection of methods for declaring other methods.
10
+ #
11
+ # class MyClass
12
+ # extend Hglib::MethodUtilities
13
+ #
14
+ # singleton_attr_accessor :types
15
+ # singleton_method_alias :kinds, :types
16
+ # end
17
+ #
18
+ # MyClass.types = [ :pheno, :proto, :stereo ]
19
+ # MyClass.kinds # => [:pheno, :proto, :stereo]
20
+ #
21
+ module MethodUtilities
22
+
23
+ ### Creates instance variables and corresponding methods that return their
24
+ ### values for each of the specified +symbols+ in the singleton of the
25
+ ### declaring object (e.g., class instance variables and methods if declared
26
+ ### in a Class).
27
+ def singleton_attr_reader( *symbols )
28
+ symbols.each do |sym|
29
+ singleton_class.__send__( :attr_reader, sym )
30
+ end
31
+ end
32
+
33
+ ### Creates methods that allow assignment to the attributes of the singleton
34
+ ### of the declaring object that correspond to the specified +symbols+.
35
+ def singleton_attr_writer( *symbols )
36
+ symbols.each do |sym|
37
+ singleton_class.__send__( :attr_writer, sym )
38
+ end
39
+ end
40
+
41
+ ### Creates readers and writers that allow assignment to the attributes of
42
+ ### the singleton of the declaring object that correspond to the specified
43
+ ### +symbols+.
44
+ def singleton_attr_accessor( *symbols )
45
+ symbols.each do |sym|
46
+ singleton_class.__send__( :attr_accessor, sym )
47
+ end
48
+ end
49
+
50
+ ### Creates an alias for the +original+ method named +newname+.
51
+ def singleton_method_alias( newname, original )
52
+ singleton_class.__send__( :alias_method, newname, original )
53
+ end
54
+
55
+
56
+ ### Create a reader in the form of a predicate for the given +attrname+.
57
+ def attr_predicate( attrname )
58
+ attrname = attrname.to_s.chomp( '?' )
59
+ define_method( "#{attrname}?" ) do
60
+ instance_variable_get( "@#{attrname}" ) ? true : false
61
+ end
62
+ end
63
+
64
+
65
+ ### Create a reader in the form of a predicate for the given +attrname+
66
+ ### as well as a regular writer method.
67
+ def attr_predicate_accessor( attrname )
68
+ attrname = attrname.to_s.chomp( '?' )
69
+ attr_writer( attrname )
70
+ attr_predicate( attrname )
71
+ end
72
+
73
+ end # module MethodUtilities
74
+
75
+
76
+ end # module Hglib
77
+
data/lib/hglib/repo.rb CHANGED
@@ -13,8 +13,11 @@ class Hglib::Repo
13
13
  log_to :hglib
14
14
 
15
15
 
16
+ autoload :Bookmark, 'hglib/repo/bookmark'
16
17
  autoload :Id, 'hglib/repo/id'
17
18
  autoload :LogEntry, 'hglib/repo/log_entry'
19
+ autoload :StatusEntry, 'hglib/repo/status_entry'
20
+ autoload :Tag, 'hglib/repo/tag'
18
21
 
19
22
 
20
23
  ### Create a new Repo object that will operate on the Mercurial repo at the
@@ -44,15 +47,10 @@ class Hglib::Repo
44
47
  ### the file. An empty Hash is returned if there are no files with one of the
45
48
  ### requested statuses.
46
49
  def status( *args, **options )
47
- response = self.server.run( :status, *args, **options )
50
+ response = self.server.run_with_json_template( :status, *args, **options )
48
51
  self.logger.debug "Parsing status response: %p" % [ response ]
49
52
 
50
- return {} if response.length == 1 && response.first.empty?
51
- return response.each_slice( 2 ).inject({}) do |hash, (raw_status, path)|
52
- path = Pathname( path.chomp )
53
- hash[ path ] = raw_status.strip
54
- hash
55
- end
53
+ return response.map {|entry| Hglib::Repo::StatusEntry.new(entry) }
56
54
  end
57
55
  alias_method :stat, :status
58
56
 
@@ -60,30 +58,78 @@ class Hglib::Repo
60
58
  ### Return a Hglib::Repo::Id that identifies the repository state at the
61
59
  ### specified +revision+, or the current revision if unspecified. A +revision+
62
60
  ### of `.` identifies the working directory parent without uncommitted changes.
63
- def id( revision=nil )
64
- options = {}
65
- options[:rev] = revision if revision
66
-
67
- response = self.server.run( :id, **options )
61
+ def identify( source=nil, **options )
62
+ response = self.server.run_with_json_template( :identify, source, **options )
68
63
  self.logger.debug "Got ID response: %p" % [ response ]
69
64
 
70
- return Hglib::Repo::Id.parse( response.first )
65
+ data = response.first
66
+ return Hglib::Repo::Id.new( **data )
71
67
  end
68
+ alias_method :id, :identify
72
69
 
73
70
 
74
71
  ### Return an Array of Hglib::Repo::LogEntry objects that describes the revision
75
72
  ### history of the specified +files+ or the entire project.
76
73
  def log( *files, **options )
77
74
  options[:graph] = false
78
- options[:T] = 'json'
79
75
 
80
- jsonlog = self.server.run( :log, *files, **options )
81
- entries = JSON.parse( jsonlog.join )
76
+ entries = self.server.run_with_json_template( :log, *files, **options )
77
+ self.logger.debug "Got log response: %p" % [ entries ]
82
78
 
83
79
  return entries.map {|entry| Hglib::Repo::LogEntry.new(entry) }
84
80
  end
85
81
 
86
82
 
83
+ ### Return a String showing differences between revisions for the specified
84
+ ### +files+ in the unified diff format.
85
+ def diff( *files, **options )
86
+ response = self.server.run( :diff, *files, **options )
87
+ self.logger.debug "Got diff response: %p" % [ truncate(response) ]
88
+ return response
89
+ end
90
+
91
+
92
+ ### Schedule the given +files+ to be version controlled and added to the
93
+ ### repository on the next commit. To undo an add before that, see #forget.
94
+ ###
95
+ ### If no +files+ are given, add all files to the repository (except files
96
+ ### matching ".hgignore").
97
+ ###
98
+ ### Returns <code>true</code> if all files are successfully added.
99
+ def add( *files, **options )
100
+ response = self.server.run( :add, *files, **options )
101
+ self.logger.debug "Got ADD response: %p" % [ response ]
102
+
103
+ return true
104
+ end
105
+
106
+
107
+ ### Add all new files and remove all missing files from the repository.
108
+ ###
109
+ ### Unless +files+ are given, new files are ignored if they match any of the
110
+ ### patterns in ".hgignore". As with #add, these changes take effect at the
111
+ ### next commit.
112
+ ###
113
+ ### Use the :similarity option to detect renamed files. This option takes a
114
+ ### percentage between 0 (disabled) and 100 (files must be identical) as its
115
+ ### value. With a value greater than 0, this compares every removed file with
116
+ ### every added file and records those similar enough as renames. Detecting
117
+ ### renamed files this way can be expensive. After using this option, you can
118
+ ### call #status with the <code>:copies</code> options to check which files
119
+ ### were identified as moved or renamed. If not specified, :similarity defaults
120
+ ### to 100 and only renames of identical files are detected.
121
+ ###
122
+ ### Returns <code>true</code> if all files are successfully added.
123
+ def addremove( *files, **options )
124
+ response = self.server.run( :addremove, *files, **options )
125
+ self.logger.debug "Got ADD response: %p" % [ response ]
126
+
127
+ return true
128
+ end
129
+ alias_method :add_remove, :addremove
130
+ alias_method :addr, :addremove
131
+
132
+
87
133
  ### Commit the specified +files+ with the given +options+.
88
134
  def commit( *files, **options )
89
135
  response = self.server.run( :commit, *files, **options )
@@ -111,6 +157,119 @@ class Hglib::Repo
111
157
  end
112
158
 
113
159
 
160
+ ### Update the working directory or switch revisions.
161
+ def update( rev=nil, **options )
162
+ response = self.server.run( :update, rev, **options )
163
+ self.logger.debug "Got UPDATE response: %p" % [ response ]
164
+
165
+ return true
166
+ end
167
+
168
+
169
+ ### Push changes to the specified +destination+.
170
+ def push( destination=nil, **options )
171
+ response = self.server.run( :push, destination, **options )
172
+ self.logger.debug "Got PUSH response: %p" % [ response ]
173
+
174
+ return true
175
+ end
176
+
177
+
178
+ ### Name a revision using +names+.
179
+ def tag( *names, **options )
180
+ raise "expected at least one tag name" if names.empty?
181
+
182
+ response = self.server.run( :tag, *names, **options )
183
+ self.logger.debug "Got TAGS response: %p" % [ response ]
184
+
185
+ return true
186
+ end
187
+
188
+
189
+ ### Return a Hglib::Repo::Tag object for each tag in the repo.
190
+ def tags
191
+ response = self.server.run_with_json_template( :tags )
192
+ self.logger.debug "Got a TAGS response: %p" % [ response ]
193
+
194
+ return response.flatten.map {|tag| Hglib::Repo::Tag.new(self, **tag) }
195
+ end
196
+
197
+
198
+ ### Create new bookmarks with the specified +names+.
199
+ def bookmark( *names, **options )
200
+ raise "expected at least one bookmark name" if names.empty?
201
+
202
+ response = self.server.run( :bookmark, *names, **options )
203
+ self.logger.debug "Got BOOKMARK response: %p" % [ response ]
204
+
205
+ return true
206
+ end
207
+
208
+
209
+ ### Return a Hglib::Repo::Bookmark object for each bookmark in the repo.
210
+ def bookmarks
211
+ options = { list: true }
212
+ response = self.server.run_with_json_template( :bookmarks, **options )
213
+ self.logger.debug "Got a BOOKMARKS response: %p" % [ response ]
214
+
215
+ return response.map {|bk| Hglib::Repo::Bookmark.new(self, **bk) }
216
+ end
217
+
218
+
219
+ ### Fetch the current global Mercurial config and return it as an Hglib::Config
220
+ ### object.
221
+ def config( untrusted: false )
222
+ options = { untrusted: untrusted }
223
+
224
+ config = self.server.run_with_json_template( :showconfig, **options )
225
+ return Hglib::Config.new( config )
226
+ end
227
+
228
+
229
+ ### Fetch a Hash of aliases for remote repositories.
230
+ def paths
231
+ response = self.server.run_with_json_template( :paths )
232
+ self.logger.debug "Got a PATHS response: %p" % [ response ]
233
+
234
+ return response.each_with_object({}) do |entry, hash|
235
+ hash[ entry[:name].to_sym ] = URI( entry[:url] )
236
+ end
237
+ end
238
+
239
+
240
+ ### Sign the given +rev+ (or the current one if not specified).
241
+ def sign( rev=nil, **options )
242
+ response = self.server.run( :sign, rev, **options )
243
+ self.logger.debug "Got a SIGN response: %p" % [ response ]
244
+
245
+ return response.chomp
246
+ end
247
+
248
+
249
+ ### Fetch a Hash of version information about the Mercurial that is being used.
250
+ def versions
251
+ response = self.server.run_with_json_template( :version )
252
+ self.logger.debug "Got a VERSION response: %p" % [ response ]
253
+
254
+ return response.first
255
+ end
256
+
257
+
258
+ ### Fetch the version of Mercurial that's being used as a String.
259
+ def version
260
+ return self.versions[ :ver ]
261
+ end
262
+
263
+
264
+ ### Fetch the version of the Mercurial extensions that're being used as a Hash.
265
+ def extension_versions
266
+ ext_info = self.versions[ :extensions ]
267
+ return ext_info.each_with_object({}) do |ext, hash|
268
+ hash[ ext.delete(:name).to_sym ] = ext
269
+ end
270
+ end
271
+
272
+
114
273
  #########
115
274
  protected
116
275
  #########
@@ -126,4 +285,16 @@ class Hglib::Repo
126
285
  return Loggability[ self ]
127
286
  end
128
287
 
288
+
289
+ ### Return the given +string+ with the middle truncated so that it's +maxlength+
290
+ ### characters long if it exceeds that length.
291
+ def truncate( string, maxlength=80 )
292
+ return string if maxlength < 8
293
+ return string if string.length - maxlength - 5 <= 0
294
+
295
+ return string[0 .. (maxlength / 2) - 3 ] +
296
+ ' ... ' +
297
+ string[ -((maxlength / 2) -3) .. ]
298
+ end
299
+
129
300
  end # class Hglib::Repo