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.
@@ -0,0 +1,74 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'loggability'
5
+ require 'hglib/repo' unless defined?( Hglib::Repo )
6
+ require 'hglib/mixins'
7
+
8
+
9
+ class Hglib::Repo::Bookmark
10
+ extend Loggability,
11
+ Hglib::MethodUtilities
12
+
13
+
14
+ # Send logs to Hglib's logger
15
+ log_to :hglib
16
+
17
+
18
+ # {
19
+ # "active" => true,
20
+ # "bookmark" => "master",
21
+ # "node" => "720c115412188539039b87baf57931fb5415a0bf",
22
+ # "rev" => 26
23
+ # }
24
+
25
+ ### Create a new Bookmark with the given values.
26
+ def initialize( repo, bookmark:, node:, rev:, active: false )
27
+ @repo = repo
28
+ @name = bookmark
29
+ @node = node
30
+ @rev = rev
31
+ @active = active
32
+ end
33
+
34
+
35
+ ######
36
+ public
37
+ ######
38
+
39
+ ##
40
+ # The Hglib::Repo the bookmark lives in
41
+ attr_reader :repo
42
+
43
+ ##
44
+ # The name of the bookmark
45
+ attr_reader :name
46
+
47
+ ##
48
+ # The SHA of the commit the bookmark is currently on
49
+ attr_reader :node
50
+
51
+ ##
52
+ # The revision number of the commit the bookmark is currently on
53
+ attr_reader :rev
54
+
55
+ ##
56
+ # Whether or not the bookmark is currently active
57
+ attr_predicate :active
58
+
59
+
60
+ ### Delete the bookmark from its repository.
61
+ def delete
62
+ return self.repo.bookmark( self.name, delete: true )
63
+ end
64
+
65
+
66
+ ### Move the bookmark to the specified +revision+.
67
+ def move_to( revision )
68
+ return self.repo.bookmark( self.name, rev: revision, force: true )
69
+ end
70
+
71
+
72
+ end # class Hglib::Repo::Bookmark
73
+
74
+
data/lib/hglib/repo/id.rb CHANGED
@@ -2,34 +2,36 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'hglib/repo' unless defined?( Hglib::Repo )
5
+ require 'hglib/mixins'
5
6
 
6
7
 
7
8
  # The identification of a particular revision of a repository.
8
9
  class Hglib::Repo::Id
10
+ extend Hglib::MethodUtilities
9
11
 
10
- ### Parse the given +raw_id+ and return a new Id that contains the parsed
11
- ### information.
12
- def self::parse( raw_id )
13
- global, tags, bookmarks = raw_id.chomp.split( ' ', 3 )
14
- has_plus = global.chomp!( '+' ) ? true : false
15
12
 
16
- tags ||= ''
17
- tags = tags.split( '/' )
13
+ # The SHA of the zeroth node
14
+ DEFAULT_ID = '0000000000000000000000000000000000000000'
18
15
 
19
- bookmarks ||= ''
20
- bookmarks = bookmarks.split( '/' )
16
+ # The default branch name to use
17
+ DEFAULT_BRANCH = 'default'
21
18
 
22
- return self.new( global, *tags, uncommitted_changes: has_plus, bookmarks: bookmarks )
23
- end
19
+ # The SHA of the node when the repo is at tip
20
+ DEFAULT_NODE = 'ffffffffffffffffffffffffffffffffffffffff'
24
21
 
25
22
 
26
23
  ### Create a new repository ID with the given +global+ revision identifier, one
27
24
  ### or more +tags+, and other options.
28
- def initialize( global, *tags, uncommitted_changes: false, bookmarks: [] )
29
- @global = global
30
- @tags = tags
25
+ def initialize( id:, branch: DEFAULT_BRANCH, node: DEFAULT_NODE, dirty: false,
26
+ parents: [], tags: [], bookmarks: [] )
27
+
28
+ @id = id[ /\p{XDigit}{40}/ ]
29
+ @branch = branch
30
+ @node = node
31
+ @dirty = dirty == '+'
32
+ @tags = Array( tags )
33
+ @parents = Array( parents )
31
34
  @bookmarks = Array( bookmarks )
