git-ce 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/git/base.rb ADDED
@@ -0,0 +1,555 @@
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
+ def rebase(branch)
340
+ self.lib.rebase(branch)
341
+ end
342
+
343
+ # iterates over the files which are unmerged
344
+ def each_conflict(&block) # :yields: file, your_version, their_version
345
+ self.lib.conflicts(&block)
346
+ end
347
+
348
+ # pulls the given branch from the given remote into the current branch
349
+ #
350
+ # @git.pull # pulls from origin/master
351
+ # @git.pull('upstream') # pulls from upstream/master
352
+ # @git.pull('upstream', 'develope') # pulls from upstream/develop
353
+ #
354
+ def pull(remote='origin', branch='master')
355
+ self.lib.pull(remote, branch)
356
+ end
357
+
358
+ # returns an array of Git:Remote objects
359
+ def remotes
360
+ self.lib.remotes.map { |r| Git::Remote.new(self, r) }
361
+ end
362
+
363
+ # adds a new remote to this repository
364
+ # url can be a git url or a Git::Base object if it's a local reference
365
+ #
366
+ # @git.add_remote('scotts_git', 'git://repo.or.cz/rubygit.git')
367
+ # @git.fetch('scotts_git')
368
+ # @git.merge('scotts_git/master')
369
+ #
370
+ # Options:
371
+ # :fetch => true
372
+ # :track => <branch_name>
373
+ def add_remote(name, url, opts = {})
374
+ url = url.repo.path if url.is_a?(Git::Base)
375
+ self.lib.remote_add(name, url, opts)
376
+ Git::Remote.new(self, name)
377
+ end
378
+
379
+ # sets the url for a remote
380
+ # url can be a git url or a Git::Base object if it's a local reference
381
+ #
382
+ # @git.set_remote_url('scotts_git', 'git://repo.or.cz/rubygit.git')
383
+ #
384
+ def set_remote_url(name, url)
385
+ url = url.repo.path if url.is_a?(Git::Base)
386
+ self.lib.remote_set_url(name, url)
387
+ Git::Remote.new(self, name)
388
+ end
389
+
390
+ # removes a remote from this repository
391
+ #
392
+ # @git.remove_remote('scott_git')
393
+ def remove_remote(name)
394
+ self.lib.remote_remove(name)
395
+ end
396
+
397
+ # returns an array of all Git::Tag objects for this repository
398
+ def tags
399
+ self.lib.tags.map { |r| tag(r) }
400
+ end
401
+
402
+ # Creates a new git tag (Git::Tag)
403
+ # Usage:
404
+ # repo.add_tag('tag_name', object_reference)
405
+ # repo.add_tag('tag_name', object_reference, {:options => 'here'})
406
+ # repo.add_tag('tag_name', {:options => 'here'})
407
+ #
408
+ # Options:
409
+ # :a | :annotate -> true
410
+ # :d -> true
411
+ # :f -> true
412
+ # :m | :message -> String
413
+ # :s -> true
414
+ #
415
+ def add_tag(name, *opts)
416
+ self.lib.tag(name, *opts)
417
+ self.tag(name)
418
+ end
419
+
420
+ # deletes a tag
421
+ def delete_tag(name)
422
+ self.lib.tag(name, {:d => true})
423
+ end
424
+
425
+ # creates an archive file of the given tree-ish
426
+ def archive(treeish, file = nil, opts = {})
427
+ self.object(treeish).archive(file, opts)
428
+ end
429
+
430
+ # repacks the repository
431
+ def repack
432
+ self.lib.repack
433
+ end
434
+
435
+ def gc
436
+ self.lib.gc
437
+ end
438
+
439
+ def apply(file)
440
+ if File.exist?(file)
441
+ self.lib.apply(file)
442
+ end
443
+ end
444
+
445
+ def apply_mail(file)
446
+ self.lib.apply_mail(file) if File.exist?(file)
447
+ end
448
+
449
+ # Shows objects
450
+ #
451
+ # @param [String|NilClass] objectish the target object reference (nil == HEAD)
452
+ # @param [String|NilClass] path the path of the file to be shown
453
+ # @return [String] the object information
454
+ def show(objectish=nil, path=nil)
455
+ self.lib.show(objectish, path)
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 write_and_commit_tree(opts = {})
496
+ tree = write_tree
497
+ commit_tree(tree, opts)
498
+ end
499
+
500
+ def update_ref(branch, commit)
501
+ branch(branch).update_ref(commit)
502
+ end
503
+
504
+
505
+ def ls_files(location=nil)
506
+ self.lib.ls_files(location)
507
+ end
508
+
509
+ def with_working(work_dir) # :yields: the Git::WorkingDirectory
510
+ return_value = false
511
+ old_working = @working_directory
512
+ set_working(work_dir)
513
+ Dir.chdir work_dir do
514
+ return_value = yield @working_directory
515
+ end
516
+ set_working(old_working)
517
+ return_value
518
+ end
519
+
520
+ def with_temp_working &blk
521
+ tempfile = Tempfile.new("temp-workdir")
522
+ temp_dir = tempfile.path
523
+ tempfile.close
524
+ tempfile.unlink
525
+ Dir.mkdir(temp_dir, 0700)
526
+ with_working(temp_dir, &blk)
527
+ end
528
+
529
+
530
+ # runs git rev-parse to convert the objectish to a full sha
531
+ #
532
+ # @git.revparse("HEAD^^")
533
+ # @git.revparse('v2.4^{tree}')
534
+ # @git.revparse('v2.4:/doc/index.html')
535
+ #
536
+ def revparse(objectish)
537
+ self.lib.revparse(objectish)
538
+ end
539
+
540
+ def ls_tree(objectish)
541
+ self.lib.ls_tree(objectish)
542
+ end
543
+
544
+ def cat_file(objectish)
545
+ self.lib.object_contents(objectish)
546
+ end
547
+
548
+ # returns the name of the branch the working directory is currently on
549
+ def current_branch
550
+ self.lib.branch_current
551
+ end
552
+
553
+ end
554
+
555
+ end
data/lib/git/branch.rb ADDED
@@ -0,0 +1,131 @@
1
+ require 'git/path'
2
+
3
+ module Git
4
+
5
+ class Branch < Path
6
+
7
+ attr_accessor :full, :remote, :name
8
+
9
+ def initialize(base, name)
10
+ @full = name
11
+ @base = base
12
+ @gcommit = nil
13
+ @stashes = nil
14
+ @remote, @name = parse_name(name)
15
+ end
16
+
17
+ def gcommit
18
+ @gcommit ||= @base.gcommit(@full)
19
+ @gcommit
20
+ end
21
+
22
+ def stashes
23
+ @stashes ||= Git::Stashes.new(@base)
24
+ end
25
+
26
+ def checkout
27
+ check_if_create
28
+ @base.checkout(@full)
29
+ end
30
+
31
+ def archive(file, opts = {})
32
+ @base.lib.archive(@full, file, opts)
33
+ end
34
+
35
+ # g.branch('new_branch').in_branch do
36
+ # # create new file
37
+ # # do other stuff
38
+ # return true # auto commits and switches back
39
+ # end
40
+ def in_branch (message = 'in branch work')
41
+ old_current = @base.lib.branch_current
42
+ checkout
43
+ if yield
44
+ @base.commit_all(message)
45
+ else
46
+ @base.reset_hard
47
+ end
48
+ @base.checkout(old_current)
49
+ end
50
+
51
+ def create
52
+ check_if_create
53
+ end
54
+
55
+ def delete
56
+ @base.lib.branch_delete(@name)
57
+ end
58
+
59
+ def current
60
+ determine_current
61
+ end
62
+
63
+ def contains?(commit)
64
+ !@base.lib.branch_contains(commit, self.name).empty?
65
+ end
66
+
67
+ def merge(branch = nil, message = nil)
68
+ if branch
69
+ in_branch do
70
+ @base.merge(branch, message)
71
+ false
72
+ end
73
+ # merge a branch into this one
74
+ else
75
+ # merge this branch into the current one
76
+ @base.merge(@name)
77
+ end
78
+ end
79
+
80
+ def rebase(branch)
81
+ @base.rebase(branch)
82
+ end
83
+
84
+
85
+ def update_ref(commit)
86
+ @base.lib.update_ref(@full, commit)
87
+ end
88
+
89
+ def to_a
90
+ [@full]
91
+ end
92
+
93
+ def to_s
94
+ @full
95
+ end
96
+
97
+ private
98
+
99
+ def check_if_create
100
+ @base.lib.branch_new(@name) rescue nil
101
+ end
102
+
103
+ def determine_current
104
+ @base.lib.branch_current == @name
105
+ end
106
+
107
+ # Given a full branch name return an Array containing the remote and branch names.
108
+ #
109
+ # Removes 'remotes' from the beggining of the name (if present).
110
+ # Takes the second part (splittign by '/') as the remote name.
111
+ # Takes the rest as the repo name (can also hold one or more '/').
112
+ #
113
+ # Example:
114
+ # parse_name('master') #=> [nil, 'master']
115
+ # parse_name('origin/master') #=> ['origin', 'master']
116
+ # parse_name('remotes/origin/master') #=> ['origin', 'master']
117
+ # parse_name('origin/master/v2') #=> ['origin', 'master/v2']
118
+ #
119
+ # param [String] name branch full name.
120
+ # return [<Git::Remote,NilClass,String>] an Array containing the remote and branch names.
121
+ def parse_name(name)
122
+ if name.match(/^(?:remotes)?\/([^\/]+)\/(.+)/)
123
+ return [Git::Remote.new(@base, $1), $2]
124
+ end
125
+
126
+ return [nil, name]
127
+ end
128
+
129
+ end
130
+
131
+ end