git 1.2.8 → 1.2.9

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of git might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: aaf7dd4bc18409515ebafd5cbb88ae6b09b0cedd
4
- data.tar.gz: e1514ed938b01b97f2bd47a85fec625918753c9c
3
+ metadata.gz: 2f8c43aab9887b1c224856449e057fa497c9d6ed
4
+ data.tar.gz: a607710a83056295b99a6eb5f0715109ccb4ea77
5
5
  SHA512:
6
- metadata.gz: 5f4b39bd857530e4d47d16489d6749e9ab358097c06713351c94fce99b5254b4ba8da40cb5e82c167019057160a2afe6833cdff64914adfc38ed6b3b4ab5cdbf
7
- data.tar.gz: 7726b6ac3a385d4904969a52c0c3718945fb47badfea35a80a7bb62130bd58f3a1bf763de1c9e16e4e063fc15ab4f113006f82154ab00832fcf289253d3f585a
6
+ metadata.gz: bcb2bc710c57c674a7f1d49a7c605ae86a76005a7ee55721c745b5fb215ecbc944892d7f3401420cf6af717c3744e730a6af32453d20f0a381f19dd23f67c068
7
+ data.tar.gz: e54136f3c5c988e9b4582fc921739de685bc5aa3ea10d2917d60bfe3c278acb8224b59c877426d718410d0a2ef92983d7066f863505f29bc4dfdcc2c15acf253
@@ -0,0 +1,75 @@
1
+ == 1.2.9
2
+
3
+ * Adding Git.configure (to configure the git env)
4
+ * Adding Git.ls_remote [Git.ls_remote(repo_path_or_url='.')]
5
+ * Adding Git.describe [repo.describe(objectish, opts)]
6
+ * Adding Git.show [repo.show(objectish=nil, path=nil)]
7
+ * Fixing Git::Diff to support default references (implicit references)
8
+ * Fixing Git::Diff to support diff over git .patch files
9
+ * Fixing Git.checkout when using :new_branch opt
10
+ * Fixing Git::Object::Commit to preserve its sha after fetching metadata
11
+ * Fixing Git.is_remote_branch? to actually check against remote branches
12
+ * Improvements over how ENV variables are modified
13
+ * Improving thrade safety (using --git-dir and --work-tree git opts)
14
+ * Improving Git::Object::Tag. Adding annotated?, tagger and message
15
+ * Supporting a submodule path as a valid repo
16
+ * Git.checkout - supporting -f and -b
17
+ * Git.clone - supporting --branch
18
+ * Git.fetch - supporting --prune
19
+ * Git.tag - supporting
20
+
21
+ == 1.2.8
22
+
23
+ * Keeping the old escape format for windows users
24
+ * revparse: Supporting ref names containing SHA like substrings (40-hex strings)
25
+ * Fix warnings on Ruby 2.1.2
26
+
27
+ == 1.2.7
28
+
29
+ * Fixing mesages encoding
30
+ * Fixing -f flag in git push
31
+ * Fixing log parser for multiline messages
32
+ * Supporting object references on Git.add_tag
33
+ * Including dotfiles on Git.status
34
+ * Git.fetch - supporting --tags
35
+ * Git.clean - supporting -x
36
+ * Git.add_tag options - supporting -a, -m and -s
37
+ * Added Git.delete_tag
38
+
39
+ == 1.2.6
40
+
41
+ * Ruby 1.9.X/2.0 fully supported
42
+ * JRuby 1.8/1.9 support
43
+ * Rubinius support
44
+ * Git.clone - supporting --recursive and --config
45
+ * Git.log - supporting last and [] over the results
46
+ * Git.add_remote - supporting -f and -t
47
+ * Git.add - supporting --fore
48
+ * Git.init - supporting --bare
49
+ * Git.commit - supporting --all and --amend
50
+ * Added Git.remote_remote, Git.revert and Git.clean
51
+ * Added Bundler to the formula
52
+ * Travis configuration
53
+ * Licence included with the gem
54
+
55
+ == 1.0.4
56
+
57
+ * added camping/gitweb.rb frontend
58
+ * added a number of speed-ups
59
+
60
+ == 1.0.3
61
+
62
+ * Sped up most of the operations
63
+ * Added some predicate functions (commit?, tree?, etc)
64
+ * Added a number of lower level operations (read-tree, write-tree, checkout-index, etc)
65
+ * Fixed a bug with using bare repositories
66
+ * Updated a good amount of the documentation
67
+
68
+ == 1.0.2
69
+
70
+ * Added methods to the git objects that might be helpful
71
+
72
+ == 1.0.1
73
+
74
+ * Initial version
75
+
data/README.md CHANGED
@@ -57,6 +57,20 @@ Require the 'git' gem.
57
57
  require 'git'
58
58
  ```
59
59
 
60
+ Git env config
61
+
62
+ ```ruby
63
+ Git.configure do |config|
64
+ # If you want to use a custom git binary
65
+ config.binary_path = '/git/bin/path'
66
+
67
+ # If you need to use a custom SSH Key
68
+ config.git_ssh = '/path/to/ssh/script'
69
+ end
70
+
71
+ ```
72
+
73
+
60
74
  Here are the operations that need read permission only.
61
75
 
62
76
  ```ruby
