davetron5000-grit 1.1.2

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.
Files changed (70) hide show
  1. data/API.txt +101 -0
  2. data/History.txt +49 -0
  3. data/LICENSE +22 -0
  4. data/README.md +216 -0
  5. data/VERSION.yml +4 -0
  6. data/examples/ex_add_commit.rb +13 -0
  7. data/examples/ex_index.rb +14 -0
  8. data/lib/grit/actor.rb +41 -0
  9. data/lib/grit/blame.rb +61 -0
  10. data/lib/grit/blob.rb +126 -0
  11. data/lib/grit/commit.rb +242 -0
  12. data/lib/grit/commit_stats.rb +128 -0
  13. data/lib/grit/config.rb +44 -0
  14. data/lib/grit/diff.rb +70 -0
  15. data/lib/grit/errors.rb +7 -0
  16. data/lib/grit/git-ruby/commit_db.rb +52 -0
  17. data/lib/grit/git-ruby/file_index.rb +193 -0
  18. data/lib/grit/git-ruby/git_object.rb +350 -0
  19. data/lib/grit/git-ruby/internal/file_window.rb +58 -0
  20. data/lib/grit/git-ruby/internal/loose.rb +137 -0
  21. data/lib/grit/git-ruby/internal/pack.rb +382 -0
  22. data/lib/grit/git-ruby/internal/raw_object.rb +37 -0
  23. data/lib/grit/git-ruby/object.rb +325 -0
  24. data/lib/grit/git-ruby/repository.rb +736 -0
  25. data/lib/grit/git-ruby.rb +186 -0
  26. data/lib/grit/git.rb +140 -0
  27. data/lib/grit/index.rb +122 -0
  28. data/lib/grit/lazy.rb +33 -0
  29. data/lib/grit/merge.rb +45 -0
  30. data/lib/grit/ref.rb +99 -0
  31. data/lib/grit/repo.rb +447 -0
  32. data/lib/grit/ruby1.9.rb +7 -0
  33. data/lib/grit/status.rb +151 -0
  34. data/lib/grit/submodule.rb +88 -0
  35. data/lib/grit/tag.rb +66 -0
  36. data/lib/grit/tree.rb +123 -0
  37. data/lib/grit.rb +68 -0
  38. data/lib/open3_detach.rb +46 -0
  39. data/test/bench/benchmarks.rb +126 -0
  40. data/test/helper.rb +18 -0
  41. data/test/profile.rb +21 -0
  42. data/test/suite.rb +6 -0
  43. data/test/test_actor.rb +51 -0
  44. data/test/test_blame.rb +32 -0
  45. data/test/test_blame_tree.rb +33 -0
  46. data/test/test_blob.rb +83 -0
  47. data/test/test_commit.rb +206 -0
  48. data/test/test_commit_stats.rb +33 -0
  49. data/test/test_commit_write.rb +54 -0
  50. data/test/test_config.rb +58 -0
  51. data/test/test_diff.rb +18 -0
  52. data/test/test_file_index.rb +56 -0
  53. data/test/test_git.rb +84 -0
  54. data/test/test_grit.rb +32 -0
  55. data/test/test_head.rb +47 -0
  56. data/test/test_index_status.rb +40 -0
  57. data/test/test_merge.rb +17 -0
  58. data/test/test_raw.rb +16 -0
  59. data/test/test_real.rb +19 -0
  60. data/test/test_reality.rb +17 -0
  61. data/test/test_remote.rb +14 -0
  62. data/test/test_repo.rb +347 -0
  63. data/test/test_rubygit.rb +188 -0
  64. data/test/test_rubygit_alt.rb +40 -0
  65. data/test/test_rubygit_index.rb +76 -0
  66. data/test/test_rubygit_iv2.rb +28 -0
  67. data/test/test_submodule.rb +69 -0
  68. data/test/test_tag.rb +67 -0
  69. data/test/test_tree.rb +101 -0
  70. metadata +141 -0
