schacon-grit 0.9.4 → 1.1.1
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/API.txt +101 -0
- data/History.txt +42 -2
- data/LICENSE +22 -0
- data/README.md +210 -0
- data/VERSION.yml +4 -0
- data/lib/grit.rb +17 -5
- data/lib/grit/blame.rb +61 -0
- data/lib/grit/blob.rb +11 -2
- data/lib/grit/commit.rb +44 -31
- data/lib/grit/commit_stats.rb +26 -2
- data/lib/grit/diff.rb +8 -8
- data/lib/grit/git-ruby.rb +87 -6
- data/lib/grit/git-ruby/git_object.rb +29 -6
- data/lib/grit/git-ruby/internal/{mmap.rb → file_window.rb} +2 -2
- data/lib/grit/git-ruby/internal/loose.rb +6 -6
- data/lib/grit/git-ruby/internal/pack.rb +22 -20
- data/lib/grit/git-ruby/object.rb +8 -2
- data/lib/grit/git-ruby/repository.rb +33 -24
- data/lib/grit/git.rb +189 -11
- data/lib/grit/index.rb +15 -14
- data/lib/grit/merge.rb +45 -0
- data/lib/grit/ref.rb +8 -29
- data/lib/grit/repo.rb +144 -21
- data/lib/grit/ruby1.9.rb +7 -0
- data/lib/grit/submodule.rb +5 -1
- data/lib/grit/tag.rb +22 -66
- data/lib/grit/tree.rb +20 -1
- metadata +32 -47
- data/Manifest.txt +0 -71
- data/README.txt +0 -213
- data/Rakefile +0 -29
- data/grit.gemspec +0 -62
- data/lib/grit/head.rb +0 -83
- data/test/test_actor.rb +0 -35
- data/test/test_blob.rb +0 -79
- data/test/test_commit.rb +0 -190
- data/test/test_config.rb +0 -58
- data/test/test_diff.rb +0 -18
- data/test/test_git.rb +0 -64
- data/test/test_grit.rb +0 -32
- data/test/test_head.rb +0 -47
- data/test/test_real.rb +0 -19
- data/test/test_reality.rb +0 -17
- data/test/test_remote.rb +0 -14
- data/test/test_repo.rb +0 -285
- data/test/test_tag.rb +0 -25
- data/test/test_tree.rb +0 -96
data/lib/grit/index.rb
CHANGED
@@ -29,12 +29,20 @@ module Grit
|
|
29
29
|
current[filename] = data
|
30
30
|
end
|
31
31
|
|
32
|
+
# Sets the current tree
|
33
|
+
# +tree+ the branch/tag/sha... to use - a string
|
34
|
+
#
|
35
|
+
# Returns index (self)
|
32
36
|
def read_tree(tree)
|
33
37
|
self.current_tree = self.repo.tree(tree)
|
34
38
|
end
|
35
39
|
|
36
40
|
# Commit the contents of the index
|
37
|
-
# +message+ is the commit message
|
41
|
+
# +message+ is the commit message [nil]
|
42
|
+
# +parents+ is one or more commits to attach this commit to to form a new head [nil]
|
43
|
+
# +actor+ is the details of the user making the commit [nil]
|
44
|
+
# +last_tree+ is a tree to compare with - to avoid making empty commits [nil]
|
45
|
+
# +head+ is the branch to write this head to [master]
|
38
46
|
#
|
39
47
|
# Returns a String of the SHA1 of the commit
|
40
48
|
def commit(message, parents = nil, actor = nil, last_tree = nil, head = 'master')
|
@@ -62,16 +70,9 @@ module Grit
|
|
62
70
|
contents << ''
|
63
71
|
contents << message
|
64
72
|
|
65
|
-
commit_sha1 = self.repo.git.
|
66
|
-
|
67
|
-
# self.repo.git.update_ref({}, 'HEAD', commit_sha1)
|
68
|
-
ref_heads = File.join(self.repo.path, 'refs', 'heads')
|
69
|
-
FileUtils.mkdir_p(ref_heads)
|
70
|
-
File.open(File.join(ref_heads, head), 'w') do |f|
|
71
|
-
f.write(commit_sha1)
|
72
|
-
end if commit_sha1
|
73
|
+
commit_sha1 = self.repo.git.put_raw_object(contents.join("\n"), 'commit')
|
73
74
|
|
74
|
-
commit_sha1
|
75
|
+
self.repo.update_ref(head, commit_sha1)
|
75
76
|
end
|
76
77
|
|
77
78
|
# Recursively write a tree to the index
|
@@ -92,12 +93,12 @@ module Grit
|
|
92
93
|
# overwrite with new tree contents
|
93
94
|
tree.each do |k, v|
|
94
95
|
case v
|
95
|
-
when String
|
96
|
+
when String
|
96
97
|
sha = write_blob(v)
|
97
98
|
sha = [sha].pack("H*")
|
98
99
|
str = "%s %s\0%s" % ['100644', k, sha]
|
99
100
|
tree_contents[k] = str
|
100
|
-
when Hash
|
101
|
+
when Hash
|
101
102
|
ctree = now_tree/k if now_tree
|
102
103
|
sha = write_tree(v, ctree)
|
103
104
|
sha = [sha].pack("H*")
|
@@ -106,7 +107,7 @@ module Grit
|
|
106
107
|
end
|
107
108
|
end
|
108
109
|
tr = tree_contents.sort.map { |k, v| v }.join('')
|
109
|
-
self.repo.git.
|
110
|
+
self.repo.git.put_raw_object(tr, 'tree')
|
110
111
|
end
|
111
112
|
|
112
113
|
# Write the blob to the index
|
@@ -114,7 +115,7 @@ module Grit
|
|
114
115
|
#
|
115
116
|
# Returns the SHA1 String of the blob
|
116
117
|
def write_blob(data)
|
117
|
-
self.repo.git.
|
118
|
+
self.repo.git.put_raw_object(data, 'blob')
|
118
119
|
end
|
119
120
|
end # Index
|
120
121
|
|
data/lib/grit/merge.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
module Grit
|
2
|
+
|
3
|
+
class Merge
|
4
|
+
|
5
|
+
STATUS_BOTH = 'both'
|
6
|
+
STATUS_OURS = 'ours'
|
7
|
+
STATUS_THEIRS = 'theirs'
|
8
|
+
|
9
|
+
attr_reader :conflicts, :text, :sections
|
10
|
+
|
11
|
+
def initialize(str)
|
12
|
+
status = STATUS_BOTH
|
13
|
+
|
14
|
+
section = 1
|
15
|
+
@conflicts = 0
|
16
|
+
@text = {}
|
17
|
+
|
18
|
+
lines = str.split("\n")
|
19
|
+
lines.each do |line|
|
20
|
+
if /^<<<<<<< (.*?)/.match(line)
|
21
|
+
status = STATUS_OURS
|
22
|
+
@conflicts += 1
|
23
|
+
section += 1
|
24
|
+
elsif line == '======='
|
25
|
+
status = STATUS_THEIRS
|
26
|
+
elsif /^>>>>>>> (.*?)/.match(line)
|
27
|
+
status = STATUS_BOTH
|
28
|
+
section += 1
|
29
|
+
else
|
30
|
+
@text[section] ||= {}
|
31
|
+
@text[section][status] ||= []
|
32
|
+
@text[section][status] << line
|
33
|
+
end
|
34
|
+
end
|
35
|
+
@text = @text.values
|
36
|
+
@sections = @text.size
|
37
|
+
end
|
38
|
+
|
39
|
+
# Pretty object inspection
|
40
|
+
def inspect
|
41
|
+
%Q{#<Grit::Merge}
|
42
|
+
end
|
43
|
+
end # Merge
|
44
|
+
|
45
|
+
end # Grit
|
data/lib/grit/ref.rb
CHANGED
@@ -9,32 +9,13 @@ module Grit
|
|
9
9
|
# +options+ is a Hash of options
|
10
10
|
#
|
11
11
|
# Returns Grit::Ref[] (baked)
|
12
|
-
def find_all(repo, options = {})
|
13
|
-
refs =
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
if m = /^(\w{40}) (.*?)$/.match(line)
|
19
|
-
next if !Regexp.new('^' + prefix).match(m[2])
|
20
|
-
name = m[2].sub("#{prefix}/", '')
|
21
|
-
commit = Commit.create(repo, :id => m[1])
|
22
|
-
refs << self.new(name, commit)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
files = Dir.glob(prefix + '/**/*')
|
28
|
-
files.each do |ref|
|
29
|
-
next if !File.file?(ref)
|
30
|
-
id = File.read(ref).chomp
|
31
|
-
name = ref.sub("#{prefix}/", '')
|
32
|
-
commit = Commit.create(repo, :id => id)
|
33
|
-
refs << self.new(name, commit)
|
34
|
-
end
|
12
|
+
def find_all(repo, options = {})
|
13
|
+
refs = repo.git.refs(options, prefix)
|
14
|
+
refs.split("\n").map do |ref|
|
15
|
+
name, id = *ref.split(' ')
|
16
|
+
commit = Commit.create(repo, :id => id)
|
17
|
+
self.new(name, commit)
|
35
18
|
end
|
36
|
-
|
37
|
-
refs
|
38
19
|
end
|
39
20
|
|
40
21
|
protected
|
@@ -80,7 +61,7 @@ module Grit
|
|
80
61
|
#
|
81
62
|
# Returns Grit::Head (baked)
|
82
63
|
def self.current(repo, options = {})
|
83
|
-
head =
|
64
|
+
head = repo.git.fs_read('HEAD').chomp
|
84
65
|
if /ref: refs\/heads\/(.*)/.match(head)
|
85
66
|
self.new($1, repo.git.rev_parse(options, 'HEAD'))
|
86
67
|
end
|
@@ -88,8 +69,6 @@ module Grit
|
|
88
69
|
|
89
70
|
end # Head
|
90
71
|
|
91
|
-
class Tag < Ref ; end
|
92
|
-
|
93
72
|
class Remote < Ref; end
|
94
|
-
|
73
|
+
|
95
74
|
end # Grit
|
data/lib/grit/repo.rb
CHANGED
@@ -13,6 +13,7 @@ module Grit
|
|
13
13
|
|
14
14
|
# Create a new Repo instance
|
15
15
|
# +path+ is the path to either the root git directory or the bare git repo
|
16
|
+
# +options+ :is_bare force to load a bare repo
|
16
17
|
#
|
17
18
|
# Examples
|
18
19
|
# g = Repo.new("/Users/tom/dev/grit")
|
@@ -37,7 +38,8 @@ module Grit
|
|
37
38
|
|
38
39
|
self.git = Git.new(self.path)
|
39
40
|
end
|
40
|
-
|
41
|
+
|
42
|
+
# Does nothing yet...
|
41
43
|
def self.init(path)
|
42
44
|
# !! TODO !!
|
43
45
|
# create directory
|
@@ -49,8 +51,13 @@ module Grit
|
|
49
51
|
#
|
50
52
|
# Returns String
|
51
53
|
def description
|
52
|
-
|
54
|
+
self.git.fs_read('description').chomp
|
55
|
+
end
|
56
|
+
|
57
|
+
def blame(file, commit = nil)
|
58
|
+
Blame.new(self, file, commit)
|
53
59
|
end
|
60
|
+
|
54
61
|
|
55
62
|
# An array of Head objects representing the branch heads in
|
56
63
|
# this repo
|
@@ -62,10 +69,14 @@ module Grit
|
|
62
69
|
|
63
70
|
alias_method :branches, :heads
|
64
71
|
|
65
|
-
def
|
72
|
+
def get_head(head_name)
|
66
73
|
heads.find { |h| h.name == head_name }
|
67
74
|
end
|
68
75
|
|
76
|
+
def is_head?(head_name)
|
77
|
+
get_head(head_name)
|
78
|
+
end
|
79
|
+
|
69
80
|
# Object reprsenting the current repo head.
|
70
81
|
#
|
71
82
|
# Returns Grit::Head (baked)
|
@@ -93,7 +104,7 @@ module Grit
|
|
93
104
|
self.git.add({}, *files.flatten)
|
94
105
|
end
|
95
106
|
|
96
|
-
#
|
107
|
+
# Remove files from the index
|
97
108
|
def remove(*files)
|
98
109
|
self.git.rm({}, *files.flatten)
|
99
110
|
end
|
@@ -128,6 +139,34 @@ module Grit
|
|
128
139
|
def remotes
|
129
140
|
Remote.find_all(self)
|
130
141
|
end
|
142
|
+
|
143
|
+
def remote_list
|
144
|
+
self.git.list_remotes
|
145
|
+
end
|
146
|
+
|
147
|
+
def remote_add(name, url)
|
148
|
+
self.git.remote({}, 'add', name, url)
|
149
|
+
end
|
150
|
+
|
151
|
+
def remote_fetch(name)
|
152
|
+
self.git.fetch({}, name)
|
153
|
+
end
|
154
|
+
|
155
|
+
# takes an array of remote names and last pushed dates
|
156
|
+
# fetches from all of the remotes where the local fetch
|
157
|
+
# date is earlier than the passed date, then records the
|
158
|
+
# last fetched date
|
159
|
+
#
|
160
|
+
# { 'origin' => date,
|
161
|
+
# 'peter => date,
|
162
|
+
# }
|
163
|
+
def remotes_fetch_needed(remotes)
|
164
|
+
remotes.each do |remote, date|
|
165
|
+
# TODO: check against date
|
166
|
+
self.remote_fetch(remote)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
131
170
|
|
132
171
|
# An array of Ref objects representing the refs in
|
133
172
|
# this repo
|
@@ -198,6 +237,53 @@ module Grit
|
|
198
237
|
Commit.find_all(self, id, options).first
|
199
238
|
end
|
200
239
|
|
240
|
+
# Returns a list of commits that is in +other_repo+ but not in self
|
241
|
+
#
|
242
|
+
# Returns Grit::Commit[]
|
243
|
+
def commit_deltas_from(other_repo, ref = "master", other_ref = "master")
|
244
|
+
# TODO: we should be able to figure out the branch point, rather than
|
245
|
+
# rev-list'ing the whole thing
|
246
|
+
repo_refs = self.git.rev_list({}, ref).strip.split("\n")
|
247
|
+
other_repo_refs = other_repo.git.rev_list({}, other_ref).strip.split("\n")
|
248
|
+
|
249
|
+
(other_repo_refs - repo_refs).map do |ref|
|
250
|
+
Commit.find_all(other_repo, ref, {:max_count => 1}).first
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def objects(refs)
|
255
|
+
Grit.no_quote = true
|
256
|
+
obj = self.git.rev_list({:objects => true}, refs).split("\n").map { |a| a[0, 40] }
|
257
|
+
Grit.no_quote = false
|
258
|
+
obj
|
259
|
+
end
|
260
|
+
|
261
|
+
def objects_between(ref1, ref2 = nil)
|
262
|
+
if ref2
|
263
|
+
refs = "#{ref2}..#{ref1}"
|
264
|
+
else
|
265
|
+
refs = ref1
|
266
|
+
end
|
267
|
+
self.objects(refs)
|
268
|
+
end
|
269
|
+
|
270
|
+
def diff_objects(commit_sha, parents = true)
|
271
|
+
revs = []
|
272
|
+
Grit.no_quote = true
|
273
|
+
if parents
|
274
|
+
# PARENTS:
|
275
|
+
cmd = "-r -t -m #{commit_sha}"
|
276
|
+
revs = self.git.diff_tree({}, cmd).strip.split("\n").map{ |a| r = a.split(' '); r[3] if r[1] != '160000' }
|
277
|
+
else
|
278
|
+
# NO PARENTS:
|
279
|
+
cmd = "-r -t #{commit_sha}"
|
280
|
+
revs = self.git.method_missing('ls-tree', {}, "-r -t #{commit_sha}").split("\n").map{ |a| a.split("\t").first.split(' ')[2] }
|
281
|
+
end
|
282
|
+
revs << self.commit(commit_sha).tree.id
|
283
|
+
Grit.no_quote = false
|
284
|
+
return revs.uniq.compact
|
285
|
+
end
|
286
|
+
|
201
287
|
# The Tree object for the given treeish reference
|
202
288
|
# +treeish+ is the reference (default 'master')
|
203
289
|
# +paths+ is an optional Array of directory paths to restrict the tree (deafult [])
|
@@ -255,20 +341,31 @@ module Grit
|
|
255
341
|
# Returns Grit::Repo (the newly created repo)
|
256
342
|
def self.init_bare(path, git_options = {}, repo_options = {})
|
257
343
|
git = Git.new(path)
|
344
|
+
git.fs_mkdir('..')
|
258
345
|
git.init(git_options)
|
259
346
|
self.new(path, repo_options)
|
260
347
|
end
|
348
|
+
|
349
|
+
def self.init_bare_or_open(path, git_options = {}, repo_options = {})
|
350
|
+
git = Git.new(path)
|
351
|
+
if !git.exist?
|
352
|
+
git.fs_mkdir(path)
|
353
|
+
git.init(git_options)
|
354
|
+
end
|
355
|
+
self.new(path, repo_options)
|
356
|
+
end
|
261
357
|
|
262
358
|
# Fork a bare git repository from this repo
|
263
359
|
# +path+ is the full path of the new repo (traditionally ends with /<name>.git)
|
264
|
-
# +options+ is any additional options to the git clone command
|
360
|
+
# +options+ is any additional options to the git clone command (:bare and :shared are true by default)
|
265
361
|
#
|
266
362
|
# Returns Grit::Repo (the newly forked repo)
|
267
363
|
def fork_bare(path, options = {})
|
268
364
|
default_options = {:bare => true, :shared => true}
|
269
365
|
real_options = default_options.merge(options)
|
366
|
+
Git.new(path).fs_mkdir('..')
|
270
367
|
self.git.clone(real_options, self.path, path)
|
271
|
-
|
368
|
+
self.new(path)
|
272
369
|
end
|
273
370
|
|
274
371
|
# Archive the given treeish
|
@@ -310,23 +407,30 @@ module Grit
|
|
310
407
|
def archive_tar_gz(treeish = 'master', prefix = nil)
|
311
408
|
options = {}
|
312
409
|
options[:prefix] = prefix if prefix
|
313
|
-
self.git.archive(options, treeish, "| gzip")
|
410
|
+
self.git.archive(options, treeish, "| gzip -n")
|
314
411
|
end
|
315
412
|
|
316
|
-
#
|
317
|
-
|
413
|
+
# Write an archive directly to a file
|
414
|
+
# +treeish+ is the treeish name/id (default 'master')
|
415
|
+
# +prefix+ is the optional prefix (default nil)
|
416
|
+
# +filename+ is the name of the file (default 'archive.tar.gz')
|
417
|
+
# +format+ is the optional format (default nil)
|
418
|
+
# +pipe+ is the command to run the output through (default 'gzip')
|
419
|
+
#
|
420
|
+
# Returns nothing
|
421
|
+
def archive_to_file(treeish = 'master', prefix = nil, filename = 'archive.tar.gz', format = nil, pipe = "gzip")
|
318
422
|
options = {}
|
319
423
|
options[:prefix] = prefix if prefix
|
320
|
-
|
424
|
+
options[:format] = format if format
|
425
|
+
self.git.archive(options, treeish, "| #{pipe} > #{filename}")
|
321
426
|
end
|
322
427
|
|
323
|
-
|
324
428
|
# Enable git-daemon serving of this repository by writing the
|
325
429
|
# git-daemon-export-ok file to its git directory
|
326
430
|
#
|
327
431
|
# Returns nothing
|
328
432
|
def enable_daemon_serve
|
329
|
-
|
433
|
+
self.git.fs_write(DAEMON_EXPORT_FILE, '')
|
330
434
|
end
|
331
435
|
|
332
436
|
# Disable git-daemon serving of this repository by ensuring there is no
|
@@ -334,17 +438,20 @@ module Grit
|
|
334
438
|
#
|
335
439
|
# Returns nothing
|
336
440
|
def disable_daemon_serve
|
337
|
-
|
441
|
+
self.git.fs_delete(DAEMON_EXPORT_FILE)
|
442
|
+
end
|
443
|
+
|
444
|
+
def gc_auto
|
445
|
+
self.git.gc({:auto => true})
|
338
446
|
end
|
339
447
|
|
340
448
|
# The list of alternates for this repo
|
341
449
|
#
|
342
450
|
# Returns Array[String] (pathnames of alternates)
|
343
451
|
def alternates
|
344
|
-
alternates_path =
|
345
|
-
|
346
|
-
|
347
|
-
File.read(alternates_path).strip.split("\n")
|
452
|
+
alternates_path = "objects/info/alternates"
|
453
|
+
if self.git.fs_exist?(alternates_path)
|
454
|
+
self.git.fs_read(alternates_path).strip.split("\n")
|
348
455
|
else
|
349
456
|
[]
|
350
457
|
end
|
@@ -362,11 +469,9 @@ module Grit
|
|
362
469
|
end
|
363
470
|
|
364
471
|
if alts.empty?
|
365
|
-
|
472
|
+
self.git.fs_write('objects/info/alternates', '')
|
366
473
|
else
|
367
|
-
|
368
|
-
f.write alts.join("\n")
|
369
|
-
end
|
474
|
+
self.git.fs_write('objects/info/alternates', alts.join("\n"))
|
370
475
|
end
|
371
476
|
end
|
372
477
|
|
@@ -378,6 +483,24 @@ module Grit
|
|
378
483
|
Index.new(self)
|
379
484
|
end
|
380
485
|
|
486
|
+
def update_ref(head, commit_sha)
|
487
|
+
return nil if !commit_sha || (commit_sha.size != 40)
|
488
|
+
self.git.fs_write("refs/heads/#{head}", commit_sha)
|
489
|
+
commit_sha
|
490
|
+
end
|
491
|
+
|
492
|
+
# Rename the current repository directory.
|
493
|
+
# +name+ is the new name
|
494
|
+
#
|
495
|
+
# Returns nothing
|
496
|
+
def rename(name)
|
497
|
+
if @bare
|
498
|
+
self.git.fs_move('/', "../#{name}")
|
499
|
+
else
|
500
|
+
self.git.fs_move('/', "../../#{name}")
|
501
|
+
end
|
502
|
+
end
|
503
|
+
|
381
504
|
# Pretty object inspection
|
382
505
|
def inspect
|
383
506
|
%Q{#<Grit::Repo "#{@path}">}
|