@@ -113,7 +127,10 @@ Here are the operations that need read permission only.
113
127
  g.grep('hello') # implies HEAD
114
128
  g.blob('v2.5:Makefile').grep('hello')
115
129
  g.tag('v2.5').grep('hello', 'docs/')
116
-
130
+ g.describe()
131
+ g.describe('0djf2aa')
132
+ g.describe('HEAD', {:all => true, :tags => true})
133
+
117
134
  g.diff(commit1, commit2).size
118
135
  g.diff(commit1, commit2).stats
119
136
  g.gtree('v2.5').diff('v2.6').insertions
@@ -132,6 +149,15 @@ Here are the operations that need read permission only.
132
149
  g.config # returns whole config hash
133
150
 
134
151
  g.tags # returns array of Git::Tag objects
152
+
153
+ g.show()
154
+ g.show('HEAD')
155
+ g.show('v2.8', 'README.md')
156
+
157
+ Git.ls_remote('https://github.com/schacon/ruby-git.git') # returns a hash containing the available references of the repo.
158
+ Git.ls_remote('/path/to/local/repo')
159
+ Git.ls_remote() # same as Git.ls_remote('.')
160
+
135
161
  ```
136
162
 
137
163
  And here are the operations that will need to write to your git repository.
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.2.9
data/lib/git.rb CHANGED
@@ -7,6 +7,7 @@ require 'git/author'
7
7
  require 'git/base'
8
8
  require 'git/branch'
9
9
  require 'git/branches'
10
+ require 'git/config'
10
11
  require 'git/diff'
11
12
  require 'git/index'
12
13
  require 'git/lib'
@@ -60,6 +61,14 @@ module Git
60
61
  end
61
62
  end
62
63
 
64
+ def self.configure
65
+ yield Base.config
66
+ end
67
+
68
+ def self.config
69
+ return Base.config
70
+ end
71
+
63
72
  def global_config(name = nil, value = nil)
64
73
  self.class.global_config(name, value)
65
74
  end
@@ -130,6 +139,15 @@ module Git
130
139
  def self.init(working_dir = '.', options = {})
131
140
  Base.init(working_dir, options)
132
141
  end
142
+
143
+ # returns a Hash containing information about the references
144
+ # of the target repository
145
+ #
146
+ # @param [String|NilClass] location the target repository location or nil for '.'
147
+ # @return [{String=>Hash}] the available references of the target repo.
148
+ def self.ls_remote(location=nil)
149
+ Git::Lib.new.ls_remote(location)
150
+ end
133
151
 
134
152
  # open an existing git working directory
135
153
  #
@@ -11,4 +11,4 @@ module Git
11
11
  end
12
12
 
13
13
  end
14
- end
14
+ end
@@ -1,16 +1,39 @@
1
+ require 'git/base/factory'
2
+
1
3
  module Git
2
4
 
3
5
  class Base
4
6
 
7
+ include Git::Base::Factory
8
+
5
9
  # opens a bare Git Repository - no working directory options
6
10
  def self.bare(git_dir, opts = {})
7
11
  self.new({:repository => git_dir}.merge(opts))
8
12
  end
9
13
 
10
- # opens a new Git Project from a working directory
11
- # you can specify non-standard git_dir and index file in the options
12
- def self.open(working_dir, opts={})
13
- self.new({:working_directory => working_dir}.merge(opts))
14
+ # clones a git repository locally
15
+ #
16
+ # repository - http://repo.or.cz/w/sinatra.git
17
+ # name - sinatra
18
+ #
19
+ # options:
20
+ # :repository
21
+ #
22
+ # :bare
23
+ # or
24
+ # :working_directory
25
+ # :index_file
26
+ #
27
+ def self.clone(repository, name, opts = {})
28
+ # run git-clone
29
+ self.new(Git::Lib.new.clone(repository, name, opts))
30
+ end
31
+
32
+ # Returns (and initialize if needed) a Git::Config instance
33
+ #
34
+ # @return [Git::Config] the current config instance.
35
+ def self.config
36
+ return @@config ||= Config.new
14
37
  end
15
38
 
16
39
  # initializes a git repository
@@ -21,8 +44,8 @@ module Git
21
44
  # :repository
22
45
  #
23
46
  def self.init(working_dir, opts = {})
24
- opts[:working_directory] = working_dir if !opts[:working_directory]
25
- opts[:repository] = File.join(opts[:working_directory], '.git') if !opts[:repository]
47
+ opts[:working_directory] ||= working_dir
48
+ opts[:repository] ||= File.join(opts[:working_directory], '.git')
26
49
 
27
50
  FileUtils.mkdir_p(opts[:working_directory]) if opts[:working_directory] && !File.directory?(opts[:working_directory])
28
51
 
@@ -31,30 +54,27 @@ module Git
31
54
  }
32
55
 
33
56
  opts.delete(:working_directory) if opts[:bare]
57
+
58
+ # Submodules have a .git *file* not a .git folder.
59
+ # This file's contents point to the location of
60
+ # where the git refs are held (In the parent repo)
61
+ if File.file?('.git')
62
+ git_file = File.open('.git').read[8..-1].strip
63
+ opts[:repository] = git_file
64
+ opts[:index] = git_file + '/index'
65
+ end
34
66
 
35
67
  Git::Lib.new(opts).init(init_opts)
36
68
 
37
69
  self.new(opts)
38
70
  end
39
-
40
- # clones a git repository locally
41
- #
42
- # repository - http://repo.or.cz/w/sinatra.git
43
- # name - sinatra
44
- #
45
- # options:
46
- # :repository
47
- #
48
- # :bare
49
- # or
50
- # :working_directory
51
- # :index_file
52
- #
53
- def self.clone(repository, name, opts = {})
54
- # run git-clone
55
- self.new(Git::Lib.new.clone(repository, name, opts))
71
+
72
+ # opens a new Git Project from a working directory
73
+ # you can specify non-standard git_dir and index file in the options
74
+ def self.open(working_dir, opts={})
75
+ self.new({:working_directory => working_dir}.merge(opts))
56
76
  end
57
-
77
+
58
78
  def initialize(options = {})
59
79
  if working_dir = options[:working_directory]
60
80
  options[:repository] ||= File.join(working_dir, '.git')
@@ -71,36 +91,6 @@ module Git
71
91
  @repository = options[:repository] ? Git::Repository.new(options[:repository]) : nil
72
92
  @index = options[:index] ? Git::Index.new(options[:index], false) : nil
73
93
  end
74
-
75
-
76
- # returns a reference to the working directory
77
- # @git.dir.path
78
- # @git.dir.writeable?
79
- def dir
80
- @working_directory
81
- end
82
-
83
- # returns reference to the git repository directory
84
- # @git.dir.path
85
- def repo
86
- @repository
87
- end
88
-
89
- # returns reference to the git index file
90
- def index
91
- @index
92
- end
93
-
94
-
95
- def set_working(work_dir, check = true)
96
- @lib = nil
97
- @working_directory = Git::WorkingDirectory.new(work_dir.to_s, check)
98
- end
99
-
100
- def set_index(index_file, check = true)
101
- @lib = nil
102
- @index = Git::Index.new(index_file.to_s, check)
103
- end
104
94
 
105
95
  # changes current working directory for a block
106
96
  # to the git working directory
@@ -117,13 +107,6 @@ module Git
117
107
  end
118
108
  end
119
109
 
120
- # returns the repository size in bytes
121
- def repo_size
122
- Dir.chdir(repo.path) do
123
- return `du -s`.chomp.split.first.to_i
124
- end
125
- end
126
-
127
110
  #g.config('user.name', 'Scott Chacon') # sets value
128
111
  #g.config('user.email', 'email@email.com') # sets value
129
112
  #g.config('user.name') # returns 'Scott Chacon'
@@ -140,51 +123,40 @@ module Git
140
123
  lib.config_list
141
124
  end
142
125
  end
143
-
144
- # factory methods
145
-
146
- # returns a Git::Object of the appropriate type
147
- # you can also call @git.gtree('tree'), but that's
148
- # just for readability. If you call @git.gtree('HEAD') it will
149
- # still return a Git::Object::Commit object.
150
- #
151
- # @git.object calls a factory method that will run a rev-parse
152
- # on the objectish and determine the type of the object and return
153
- # an appropriate object for that type
154
- def object(objectish)
155
- Git::Object.new(self, objectish)
126
+
127
+ # returns a reference to the working directory
128
+ # @git.dir.path
129
+ # @git.dir.writeable?
130
+ def dir
131
+ @working_directory
156
132
  end
157
133
 
158
- def gtree(objectish)
159
- Git::Object.new(self, objectish, 'tree')
134
+ # returns reference to the git index file
135
+ def index
136
+ @index
160
137
  end
161
-
162
- def gcommit(objectish)
163
- Git::Object.new(self, objectish, 'commit')
138
+
139
+ # returns reference to the git repository directory
140
+ # @git.dir.path
141
+ def repo
142
+ @repository
164
143
  end
165
144
 
166
- def gblob(objectish)
167
- Git::Object.new(self, objectish, 'blob')
145
+ # returns the repository size in bytes
146
+ def repo_size
147
+ Dir.chdir(repo.path) do
148
+ return `du -s`.chomp.split.first.to_i
149
+ end
168
150
  end
169
151
 
170
- # returns a Git::Log object with count commits
171
- def log(count = 30)
172
- Git::Log.new(self, count)
173
- end
174
-
175
- # returns a Git::Status object
176
- def status
177
- Git::Status.new(self)
178
- end
179
-
180
- # returns a Git::Branches object of all the Git::Branch objects for this repo
181
- def branches
182
- Git::Branches.new(self)
152
+ def set_index(index_file, check = true)
153
+ @lib = nil
154
+ @index = Git::Index.new(index_file.to_s, check)
183
155
  end
184
156
 
185
- # returns a Git::Branch object for branch_name
186
- def branch(branch_name = 'master')
187
- Git::Branch.new(self, branch_name)
157
+ def set_working(work_dir, check = true)
158
+ @lib = nil
159
+ @working_directory = Git::WorkingDirectory.new(work_dir.to_s, check)
188
160
  end
189
161
 
190
162
  # returns +true+ if the branch exists locally
@@ -195,7 +167,7 @@ module Git
195
167
 
196
168
  # returns +true+ if the branch exists remotely
197
169
  def is_remote_branch?(branch)
198
- branch_names = self.branches.local.map {|b| b.name}
170
+ branch_names = self.branches.remote.map {|b| b.name}
199
171
  branch_names.include?(branch)
200
172
  end
201
173
 
@@ -205,11 +177,6 @@ module Git
205
177
  branch_names.include?(branch)
206
178
  end
207
179
 
208
- # returns a Git::Remote object
209
- def remote(remote_name = 'origin')
210
- Git::Remote.new(self, remote_name)
211
- end
212
-
213
180
  # this is a convenience method for accessing the class that wraps all the
214
181
  # actual 'git' forked system calls. At some point I hope to replace the Git::Lib
215
182
  # class with one that uses native methods or libgit C bindings
@@ -240,12 +207,7 @@ module Git
240
207
  self.object('HEAD').grep(string, path_limiter, opts)
241
208
  end
242
209
 
243
- # returns a Git::Diff object
244
- def diff(objectish = 'HEAD', obj2 = nil)
245
- Git::Diff.new(self, objectish, obj2)
246
- end
247
-
248
- # updates the repository index using the workig dorectory content
210
+ # updates the repository index using the working directory content
249
211
  #
250
212
  # @git.add('path/to/file')
251
213
  # @git.add(['path/to/file1','path/to/file2'])
@@ -290,6 +252,25 @@ module Git
290
252
  self.lib.clean(opts)
291
253
  end
292
254
 
255
+ # returns the most recent tag that is reachable from a commit
256
+ #
257
+ # options:
258
+ # :all
259
+ # :tags
260
+ # :contains
261
+ # :debug
262
+ # :exact_match
263
+ # :dirty
264
+ # :abbrev
265
+ # :candidates
266
+ # :long
267
+ # :always
268
+ # :match
269
+ #
270
+ def describe(committish=nil, opts={})
271
+ self.lib.describe(committish, opts)
272
+ end
273
+
293
274
  # reverts the working directory to the provided commitish.
294
275
  # Accepts a range, such as comittish..HEAD
295
276
  #
@@ -402,11 +383,6 @@ module Git
402
383
  def tags
403
384
  self.lib.tags.map { |r| tag(r) }
404
385
  end
405
-
406
- # returns a Git::Tag object
407
- def tag(tag_name)
408
- Git::Object.new(self, tag_name, 'tag', true)
409
- end
410
386
 
411
387
  # Creates a new git tag (Git::Tag)
412
388
  # Usage:
@@ -423,7 +399,7 @@ module Git
423
399
  #
424
400
  def add_tag(name, *opts)
425
401
  self.lib.tag(name, *opts)
426
- tag(name)
402
+ self.tag(name)
427
403
  end
428
404
 
429
405
  # deletes a tag
@@ -454,6 +430,15 @@ module Git
454
430
  def apply_mail(file)
455
431
  self.lib.apply_mail(file) if File.exist?(file)
456
432
  end
433
+
434
+ # Shows objects
435
+ #
436
+ # @param [String|NilClass] objectish the target object reference (nil == HEAD)
437
+ # @param [String|NilClass] path the path of the file to be shown
438
+ # @return [String] the object information
439
+ def show(objectish=nil, path=nil)
440
+ self.lib.show(objectish, path)
441
+ end
457
442
 
458
443
  ## LOWER LEVEL INDEX OPERATIONS ##
459
444
 
@@ -492,10 +477,6 @@ module Git
492
477
  self.lib.write_tree
493
478
  end
494
479
 
495
- def commit_tree(tree = nil, opts = {})
496
- Git::Object::Commit.new(self, self.lib.commit_tree(tree, opts))
497
- end
498
-
499
480
  def write_and_commit_tree(opts = {})
500
481
  tree = write_tree
501
482
  commit_tree(tree, opts)
@@ -553,7 +534,6 @@ module Git
553
534
  def current_branch
554
535
  self.lib.branch_current
555
536
  end
556
-
557
537
 
558
538
  end
559
539
 
@@ -0,0 +1,75 @@
1
+ module Git
2
+
3
+ class Base
4
+
5
+ module Factory
6
+
7
+ # returns a Git::Branch object for branch_name
8
+ def branch(branch_name = 'master')
9
+ Git::Branch.new(self, branch_name)
10
+ end
11
+
12
+ # returns a Git::Branches object of all the Git::Branch
13
+ # objects for this repo
14
+ def branches
15
+ Git::Branches.new(self)
16
+ end
17
+
18
+ def commit_tree(tree = nil, opts = {})
19
+ Git::Object::Commit.new(self, self.lib.commit_tree(tree, opts))
20
+ end
21
+
22
+ # returns a Git::Diff object
23
+ def diff(objectish = 'HEAD', obj2 = nil)
24
+ Git::Diff.new(self, objectish, obj2)
25
+ end
26
+
27
+ def gblob(objectish)
28
+ Git::Object.new(self, objectish, 'blob')
29
+ end
30
+
31
+ def gcommit(objectish)
32
+ Git::Object.new(self, objectish, 'commit')
33
+ end
34
+
35
+ def gtree(objectish)
36
+ Git::Object.new(self, objectish, 'tree')
37
+ end
38
+
39
+ # returns a Git::Log object with count commits
40
+ def log(count = 30)
41
+ Git::Log.new(self, count)
42
+ end
43
+
44
+ # returns a Git::Object of the appropriate type
45
+ # you can also call @git.gtree('tree'), but that's
46
+ # just for readability. If you call @git.gtree('HEAD') it will
47
+ # still return a Git::Object::Commit object.
48
+ #
49
+ # @git.object calls a factory method that will run a rev-parse
50
+ # on the objectish and determine the type of the object and return
51
+ # an appropriate object for that type
52
+ def object(objectish)
53
+ Git::Object.new(self, objectish)
54
+ end
55
+
56
+ # returns a Git::Remote object
57
+ def remote(remote_name = 'origin')
58
+ Git::Remote.new(self, remote_name)
59
+ end
60
+
61
+ # returns a Git::Status object
62
+ def status
63
+ Git::Status.new(self)
64
+ end
65
+
66
+ # returns a Git::Tag object
67
+ def tag(tag_name)
68
+ Git::Object.new(self, tag_name, 'tag', true)
69
+ end
70
+
71
+ end
72
+
73
+ end
74
+
75
+ end
@@ -0,0 +1,19 @@
1
+ module Git
2
+
3
+ class Config
4
+
5
+ attr_writer :binary_path
6
+
7
+ attr_accessor :git_ssh
8
+
9
+ def initialize
10
+ @binary_path = nil
11
+ end
12
+
13
+ def binary_path
14
+ @binary_path || 'git'
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -6,8 +6,8 @@ module Git
6
6
 
7
7
  def initialize(base, from = nil, to = nil)
8
8
  @base = base
9
- @from = from.to_s
10
- @to = to.to_s
9
+ @from = from && from.to_s
10
+ @to = to && to.to_s
11
11
 
12
12
  @path = nil
13
13
  @full_diff = nil
@@ -116,20 +116,25 @@ module Git
116
116
 
117
117
  # break up @diff_full
118
118
  def process_full_diff
119
+ defaults = {
120
+ :mode => '',
121
+ :src => '',
122
+ :dst => '',
123
+ :type => 'modified'
124
+ }
119
125
  final = {}
120
126
  current_file = nil
121
127
  @full_diff.split("\n").each do |line|
122
- if m = /diff --git a\/(.*?) b\/(.*?)/.match(line)
128
+ if m = /^diff --git a\/(.*?) b\/(.*?)/.match(line)
123
129
  current_file = m[1]
124
- final[current_file] = {:patch => line, :path => current_file,
125
- :mode => '', :src => '', :dst => '', :type => 'modified'}
130
+ final[current_file] = defaults.merge({:patch => line, :path => current_file})
126
131
  else
127
- if m = /index (.......)\.\.(.......)( ......)*/.match(line)
132
+ if m = /^index (.......)\.\.(.......)( ......)*/.match(line)
128
133
  final[current_file][:src] = m[1]
129
134
  final[current_file][:dst] = m[2]
130
135
  final[current_file][:mode] = m[3].strip if m[3]
131
136
  end
132
- if m = /(.*?) file mode (......)/.match(line)
137
+ if m = /^([[:alpha:]]*?) file mode (......)/.match(line)
133
138
  final[current_file][:type] = m[1]
134
139
  final[current_file][:mode] = m[2]
135
140
  end
@@ -6,7 +6,9 @@ module Git
6
6
  end
7
7
 
8
8
  class Lib
9
-
9
+
10
+ @@semaphore = Mutex.new
11
+
10
12
  def initialize(base = nil, logger = nil)
11
13
  @git_dir = nil
12
14
  @git_index_file = nil
@@ -44,10 +46,13 @@ module Git
44
46
  # {:working_directory} otherwise
45
47
  #
46
48
  # accepts options:
47
- # :remote:: name of remote (rather than 'origin')
48
49
  # :bare:: no working directory
49
- # :recursive:: after the clone is created, initialize all submodules within, using their default settings.
50
+ # :branch:: name of branch to track (rather than 'master')
50
51
  # :depth:: the number of commits back to pull
52
+ # :origin:: name of remote (same as remote)
53
+ # :path:: directory where the repo will be cloned
54
+ # :remote:: name of remote (rather than 'origin')
55
+ # :recursive:: after the clone is created, initialize all submodules within, using their default settings.
51
56
  #
52
57
  # TODO - make this work with SSH password or auth_key
53
58
  #
@@ -56,13 +61,15 @@ module Git
56
61
  clone_dir = opts[:path] ? File.join(@path, name) : name
57
62
 
58
63
  arr_opts = []
59
- arr_opts << "--bare" if opts[:bare]
60
- arr_opts << "--recursive" if opts[:recursive]
61
- arr_opts << "-o" << opts[:remote] if opts[:remote]
62
- arr_opts << "--depth" << opts[:depth].to_i if opts[:depth] && opts[:depth].to_i > 0
63
- arr_opts << "--config" << opts[:config] if opts[:config]
64
+ arr_opts << '--bare' if opts[:bare]
65
+ arr_opts << '--branch' << opts[:branch] if opts[:branch]
66
+ arr_opts << '--depth' << opts[:depth].to_i if opts[:depth] && opts[:depth].to_i > 0
67
+ arr_opts << '--config' << opts[:config] if opts[:config]
68
+ arr_opts << '--origin' << opts[:remote] || opts[:origin] if opts[:remote] || opts[:origin]
69
+ arr_opts << '--recursive' if opts[:recursive]
64
70
 
65
71
  arr_opts << '--'
72
+
66
73
  arr_opts << repository
67
74
  arr_opts << clone_dir
68
75
 
@@ -74,6 +81,49 @@ module Git
74
81
 
75
82
  ## READ COMMANDS ##
76
83
 
84
+ #
85
+ # Returns most recent tag that is reachable from a commit
86
+ #
87
+ # accepts options:
88
+ # :all
89
+ # :tags
90
+ # :contains
91
+ # :debug
92
+ # :exact_match
93
+ # :dirty
94
+ # :abbrev
95
+ # :candidates
96
+ # :long
97
+ # :always
98
+ # :math
99
+ #
100
+ # @param [String|NilClass] committish target commit sha or object name
101
+ # @param [{Symbol=>Object}] opts the given options
102
+ # @return [String] the tag name
103
+ #
104
+ def describe(committish=nil, opts={})
105
+ arr_opts = []
106
+
107
+ arr_opts << '--all' if opts[:all]
108
+ arr_opts << '--tags' if opts[:tags]
109
+ arr_opts << '--contains' if opts[:contains]
110
+ arr_opts << '--debug' if opts[:debug]
111
+ arr_opts << '--long' if opts[:long]
112
+ arr_opts << '--always' if opts[:always]
113
+ arr_opts << '--exact-match' if opts[:exact_match] || opts[:"exact-match"]
114
+
115
+ arr_opts << '--dirty' if opts['dirty'] == true
116
+ arr_opts << "--dirty=#{opts['dirty']}" if opts['dirty'].is_a?(String)
117
+
118
+ arr_opts << "--abbrev=#{opts['abbrev']}" if opts[:abbrev]
119
+ arr_opts << "--candidates=#{opts['candidates']}" if opts[:candidates]
120
+ arr_opts << "--match=#{opts['match']}" if opts[:match]
121
+
122
+ arr_opts << committish if committish
123
+
124
+ return command('describe', arr_opts)
125
+ end
126
+
77
127
  def log_commits(opts={})
78
128
  arr_opts = log_common_options(opts)
79
129
 
@@ -150,6 +200,31 @@ module Git
150
200
 
151
201
  return hsh
152
202
  end
203
+
204
+ def tag_data(name)
205
+ sha = sha.to_s
206
+ tdata = command_lines('cat-file', ['tag', name])
207
+ process_tag_data(tdata, name, 0)
208
+ end
209
+
210
+ def process_tag_data(data, name, indent=4)
211
+ hsh = {
212
+ 'name' => name,
213
+ 'message' => ''
214
+ }
215
+
216
+ loop do
217
+ key, *value = data.shift.split
218
+
219
+ break if key.nil?
220
+
221
+ hsh[key] = value.join(' ')
222
+ end
223
+
224
+ hsh['message'] = data.collect {|line| line[indent..-1]}.join("\n") + "\n"
225
+
226
+ return hsh
227
+ end
153
228
 
154
229
  def process_commit_log_data(data)
155
230
  in_message = false
@@ -317,6 +392,19 @@ module Git
317
392
  hsh
318
393
  end
319
394
 
395
+ def ls_remote(location=nil)
396
+ location ||= '.'
397
+ Hash.new{ |h,k| h[k] = {} }.tap do |hsh|
398
+ command_lines('ls-remote', [location], false).each do |line|
399
+ (sha, info) = line.split("\t")
400
+ (ref, type, name) = info.split('/', 3)
401
+ type ||= 'head'
402
+ type = 'branches' if type == 'heads'
403
+ value = {:ref => ref, :sha => sha}
404
+ hsh[type].update( name.nil? ? value : { name => value })
405
+ end
406
+ end
407
+ end
320
408
 
321
409
  def ignored_files
322
410
  command_lines('ls-files', ['--others', '-i', '--exclude-standard'])
@@ -377,6 +465,19 @@ module Git
377
465
  def parse_config(file)
378
466
  parse_config_list command_lines('config', ['--list', '--file', file], false)
379
467
  end
468
+
469
+ # Shows objects
470
+ #
471
+ # @param [String|NilClass] objectish the target object reference (nil == HEAD)
472
+ # @param [String|NilClass] path the path of the file to be shown
473
+ # @return [String] the object information
474
+ def show(objectish=nil, path=nil)
475
+ arr_opts = []
476
+
477
+ arr_opts << (path ? "#{objectish}:#{path}" : objectish)
478
+
479
+ command('show', arr_opts.compact)
480
+ end
380
481
 
381
482
  ## WRITE COMMANDS ##
382
483
 
@@ -388,7 +489,7 @@ module Git
388
489
  command('config', ['--global', name, value], false)
389
490
  end
390
491
 
391
- # updates the repository index using the workig dorectory content
492
+ # updates the repository index using the working directory content
392
493
  #
393
494
  # lib.add('path/to/file')
394
495
  # lib.add(['path/to/file1','path/to/file2'])
@@ -521,8 +622,8 @@ module Git
521
622
 
522
623
  def checkout(branch, opts = {})
523
624
  arr_opts = []
524
- arr_opts << '-f' if opts[:force]
525
- arr_opts << '-b' << opts[:new_branch] if opts[:new_branch]
625
+ arr_opts << '-b' if opts[:new_branch] || opts[:b]
626
+ arr_opts << '--force' if opts[:force] || opts[:f]
526
627
  arr_opts << branch
527
628
 
528
629
  command('checkout', arr_opts)
@@ -610,6 +711,7 @@ module Git
610
711
  def fetch(remote, opts)
611
712
  arr_opts = [remote]
612
713
  arr_opts << '--tags' if opts[:t] || opts[:tags]
714
+ arr_opts << '--prune' if opts[:p] || opts[:prune]
613
715
 
614
716
  command('fetch', arr_opts)
615
717
  end
@@ -699,8 +801,14 @@ module Git
699
801
  opts[:add_gzip] = true
700
802
  end
701
803
 
702
- file ||= Tempfile.new('archive').path
703
-
804
+ if !file
805
+ tempfile = Tempfile.new('archive')
806
+ file = tempfile.path
807
+ # delete it now, before we write to it, so that Ruby doesn't delete it
808
+ # when it finalizes the Tempfile.
809
+ tempfile.close!
810
+ end
811
+
704
812
  arr_opts = []
705
813
  arr_opts << "--format=#{opts[:format]}" if opts[:format]
706
814
  arr_opts << "--prefix=#{opts[:prefix]}" if opts[:prefix]
@@ -728,42 +836,88 @@ module Git
728
836
 
729
837
 
730
838
  private
839
+
840
+ # Systen ENV variables involved in the git commands.
841
+ #
842
+ # @return [<String>] the names of the EVN variables involved in the git commands
843
+ ENV_VARIABLE_NAMES = ['GIT_DIR', 'GIT_WORK_TREE', 'GIT_INDEX_FILE', 'GIT_SSH']
731
844
 
732
845
  def command_lines(cmd, opts = [], chdir = true, redirect = '')
733
846
  command(cmd, opts, chdir).split("\n")
734
847
  end
735
848
 
736
- def command(cmd, opts = [], chdir = true, redirect = '', &block)
849
+ # Takes the current git's system ENV variables and store them.
850
+ def store_git_system_env_variables
851
+ @git_system_env_variables = {}
852
+ ENV_VARIABLE_NAMES.each do |env_variable_name|
853
+ @git_system_env_variables[env_variable_name] = ENV[env_variable_name]
854
+ end
855
+ end
856
+
857
+ # Takes the previously stored git's ENV variables and set them again on ENV.
858
+ def restore_git_system_env_variables
859
+ ENV_VARIABLE_NAMES.each do |env_variable_name|
860
+ ENV[env_variable_name] = @git_system_env_variables[env_variable_name]
861
+ end
862
+ end
863
+
864
+ # Sets git's ENV variables to the custom values for the current instance.
865
+ def set_custom_git_env_variables
737
866
  ENV['GIT_DIR'] = @git_dir
738
867
  ENV['GIT_WORK_TREE'] = @git_work_dir
739
868
  ENV['GIT_INDEX_FILE'] = @git_index_file
869
+ ENV['GIT_SSH'] = Git::Base.config.git_ssh
870
+ end
740
871
 
741
- path = @git_work_dir || @git_dir || @path
742
-
872
+ # Runs a block inside an environment with customized ENV variables.
873
+ # It restores the ENV after execution.
874
+ #
875
+ # @param [Proc] block block to be executed within the customized environment
876
+ def with_custom_env_variables(&block)
877
+ @@semaphore.synchronize do
878
+ store_git_system_env_variables()
879
+ set_custom_git_env_variables()
880
+ return block.call()
881
+ end
882
+ ensure
883
+ restore_git_system_env_variables()
884
+ end
885
+
886
+ def command(cmd, opts = [], chdir = true, redirect = '', &block)
887
+ global_opts = []
888
+ global_opts << "--git-dir=#{@git_dir}" if !@git_dir.nil?
889
+ global_opts << "--work-tree=#{@git_work_dir}" if !@git_work_dir.nil?
890
+
743
891
  opts = [opts].flatten.map {|s| escape(s) }.join(' ')
892
+
893
+ global_opts = global_opts.flatten.map {|s| escape(s) }.join(' ')
894
+
895
+ git_cmd = "#{Git::Base.config.binary_path} #{global_opts} #{cmd} #{opts} #{redirect} 2>&1"
896
+
897
+ output = nil
898
+
899
+ command_thread = nil;
900
+
901
+ exitstatus = nil
744
902
 
745
- git_cmd = "git #{cmd} #{opts} #{redirect} 2>&1"
746
-
747
- out = nil
748
- if chdir && (Dir.getwd != path)
749
- Dir.chdir(path) { out = run_command(git_cmd, &block) }
750
- else
751
-
752
- out = run_command(git_cmd, &block)
903
+ with_custom_env_variables do
904
+ command_thread = Thread.new do
905
+ output = run_command(git_cmd, &block)
906
+ exitstatus = $?.exitstatus
907
+ end
908
+ command_thread.join
753
909
  end
754
-
910
+
755
911
  if @logger
756
912
  @logger.info(git_cmd)
757
- @logger.debug(out)
913
+ @logger.debug(output)
758
914
  end
759
915
 
760
- if $?.exitstatus > 0
761
- if $?.exitstatus == 1 && out == ''
762
- return ''
763
- end
764
- raise Git::GitExecuteError.new(git_cmd + ':' + out.to_s)
916
+ if exitstatus > 1 || (exitstatus == 1 && output != '')
917
+ raise Git::GitExecuteError.new(git_cmd + ':' + output.to_s)
765
918
  end
766
- out
919
+
920
+ return output
767
921
  end
768
922
 
769
923
  # Takes the diff command line output (as Array) and parse it into a Hash
@@ -816,16 +970,13 @@ module Git
816
970
 
817
971
  arr_opts << opts[:object] if opts[:object].is_a? String
818
972
  arr_opts << '--' << opts[:path_limiter] if opts[:path_limiter]
819
-
820
973
  arr_opts
821
974
  end
822
975
 
823
976
  def run_command(git_cmd, &block)
824
- if block_given?
825
- IO.popen(git_cmd, &block)
826
- else
827
- `#{git_cmd}`.chomp
828
- end
977
+ return IO.popen(git_cmd, &block) if block_given?
978
+
979
+ `#{git_cmd}`.chomp
829
980
  end