32
- @uncommitted_changes = uncommitted_changes
33
35
  end
34
36
 
35
37
 
@@ -38,8 +40,27 @@ class Hglib::Repo::Id
38
40
  ######
39
41
 
40
42
  ##
41
- # The repo's global (short-form) revision identifier.
42
- attr_reader :global
43
+ # The long-form revision ID
44
+ attr_reader :id
45
+ alias_method :global, :id
46
+
47
+ ##
48
+ # The name of the current branch
49
+ attr_reader :branch
50
+
51
+ ##
52
+ # The ID of the current
53
+ attr_reader :node
54
+
55
+ ##
56
+ # The current IDs of the current revision's parent(s).
57
+ attr_reader :parents
58
+
59
+ ##
60
+ # Does the repo have uncommitted changes?
61
+ attr_predicate :dirty
62
+ alias_method :uncommitted_changes?, :dirty?
63
+ alias_method :has_uncommitted_changes?, :dirty?
43
64
 
44
65
  ##
45
66
  # The tags belonging to the revision of the repo.
@@ -50,13 +71,6 @@ class Hglib::Repo::Id
50
71
  attr_reader :bookmarks
51
72
 
52
73
 
53
- ### Returns +true+ if the repo's working directory has uncommitted changes.
54
- def uncommitted_changes?
55
- return @uncommitted_changes
56
- end
57
- alias_method :has_uncommitted_changes?, :uncommitted_changes?
58
-
59
-
60
74
  ### Return the ID as a String in the form used by the command line.
61
75
  def to_s
62
76
  str = self.global.dup
@@ -30,18 +30,18 @@ class Hglib::Repo::LogEntry
30
30
 
31
31
  ### Create a new log entry from the raw +entryhash+.
32
32
  def initialize( entryhash )
33
- @bookmarks = entryhash[ "bookmarks" ]
34
- @branch = entryhash[ "branch" ]
35
- @date = entryhash[ "date" ]
36
- @desc = entryhash[ "desc" ]
37
- @node = entryhash[ "node" ]
38
- @parents = entryhash[ "parents" ]
39
- @phase = entryhash[ "phase" ]
40
- @rev = entryhash[ "rev" ]
41
- @tags = entryhash[ "tags" ]
42
- @user = entryhash[ "user" ]
43
- @date = entryhash[ "date" ]
44
- @files = entryhash[ "files" ] || []
33
+ @bookmarks = entryhash[ :bookmarks ]
34
+ @branch = entryhash[ :branch ]
35
+ @date = entryhash[ :date ]
36
+ @desc = entryhash[ :desc ]
37
+ @node = entryhash[ :node ]
38
+ @parents = entryhash[ :parents ]
39
+ @phase = entryhash[ :phase ]
40
+ @rev = entryhash[ :rev ]
41
+ @tags = entryhash[ :tags ]
42
+ @user = entryhash[ :user ]
43
+ @date = entryhash[ :date ]
44
+ @files = entryhash[ :files ] || []
45
45
  end
46
46
 
47
47
 
@@ -0,0 +1,52 @@
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
3
+
4
+ require 'loggability'
5
+
6
+ require 'hglib/repo' unless defined?( Hglib::Repo )
7
+
8
+
9
+ class Hglib::Repo::Tag
10
+ extend Loggability
11
+
12
+
13
+ # Send logs to the Hglib logger
14
+ log_to :hglib
15
+
16
+
17
+ ### Create a tag object.
18
+ def initialize( repo, tag:, node:, rev: 0, type: '' )
19
+ @repo = repo
20
+ @name = tag
21
+ @node = node
22
+ @rev = rev
23
+ @type = type
24
+ end
25
+
26
+
27
+ ######
28
+ public
29
+ ######
30
+
31
+ ##
32
+ # The repo the tag belongs to
33
+ attr_reader :repo
34
+
35
+ ##
36
+ # The tag's name
37
+ attr_reader :name
38
+
39
+ ##
40
+ # The SHA of the node the tag points to
41
+ attr_reader :node
42
+
43
+ ##
44
+ # The numeric revision the tag points to
45
+ attr_reader :rev
46
+
47
+ ##
48
+ # The tag type
49
+ attr_reader :type
50
+
51
+ end # class Hglib::Repo::Tag
52
+
data/lib/hglib/server.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # -*- ruby -*-
2
2
  # frozen_string_literal: true
