hglib 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+