@@ -0,0 +1,193 @@
1
+ # this implements a file-based 'file index', an simple index of
2
+ # all of the reachable commits in a repo, along with the parents
3
+ # and which files were modified during each commit
4
+ #
5
+ # this class looks for a file named '[.git]/file-index', generated via:
6
+ #
7
+ # git log --pretty=oneline --name-only --parents --reverse --all > file-index
8
+ #
9
+ # for this to work properly, you'll want to add the following as a post-receive hook
10
+ # to keep the index up to date
11
+ #
12
+ # git log --pretty=oneline --name-only --parents --reverse [old-rev]..[new-rev] >> file-index
13
+ #
14
+ module Grit
15
+ module GitRuby
16
+
17
+ class FileIndex
18
+
19
+ class IndexFileNotFound < StandardError
20
+ end
21
+
22
+ class UnsupportedRef < StandardError
23
+ end
24
+
25
+ class << self
26
+ attr_accessor :max_file_size
27
+ end
28
+
29
+ self.max_file_size = 10_000_000 # ~10M
30
+
31
+ attr_reader :files
32
+
33
+ # initializes index given repo_path
34
+ def initialize(repo_path)
35
+ @index_file = File.join(repo_path, 'file-index')
36
+ if File.file?(@index_file) && (File.size(@index_file) < Grit::GitRuby::FileIndex.max_file_size)
37
+ read_index
38
+ else
39
+ raise IndexFileNotFound
40
+ end
41
+ end
42
+
43
+ # returns count of all commits
44
+ def count_all
45
+ @sha_count
46
+ end
47
+
48
+ # returns count of all commits reachable from SHA
49
+ # note: originally did this recursively, but ruby gets pissed about that on
50
+ # really big repos where the stack level gets 'too deep' (thats what she said)
51
+ def count(commit_sha)
52
+ commits_from(commit_sha).size
53
+ end
54
+
55
+ # builds a list of all commits reachable from a single commit
56
+ def commits_from(commit_sha)
57
+ raise UnsupportedRef if commit_sha.is_a? Array
58
+
59
+ already = {}
60
+ final = []
61
+ left_to_do = [commit_sha]
62
+
63
+ while commit_sha = left_to_do.shift
64
+ next if already[commit_sha]
65
+
66
+ final << commit_sha
67
+ already[commit_sha] = true
68
+
69
+ commit = @commit_index[commit_sha]
70
+ commit[:parents].each do |sha|
71
+ left_to_do << sha
72
+ end if commit
73
+ end
74
+
75
+ sort_commits(final)
76
+ end
77
+
78
+ def sort_commits(sha_array)
79
+ sha_array.sort { |a, b| @commit_order[b].to_i <=> @commit_order[a].to_i }
80
+ end
81
+
82
+ # returns files changed at commit sha
83
+ def files(commit_sha)
84
+ @commit_index[commit_sha][:files] rescue nil
85
+ end
86
+
87
+ # returns all commits for a file
88
+ def commits_for(file)
89
+ @all_files[file]
90
+ end
91
+
92
+ # returns the shas of the last commits for all
93
+ # the files in [] from commit_sha
94
+ # files_matcher can be a regexp or an array
95
+ def last_commits(commit_sha, files_matcher)
96
+ acceptable = commits_from(commit_sha)
97
+
98
+ matches = {}
99
+
100
+ if files_matcher.is_a? Regexp
101
+ files = @all_files.keys.select { |file| file =~ files_matcher }
102
+ files_matcher = files
103
+ end
104
+
105
+ if files_matcher.is_a? Array
106
+ # find the last commit for each file in the array
107
+ files_matcher.each do |f|
108
+ @all_files[f].each do |try|
109
+ if acceptable.include?(try)
110
+ matches[f] = try
111
+ break
112
+ end
113
+ end if @all_files[f]
114
+ end
115
+ end
116
+
117
+ matches
118
+ end
119
+
120
+ private
121
+
122
+ # read and parse the file-index data
123
+ def read_index
124
+ f = File.new(@index_file)
125
+ @sha_count = 0
126
+ @commit_index = {}
127
+ @commit_order = {}
128
+ @all_files = {}
129
+ while line = f.gets
130
+ if /^(\w{40})/.match(line)
131
+ shas = line.scan(/(\w{40})/)
132
+ current_sha = shas.shift.first
133
+ parents = shas.map { |sha| sha.first }
134
+ @commit_index[current_sha] = {:files => [], :parents => parents }
135
+ @commit_order[current_sha] = @sha_count
136
+ @sha_count += 1
137
+ else
138
+ file_name = line.chomp
139
+ tree = ''
140
+ File.dirname(file_name).split('/').each do |part|
141
+ next if part == '.'
142
+ tree += part + '/'
143
+ @all_files[tree] ||= []
144
+ @all_files[tree].unshift(current_sha)
145
+ end
146
+ @all_files[file_name] ||= []
147
+ @all_files[file_name].unshift(current_sha)
148
+ @commit_index[current_sha][:files] << file_name
149
+ end
150
+ end
151
+ end
152
+
153
+ end
154
+
155
+ end
156
+ end
157
+
158
+
159
+ # benchmark testing on big-ass repos
160
+
161
+ if __FILE__ == $0
162
+
163
+ #repo = '/Users/schacon/projects/git/.git'
164
+ #commit = 'd8933f013a66cc1deadf83a9c24eccb6fee78a35'
165
+ #file_list = ["builtin-merge-recursive.c", "git-send-email-script", "git-parse-remote.sh", "builtin-add.c", "merge-base.c", "read-cache.c", "fsck.h", "diff.c", "refs.c", "diffcore-rename.c", "epoch.c", "pack-intersect.c", "fast-import.c", "git-applypatch.sh", "git.spec.in", "rpush.c", "git-clone-script", "utf8.c", "git-external-diff-script", "builtin-init-db.c", "pack-redundant.c", "builtin-diff-index.c", "index.c", "update-index.c", "fetch-clone.c", "pager.c", "diff.h", "unpack-trees.c", "git-browse-help.sh", "git-rename-script", "refs.h", "get-tar-commit-id.c", "git-push.sh", "README", "delta.c", "mailsplit.c", "gitweb.cgi", "var.c", "epoch.h", "gsimm.c", "archive.c", "sideband.c", "utf8.h", "local-fetch.c", "git-request-pull-script", "builtin-send-pack.c", "blob.c", "builtin-ls-remote.c", "pretty.c", "git-diff.sh", "diffcore-break.c", "unpack-trees.h", "git-mv.perl", "interpolate.c", "builtin-diff-files.c", "delta.h", "commit-tree.c", "git-diff-script", "decorate.c", "builtin-name-rev.c", "tree-walk.c", "git-revert-script", "git-sh-setup.sh", "transport.c", "gsimm.h", "archive.h", "count-delta.c", "sideband.h", "git-merge.sh", "git-gui.sh", "git-core.spec.in", "cvs2git.c", "blob.h", "git.sh", "http-push.c", "builtin-commit-tree.c", "diff-helper.c", "builtin-push.c", "interpolate.h", "decorate.h", "git-citool", "dotest", "builtin-verify-tag.c", "git-mergetool.sh", "tree-walk.h", "log-tree.c", "name-rev.c", "applypatch", "cat-file.c", "test-delta.c", "server-info.c", "count-delta.h", "write-tree.c", "local-pull.c", "transport.h", "git-rm.sh", "unpack-objects.c", "xdiff-interface.c", "git-repack-script", "commit.c", "hash-object.c", "git-merge-recursive.py", "git-clone-dumb-http", "thread-utils.c", "git-send-email.perl", "git-whatchanged.sh", "log-tree.h", "builtin-annotate.c", "show-index.c", "pkt-line.c", "ident.c", "git-rebase-script", "name-hash.c", "git-archimport.perl", "xdiff-interface.h", "commit.h", "diff-lib.c", "wt-status.c", "base85.c", "builtin-fetch--tool.c", "unpack-file.c", "builtin-diff-stages.c", "merge-index.c", "color.c", "diff-tree.c", "git-checkout.sh", "thread-utils.h", "grep.c", "pkt-line.h", "builtin-help.c", "test-parse-options.c", "show-files.c", "git.sh.in", "pack.h", "wt-status.h", "git-prune-script", "test-sha1.c", "git-octopus.sh", "dump-cache-tree.c", "git-web--browse.sh", "builtin-upload-tar.c", "builtin-clone.c", "copy.c", "color.h", "show-branch.c", "peek-remote.c", "git-merge-recursive-old.py", "cmd-rename.sh", "git-apply-patch-script", "git-export.c", "git-relink-script", "grep.h", "usage.c", "git-fetch-dumb-http", "fsck-objects.c", "update-cache.c", "diff-stages.c", "patch-ids.c", "builtin-rev-list.c", "builtin-bundle.c", "builtin-show-branch.c", "builtin-pack-refs.c", "tree.c", "git.c", "verify_pack.c", "update-ref.c", "builtin-peek-remote.c", "diffcore-pathspec.c", "git-merge-octopus.sh", "git-show-branches-script", "builtin-archive.c", "builtin-unpack-objects.c", "git-rerere.perl", "walker.c", "builtin-mailsplit.c", "convert.c", "builtin-branch.c", "export.c", "patch-ids.h", "check-builtins.sh", "git-pull-script", "tree.h", "alloc.c", "git-commit.sh", "git-lost-found.sh", "mailmap.c", "rsh.c", "exec_cmd.c", "git-compat-util.h", "ws.c", "rev-list.c", "git-verify-tag.sh", "git-ls-remote-script", "mktree.c", "walker.h", "builtin-blame.c", "builtin-fsck.c", "setup.c", "git-cvsimport-script", "git-add.sh", "symlinks.c", "checkout-index.c", "receive-pack.c", "git-merge-one-file-script", "mailmap.h", "git-cvsimport.perl", "builtin-count.c", "exec_cmd.h", "builtin-stripspace.c", "git-grep.sh", "hash.c", "builtin-prune-packed.c", "git-rebase--interactive.sh", "rsh.h", "match-trees.c", "git-format-patch.sh", "git-push-script", "parse-options.c", "git-status-script", "http-walker.c", "pack-write.c", "git-status.sh", "diff-delta.c", "hash.h", "generate-cmdlist.sh", "config-set.c", "builtin-fetch.c", "ll-merge.c", "t1300-config-set.sh", "ls-tree.c", "write_or_die.c", "builtin-check-ref-format.c", "fetch-pack.c", "git-commit-script", "builtin-describe.c", "parse-options.h", "builtin-checkout.c", "prune-packed.c", "fixup-builtins", "http-fetch.c", "test-absolute-path.c", "git-log.sh", "builtin-merge-ours.c", "git-whatchanged", "pull.c", "merge-tree.c", "ll-merge.h", "builtin.h", "Makefile", "cache-tree.c", "builtin-log.c", "merge-cache.c", "fetch-pack.h", "git-shortlog.perl", "git-bisect-script", "git-am.sh", "check-ref-format.c", "git-count-objects-script", "mkdelta.c", "builtin-diff.c", "merge-recursive.c", "builtin-config.c", "gitenv.c", "describe.c", "git-add--interactive.perl", "pull.h", "builtin-apply.c", "diff-index.c", "ssh-pull.c", "builtin-merge-file.c", "strbuf.c", "git-submodule.sh", "repo-config.c", "run-command.c", "git-applymbox.sh", "cache-tree.h", "builtin-clean.c", "cache.h", "git-prune.sh", "fsck-cache.c", "builtin-remote.c", "sha1_file.c", "shallow.c", "merge-recursive.h", "builtin-checkout-index.c", "git-clone.sh", "builtin-mv.c", "builtin-reflog.c", "lockfile.c", "git-octopus-script", ".mailmap", "strbuf.h", "git-p4import.py", "builtin-repo-config.c", "patch-delta.c", "builtin-merge-base.c", "run-command.h", "check-racy.c", "git-filter-branch.sh", "git-branch.sh", "git-merge-stupid.sh", "diff-files.c", "test-sha1.sh", "COPYING", "git-lost+found.sh", "git-tag.sh", "git-branch-script", "check-files.c", "builtin-reset.c", "builtin-ls-files.c", "builtin-fmt-merge-msg.c", "builtin-for-each-ref.c", "csum-file.c", "git-gc.sh", "git-parse-remote-script", "command-list.txt", "builtin-pack-objects.c", "dir.c", "test-date.c", "builtin-grep.c", "list-objects.c", "clone-pack.c", "git-gui", "convert-cache.c", "git-reset-script", "checkout-cache.c", "git-ls-remote.sh", "read-tree.c", "git-instaweb.sh", "progress.c", "rabinpoly.c", "ls-files.c", "mktag.c", "gitMergeCommon.py", "git-merge-ours.sh", "rpull.c", "git-annotate.perl", "csum-file.h", "builtin-shortlog.c", "builtin-commit.c", "http-pull.c", "git-fetch.sh", "apply.c", "git-add-script", "dir.h", "diff-tree-helper.c", "list-objects.h", "rev-tree.c", "builtin-tar-tree.c", "progress.h", "builtin-pickaxe.c", "git-merge-fredrik.py", "path.c", "builtin-diff-tree.c", "rabinpoly.h", "builtin-ls-tree.c", "tar.h", "trace.c", "graph.c", "ssh-fetch.c", "show-diff.c", "sha1-lookup.c", "builtin-revert.c", "builtin-symbolic-ref.c", "builtin-write-tree.c", "git-sh-setup-script", "rev-cache.c", "blame.c", "builtin-mailinfo.c", "git-cherry", "git-resolve-script", "INSTALL", "git-findtags.perl", "diffcore-delta.c", "entry.c", "git-applypatch", "connect.c", "tar-tree.c", "graph.h", "missing-revs.c", "builtin-fast-export.c", "sha1-lookup.h", "rev-parse.c", "configure.ac", "rev-cache.h", "build-rev-cache.c", "reachable.c", "index-pack.c", "git", "send-pack.c", "git-cherry.sh", "git-tag-script", "revision.c", "CREDITS-GEN", "bundle.c", "mailinfo.c", "symbolic-ref.c", "attr.c", "git-archimport-script", "archive-zip.c", "diff-cache.c", "fetch.c", "builtin-gc.c", "git-remote.perl", "path-list.c", "ssh-upload.c", "reachable.h", "diff-no-index.c", "diffcore.h", "send-pack.h", "tree-diff.c", "git-checkout-script", "pack-revindex.c", "show-rev-cache.c", "TODO", "revision.h", "bundle.h", "unresolve.c", "git-deltafy-script", "git-relink.perl", "archive-tar.c", "attr.h", "git-resolve.sh", "config.mak.in", "builtin-update-index.c", "convert-objects.c", "fetch.h", "builtin-runstatus.c", "quote.c", "init-db.c", "git-shortlog", "builtin-prune.c", "builtin-rerere.c", "verify-pack.c", "gitk", "patch-id.c", ".gitattributes", "date.c", "git-format-patch-script", "path-list.h", "pack-revindex.h", "GIT-VERSION-GEN", "combine-diff.c", "environment.c", "git-cvsserver.perl", "git-repack.sh", "diffcore-order.c", "reflog-walk.c", "config.c", "test-match-trees.c", "git-svnimport.perl", "quote.h", "write-blob.c", "diffcore-pickaxe.c", "builtin-update-ref.c", "stripspace.c", "help.c", "pack-objects.c", "branch.c", "git-verify-tag-script", "TEST", "daemon.c", "remote.c", "git-log-script", "git-pull.sh", "git-quiltimport.sh", "git-count-objects.sh", "reflog-walk.h", "git-applymbox", "builtin-show-ref.c", "RelNotes", "git-fmt-merge-msg.perl", "git-rebase.sh", "git-parse-remote", "git-browse--help.sh", "git-stash.sh", "alias.c", "branch.h", "gitweb.pl", "builtin-upload-archive.c", "builtin-cat-file.c", "sha1_name.c", "http.c", "test-chmtime.c", "remote.h", "ssh-push.c", "tag.c", "update-server-info.c", "git-cvsexportcommit.perl", "builtin-check-attr.c", "git-revert.sh", "builtin-verify-pack.c", "object.c", "git-merge-resolve.sh", "shortlog.h", "git-fetch-script", "test-genrandom.c", "shell.c", "builtin-rm.c", "builtin-zip-tree.c", "upload-pack.c", "git-rename.perl", ".gitignore", "tag.h", "http.h", "git-request-pull.sh", "object.h", "git-svn.perl", "builtin-fetch-pack.c", "git-bisect.sh", "pack-check.c", "builtin-rev-parse.c", "object-refs.c", "test-gsimm.c", "builtin-read-tree.c", "git-help--browse.sh", "merge-file.c", "fsck.c", "builtin-tag.c", "builtin-http-fetch.c", "builtin-count-objects.c", "git-reset.sh", "git-clean.sh", "git-merge-one-file.sh", "ctype.c", "git-mktag.c", "imap-send.c"]
166
+
167
+ repo = '/Users/schacon/projects/grit/.git'
168
+ commit = 'c87612bc84c95ba9df17674d911dde10f34fefaa'
169
+
170
+ require 'benchmark'
171
+
172
+ Benchmark.bm(20) do |x|
173
+ x.report('index build') do
174
+ i = Grit::GitRuby::FileIndex.new(repo)
175
+ end
176
+ x.report('commit count') do
177
+ i = Grit::GitRuby::FileIndex.new(repo)
178
+ i.count(commit)
179
+ end
180
+ x.report('commits list') do
181
+ i = Grit::GitRuby::FileIndex.new(repo)
182
+ i.commits_from(commit)
183
+ end
184
+ x.report('last commits') do
185
+ i = Grit::GitRuby::FileIndex.new(repo)
186
+ #arr = i.last_commits(commit, file_list)
187
+ arr = i.last_commits(commit, /^[^\/]*$/)
188
+ end
189
+ end
190
+ end
191
+
192
+
193
+
@@ -0,0 +1,350 @@
1
+ #
2
+ # converted from the gitrb project
3
+ #
4
+ # authors:
5
+ # Matthias Lederhofer <matled@gmx.net>
6
+ # Simon 'corecode' Schubert <corecode@fs.ei.tum.de>
7
+ # Scott Chacon <schacon@gmail.com>
8
+ #
9
+ # provides native ruby access to git objects and pack files
10
+ #
11
+
12
+ # These classes translate the raw binary data kept in the sha encoded files
13
+ # into parsed data that can then be used in another fashion
14
+ require 'stringio'
15
+
16
+ module Grit
17
+ module GitRuby
18
+
19
+ # class for author/committer/tagger lines
20
+ class UserInfo
21
+ attr_accessor :name, :email, :date, :offset
22
+
23
+ def initialize(str)
24
+ @email = ''
25
+ @date = Time.now
26
+ @offset = 0
27
+
28
+ m = /^(.*?) <(.*)> (\d+) ([+-])0*(\d+?)$/.match(str)
29
+ if !m
30
+ case str
31
+ when /<.+>/
32
+ m, @name, @email = *str.match(/(.*) <(.+?)>/)
33
+ else
34
+ @name = str
35
+ end
36
+ else
37
+ @name = m[1]
38
+ @email = m[2]
39
+ @date = Time.at(Integer(m[3]))
40
+ @offset = (m[4] == "-" ? -1 : 1)*Integer(m[5])
41
+ end
42
+ end
43
+
44
+ def to_s
45
+ "%s <%s> %s %+05d" % [@name, @email, @date.to_i, @offset]
46
+ end
47
+ end
48
+
49
+ # base class for all git objects (blob, tree, commit, tag)
50
+ class GitObject
51
+ attr_accessor :repository
52
+
53
+ def GitObject.from_raw(rawobject, repository = nil)
54
+ case rawobject.type
55
+ when :blob
56
+ return Blob.from_raw(rawobject, repository)
57
+ when :tree
58
+ return Tree.from_raw(rawobject, repository)
59
+ when :commit
60
+ return Commit.from_raw(rawobject, repository)
61
+ when :tag
62
+ return Tag.from_raw(rawobject, repository)
63
+ else
64
+ raise RuntimeError, "got invalid object-type"
65
+ end
66
+ end
67
+
68
+ def initialize
69
+ raise NotImplemented, "abstract class"
70
+ end
71
+
72
+ def type
73
+ raise NotImplemented, "abstract class"
74
+ end
75
+
76
+ def raw_content
77
+ raise NotImplemented, "abstract class"
78
+ end
79
+
80
+ def sha1
81
+ Digest::SHA1.hexdigest("%s %d\0" % \
82
+ [self.type, self.raw_content.length] + \
83
+ self.raw_content)
84
+ end
85
+ end
86
+
87
+ class Blob < GitObject
88
+ attr_accessor :content
89
+
90
+ def self.from_raw(rawobject, repository)
91
+ new(rawobject.content)
92
+ end
93
+
94
+ def initialize(content, repository=nil)
95
+ @content = content
96
+ @repository = repository
97
+ end
98
+
99
+ def type
100
+ :blob
101
+ end
102
+
103
+ def raw_content
104
+ @content
105
+ end
106
+ end
107
+
108
+ class DirectoryEntry
109
+ S_IFMT = 00170000
110
+ S_IFLNK = 0120000
111
+ S_IFREG = 0100000
112
+ S_IFDIR = 0040000
113
+ S_IFGITLINK = 0160000
114
+ attr_accessor :mode, :name, :sha1
115
+ def initialize(mode, filename, sha1o)
116
+ @mode = 0
117
+ mode.each_byte do |i|
118
+ @mode = (@mode << 3) | (i-'0'.getord(0))
119
+ end
120
+ @name = filename
121
+ @sha1 = sha1o
122
+ if ![S_IFLNK, S_IFDIR, S_IFREG, S_IFGITLINK].include?(@mode & S_IFMT)
123
+ raise RuntimeError, "unknown type for directory entry"
124
+ end
125
+ end
126
+
127
+ def type
128
+ case @mode & S_IFMT
129
+ when S_IFGITLINK
130
+ @type = :submodule
131
+ when S_IFLNK
132
+ @type = :link
133
+ when S_IFDIR
134
+ @type = :directory
135
+ when S_IFREG
136
+ @type = :file
137
+ else
138
+ raise RuntimeError, "unknown type for directory entry"
139
+ end
140
+ end
141
+
142
+ def type=(type)
143
+ case @type
144
+ when :link
145
+ @mode = (@mode & ~S_IFMT) | S_IFLNK
146
+ when :directory
147
+ @mode = (@mode & ~S_IFMT) | S_IFDIR
148
+ when :file
149
+ @mode = (@mode & ~S_IFMT) | S_IFREG
150
+ when :submodule
151
+ @mode = (@mode & ~S_IFMT) | S_IFGITLINK
152
+ else
153
+ raise RuntimeError, "invalid type"
154
+ end
155
+ end
156
+
157
+ def format_type
158
+ case type
159
+ when :link
160
+ 'link'
161
+ when :directory
162
+ 'tree'
163
+ when :file
164
+ 'blob'
165
+ when :submodule
166
+ 'commit'
167
+ end
168
+ end
169
+
170
+ def format_mode
171
+ "%06o" % @mode
172
+ end
173
+
174
+ def raw
175
+ "%o %s\0%s" % [@mode, @name, [@sha1].pack("H*")]
176
+ end
177
+ end
178
+
179
+
180
+ def self.read_bytes_until(io, char)
181
+ string = ''
182
+ if RUBY_VERSION > '1.9'
183
+ while ((next_char = io.getc) != char) && !io.eof
184
+ string += next_char
185
+ end
186
+ else
187
+ while ((next_char = io.getc.chr) != char) && !io.eof
188
+ string += next_char
189
+ end
190
+ end
191
+ string
192
+ end
193
+
194
+
195
+ class Tree < GitObject
196
+ attr_accessor :entry
197
+
198
+ def self.from_raw(rawobject, repository=nil)
199
+ raw = StringIO.new(rawobject.content)
200
+
201
+ entries = []
202
+ while !raw.eof?
203
+ mode = Grit::GitRuby.read_bytes_until(raw, ' ')
204
+ file_name = Grit::GitRuby.read_bytes_until(raw, "\0")
205
+ raw_sha = raw.read(20)
206
+ sha = raw_sha.unpack("H*").first
207
+
208
+ entries << DirectoryEntry.new(mode, file_name, sha)
209
+ end
210
+ new(entries, repository)
211
+ end
212
+
213
+ def initialize(entries=[], repository = nil)
214
+ @entry = entries
215
+ @repository = repository
216
+ end
217
+
218
+ def type
219
+ :tree
220
+ end
221
+
222
+ def raw_content
223
+ # TODO: sort correctly
224
+ #@entry.sort { |a,b| a.name <=> b.name }.
225
+ @entry.collect { |e| [[e.format_mode, e.format_type, e.sha1].join(' '), e.name].join("\t") }.join("\n")
226
+ end
227
+
228
+ def actual_raw
229
+ #@entry.collect { |e| e.raw.join(' '), e.name].join("\t") }.join("\n")
230
+ end
231
+ end
232
+
233
+ class Commit < GitObject
234
+ attr_accessor :author, :committer, :tree, :parent, :message, :headers
235
+
236
+ def self.from_raw(rawobject, repository=nil)
237
+ parent = []
238
+ tree = author = committer = nil
239
+
240
+ headers, message = rawobject.content.split(/\n\n/, 2)
241
+ all_headers = headers.split(/\n/).map { |header| header.split(/ /, 2) }
242
+ all_headers.each do |key, value|
243
+ case key
244
+ when "tree"
245
+ tree = value
246
+ when "parent"
247
+ parent.push(value)
248
+ when "author"
249
+ author = UserInfo.new(value)
250
+ when "committer"
251
+ committer = UserInfo.new(value)
252
+ else
253
+ warn "unknown header '%s' in commit %s" % \
254
+ [key, rawobject.sha1.unpack("H*")[0]]
255
+ end
256
+ end
257
+ if not tree && author && committer
258
+ raise RuntimeError, "incomplete raw commit object"
259
+ end
260
+ new(tree, parent, author, committer, message, headers, repository)
261
+ end
262
+
263
+ def initialize(tree, parent, author, committer, message, headers, repository=nil)
264
+ @tree = tree
265
+ @author = author
266
+ @parent = parent
267
+ @committer = committer
268
+ @message = message
269
+ @headers = headers
270
+ @repository = repository
271
+ end
272
+
273
+ def type
274
+ :commit
275
+ end
276
+
277
+ def raw_content
278
+ "tree %s\n%sauthor %s\ncommitter %s\n\n" % [
279
+ @tree,
280
+ @parent.collect { |i| "parent %s\n" % i }.join,
281
+ @author, @committer] + @message
282
+ end
283
+
284
+ def raw_log(sha)
285
+ output = "commit #{sha}\n"
286
+ output += @headers + "\n\n"
287
+ output += @message.split("\n").map { |l| ' ' + l }.join("\n") + "\n\n"
288
+ end
289
+
290
+ end
291
+
292
+ class Tag < GitObject
293
+ attr_accessor :object, :type, :tag, :tagger, :message
294
+
295
+ def self.from_raw(rawobject, repository=nil)
296
+
297
+ headers, message = rawobject.content.split(/\n\n/, 2)
298
+ headers = headers.split(/\n/).map { |header| header.split(' ', 2) }
299
+
300
+ object = ''
301
+ type = ''
302
+ tag = ''
303
+ tagger = ''
304
+
305
+ headers.each do |key, value|
306
+ case key
307
+ when "object"
308
+ object = value
309
+ when "type"
310
+ if !["blob", "tree", "commit", "tag"].include?(value)
311
+ raise RuntimeError, "invalid type in tag"
312
+ end
313
+ type = value.to_sym
314
+ when "tag"
315
+ tag = value
316
+ when "tagger"
317
+ tagger = UserInfo.new(value)
318
+ else
319
+ warn "unknown header '%s' in tag" % \
320
+ [key, rawobject.sha1.unpack("H*")[0]]
321
+ end
322
+ end
323
+
324
+ if not object && type && tag && tagger
325
+ raise RuntimeError, "incomplete raw tag object"
326
+ end
327
+ new(object, type, tag, tagger, message, repository)
328
+ end
329
+
330
+ def initialize(object, type, tag, tagger, message, repository=nil)
331
+ @object = object
332
+ @type = type
333
+ @tag = tag
334
+ @tagger = tagger
335
+ @repository = repository
336
+ @message = message
337
+ end
338
+
339
+ def raw_content
340
+ "object %s\ntype %s\ntag %s\ntagger %s\n\n" % \
341
+ [@object, @type, @tag, @tagger] + @message
342
+ end
343
+
344
+ def type
345
+ :tag
346
+ end
347
+ end
348
+
349
+ end
350
+ end
@@ -0,0 +1,58 @@
1
+ #
2
+ # converted from the gitrb project
3
+ #
4
+ # authors:
5
+ # Matthias Lederhofer <matled@gmx.net>
6
+ # Simon 'corecode' Schubert <corecode@fs.ei.tum.de>
7
+ # Scott Chacon <schacon@gmail.com>
8
+ #
9
+ # provides native ruby access to git objects and pack files
10
+ #
11
+
12
+ module Grit
13
+ module GitRuby
14
+ module Internal
15
+ class FileWindow
16
+ def initialize(file, version = 1)
17
+ @file = file
18
+ @offset = nil
19
+ if version == 2
20
+ @global_offset = 8
21
+ else
22
+ @global_offset = 0
23
+ end
24
+ end
25
+
26
+ def unmap
27
+ @file = nil
28
+ end
29
+
30
+ def [](*idx)
31
+ idx = idx[0] if idx.length == 1
32
+ case idx
33
+ when Range
34
+ offset = idx.first
35
+ len = idx.last - idx.first + idx.exclude_end? ? 0 : 1
36
+ when Fixnum
37
+ offset = idx
38
+ len = nil
39
+ when Array
40
+ offset, len = idx
41
+ else
42
+ raise RuntimeError, "invalid index param: #{idx.class}"
43
+ end
44
+ if @offset != offset
45
+ @file.seek(offset + @global_offset)
46
+ end
47
+ @offset = offset + len ? len : 1
48
+ if not len
49
+ @file.read(1).getord(0)
50
+ else
51
+ @file.read(len)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+