hglib 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/History.md +10 -0
- data/lib/hglib.rb +51 -36
- data/lib/hglib/extension.rb +50 -0
- data/lib/hglib/extension/gpg.rb +36 -0
- data/lib/hglib/mixins.rb +4 -1
- data/lib/hglib/repo.rb +11 -44
- data/lib/hglib/repo/status_entry.rb +77 -0
- data/lib/hglib/server.rb +27 -3
- data/lib/hglib/version_info.rb +40 -0
- data/spec/hglib/extension/gpg_spec.rb +33 -0
- data/spec/hglib/extension_spec.rb +18 -0
- data/spec/hglib/repo/status_entry_spec.rb +42 -0
- data/spec/hglib/version_info_spec.rb +88 -0
- data/spec/hglib_spec.rb +22 -76
- metadata +16 -8
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8c3e4ce0e0a5a9fae8575225359cc5b8e922cfd009b1ceade3a3ed4f4a9865a9
|
4
|
+
data.tar.gz: 5d1e25766012a16829958a194398394eed379555369e36e8c1617641e9ad6da8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7f61ab1c13cea52539d27ed42837e58c0be53e4472881c8e5d4e08d9db43bdbf71fbaed4b2fc6b52ea75e14bcdf135121e430449d2953f9ce5c9ad3ee77201d8
|
7
|
+
data.tar.gz: 86e692ee3e36cc058266f644bb84ea2cceeacc2d2f19c6a5898719b67786ffda938843622dbdc28e232d09c4f50ac5785ac8fbe2ddf372a7c0b5b1e679ca0fa1
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/History.md
CHANGED
@@ -2,6 +2,16 @@
|
|
2
2
|
|
3
3
|
---
|
4
4
|
|
5
|
+
## v0.5.0 [2019-10-14] Michael Granger <ged@FaerieMUD.org>
|
6
|
+
|
7
|
+
Improvements:
|
8
|
+
|
9
|
+
- Add a mechanism for defining methods that support Mercurial extensions
|
10
|
+
- Move the version methods into a mixin and expose them on both Hglib and
|
11
|
+
Hglib::Repo instances.
|
12
|
+
- Add an extension for gpg
|
13
|
+
|
14
|
+
|
5
15
|
## v0.4.0 [2019-10-12] Michael Granger <ged@FaerieMUD.org>
|
6
16
|
|
7
17
|
Changes:
|
data/lib/hglib.rb
CHANGED
@@ -12,7 +12,7 @@ module Hglib
|
|
12
12
|
Exception2MessageMapper
|
13
13
|
|
14
14
|
# Package version
|
15
|
-
VERSION = '0.
|
15
|
+
VERSION = '0.5.0'
|
16
16
|
|
17
17
|
# Version control revision
|
18
18
|
REVISION = %q$Revision$
|
@@ -30,15 +30,18 @@ module Hglib
|
|
30
30
|
# Base exception class for errors raised by this library
|
31
31
|
def_exception :Error, "hglib error"
|
32
32
|
|
33
|
-
|
33
|
+
# Specialized exception for handling errors returned by the command server.
|
34
34
|
class CommandError < Hglib::Error
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
@
|
36
|
+
### Create a new CommandError with the given +args+.
|
37
|
+
def initialize( command, *messages, details: nil )
|
38
|
+
@command = command
|
39
|
+
@messages = messages.flatten.map( &:chomp )
|
39
40
|
@messages << "error in hg command" if @messages.empty?
|
41
|
+
@details = details
|
40
42
|
end
|
41
43
|
|
44
|
+
|
42
45
|
##
|
43
46
|
# The command that resulted in an error
|
44
47
|
attr_reader :command
|
@@ -47,6 +50,10 @@ module Hglib
|
|
47
50
|
# The Array of error messages generated by the command
|
48
51
|
attr_reader :messages
|
49
52
|
|
53
|
+
##
|
54
|
+
# Additional details of the error
|
55
|
+
attr_reader :details
|
56
|
+
|
50
57
|
|
51
58
|
### Returns +true+ if the command resulted in more than one error message.
|
52
59
|
def multiple?
|
@@ -54,7 +61,7 @@ module Hglib
|
|
54
61
|
end
|
55
62
|
|
56
63
|
|
57
|
-
### Overridden to
|
64
|
+
### Overridden to format multi-message errors in a more-readable way.
|
58
65
|
def message
|
59
66
|
msg = String.new( encoding: 'utf-8' )
|
60
67
|
|
@@ -69,12 +76,46 @@ module Hglib
|
|
69
76
|
msg << ' ' << self.messages.first
|
70
77
|
end
|
71
78
|
|
79
|
+
msg << "\n" << self.details if self.details
|
80
|
+
|
72
81
|
return msg
|
73
82
|
end
|
74
83
|
|
75
84
|
end # class CommandError
|
76
85
|
|
77
86
|
|
87
|
+
# Exception raised when a command failed because the extension it belongs to was
|
88
|
+
# disabled.
|
89
|
+
class DisabledExtensionError < Hglib::Error
|
90
|
+
|
91
|
+
### Create a new instance for the given +command+ and the name of the
|
92
|
+
### +extension+ which defines it.
|
93
|
+
def initialize( command, extension )
|
94
|
+
@command = command
|
95
|
+
@extension = extension
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
##
|
100
|
+
# The command that failed
|
101
|
+
attr_reader :command
|
102
|
+
|
103
|
+
##
|
104
|
+
# The name of the extension which defines the #command
|
105
|
+
attr_reader :extension
|
106
|
+
|
107
|
+
|
108
|
+
### Return an message describing what the command and disabled extension were.
|
109
|
+
def message
|
110
|
+
return "`%s`: command is provided by disabled extension `%s`" % [
|
111
|
+
self.command,
|
112
|
+
self.extension,
|
113
|
+
]
|
114
|
+
end
|
115
|
+
|
116
|
+
end # class DisabledError
|
117
|
+
|
118
|
+
|
78
119
|
# Loggability API -- set up a Logger for Hglib objects
|
79
120
|
log_as :hglib
|
80
121
|
|
@@ -95,6 +136,8 @@ module Hglib
|
|
95
136
|
autoload :Config, 'hglib/config'
|
96
137
|
autoload :Server, 'hglib/server'
|
97
138
|
autoload :Repo, 'hglib/repo'
|
139
|
+
autoload :Extension, 'hglib/extension'
|
140
|
+
autoload :VersionInfo, 'hglib/version_info'
|
98
141
|
|
99
142
|
|
100
143
|
### Return an Hglib::Server started with no repository.
|
@@ -149,36 +192,8 @@ module Hglib
|
|
149
192
|
end
|
150
193
|
|
151
194
|
|
152
|
-
|
153
|
-
|
154
|
-
response = self.server.run_with_json_template( :version )
|
155
|
-
self.logger.debug "Got a VERSION response: %p" % [ response ]
|
156
|
-
|
157
|
-
return response.first
|
158
|
-
end
|
159
|
-
|
160
|
-
|
161
|
-
### Fetch the version of Mercurial that's being used as a String.
|
162
|
-
def self::version
|
163
|
-
return self.versions[ :ver ]
|
164
|
-
end
|
165
|
-
|
166
|
-
|
167
|
-
### Fetch the version of the Mercurial extensions that're being used as a Hash.
|
168
|
-
def self::extension_versions
|
169
|
-
ext_info = self.versions[ :extensions ]
|
170
|
-
return ext_info.each_with_object({}) do |ext, hash|
|
171
|
-
ext = ext.dup
|
172
|
-
hash[ ext.delete(:name).to_sym ] = ext
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
|
177
|
-
### Returns +true+ if the extension with the given +name+ is enabled in the
|
178
|
-
### current (global) configuration.
|
179
|
-
def self::extension_enabled?( name )
|
180
|
-
return self.extension_versions.key?( name.to_sym )
|
181
|
-
end
|
195
|
+
extend Hglib::VersionInfo
|
196
|
+
Hglib::Extension.load_all
|
182
197
|
|
183
198
|
end # module Hglib
|
184
199
|
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'pathname'
|
5
|
+
require 'loggability'
|
6
|
+
|
7
|
+
require 'hglib' unless defined?( Hglib )
|
8
|
+
|
9
|
+
|
10
|
+
module Hglib::Extension
|
11
|
+
extend Loggability
|
12
|
+
|
13
|
+
# Loggability API -- log to the Hglib logger
|
14
|
+
log_to :hglib
|
15
|
+
|
16
|
+
|
17
|
+
### Load all of the extensions.
|
18
|
+
def self::load_all
|
19
|
+
# :TODO: Allow gem extensions?
|
20
|
+
extdir = Pathname( __FILE__ ).dirname + 'extension'
|
21
|
+
Pathname.glob( extdir + '*.rb' ).each do |extpath|
|
22
|
+
self.log.debug "Loading extensions from %s" % [ extpath ]
|
23
|
+
require( extpath )
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
### Define one or more commands that should be attached to Repo objects.
|
29
|
+
def repo_commands( &block )
|
30
|
+
raise LocalJumpError, "no block given" unless block
|
31
|
+
|
32
|
+
mod = Module.new
|
33
|
+
mod.class_eval( &block )
|
34
|
+
|
35
|
+
Hglib::Repo.include( mod )
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
### Define one or more commands that should be attached to the Hglib module.
|
40
|
+
def global_commands( &block )
|
41
|
+
raise LocalJumpError, "no block given" unless block
|
42
|
+
|
43
|
+
mod = Module.new
|
44
|
+
mod.class_eval( &block )
|
45
|
+
|
46
|
+
Hglib.extend( mod )
|
47
|
+
end
|
48
|
+
|
49
|
+
end # module Hglib::Extension
|
50
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'hglib/extension' unless defined?( Hglib::Extension )
|
5
|
+
|
6
|
+
|
7
|
+
module Hglib::Extension::GPG
|
8
|
+
extend Hglib::Extension
|
9
|
+
|
10
|
+
|
11
|
+
repo_commands do
|
12
|
+
|
13
|
+
### Sign the given +rev+ (or the current one if not specified).
|
14
|
+
def sign( rev=nil, **options )
|
15
|
+
response = self.server.run( :sign, rev, **options )
|
16
|
+
return response.chomp
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
### Check the signature of the given +rev+.
|
21
|
+
def sigcheck( rev, **options )
|
22
|
+
response = self.server.run( :sigcheck, rev, **options )
|
23
|
+
return response.chomp
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
### Return an Array describing all of the signed changesets.
|
28
|
+
def sigs( **options )
|
29
|
+
response = self.server.run( :sigs, **options )
|
30
|
+
return response.lines.map( &:chomp )
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end # module Hglib::Extension::GPG
|
36
|
+
|
data/lib/hglib/mixins.rb
CHANGED
@@ -57,7 +57,10 @@ module Hglib
|
|
57
57
|
def attr_predicate( attrname )
|
58
58
|
attrname = attrname.to_s.chomp( '?' )
|
59
59
|
define_method( "#{attrname}?" ) do
|
60
|
-
|
60
|
+
ivar = "@#{attrname}"
|
61
|
+
instance_variable_defined?( ivar ) && instance_variable_get( ivar ) ?
|
62
|
+
true :
|
63
|
+
false
|
61
64
|
end
|
62
65
|
end
|
63
66
|
|
data/lib/hglib/repo.rb
CHANGED
@@ -3,11 +3,14 @@
|
|
3
3
|
|
4
4
|
require 'json'
|
5
5
|
require 'loggability'
|
6
|
+
|
6
7
|
require 'hglib' unless defined?( Hglib )
|
8
|
+
require 'hglib/version_info'
|
7
9
|
|
8
10
|
|
9
11
|
class Hglib::Repo
|
10
12
|
extend Loggability
|
13
|
+
include Hglib::VersionInfo
|
11
14
|
|
12
15
|
# Loggability API -- log to the hglib logger
|
13
16
|
log_to :hglib
|
@@ -48,7 +51,6 @@ class Hglib::Repo
|
|
48
51
|
### requested statuses.
|
49
52
|
def status( *args, **options )
|
50
53
|
response = self.server.run_with_json_template( :status, *args, **options )
|
51
|
-
self.logger.debug "Parsing status response: %p" % [ response ]
|
52
54
|
|
53
55
|
return response.map {|entry| Hglib::Repo::StatusEntry.new(entry) }
|
54
56
|
end
|
@@ -60,7 +62,6 @@ class Hglib::Repo
|
|
60
62
|
### of `.` identifies the working directory parent without uncommitted changes.
|
61
63
|
def identify( source=nil, **options )
|
62
64
|
response = self.server.run_with_json_template( :identify, source, **options )
|
63
|
-
self.logger.debug "Got ID response: %p" % [ response ]
|
64
65
|
|
65
66
|
data = response.first
|
66
67
|
return Hglib::Repo::Id.new( **data )
|
@@ -75,7 +76,6 @@ class Hglib::Repo
|
|
75
76
|
options[:graph] = false
|
76
77
|
|
77
78
|
entries = self.server.run_with_json_template( :log, *files, **options )
|
78
|
-
self.logger.debug "Got log response: %p" % [ entries ]
|
79
79
|
|
80
80
|
return entries.map {|entry| Hglib::Repo::LogEntry.new(entry) }
|
81
81
|
end
|
@@ -84,9 +84,7 @@ class Hglib::Repo
|
|
84
84
|
### Return a String showing differences between revisions for the specified
|
85
85
|
### +files+ in the unified diff format.
|
86
86
|
def diff( *files, **options )
|
87
|
-
|
88
|
-
self.logger.debug "Got diff response: %p" % [ truncate(response) ]
|
89
|
-
return response
|
87
|
+
return self.server.run( :diff, *files, **options )
|
90
88
|
end
|
91
89
|
|
92
90
|
|
@@ -98,9 +96,7 @@ class Hglib::Repo
|
|
98
96
|
###
|
99
97
|
### Returns <code>true</code> if all files are successfully added.
|
100
98
|
def add( *files, **options )
|
101
|
-
|
102
|
-
self.logger.debug "Got ADD response: %p" % [ response ]
|
103
|
-
|
99
|
+
self.server.run( :add, *files, **options )
|
104
100
|
return true
|
105
101
|
end
|
106
102
|
|
@@ -122,9 +118,7 @@ class Hglib::Repo
|
|
122
118
|
###
|
123
119
|
### Returns <code>true</code> if all files are successfully added.
|
124
120
|
def addremove( *files, **options )
|
125
|
-
|
126
|
-
self.logger.debug "Got ADD response: %p" % [ response ]
|
127
|
-
|
121
|
+
self.server.run( :addremove, *files, **options )
|
128
122
|
return true
|
129
123
|
end
|
130
124
|
alias_method :add_remove, :addremove
|
@@ -133,9 +127,7 @@ class Hglib::Repo
|
|
133
127
|
|
134
128
|
### Commit the specified +files+ with the given +options+.
|
135
129
|
def commit( *files, **options )
|
136
|
-
|
137
|
-
self.logger.debug "Got COMMIT response: %p" % [ response ]
|
138
|
-
|
130
|
+
self.server.run( :commit, *files, **options )
|
139
131
|
return true
|
140
132
|
end
|
141
133
|
|
@@ -143,9 +135,7 @@ class Hglib::Repo
|
|
143
135
|
### Pull changes from the specified +source+ (which defaults to the +default+
|
144
136
|
### path) into the local repository.
|
145
137
|
def pull( source=nil, **options )
|
146
|
-
|
147
|
-
self.logger.debug "Got PULL response: %p" % [ response ]
|
148
|
-
|
138
|
+
self.server.run( :pull, source, **options )
|
149
139
|
return true
|
150
140
|
end
|
151
141
|
|
@@ -160,18 +150,14 @@ class Hglib::Repo
|
|
160
150
|
|
161
151
|
### Update the working directory or switch revisions.
|
162
152
|
def update( rev=nil, **options )
|
163
|
-
|
164
|
-
self.logger.debug "Got UPDATE response: %p" % [ response ]
|
165
|
-
|
153
|
+
self.server.run( :update, rev, **options )
|
166
154
|
return true
|
167
155
|
end
|
168
156
|
|
169
157
|
|
170
158
|
### Push changes to the specified +destination+.
|
171
159
|
def push( destination=nil, **options )
|
172
|
-
|
173
|
-
self.logger.debug "Got PUSH response: %p" % [ response ]
|
174
|
-
|
160
|
+
self.server.run( :push, destination, **options )
|
175
161
|
return true
|
176
162
|
end
|
177
163
|
|
@@ -190,8 +176,6 @@ class Hglib::Repo
|
|
190
176
|
### Return a Hglib::Repo::Tag object for each tag in the repo.
|
191
177
|
def tags
|
192
178
|
response = self.server.run_with_json_template( :tags )
|
193
|
-
self.logger.debug "Got a TAGS response: %p" % [ response ]
|
194
|
-
|
195
179
|
return response.flatten.map {|tag| Hglib::Repo::Tag.new(self, **tag) }
|
196
180
|
end
|
197
181
|
|
@@ -200,9 +184,7 @@ class Hglib::Repo
|
|
200
184
|
def bookmark( *names, **options )
|
201
185
|
raise "expected at least one bookmark name" if names.empty?
|
202
186
|
|
203
|
-
|
204
|
-
self.logger.debug "Got BOOKMARK response: %p" % [ response ]
|
205
|
-
|
187
|
+
self.server.run( :bookmark, *names, **options )
|
206
188
|
return true
|
207
189
|
end
|
208
190
|
|
@@ -211,8 +193,6 @@ class Hglib::Repo
|
|
211
193
|
def bookmarks
|
212
194
|
options = { list: true }
|
213
195
|
response = self.server.run_with_json_template( :bookmarks, **options )
|
214
|
-
self.logger.debug "Got a BOOKMARKS response: %p" % [ response ]
|
215
|
-
|
216
196
|
return response.map {|bk| Hglib::Repo::Bookmark.new(self, **bk) }
|
217
197
|
end
|
218
198
|
|
@@ -221,7 +201,6 @@ class Hglib::Repo
|
|
221
201
|
### object.
|
222
202
|
def config( untrusted: false )
|
223
203
|
options = { untrusted: untrusted }
|
224
|
-
|
225
204
|
config = self.server.run_with_json_template( :showconfig, **options )
|
226
205
|
return Hglib::Config.new( config )
|
227
206
|
end
|
@@ -230,23 +209,12 @@ class Hglib::Repo
|
|
230
209
|
### Fetch a Hash of aliases for remote repositories.
|
231
210
|
def paths
|
232
211
|
response = self.server.run_with_json_template( :paths )
|
233
|
-
self.logger.debug "Got a PATHS response: %p" % [ response ]
|
234
|
-
|
235
212
|
return response.each_with_object({}) do |entry, hash|
|
236
213
|
hash[ entry[:name].to_sym ] = URI( entry[:url] )
|
237
214
|
end
|
238
215
|
end
|
239
216
|
|
240
217
|
|
241
|
-
### Sign the given +rev+ (or the current one if not specified).
|
242
|
-
def sign( rev=nil, **options )
|
243
|
-
response = self.server.run( :sign, rev, **options )
|
244
|
-
self.logger.debug "Got a SIGN response: %p" % [ response ]
|
245
|
-
|
246
|
-
return response.chomp
|
247
|
-
end
|
248
|
-
|
249
|
-
|
250
218
|
### Set or show the current phase name for a +revset+.
|
251
219
|
###
|
252
220
|
### With no +revset+, operates on the current changeset.
|
@@ -263,7 +231,6 @@ class Hglib::Repo
|
|
263
231
|
### setting the phase.
|
264
232
|
def phase( revset=nil, **options )
|
265
233
|
response = self.server.run( :phase, revset, **options )
|
266
|
-
self.logger.debug "Got a PHASE response: %p" % [ response ]
|
267
234
|
|
268
235
|
return {} if response.empty?
|
269
236
|
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'pathname'
|
5
|
+
|
6
|
+
require 'hglib/repo' unless defined?( Hglib::Repo )
|
7
|
+
|
8
|
+
|
9
|
+
# An entry in a repository's status list.
|
10
|
+
class Hglib::Repo::StatusEntry
|
11
|
+
extend Loggability
|
12
|
+
|
13
|
+
|
14
|
+
# Loggability API -- output to the hglib logger
|
15
|
+
log_to :hglib
|
16
|
+
|
17
|
+
|
18
|
+
# {
|
19
|
+
# :path=>"Rakefile",
|
20
|
+
# :status=>"M"
|
21
|
+
# }
|
22
|
+
|
23
|
+
### Create a new log entry from the raw +entryhash+.
|
24
|
+
def initialize( entryhash )
|
25
|
+
@path = Pathname( entryhash[:path] )
|
26
|
+
@source = Pathname( entryhash[:source] ) if entryhash.key?( :source )
|
27
|
+
@status = entryhash[ :status ]
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
######
|
32
|
+
public
|
33
|
+
######
|
34
|
+
|
35
|
+
##
|
36
|
+
# Return the Pathname of the file the status applies to
|
37
|
+
attr_reader :path
|
38
|
+
|
39
|
+
##
|
40
|
+
# The Pathname of the file the status applies to was copied from (if the status
|
41
|
+
# is `A`/`added`) and the addition was via a copy/move operation.
|
42
|
+
attr_reader :source
|
43
|
+
|
44
|
+
##
|
45
|
+
# Return the character that denotes the file's status
|
46
|
+
attr_reader :status
|
47
|
+
|
48
|
+
|
49
|
+
### Return the human-readable status.
|
50
|
+
def status_description
|
51
|
+
return case self.status
|
52
|
+
when 'M' then 'modified'
|
53
|
+
when 'A' then 'added'
|
54
|
+
when 'R' then 'removed'
|
55
|
+
when 'C' then 'clean'
|
56
|
+
when '!' then 'missing'
|
57
|
+
when '?' then 'not tracked'
|
58
|
+
when 'I' then 'ignored'
|
59
|
+
else
|
60
|
+
raise "unknown status %p" % [ self.status ]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
### Return a human-readable representation of the StatusEntry as a String.
|
66
|
+
def inspect
|
67
|
+
return "#<%p:#%x %s: %s%s>" % [
|
68
|
+
self.class,
|
69
|
+
self.object_id * 2,
|
70
|
+
self.path,
|
71
|
+
self.status_description,
|
72
|
+
self.source ? " via copy/move from #{self.source}" : '',
|
73
|
+
]
|
74
|
+
end
|
75
|
+
|
76
|
+
end # class Hglib::Repo::StatusEntry
|
77
|
+
|
data/lib/hglib/server.rb
CHANGED
@@ -26,6 +26,14 @@ class Hglib::Server
|
|
26
26
|
# Array#pack template for plain messages sent to the command server
|
27
27
|
MESSAGE_TEMPLATE = 'I>A*'
|
28
28
|
|
29
|
+
# A Regexp to match the detail message when a command belongs to a disabled
|
30
|
+
# extension.
|
31
|
+
EXTENSION_DISABLED_DETAILS = %r{
|
32
|
+
(?-x:is provided by the following extension:)
|
33
|
+
\s+
|
34
|
+
(?<extension_name>\w+)
|
35
|
+
}x
|
36
|
+
|
29
37
|
|
30
38
|
# Loggability API -- send logs to the logger in the top-level module
|
31
39
|
log_to :hglib
|
@@ -147,7 +155,7 @@ class Hglib::Server
|
|
147
155
|
### callbacks is not registered, an IOError will be raised.
|
148
156
|
def run( command, *args, **options )
|
149
157
|
args = args.compact
|
150
|
-
self.log.debug "Running command: %p" % [ Shellwords.join([command.to_s] + args) ]
|
158
|
+
self.log.debug { "Running command: %p" % [ Shellwords.join([command.to_s] + args) ] }
|
151
159
|
self.start unless self.started?
|
152
160
|
|
153
161
|
done = false
|
@@ -184,12 +192,28 @@ class Hglib::Server
|
|
184
192
|
end
|
185
193
|
end
|
186
194
|
|
187
|
-
|
195
|
+
self.handle_errors( command, errors, output ) unless errors.empty?
|
188
196
|
|
197
|
+
self.log.debug { "Got %s response: %p" % [ command.to_s.upcase, output ] }
|
189
198
|
return output
|
190
199
|
end
|
191
200
|
|
192
201
|
|
202
|
+
### Form and raise an exception for the given +errors+ resulting from running
|
203
|
+
### +command+.
|
204
|
+
def handle_errors( command, errors, details )
|
205
|
+
err = nil
|
206
|
+
|
207
|
+
if details && (m = details.match(EXTENSION_DISABLED_DETAILS) )
|
208
|
+
err = Hglib::DisabledExtensionError.new( command, m[:extension_name] )
|
209
|
+
else
|
210
|
+
err = Hglib::CommandError.new( command, errors, details: details )
|
211
|
+
end
|
212
|
+
|
213
|
+
raise( err, nil, caller(2) )
|
214
|
+
end
|
215
|
+
|
216
|
+
|
193
217
|
### Run the specified +command+ with the given +args+ with the JSON template and
|
194
218
|
### return the result.
|
195
219
|
def run_with_json_template( command, *args, symbolize: true, **options )
|
@@ -318,7 +342,7 @@ class Hglib::Server
|
|
318
342
|
|
319
343
|
self.log.debug "Reading %d more bytes of the message" % [ bytes ]
|
320
344
|
message = self.reader.read( bytes ) unless bytes.zero?
|
321
|
-
self.log.debug " read message: %p" % [ message ]
|
345
|
+
self.log.debug { " read message: %p" % [ message ] }
|
322
346
|
return channel, message
|
323
347
|
end
|
324
348
|
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'hglib' unless defined?( Hglib )
|
5
|
+
|
6
|
+
|
7
|
+
# Version information methods for Repos and the top-level module.
|
8
|
+
module Hglib::VersionInfo
|
9
|
+
|
10
|
+
### Fetch a Hash of version information about the Mercurial that is being used.
|
11
|
+
def versions
|
12
|
+
response = self.server.run_with_json_template( :version )
|
13
|
+
return response.first
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
### Fetch the version of Mercurial that's being used as a String.
|
18
|
+
def version
|
19
|
+
return self.versions[ :ver ]
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
### Fetch the version of the Mercurial extensions that're being used as a Hash.
|
24
|
+
def extension_versions
|
25
|
+
ext_info = self.versions[ :extensions ]
|
26
|
+
return ext_info.each_with_object({}) do |ext, hash|
|
27
|
+
ext = ext.dup
|
28
|
+
hash[ ext.delete(:name).to_sym ] = ext
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
### Returns +true+ if the extension with the given +name+ is enabled in the
|
34
|
+
### current (global) configuration.
|
35
|
+
def extension_enabled?( name )
|
36
|
+
return self.extension_versions.key?( name.to_sym )
|
37
|
+
end
|
38
|
+
|
39
|
+
end # module Hglib::VersionInfo
|
40
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env rspec -cfd
|
2
|
+
|
3
|
+
require_relative '../../spec_helper'
|
4
|
+
|
5
|
+
require 'hglib/extension/gpg'
|
6
|
+
|
7
|
+
|
8
|
+
RSpec.describe Hglib::Extension::GPG do
|
9
|
+
|
10
|
+
let( :repo_dir ) do
|
11
|
+
Dir.mktmpdir( ['hglib', 'repodir'] )
|
12
|
+
end
|
13
|
+
|
14
|
+
let( :server ) { instance_double(Hglib::Server) }
|
15
|
+
|
16
|
+
let( :repo ) { Hglib::Repo.new( repo_dir ) }
|
17
|
+
|
18
|
+
before( :each ) do
|
19
|
+
allow( Hglib::Server ).to receive( :new ).and_return( server )
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
it "can sign a revision" do
|
25
|
+
expect( server ).to receive( :run ).
|
26
|
+
with( :sign, nil, {} ).
|
27
|
+
and_return( "signing 2:2b937981802a\n" )
|
28
|
+
|
29
|
+
expect( repo.sign ).to eq( "signing 2:2b937981802a" )
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env rspec -cfd
|
2
|
+
|
3
|
+
require_relative '../spec_helper'
|
4
|
+
|
5
|
+
require 'hglib'
|
6
|
+
require 'hglib/extension'
|
7
|
+
require 'hglib/repo'
|
8
|
+
|
9
|
+
|
10
|
+
RSpec.describe Hglib::Extension do
|
11
|
+
|
12
|
+
it "adds methods to support Mercurial extensions when loaded" do
|
13
|
+
described_class.load_all
|
14
|
+
expect( Hglib::Repo.instance_methods ).to include( :sign )
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/env ruby -S rspec -cfd
|
2
|
+
|
3
|
+
require_relative '../../spec_helper'
|
4
|
+
|
5
|
+
require 'hglib/repo/status_entry'
|
6
|
+
|
7
|
+
|
8
|
+
RSpec.describe Hglib::Repo::StatusEntry do
|
9
|
+
|
10
|
+
RAW_STATUS_ENTRY = {
|
11
|
+
path: 'Rakefile',
|
12
|
+
status: 'M',
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
RAW_COPY_STATUS_ENTRY = {
|
16
|
+
path: "AnotherRakefile",
|
17
|
+
source: "Rakefile",
|
18
|
+
status: "A"
|
19
|
+
}
|
20
|
+
|
21
|
+
it "can be created from the JSON status hash" do
|
22
|
+
entry = described_class.new( RAW_STATUS_ENTRY )
|
23
|
+
|
24
|
+
expect( entry ).to be_a( described_class )
|
25
|
+
expect( entry.path ).to eq( Pathname('Rakefile') )
|
26
|
+
expect( entry.status ).to eq( 'M' )
|
27
|
+
expect( entry.status_description ).to eq( 'modified' )
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
it "can be created from the JSON status hash run with --copies enabled" do
|
32
|
+
entry = described_class.new( RAW_COPY_STATUS_ENTRY )
|
33
|
+
|
34
|
+
expect( entry ).to be_a( described_class )
|
35
|
+
expect( entry.path ).to eq( Pathname('AnotherRakefile') )
|
36
|
+
expect( entry.status ).to eq( 'A' )
|
37
|
+
expect( entry.source ).to eq( Pathname('Rakefile') )
|
38
|
+
expect( entry.status_description ).to eq( 'added' )
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
@@ -0,0 +1,88 @@
|
|
1
|
+
#!/usr/bin/env rspec -cfd
|
2
|
+
|
3
|
+
require_relative '../spec_helper'
|
4
|
+
|
5
|
+
require 'loggability'
|
6
|
+
require 'hglib/version_info'
|
7
|
+
|
8
|
+
|
9
|
+
RSpec.describe Hglib::VersionInfo do
|
10
|
+
|
11
|
+
let( :version_info ) {[{
|
12
|
+
extensions: [
|
13
|
+
{bundled: true, name: "churn", ver: nil},
|
14
|
+
{bundled: true, name: "convert", ver: nil},
|
15
|
+
{bundled: false, name: "evolve", ver: "9.2.0"},
|
16
|
+
{bundled: true, name: "extdiff", ver: nil},
|
17
|
+
{bundled: true, name: "gpg", ver: nil},
|
18
|
+
{bundled: false, name: "hggit", ver: "0.8.12 (dulwich 0.19.10)"},
|
19
|
+
{bundled: true, name: "strip", ver: nil},
|
20
|
+
{bundled: true, name: "mq", ver: nil},
|
21
|
+
{bundled: false, name: "prompt", ver: nil},
|
22
|
+
{bundled: true, name: "purge", ver: nil},
|
23
|
+
{bundled: true, name: "rebase", ver: nil},
|
24
|
+
{bundled: false, name: "topic", ver: "0.17.0"},
|
25
|
+
{bundled: true, name: "histedit", ver: nil}
|
26
|
+
],
|
27
|
+
ver: "5.1.1"
|
28
|
+
}]}
|
29
|
+
|
30
|
+
let( :including_class ) do
|
31
|
+
cls = Class.new do
|
32
|
+
extend Loggability
|
33
|
+
log_to :hglib
|
34
|
+
def initialize( server )
|
35
|
+
@server = server
|
36
|
+
end
|
37
|
+
attr_reader :server
|
38
|
+
end
|
39
|
+
cls.include( described_class )
|
40
|
+
cls
|
41
|
+
end
|
42
|
+
|
43
|
+
let( :server ) { instance_double(Hglib::Server, stop: nil) }
|
44
|
+
|
45
|
+
let( :extended_object ) { including_class.new(server) }
|
46
|
+
|
47
|
+
before( :each ) do
|
48
|
+
expect( server ).to receive( :run_with_json_template ).
|
49
|
+
with( :version ).
|
50
|
+
and_return( version_info ).
|
51
|
+
at_least( :once )
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
it "can fetch the versions of Mercurial and loaded extensions" do
|
56
|
+
result = extended_object.versions
|
57
|
+
|
58
|
+
expect( result ).to eq( version_info.first )
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
it "can fetch the simple version of Mercurial" do
|
63
|
+
result = extended_object.version
|
64
|
+
|
65
|
+
expect( result ).to eq( version_info.first[:ver] )
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
it "can fetch the versions of all loaded Mercurial extensions" do
|
70
|
+
result = extended_object.extension_versions
|
71
|
+
|
72
|
+
expect( result ).to be_a( Hash )
|
73
|
+
expect( result ).to include(
|
74
|
+
churn: {bundled: true, ver: nil},
|
75
|
+
evolve: {bundled: false, ver: '9.2.0'},
|
76
|
+
topic: {bundled: false, ver: '0.17.0'},
|
77
|
+
hggit: {bundled: false, ver: "0.8.12 (dulwich 0.19.10)"}
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
it "knows if a given extension is enabled" do
|
83
|
+
expect( extended_object.extension_enabled?('topic') ).to be_truthy
|
84
|
+
expect( extended_object.extension_enabled?('keyword') ).to be_falsey
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
data/spec/hglib_spec.rb
CHANGED
@@ -86,9 +86,10 @@ RSpec.describe Hglib do
|
|
86
86
|
|
87
87
|
|
88
88
|
it "can be created with a single error message" do
|
89
|
-
exception = rescued
|
90
|
-
|
91
|
-
|
89
|
+
exception = rescued do
|
90
|
+
exc = Hglib::CommandError.new( :status, "no_status: No such file or directory\n" )
|
91
|
+
raise( exc, nil, caller(2) )
|
92
|
+
end
|
92
93
|
|
93
94
|
expect( exception ).to_not be_multiple
|
94
95
|
expect( exception.message ).to eq( "`status`: no_status: No such file or directory" )
|
@@ -96,13 +97,14 @@ RSpec.describe Hglib do
|
|
96
97
|
|
97
98
|
|
98
99
|
it "can be created with multiple error messages" do
|
99
|
-
exception = rescued
|
100
|
-
|
100
|
+
exception = rescued do
|
101
|
+
exc = Hglib::CommandError.new(
|
101
102
|
:status,
|
102
103
|
"no_status: No such file or directory\n",
|
103
104
|
"unknown: No such file or directory\n"
|
104
|
-
|
105
|
-
|
105
|
+
)
|
106
|
+
raise( exc, nil, caller(2) )
|
107
|
+
end
|
106
108
|
|
107
109
|
expect( exception ).to be_multiple
|
108
110
|
expect( exception.message ).to eq( <<~ERROR_MESSAGE )
|
@@ -112,80 +114,24 @@ RSpec.describe Hglib do
|
|
112
114
|
ERROR_MESSAGE
|
113
115
|
end
|
114
116
|
|
115
|
-
end
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
describe "version info" do
|
120
|
-
|
121
|
-
let( :version_info ) {[{
|
122
|
-
extensions: [
|
123
|
-
{bundled: true, name: "churn", ver: nil},
|
124
|
-
{bundled: true, name: "convert", ver: nil},
|
125
|
-
{bundled: false, name: "evolve", ver: "9.2.0"},
|
126
|
-
{bundled: true, name: "extdiff", ver: nil},
|
127
|
-
{bundled: true, name: "gpg", ver: nil},
|
128
|
-
{bundled: false, name: "hggit", ver: "0.8.12 (dulwich 0.19.10)"},
|
129
|
-
{bundled: true, name: "strip", ver: nil},
|
130
|
-
{bundled: true, name: "mq", ver: nil},
|
131
|
-
{bundled: false, name: "prompt", ver: nil},
|
132
|
-
{bundled: true, name: "purge", ver: nil},
|
133
|
-
{bundled: true, name: "rebase", ver: nil},
|
134
|
-
{bundled: false, name: "topic", ver: "0.17.0"},
|
135
|
-
{bundled: true, name: "histedit", ver: nil}
|
136
|
-
],
|
137
|
-
ver: "5.1.1"
|
138
|
-
}]}
|
139
|
-
|
140
|
-
let( :server ) { instance_double(Hglib::Server, stop: nil) }
|
141
|
-
|
142
|
-
|
143
|
-
before( :each ) do
|
144
|
-
described_class.reset_server
|
145
|
-
allow( Hglib::Server ).to receive( :new ).and_return( server )
|
146
|
-
expect( server ).to receive( :run_with_json_template ).
|
147
|
-
with( :version ).
|
148
|
-
and_return( version_info ).
|
149
|
-
at_least( :once )
|
150
|
-
end
|
151
|
-
after( :each ) do
|
152
|
-
described_class.reset_server
|
153
|
-
end
|
154
|
-
|
155
117
|
|
156
|
-
it "can
|
157
|
-
|
118
|
+
it "can be created with additional details" do
|
119
|
+
exception = rescued do
|
120
|
+
exc = Hglib::CommandError.new(
|
121
|
+
:sigs,
|
122
|
+
"hg sigs: option -T not recognized\n",
|
123
|
+
details: "hg sigs\n\nlist signed changesets\n\noptions:\n\n" +
|
124
|
+
" --mq operate on patch repository\n\n(use 'hg sigs -h'" +
|
125
|
+
" to show more help)\n"
|
126
|
+
)
|
127
|
+
raise( exc, nil, caller(2) )
|
128
|
+
end
|
158
129
|
|
159
|
-
expect(
|
160
|
-
end
|
161
|
-
|
162
|
-
|
163
|
-
it "can fetch the simple version of Mercurial" do
|
164
|
-
result = described_class.version
|
165
|
-
|
166
|
-
expect( result ).to eq( version_info.first[:ver] )
|
167
|
-
end
|
168
|
-
|
169
|
-
|
170
|
-
it "can fetch the versions of all loaded Mercurial extensions" do
|
171
|
-
result = described_class.extension_versions
|
172
|
-
|
173
|
-
expect( result ).to be_a( Hash )
|
174
|
-
expect( result ).to include(
|
175
|
-
churn: {bundled: true, ver: nil},
|
176
|
-
evolve: {bundled: false, ver: '9.2.0'},
|
177
|
-
topic: {bundled: false, ver: '0.17.0'},
|
178
|
-
hggit: {bundled: false, ver: "0.8.12 (dulwich 0.19.10)"}
|
179
|
-
)
|
180
|
-
end
|
181
|
-
|
182
|
-
|
183
|
-
it "knows if a given extension is enabled" do
|
184
|
-
expect( described_class.extension_enabled?('topic') ).to be_truthy
|
185
|
-
expect( described_class.extension_enabled?('keyword') ).to be_falsey
|
130
|
+
expect( exception.details ).to match( /list signed changesets/i )
|
186
131
|
end
|
187
132
|
|
188
133
|
end
|
189
134
|
|
135
|
+
|
190
136
|
end
|
191
137
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hglib
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Granger
|
@@ -34,7 +34,7 @@ cert_chain:
|
|
34
34
|
jBZSA+N+xUTgUWpXjjwsLZjzJkhWATJWq+krNXcqpwXo6HsjmdUxoFMt63RBb+sI
|
35
35
|
XrxOxp8o0uOkU7FdLSGsyqJ2LzsR4obN
|
36
36
|
-----END CERTIFICATE-----
|
37
|
-
date: 2019-10-
|
37
|
+
date: 2019-10-14 00:00:00.000000000 Z
|
38
38
|
dependencies:
|
39
39
|
- !ruby/object:Gem::Dependency
|
40
40
|
name: loggability
|
@@ -56,14 +56,14 @@ dependencies:
|
|
56
56
|
requirements:
|
57
57
|
- - "~>"
|
58
58
|
- !ruby/object:Gem::Version
|
59
|
-
version: '0.
|
59
|
+
version: '0.2'
|
60
60
|
type: :development
|
61
61
|
prerelease: false
|
62
62
|
version_requirements: !ruby/object:Gem::Requirement
|
63
63
|
requirements:
|
64
64
|
- - "~>"
|
65
65
|
- !ruby/object:Gem::Version
|
66
|
-
version: '0.
|
66
|
+
version: '0.2'
|
67
67
|
- !ruby/object:Gem::Dependency
|
68
68
|
name: simplecov
|
69
69
|
requirement: !ruby/object:Gem::Requirement
|
@@ -79,19 +79,19 @@ dependencies:
|
|
79
79
|
- !ruby/object:Gem::Version
|
80
80
|
version: '0.7'
|
81
81
|
- !ruby/object:Gem::Dependency
|
82
|
-
name: rdoc-generator-
|
82
|
+
name: rdoc-generator-fivefish
|
83
83
|
requirement: !ruby/object:Gem::Requirement
|
84
84
|
requirements:
|
85
85
|
- - "~>"
|
86
86
|
- !ruby/object:Gem::Version
|
87
|
-
version: '0'
|
87
|
+
version: '0.4'
|
88
88
|
type: :development
|
89
89
|
prerelease: false
|
90
90
|
version_requirements: !ruby/object:Gem::Requirement
|
91
91
|
requirements:
|
92
92
|
- - "~>"
|
93
93
|
- !ruby/object:Gem::Version
|
94
|
-
version: '0'
|
94
|
+
version: '0.4'
|
95
95
|
description: |-
|
96
96
|
This is a client library for the Mercurial distributed revision control tool
|
97
97
|
that uses the {Command Server}[https://www.mercurial-scm.org/wiki/CommandServer] for efficiency.
|
@@ -106,19 +106,27 @@ files:
|
|
106
106
|
- README.md
|
107
107
|
- lib/hglib.rb
|
108
108
|
- lib/hglib/config.rb
|
109
|
+
- lib/hglib/extension.rb
|
110
|
+
- lib/hglib/extension/gpg.rb
|
109
111
|
- lib/hglib/mixins.rb
|
110
112
|
- lib/hglib/repo.rb
|
111
113
|
- lib/hglib/repo/bookmark.rb
|
112
114
|
- lib/hglib/repo/id.rb
|
113
115
|
- lib/hglib/repo/log_entry.rb
|
116
|
+
- lib/hglib/repo/status_entry.rb
|
114
117
|
- lib/hglib/repo/tag.rb
|
115
118
|
- lib/hglib/server.rb
|
119
|
+
- lib/hglib/version_info.rb
|
116
120
|
- spec/hglib/config_spec.rb
|
121
|
+
- spec/hglib/extension/gpg_spec.rb
|
122
|
+
- spec/hglib/extension_spec.rb
|
117
123
|
- spec/hglib/mixins_spec.rb
|
118
124
|
- spec/hglib/repo/id_spec.rb
|
119
125
|
- spec/hglib/repo/log_entry_spec.rb
|
126
|
+
- spec/hglib/repo/status_entry_spec.rb
|
120
127
|
- spec/hglib/repo_spec.rb
|
121
128
|
- spec/hglib/server_spec.rb
|
129
|
+
- spec/hglib/version_info_spec.rb
|
122
130
|
- spec/hglib_spec.rb
|
123
131
|
- spec/spec_helper.rb
|
124
132
|
homepage: https://hg.sr.ht/~ged/hglib
|
@@ -140,7 +148,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
140
148
|
- !ruby/object:Gem::Version
|
141
149
|
version: '0'
|
142
150
|
requirements: []
|
143
|
-
rubygems_version: 3.0.
|
151
|
+
rubygems_version: 3.0.3
|
144
152
|
signing_key:
|
145
153
|
specification_version: 4
|
146
154
|
summary: This is a client library for the Mercurial distributed revision control tool
|
metadata.gz.sig
CHANGED
Binary file
|