3
3
 
4
+ require 'json'
4
5
  require 'shellwords'
5
6
  require 'loggability'
6
7
  require 'hglib' unless defined?( Hglib )
@@ -41,7 +42,7 @@ class Hglib::Server
41
42
  [ optname ]
42
43
  when FalseClass, NilClass
43
44
  [ optname.sub(/\A--/, '--no-') ] if optname.start_with?( '--' )
44
- when String
45
+ when String, Numeric
45
46
  if optname.start_with?( '--' )
46
47
  [ "#{optname}=#{val}" ]
47
48
  else
@@ -136,15 +137,15 @@ class Hglib::Server
136
137
  ### #on_byte_input and #on_line_input will be used to read it. If one of these
137
138
  ### callbacks is not registered, an IOError will be raised.
138
139
  def run( command, *args, **options )
140
+ args = args.compact
139
141
  self.log.debug "Running command: %p" % [ Shellwords.join([command.to_s] + args) ]
140
142
  self.start unless self.started?
141
143
 
142
144
  done = false
143
- output = []
145
+ output = String.new
146
+ errors = []
144
147
 
145
- args.compact!
146
148
  args += self.class.mangle_options( options )
147
-
148
149
  self.write_command( 'runcommand', command, *args )
149
150
 
150
151
  until done
@@ -158,7 +159,7 @@ class Hglib::Server
158
159
  done = true
159
160
  when 'e'
160
161
  self.log.error "Got command error: %p" % [ data ]
161
- raise Hglib::CommandError, data
162
+ errors << data
162
163
  when 'L'
163
164
  self.log.debug "Server requested line input (%d bytes)" % [ data ]
164
165
  input = self.get_line_input( data.to_i )
@@ -174,10 +175,23 @@ class Hglib::Server
174
175
  end
175
176
  end
176
177
 
178
+ raise Hglib::CommandError, [command, *errors] unless errors.empty?
179
+
177
180
  return output
178
181
  end
179
182
 
180
183
 
184
+ ### Run the specified +command+ with the given +args+ with the JSON template and
185
+ ### return the result.
186
+ def run_with_json_template( command, *args, symbolize: true, **options )
187
+ options[:T] = 'json'
188
+
189
+ json = self.run( command, *args, **options )
190
+
191
+ return JSON.parse( json, symbolize_names: symbolize )
192
+ end
193
+
194
+
181
195
  ### Returns +true+ if the underlying command server has been started.
182
196
  def is_started?
183
197
  return self.pid ? true : false
@@ -302,8 +316,11 @@ class Hglib::Server
302
316
 
303
317
  ### Return the command-line command for starting the command server.
304
318
  def server_start_command
