clc-git 1.2.8

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