git-ng 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,14 @@
1
+ module Git
2
+ class Author
3
+ attr_accessor :name, :email, :date
4
+
5
+ def initialize(author_string)
6
+ if m = /(.*?) <(.*?)> (\d+) (.*)/.match(author_string)
7
+ @name = m[1]
8
+ @email = m[2]
9
+ @date = Time.at(m[3].to_i)
10
+ end
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,540 @@
1
+ require 'git/base/factory'
2
+
3
+ module Git
4
+
5
+ class Base
6
+
7
+ include Git::Base::Factory
8
+
9
+ # opens a bare Git Repository - no working directory options
10
+ def self.bare(git_dir, opts = {})
11
+ self.new({:repository => git_dir}.merge(opts))
12
+ end
13
+
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
37
+ end
38
+
39
+ # initializes a git repository
40
+ #
41
+ # options:
42
+ # :bare
43
+ # :index
44
+ # :repository
45
+ #
46
+ def self.init(working_dir, opts = {})
47
+ opts[:working_directory] ||= working_dir
48
+ opts[:repository] ||= File.join(opts[:working_directory], '.git')
49
+
50
+ FileUtils.mkdir_p(opts[:working_directory]) if opts[:working_directory] && !File.directory?(opts[:working_directory])
51
+
52
+ init_opts = {
53
+ :bare => opts[:bare]
54
+ }
55
+
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
66
+
67
+ Git::Lib.new(opts).init(init_opts)
68
+
69
+ self.new(opts)
70
+ end
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))
76
+ end
77
+
78
+ def initialize(options = {})
79
+ if working_dir = options[:working_directory]
80
+ options[:repository] ||= File.join(working_dir, '.git')
81
+ options[:index] ||= File.join(working_dir, '.git', 'index')
82
+ end
83
+ if options[:log]
84
+ @logger = options[:log]
85
+ @logger.info("Starting Git")
86
+ else
87
+ @logger = nil
88
+ end
89
+
90
+ @working_directory = options[:working_directory] ? Git::WorkingDirectory.new(options[:working_directory]) : nil
91
+ @repository = options[:repository] ? Git::Repository.new(options[:repository]) : nil
92
+ @index = options[:index] ? Git::Index.new(options[:index], false) : nil
93
+ end
94
+
95
+ # changes current working directory for a block
96
+ # to the git working directory
97
+ #
98
+ # example
99
+ # @git.chdir do
100
+ # # write files
101
+ # @git.add
102
+ # @git.commit('message')
103
+ # end
104
+ def chdir # :yields: the Git::Path
105
+ Dir.chdir(dir.path) do
106
+ yield dir.path
107
+ end
108
+ end
109
+
110
+ #g.config('user.name', 'Scott Chacon') # sets value
111
+ #g.config('user.email', 'email@email.com') # sets value
112
+ #g.config('user.name') # returns 'Scott Chacon'
113
+ #g.config # returns whole config hash
114
+ def config(name = nil, value = nil)
115
+ if(name && value)
116
+ # set value
117
+ lib.config_set(name, value)
118
+ elsif (name)
119
+ # return value
120
+ lib.config_get(name)
121
+ else
122
+ # return hash
123
+ lib.config_list
124
+ end
125
+ end
126
+
127
+ # returns a reference to the working directory
128
+ # @git.dir.path
129
+ # @git.dir.writeable?
130
+ def dir
131
+ @working_directory
132
+ end
133
+
134
+ # returns reference to the git index file
135
+ def index
136
+ @index
137
+ end
138
+
139
+ # returns reference to the git repository directory
140
+ # @git.dir.path
141
+ def repo
142
+ @repository
143
+ end
144
+
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
150
+ end
151
+
152
+ def set_index(index_file, check = true)
153
+ @lib = nil
154
+ @index = Git::Index.new(index_file.to_s, check)
155
+ end
156
+
157
+ def set_working(work_dir, check = true)
158
+ @lib = nil
159
+ @working_directory = Git::WorkingDirectory.new(work_dir.to_s, check)
160
+ end
161
+
162
+ # returns +true+ if the branch exists locally
163
+ def is_local_branch?(branch)
164
+ branch_names = self.branches.local.map {|b| b.name}
165
+ branch_names.include?(branch)
166
+ end
167
+
168
+ # returns +true+ if the branch exists remotely
169
+ def is_remote_branch?(branch)
170
+ branch_names = self.branches.remote.map {|b| b.name}
171
+ branch_names.include?(branch)
172
+ end
173
+
174
+ # returns +true+ if the branch exists
175
+ def is_branch?(branch)
176
+ branch_names = self.branches.map {|b| b.name}
177
+ branch_names.include?(branch)
178
+ end
179
+
180
+ # this is a convenience method for accessing the class that wraps all the
181
+ # actual 'git' forked system calls. At some point I hope to replace the Git::Lib
182
+ # class with one that uses native methods or libgit C bindings
183
+ def lib
184
+ @lib ||= Git::Lib.new(self, @logger)
185
+ end
186
+
187
+ # will run a grep for 'string' on the HEAD of the git repository
188
+ #
189
+ # to be more surgical in your grep, you can call grep() off a specific
190
+ # git object. for example:
191
+ #
192
+ # @git.object("v2.3").grep('TODO')
193
+ #
194
+ # in any case, it returns a hash of arrays of the type:
195
+ # hsh[tree-ish] = [[line_no, match], [line_no, match2]]
196
+ # hsh[tree-ish] = [[line_no, match], [line_no, match2]]
197
+ #
198
+ # so you might use it like this:
199
+ #
200
+ # @git.grep("TODO").each do |sha, arr|
201
+ # puts "in blob #{sha}:"
202
+ # arr.each do |match|
203
+ # puts "\t line #{match[0]}: '#{match[1]}'"
204
+ # end
205
+ # end
206
+ def grep(string, path_limiter = nil, opts = {})
207
+ self.object('HEAD').grep(string, path_limiter, opts)
208
+ end
209
+
210
+ # updates the repository index using the working directory content
211
+ #
212
+ # @git.add('path/to/file')
213
+ # @git.add(['path/to/file1','path/to/file2'])
214
+ # @git.add(:all => true)
215
+ #
216
+ # options:
217
+ # :all => true
218
+ #
219
+ # @param [String,Array] paths files paths to be added (optional, default='.')
220
+ # @param [Hash] options
221
+ def add(*args)
222
+ if args[0].instance_of?(String) || args[0].instance_of?(Array)
223
+ self.lib.add(args[0],args[1]||{})
224
+ else
225
+ self.lib.add('.', args[0]||{})
226
+ end
227
+ end
228
+
229
+ # removes file(s) from the git repository
230
+ def remove(path = '.', opts = {})
231
+ self.lib.remove(path, opts)
232
+ end
233
+
234
+ # resets the working directory to the provided commitish
235
+ def reset(commitish = nil, opts = {})
236
+ self.lib.reset(commitish, opts)
237
+ end
238
+
239
+ # resets the working directory to the commitish with '--hard'
240
+ def reset_hard(commitish = nil, opts = {})
241
+ opts = {:hard => true}.merge(opts)
242
+ self.lib.reset(commitish, opts)
243
+ end
244
+
245
+ # cleans the working directory
246
+ #
247
+ # options:
248
+ # :force
249
+ # :d
250
+ #
251
+ def clean(opts = {})
252
+ self.lib.clean(opts)
253
+ end
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
+
274
+ # reverts the working directory to the provided commitish.
275
+ # Accepts a range, such as comittish..HEAD
276
+ #
277
+ # options:
278
+ # :no_edit
279
+ #
280
+ def revert(commitish = nil, opts = {})
281
+ self.lib.revert(commitish, opts)
282
+ end
283
+
284
+ # commits all pending changes in the index file to the git repository
285
+ #
286
+ # options:
287
+ # :all
288
+ # :allow_empty
289
+ # :amend
290
+ # :author
291
+ #
292
+ def commit(message, opts = {})
293
+ self.lib.commit(message, opts)
294
+ end
295
+
296
+ # commits all pending changes in the index file to the git repository,
297
+ # but automatically adds all modified files without having to explicitly
298
+ # calling @git.add() on them.
299
+ def commit_all(message, opts = {})
300
+ opts = {:add_all => true}.merge(opts)
301
+ self.lib.commit(message, opts)
302
+ end
303
+
304
+ # checks out a branch as the new git working directory
305
+ def checkout(branch = 'master', opts = {})
306
+ self.lib.checkout(branch, opts)
307
+ end
308
+
309
+ # checks out an old version of a file
310
+ def checkout_file(version, file)
311
+ self.lib.checkout_file(version,file)
312
+ end
313
+
314
+ # fetches changes from a remote branch - this does not modify the working directory,
315
+ # it just gets the changes from the remote if there are any
316
+ def fetch(remote = 'origin', opts={})
317
+ self.lib.fetch(remote, opts)
318
+ end
319
+
320
+ # pushes changes to a remote repository - easiest if this is a cloned repository,
321
+ # otherwise you may have to run something like this first to setup the push parameters:
322
+ #
323
+ # @git.config('remote.remote-name.push', 'refs/heads/master:refs/heads/master')
324
+ #
325
+ def push(remote = 'origin', branch = 'master', opts = {})
326
+ # Small hack to keep backwards compatibility with the 'push(remote, branch, tags)' method signature.
327
+ opts = {:tags => opts} if [true, false].include?(opts)
328
+
329
+ self.lib.push(remote, branch, opts)
330
+ end
331
+
332
+ # merges one or more branches into the current working branch
333
+ #
334
+ # you can specify more than one branch to merge by passing an array of branches
335
+ def merge(branch, message = 'merge')
336
+ self.lib.merge(branch, message)
337
+ end
338
+
339
+ # iterates over the files which are unmerged
340
+ def each_conflict(&block) # :yields: file, your_version, their_version
341
+ self.lib.conflicts(&block)
342
+ end
343
+
344
+ # pulls the given branch from the given remote into the current branch
345
+ #
346
+ # @git.pull # pulls from origin/master
347
+ # @git.pull('upstream') # pulls from upstream/master
348
+ # @git.pull('upstream', 'develope') # pulls from upstream/develop
349
+ #
350
+ def pull(remote='origin', branch='master')
351
+ self.lib.pull(remote, branch)
352
+ end
353
+
354
+ # returns an array of Git:Remote objects
355
+ def remotes
356
+ self.lib.remotes.map { |r| Git::Remote.new(self, r) }
357
+ end
358
+
359
+ # adds a new remote to this repository
360
+ # url can be a git url or a Git::Base object if it's a local reference
361
+ #
362
+ # @git.add_remote('scotts_git', 'git://repo.or.cz/rubygit.git')
363
+ # @git.fetch('scotts_git')
364
+ # @git.merge('scotts_git/master')
365
+ #
366
+ # Options:
367
+ # :fetch => true
368
+ # :track => <branch_name>
369
+ def add_remote(name, url, opts = {})
370
+ url = url.repo.path if url.is_a?(Git::Base)
371
+ self.lib.remote_add(name, url, opts)
372
+ Git::Remote.new(self, name)
373
+ end
374
+
375
+ # removes a remote from this repository
376
+ #
377
+ # @git.remove_remote('scott_git')
378
+ def remove_remote(name)
379
+ self.lib.remote_remove(name)
380
+ end
381
+
382
+ # returns an array of all Git::Tag objects for this repository
383
+ def tags
384
+ self.lib.tags.map { |r| tag(r) }
385
+ end
386
+
387
+ # Creates a new git tag (Git::Tag)
388
+ # Usage:
389
+ # repo.add_tag('tag_name', object_reference)
390
+ # repo.add_tag('tag_name', object_reference, {:options => 'here'})
391
+ # repo.add_tag('tag_name', {:options => 'here'})
392
+ #
393
+ # Options:
394
+ # :a | :annotate -> true
395
+ # :d -> true
396
+ # :f -> true
397
+ # :m | :message -> String
398
+ # :s -> true
399
+ #
400
+ def add_tag(name, *opts)
401
+ self.lib.tag(name, *opts)
402
+ self.tag(name)
403
+ end
404
+
405
+ # deletes a tag
406
+ def delete_tag(name)
407
+ self.lib.tag(name, {:d => true})
408
+ end
409
+
410
+ # creates an archive file of the given tree-ish
411
+ def archive(treeish, file = nil, opts = {})
412
+ self.object(treeish).archive(file, opts)
413
+ end
414
+
415
+ # repacks the repository
416
+ def repack
417
+ self.lib.repack
418
+ end
419
+
420
+ def gc
421
+ self.lib.gc
422
+ end
423
+
424
+ def apply(file)
425
+ if File.exist?(file)
426
+ self.lib.apply(file)
427
+ end
428
+ end
429
+
430
+ def apply_mail(file)
431
+ self.lib.apply_mail(file) if File.exist?(file)
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
442
+
443
+ ## LOWER LEVEL INDEX OPERATIONS ##
444
+
445
+ def with_index(new_index) # :yields: new_index
446
+ old_index = @index
447
+ set_index(new_index, false)
448
+ return_value = yield @index
449
+ set_index(old_index)
450
+ return_value
451
+ end
452
+
453
+ def with_temp_index &blk
454
+ # Workaround for JRUBY, since they handle the TempFile path different.
455
+ # MUST be improved to be safer and OS independent.
456
+ if RUBY_PLATFORM == 'java'
457
+ temp_path = "/tmp/temp-index-#{(0...15).map{ ('a'..'z').to_a[rand(26)] }.join}"
458
+ else
459
+ tempfile = Tempfile.new('temp-index')
460
+ temp_path = tempfile.path
461
+ tempfile.close
462
+ tempfile.unlink
463
+ end
464
+
465
+ with_index(temp_path, &blk)
466
+ end
467
+
468
+ def checkout_index(opts = {})
469
+ self.lib.checkout_index(opts)
470
+ end
471
+
472
+ def read_tree(treeish, opts = {})
473
+ self.lib.read_tree(treeish, opts)
474
+ end
475
+
476
+ def write_tree
477
+ self.lib.write_tree
478
+ end
479
+
480
+ def write_and_commit_tree(opts = {})
481
+ tree = write_tree
482
+ commit_tree(tree, opts)
483
+ end
484
+
485
+ def update_ref(branch, commit)
486
+ branch(branch).update_ref(commit)
487
+ end
488
+
489
+
490
+ def ls_files(location=nil)
491
+ self.lib.ls_files(location)
492
+ end
493
+
494
+ def with_working(work_dir) # :yields: the Git::WorkingDirectory
495
+ return_value = false
496
+ old_working = @working_directory
497
+ set_working(work_dir)
498
+ Dir.chdir work_dir do
499
+ return_value = yield @working_directory
500
+ end
501
+ set_working(old_working)
502
+ return_value
503
+ end
504
+
505
+ def with_temp_working &blk
506
+ tempfile = Tempfile.new("temp-workdir")
507
+ temp_dir = tempfile.path
508
+ tempfile.close
509
+ tempfile.unlink
510
+ Dir.mkdir(temp_dir, 0700)
511
+ with_working(temp_dir, &blk)
512
+ end
513
+
514
+
515
+ # runs git rev-parse to convert the objectish to a full sha
516
+ #
517
+ # @git.revparse("HEAD^^")
518
+ # @git.revparse('v2.4^{tree}')
519
+ # @git.revparse('v2.4:/doc/index.html')
520
+ #
521
+ def revparse(objectish)
522
+ self.lib.revparse(objectish)
523
+ end
524
+
525
+ def ls_tree(objectish)
526
+ self.lib.ls_tree(objectish)
527
+ end
528
+
529
+ def cat_file(objectish)
530
+ self.lib.object_contents(objectish)
531
+ end
532
+
533
+ # returns the name of the branch the working directory is currently on
534
+ def current_branch
535
+ self.lib.branch_current
536
+ end
537
+
538
+ end
539
+
540
+ end