319
+ hg = Hglib.hg_path
320
+ raise "couldn't find an `hg' executable in your PATH!" unless hg.executable?
321
+
305
322
  cmd = [
306
- Hglib.hg_path.to_s,
323
+ hg.to_s,
307
324
  '--config',
308
325
  'ui.interactive=True',
309
326
  'serve',
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env rspec -cfd
2
+
3
+ require_relative '../spec_helper'
4
+
5
+ require 'hglib/config'
6
+
7
+
8
+ RSpec.describe Hglib::Config do
9
+
10
+ let( :entries ) do
11
+ return [
12
+ {:name=>"progress.delay", :source=>"/Users/ged/.hgrc:96", :value=>"0.1"},
13
+ {:name=>"progress.refresh", :source=>"/Users/ged/.hgrc:97", :value=>"0.1"},
14
+ {:name=>"progress.format", :source=>"/Users/ged/.hgrc:98", :value=>"topic bar number"},
15
+ {:name=>"progress.clear-complete", :source=>"/Users/ged/.hgrc:99", :value=>"True"},
16
+ {:name=>"server.bundle1", :source=>"", :value=>"False"},
17
+ {:name=>"ui.editor", :source=>"$VISUAL", :value=>"emacs"},
18
+ {:name=>"ui.ssh", :source=>"/Users/ged/.hgrc:3", :value=>"ssh -C"},
19
+ {:name=>"ui.ignore", :source=>"/Users/ged/.hgrc:4", :value=>"~/.hgignore_global"},
20
+ {:name=>"ui.merge", :source=>"/Users/ged/.hgrc:5", :value=>"Kaleidoscope"},
21
+ {:name=>"ui.interactive", :source=>"--config", :value=>"True"},
22
+ {:name=>"ui.nontty", :source=>"commandserver", :value=>"true"},
23
+ {:name=>"web.cacerts", :source=>"/Users/ged/.hgrc:122", :value=>""},
24
+ ]
25
+ end
26
+
27
+
28
+ it "expands config items" do
29
+ instance = described_class.new( entries )
30
+
31
+ expect( instance['ui.editor'] ).to eq( 'emacs' )
32
+ expect( instance['progress.refresh'] ).to eq( '0.1' )
33
+ end
34
+
35
+ end
36
+
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env rspec -cfd
2
+
3
+ require_relative '../spec_helper'
4
+
5
+ require 'hglib/mixins'
6
+
7
+
8
+ RSpec.describe Hglib, "mixins" do
9
+
10
+ describe Hglib::MethodUtilities, 'used to extend a class' do
11
+
12
+ let( :extended_class ) do
13
+ klass = Class.new
14
+ klass.extend( Hglib::MethodUtilities )
15
+ klass
16
+ end
17
+
18
+
19
+ it "can declare a class-level attribute reader" do
20
+ extended_class.singleton_attr_reader :foo
21
+ expect( extended_class ).to respond_to( :foo )
22
+ expect( extended_class ).to_not respond_to( :foo= )
23
+ expect( extended_class ).to_not respond_to( :foo? )
24
+ end
25
+
26
+ it "can declare a class-level attribute writer" do
27
+ extended_class.singleton_attr_writer :foo
28
+ expect( extended_class ).to_not respond_to( :foo )
29
+ expect( extended_class ).to respond_to( :foo= )
30
+ expect( extended_class ).to_not respond_to( :foo? )
31
+ end
32
+
33
+ it "can declare a class-level attribute reader and writer" do
34
+ extended_class.singleton_attr_accessor :foo
35
+ expect( extended_class ).to respond_to( :foo )
36
+ expect( extended_class ).to respond_to( :foo= )
37
+ expect( extended_class ).to_not respond_to( :foo? )
38
+ end
39
+
40
+ it "can declare a class-level alias" do
41
+ def extended_class.foo
42
+ return "foo"
43
+ end
44
+ extended_class.singleton_method_alias( :bar, :foo )
45
+
46
+ expect( extended_class.bar ).to eq( 'foo' )
47
+ end
48
+
49
+ it "can declare an instance attribute predicate method" do
50
+ extended_class.attr_predicate :foo
51
+ instance = extended_class.new
52
+
53
+ expect( instance ).to_not respond_to( :foo )
54
+ expect( instance ).to_not respond_to( :foo= )
55
+ expect( instance ).to respond_to( :foo? )
56
+
57
+ expect( instance.foo? ).to eq( false )
58
+
59
+ instance.instance_variable_set( :@foo, 1 )
60
+ expect( instance.foo? ).to eq( true )
61
+ end
62
+
63
+ it "can declare an instance attribute predicate and writer" do
64
+ extended_class.attr_predicate_accessor :foo
65
+ instance = extended_class.new
66
+
67
+ expect( instance ).to_not respond_to( :foo )
68
+ expect( instance ).to respond_to( :foo= )
69
+ expect( instance ).to respond_to( :foo? )
70
+
71
+ expect( instance.foo? ).to eq( false )
72
+
73
+ instance.foo = 1
74
+ expect( instance.foo? ).to eq( true )
75
+ end
76
+
77
+ end
78
+
79
+ end
80
+