830
981
 
831
982
  def escape(s)
@@ -217,9 +217,7 @@ module Git
217
217
  end
218
218
 
219
219
  def set_commit(data)
220
- if data['sha']
221
- @sha = data['sha']
222
- end
220
+ @sha ||= data['sha']
223
221
  @committer = Git::Author.new(data['committer'])
224
222
  @author = Git::Author.new(data['author'])
225
223
  @tree = Git::Object::Tree.new(@base, data['tree'])
@@ -235,10 +233,10 @@ module Git
235
233
 
236
234
  # see if this object has been initialized and do so if not
237
235
  def check_commit
238
- unless @tree
239
- data = @base.lib.commit_data(@objectish)
240
- set_commit(data)
241
- end
236
+ return if @tree
237
+
238
+ data = @base.lib.commit_data(@objectish)
239
+ set_commit(data)
242
240
  end
243
241
 
244
242
  end
@@ -249,11 +247,43 @@ module Git
249
247
  def initialize(base, sha, name)
250
248
  super(base, sha)
251
249
  @name = name
250
+ @annotated = nil
251
+ @loaded = false
252
+ end
253
+
254
+ def annotated?
255
+ @annotated ||= (@base.lib.object_type(self.name) == 'tag')
252
256
  end
253
257
 
258
+ def message
259
+ check_tag()
260
+ return @message
261
+ end
262
+
254
263
  def tag?
