ruby-git-lacravate 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/git/base.rb ADDED
@@ -0,0 +1,485 @@
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
+ # :repository
20
+ # :index_file
21
+ #
22
+ def self.init(working_dir, opts = {})
23
+ opts = {
24
+ :working_directory => working_dir,
25
+ :repository => File.join(working_dir, '.git')
26
+ }.merge(opts)
27
+
28
+ FileUtils.mkdir_p(opts[:working_directory]) if opts[:working_directory] && !File.directory?(opts[:working_directory])
29
+
30
+ # run git_init there
31
+ Git::Lib.new(opts).init
32
+
33
+ self.new(opts)
34
+ end
35
+
36
+ # clones a git repository locally
37
+ #
38
+ # repository - http://repo.or.cz/w/sinatra.git
39
+ # name - sinatra
40
+ #
41
+ # options:
42
+ # :repository
43
+ #
44
+ # :bare
45
+ # or
46
+ # :working_directory
47
+ # :index_file
48
+ #
49
+ def self.clone(repository, name, opts = {})
50
+ # run git-clone
51
+ self.new(Git::Lib.new.clone(repository, name, opts))
52
+ end
53
+
54
+ def initialize(options = {})
55
+ if working_dir = options[:working_directory]
56
+ options[:repository] ||= File.join(working_dir, '.git')
57
+ options[:index] ||= File.join(working_dir, '.git', 'index')
58
+ end
59
+ if options[:log]
60
+ @logger = options[:log]
61
+ @logger.info("Starting Git")
62
+ else
63
+ @logger = nil
64
+ end
65
+
66
+ @working_directory = options[:working_directory] ? Git::WorkingDirectory.new(options[:working_directory]) : nil
67
+ @repository = options[:repository] ? Git::Repository.new(options[:repository]) : nil
68
+ @index = options[:index] ? Git::Index.new(options[:index], false) : nil
69
+ end
70
+
71
+
72
+ # returns a reference to the working directory
73
+ # @git.dir.path
74
+ # @git.dir.writeable?
75
+ def dir
76
+ @working_directory
77
+ end
78
+
79
+ # returns reference to the git repository directory
80
+ # @git.dir.path
81
+ def repo
82
+ @repository
83
+ end
84
+
85
+ # returns reference to the git index file
86
+ def index
87
+ @index
88
+ end
89
+
90
+
91
+ def set_working(work_dir, check = true)
92
+ @lib = nil
93
+ @working_directory = Git::WorkingDirectory.new(work_dir.to_s, check)
94
+ end
95
+
96
+ def set_index(index_file, check = true)
97
+ @lib = nil
98
+ @index = Git::Index.new(index_file.to_s, check)
99
+ end
100
+
101
+ # changes current working directory for a block
102
+ # to the git working directory
103
+ #
104
+ # example
105
+ # @git.chdir do
106
+ # # write files
107
+ # @git.add
108
+ # @git.commit('message')
109
+ # end
110
+ def chdir # :yields: the Git::Path
111
+ Dir.chdir(dir.path) do
112
+ yield dir.path
113
+ end
114
+ end
115
+
116
+ # returns the repository size in bytes
117
+ def repo_size
118
+ size = 0
119
+ Dir.chdir(repo.path) do
120
+ (size, dot) = `du -s`.chomp.split
121
+ end
122
+ size.to_i
123
+ end
124
+
125
+ #g.config('user.name', 'Scott Chacon') # sets value
126
+ #g.config('user.email', 'email@email.com') # sets value
127
+ #g.config('user.name') # returns 'Scott Chacon'
128
+ #g.config # returns whole config hash
129
+ def config(name = nil, value = nil)
130
+ if(name && value)
131
+ # set value
132
+ lib.config_set(name, value)
133
+ elsif (name)
134
+ # return value
135
+ lib.config_get(name)
136
+ else
137
+ # return hash
138
+ lib.config_list
139
+ end
140
+ end
141
+
142
+ # factory methods
143
+
144
+ # returns a Git::Object of the appropriate type
145
+ # you can also call @git.gtree('tree'), but that's
146
+ # just for readability. If you call @git.gtree('HEAD') it will
147
+ # still return a Git::Object::Commit object.
148
+ #
149
+ # @git.object calls a factory method that will run a rev-parse
150
+ # on the objectish and determine the type of the object and return
151
+ # an appropriate object for that type
152
+ def object(objectish)
153
+ Git::Object.new(self, objectish)
154
+ end
155
+
156
+ def gtree(objectish)
157
+ Git::Object.new(self, objectish, 'tree')
158
+ end
159
+
160
+ def gcommit(objectish)
161
+ Git::Object.new(self, objectish, 'commit')
162
+ end
163
+
164
+ def gblob(objectish)
165
+ Git::Object.new(self, objectish, 'blob')
166
+ end
167
+
168
+ # returns a Git::Log object with count commits
169
+ def log(count = 30)
170
+ Git::Log.new(self, count)
171
+ end
172
+
173
+ # returns a Git::Status object
174
+ def status
175
+ Git::Status.new(self)
176
+ end
177
+
178
+ # returns a Git::Branches object of all the Git::Branch objects for this repo
179
+ def branches
180
+ Git::Branches.new(self)
181
+ end
182
+
183
+ # returns a Git::Branch object for branch_name
184
+ # -- addition of options noticeably to implement
185
+ # --t switch on 'git branch' command
186
+ def branch(branch_name = 'master', options={})
187
+ #
188
+ Git::Branch.new(self, branch_name, options[:track])
189
+ end
190
+
191
+ # returns +true+ if the branch exists locally
192
+ def is_local_branch?(branch)
193
+ branch_names = self.branches.local.map {|b| b.name}
194
+ branch_names.include?(branch)
195
+ end
196
+
197
+ # returns +true+ if the branch exists remotely
198
+ def is_remote_branch?(branch)
199
+ branch_names = self.branches.local.map {|b| b.name}
200
+ branch_names.include?(branch)
201
+ end
202
+
203
+ # returns +true+ if the branch exists
204
+ def is_branch?(branch)
205
+ branch_names = self.branches.map {|b| b.name}
206
+ branch_names.include?(branch)
207
+ end
208
+
209
+ # returns a Git::Remote object
210
+ def remote(remote_name = 'origin')
211
+ Git::Remote.new(self, remote_name)
212
+ end
213
+
214
+ # this is a convenience method for accessing the class that wraps all the
215
+ # actual 'git' forked system calls. At some point I hope to replace the Git::Lib
216
+ # class with one that uses native methods or libgit C bindings
217
+ def lib
218
+ @lib ||= Git::Lib.new(self, @logger)
219
+ end
220
+
221
+ # will run a grep for 'string' on the HEAD of the git repository
222
+ #
223
+ # to be more surgical in your grep, you can call grep() off a specific
224
+ # git object. for example:
225
+ #
226
+ # @git.object("v2.3").grep('TODO')
227
+ #
228
+ # in any case, it returns a hash of arrays of the type:
229
+ # hsh[tree-ish] = [[line_no, match], [line_no, match2]]
230
+ # hsh[tree-ish] = [[line_no, match], [line_no, match2]]
231
+ #
232
+ # so you might use it like this:
233
+ #
234
+ # @git.grep("TODO").each do |sha, arr|
235
+ # puts "in blob #{sha}:"
236
+ # arr.each do |match|
237
+ # puts "\t line #{match[0]}: '#{match[1]}'"
238
+ # end
239
+ # end
240
+ def grep(string, path_limiter = nil, opts = {})
241
+ self.object('HEAD').grep(string, path_limiter, opts)
242
+ end
243
+
244
+ # returns a Git::Diff object
245
+ def diff(objectish = 'HEAD', obj2 = nil)
246
+ Git::Diff.new(self, objectish, obj2)
247
+ end
248
+
249
+ # adds files from the working directory to the git repository
250
+ def add(path = '.')
251
+ self.lib.add(path)
252
+ end
253
+
254
+ # removes file(s) from the git repository
255
+ def remove(path = '.', opts = {})
256
+ self.lib.remove(path, opts)
257
+ end
258
+
259
+ # resets the working directory to the provided commitish
260
+ def reset(commitish = nil, opts = {})
261
+ self.lib.reset(commitish, opts)
262
+ end
263
+
264
+ # resets the working directory to the commitish with '--hard'
265
+ def reset_hard(commitish = nil, opts = {})
266
+ opts = {:hard => true}.merge(opts)
267
+ self.lib.reset(commitish, opts)
268
+ end
269
+
270
+ # commits all pending changes in the index file to the git repository
271
+ #
272
+ # options:
273
+ # :add_all
274
+ # :allow_empty
275
+ # :author
276
+ def commit(message, opts = {})
277
+ self.lib.commit(message, opts)
278
+ end
279
+
280
+ # commits all pending changes in the index file to the git repository,
281
+ # but automatically adds all modified files without having to explicitly
282
+ # calling @git.add() on them.
283
+ def commit_all(message, opts = {})
284
+ opts = {:add_all => true}.merge(opts)
285
+ self.lib.commit(message, opts)
286
+ end
287
+
288
+ # checks out a branch as the new git working directory
289
+ def checkout(branch = 'master', opts = {})
290
+ self.lib.checkout(branch, opts)
291
+ end
292
+
293
+ # checks out an old version of a file
294
+ def checkout_file(version, file)
295
+ self.lib.checkout_file(version,file)
296
+ end
297
+
298
+ # fetches changes from a remote branch - this does not modify the working directory,
299
+ # it just gets the changes from the remote if there are any
300
+ def fetch(remote = 'origin')
301
+ self.lib.fetch(remote)
302
+ end
303
+
304
+ # pushes changes to a remote repository - easiest if this is a cloned repository,
305
+ # otherwise you may have to run something like this first to setup the push parameters:
306
+ #
307
+ # @git.config('remote.remote-name.push', 'refs/heads/master:refs/heads/master')
308
+ #
309
+ def push(remote = 'origin', branch = 'master', tags = false)
310
+ self.lib.push(remote, branch, tags)
311
+ end
312
+
313
+ # merges one or more branches into the current working branch
314
+ #
315
+ # you can specify more than one branch to merge by passing an array of branches
316
+ def merge(branch, message = 'merge')
317
+ self.lib.merge(branch, message)
318
+ end
319
+
320
+ # iterates over the files which are unmerged
321
+ def each_conflict(&block) # :yields: file, your_version, their_version
322
+ self.lib.conflicts(&block)
323
+ end
324
+
325
+ # fetches a branch from a remote and merges it into the current working branch
326
+ def pull(remote = 'origin', branch = 'master', message = 'origin pull')
327
+ fetch(remote)
328
+ merge(branch, message)
329
+ end
330
+
331
+ # returns an array of Git:Remote objects
332
+ def remotes
333
+ self.lib.remotes.map { |r| Git::Remote.new(self, r) }
334
+ end
335
+
336
+ # adds a new remote to this repository
337
+ # url can be a git url or a Git::Base object if it's a local reference
338
+ #
339
+ # @git.add_remote('scotts_git', 'git://repo.or.cz/rubygit.git')
340
+ # @git.fetch('scotts_git')
341
+ # @git.merge('scotts_git/master')
342
+ #
343
+ def add_remote(name, url, opts = {})
344
+ url = url.repo.path if url.is_a?(Git::Base)
345
+ self.lib.remote_add(name, url, opts)
346
+ Git::Remote.new(self, name)
347
+ end
348
+
349
+ # returns an array of all Git::Tag objects for this repository
350
+ def tags
351
+ self.lib.tags.map { |r| tag(r) }
352
+ end
353
+
354
+ # returns a Git::Tag object
355
+ def tag(tag_name)
356
+ Git::Object.new(self, tag_name, 'tag', true)
357
+ end
358
+
359
+ # creates a new git tag (Git::Tag)
360
+ def add_tag(tag_name)
361
+ self.lib.tag(tag_name)
362
+ tag(tag_name)
363
+ end
364
+
365
+ # creates an archive file of the given tree-ish
366
+ def archive(treeish, file = nil, opts = {})
367
+ self.object(treeish).archive(file, opts)
368
+ end
369
+
370
+ # repacks the repository
371
+ def repack
372
+ self.lib.repack
373
+ end
374
+
375
+ def gc
376
+ self.lib.gc
377
+ end
378
+
379
+ def apply(file)
380
+ if File.exists?(file)
381
+ self.lib.apply(file)
382
+ end
383
+ end
384
+
385
+ def apply_mail(file)
386
+ self.lib.apply_mail(file) if File.exists?(file)
387
+ end
388
+
389
+ ## LOWER LEVEL INDEX OPERATIONS ##
390
+
391
+ def with_index(new_index) # :yields: new_index
392
+ old_index = @index
393
+ set_index(new_index, false)
394
+ return_value = yield @index
395
+ set_index(old_index)
396
+ return_value
397
+ end
398
+
399
+ def with_temp_index &blk
400
+ tempfile = Tempfile.new('temp-index')
401
+ temp_path = tempfile.path
402
+ tempfile.unlink
403
+ with_index(temp_path, &blk)
404
+ end
405
+
406
+ def checkout_index(opts = {})
407
+ self.lib.checkout_index(opts)
408
+ end
409
+
410
+ def read_tree(treeish, opts = {})
411
+ self.lib.read_tree(treeish, opts)
412
+ end
413
+
414
+ def write_tree
415
+ self.lib.write_tree
416
+ end
417
+
418
+ def commit_tree(tree = nil, opts = {})
419
+ Git::Object::Commit.new(self, self.lib.commit_tree(tree, opts))
420
+ end
421
+
422
+ def write_and_commit_tree(opts = {})
423
+ tree = write_tree
424
+ commit_tree(tree, opts)
425
+ end
426
+
427
+ def update_ref(branch, commit)
428
+ branch(branch).update_ref(commit)
429
+ end
430
+
431
+
432
+ def ls_files(location=nil)
433
+ self.lib.ls_files(location)
434
+ end
435
+
436
+ def with_working(work_dir) # :yields: the Git::WorkingDirectory
437
+ return_value = false
438
+ old_working = @working_directory
439
+ set_working(work_dir)
440
+ Dir.chdir work_dir do
441
+ return_value = yield @working_directory
442
+ end
443
+ set_working(old_working)
444
+ return_value
445
+ end
446
+
447
+ def with_temp_working &blk
448
+ tempfile = Tempfile.new("temp-workdir")
449
+ temp_dir = tempfile.path
450
+ tempfile.unlink
451
+ Dir.mkdir(temp_dir, 0700)
452
+ with_working(temp_dir, &blk)
453
+ end
454
+
455
+
456
+ # runs git rev-parse to convert the objectish to a full sha
457
+ #
458
+ # @git.revparse("HEAD^^")
459
+ # @git.revparse('v2.4^{tree}')
460
+ # @git.revparse('v2.4:/doc/index.html')
461
+ #
462
+ def revparse(objectish)
463
+ self.lib.revparse(objectish)
464
+ end
465
+
466
+ # -- addition of options noticeably to implement
467
+ # a recursive ls_tree
468
+ def ls_tree(objectish, options={})
469
+ #
470
+ self.lib.ls_tree(objectish, options)
471
+ end
472
+
473
+ def cat_file(objectish)
474
+ self.lib.object_contents(objectish)
475
+ end
476
+
477
+ # returns the name of the branch the working directory is currently on
478
+ def current_branch
479
+ self.lib.branch_current
480
+ end
481
+
482
+
483
+ end
484
+
485
+ end
data/lib/git/branch.rb ADDED
@@ -0,0 +1,111 @@
1
+ module Git
2
+ class Branch < Path
3
+
4
+ attr_accessor :full, :remote, :name
5
+
6
+ # -- addition of track parameter for --t switch
7
+ def initialize(base, name, track=nil)
8
+ #
9
+ @remote = nil
10
+ @full = name
11
+ @base = base
12
+ @gcommit = nil
13
+ @stashes = nil
14
+
15
+ # -- addition of track attribute to implement
16
+ # --t switch
17
+ @track = track
18
+ name, remote = name.split(' ').first.split('/').reverse
19
+ if remote
20
+ @remote = Git::Remote.new(@base, remote)
21
+ name = "#{remote}/#{name}"
22
+ end
23
+ @name = name
24
+ #
25
+ end
26
+
27
+ def gcommit
28
+ @gcommit ||= @base.gcommit(@full)
29
+ @gcommit
30
+ end
31
+
32
+ def stashes
33
+ @stashes ||= Git::Stashes.new(@base)
34
+ end
35
+
36
+ def checkout
37
+ check_if_create
38
+ @base.checkout(@full)
39
+ end
40
+
41
+ def archive(file, opts = {})
42
+ @base.lib.archive(@full, file, opts)
43
+ end
44
+
45
+ # g.branch('new_branch').in_branch do
46
+ # # create new file
47
+ # # do other stuff
48
+ # return true # auto commits and switches back
49
+ # end
50
+ def in_branch (message = 'in branch work')
51
+ old_current = @base.lib.branch_current
52
+ checkout
53
+ if yield
54
+ @base.commit_all(message)
55
+ else
56
+ @base.reset_hard
57
+ end
58
+ @base.checkout(old_current)
59
+ end
60
+
61
+ def create
62
+ check_if_create
63
+ end
64
+
65
+ def delete
66
+ @base.lib.branch_delete(@name)
67
+ end
68
+
69
+ def current
70
+ determine_current
71
+ end
72
+
73
+ def merge(branch = nil, message = nil)
74
+ if branch
75
+ in_branch do
76
+ @base.merge(branch, message)
77
+ false
78
+ end
79
+ # merge a branch into this one
80
+ else
81
+ # merge this branch into the current one
82
+ @base.merge(@name)
83
+ end
84
+ end
85
+
86
+ def update_ref(commit)
87
+ @base.lib.update_ref(@full, commit)
88
+ end
89
+
90
+ def to_a
91
+ [@full]
92
+ end
93
+
94
+ def to_s
95
+ @full
96
+ end
97
+
98
+ private
99
+
100
+ def check_if_create
101
+ # -- addition of track attribute
102
+ @base.lib.branch_new(@name, @track) rescue nil
103
+ #
104
+ end
105
+
106
+ def determine_current
107
+ @base.lib.branch_current == @name
108
+ end
109
+
110
+ end
111
+ end
@@ -0,0 +1,48 @@
1
+ module Git
2
+
3
+ # object that holds all the available branches
4
+ class Branches
5
+ include Enumerable
6
+
7
+ def initialize(base)
8
+ @branches = {}
9
+
10
+ @base = base
11
+
12
+ @base.lib.branches_all.each do |b|
13
+ @branches[b[0]] = Git::Branch.new(@base, b[0])
14
+ end
15
+ end
16
+
17
+ def local
18
+ self.select { |b| !b.remote }
19
+ end
20
+
21
+ def remote
22
+ self.select { |b| b.remote }
23
+ end
24
+
25
+ # array like methods
26
+
27
+ def size
28
+ @branches.size
29
+ end
30
+
31
+ def each(&block)
32
+ @branches.values.each(&block)
33
+ end
34
+
35
+ def [](symbol)
36
+ @branches[symbol.to_s]
37
+ end
38
+
39
+ def to_s
40
+ out = ''
41
+ @branches.each do |k, b|
42
+ out << (b.current ? '* ' : ' ') << b.to_s << "\n"
43
+ end
44
+ out
45
+ end
46
+
47
+ end
48
+ end