255
264
  true
256
265
  end
266
+
267
+ def tagger
268
+ check_tag()
269
+ return @tagger
270
+ end
271
+
272
+ private
273
+
274
+ def check_tag
275
+ return if @loaded
276
+
277
+ if !self.annotated?
278
+ @message = @tagger = nil
279
+ else
280
+ tdata = @base.lib.tag_data(@name)
281
+ @message = tdata['message'].chomp
282
+ @tagger = Git::Author.new(tdata['tagger'])
283
+ end
284
+
285
+ @loaded = true
286
+ end
257
287
 
258
288
  end
259
289
 
@@ -2,6 +2,6 @@ module Git
2
2
 
3
3
  # The current gem version
4
4
  # @return [String] the current gem version.
5
- VERSION='1.2.8'
5
+ VERSION='1.2.9'
6
6
 
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.8
4
+ version: 1.2.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Chacon
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-31 00:00:00.000000000 Z
11
+ date: 2015-01-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -44,14 +44,20 @@ dependencies:
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: '2'
48
+ - - "<"
49
+ - !ruby/object:Gem::Version
50
+ version: '4'
48
51
  type: :development
49
52
  prerelease: false
50
53
  version_requirements: !ruby/object:Gem::Requirement
51
54
  requirements:
52
55
  - - ">="
53
56
  - !ruby/object:Gem::Version
54
- version: '0'
57
+ version: '2'
58
+ - - "<"
59
+ - !ruby/object:Gem::Version
60
+ version: '4'
55
61
  description:
56
62
  email: schacon@gmail.com
57
63
  executables: []
@@ -59,13 +65,17 @@ extensions: []
59
65
  extra_rdoc_files:
60
66
  - README.md
61
67
  files:
68
+ - CHANGELOG
62
69
  - LICENSE
63
70
  - README.md
71
+ - VERSION
64
72
  - lib/git.rb
65
73
  - lib/git/author.rb
66
74
  - lib/git/base.rb
75
+ - lib/git/base/factory.rb
67
76
  - lib/git/branch.rb
68
77
  - lib/git/branches.rb
78
+ - lib/git/config.rb
69
79
  - lib/git/diff.rb
70
80
  - lib/git/index.rb
71
81
  - lib/git/lib.rb
@@ -101,7 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
101
111
  requirements:
102
112
  - git 1.6.0.0, or greater
103
113
  rubyforge_project:
104
- rubygems_version: 2.4.1
114
+ rubygems_version: 2.4.5
105
115
  signing_key:
106
116
  specification_version: 4
107
117
  summary: Ruby/Git is a Ruby library that can be used to create, read and manipulate