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.
- data/API.txt +101 -0
- data/History.txt +49 -0
- data/LICENSE +22 -0
- data/README.md +216 -0
- data/VERSION.yml +4 -0
- data/examples/ex_add_commit.rb +13 -0
- data/examples/ex_index.rb +14 -0
- data/lib/grit/actor.rb +41 -0
- data/lib/grit/blame.rb +61 -0
- data/lib/grit/blob.rb +126 -0
- data/lib/grit/commit.rb +242 -0
- data/lib/grit/commit_stats.rb +128 -0
- data/lib/grit/config.rb +44 -0
- data/lib/grit/diff.rb +70 -0
- data/lib/grit/errors.rb +7 -0
- data/lib/grit/git-ruby/commit_db.rb +52 -0
- data/lib/grit/git-ruby/file_index.rb +193 -0
- data/lib/grit/git-ruby/git_object.rb +350 -0
- data/lib/grit/git-ruby/internal/file_window.rb +58 -0
- data/lib/grit/git-ruby/internal/loose.rb +137 -0
- data/lib/grit/git-ruby/internal/pack.rb +382 -0
- data/lib/grit/git-ruby/internal/raw_object.rb +37 -0
- data/lib/grit/git-ruby/object.rb +325 -0
- data/lib/grit/git-ruby/repository.rb +736 -0
- data/lib/grit/git-ruby.rb +186 -0
- data/lib/grit/git.rb +140 -0
- data/lib/grit/index.rb +122 -0
- data/lib/grit/lazy.rb +33 -0
- data/lib/grit/merge.rb +45 -0
- data/lib/grit/ref.rb +99 -0
- data/lib/grit/repo.rb +447 -0
- data/lib/grit/ruby1.9.rb +7 -0
- data/lib/grit/status.rb +151 -0
- data/lib/grit/submodule.rb +88 -0
- data/lib/grit/tag.rb +66 -0
- data/lib/grit/tree.rb +123 -0
- data/lib/grit.rb +68 -0
- data/lib/open3_detach.rb +46 -0
- data/test/bench/benchmarks.rb +126 -0
- data/test/helper.rb +18 -0
- data/test/profile.rb +21 -0
- data/test/suite.rb +6 -0
- data/test/test_actor.rb +51 -0
- data/test/test_blame.rb +32 -0
- data/test/test_blame_tree.rb +33 -0
- data/test/test_blob.rb +83 -0
- data/test/test_commit.rb +206 -0
- data/test/test_commit_stats.rb +33 -0
- data/test/test_commit_write.rb +54 -0
- data/test/test_config.rb +58 -0
- data/test/test_diff.rb +18 -0
- data/test/test_file_index.rb +56 -0
- data/test/test_git.rb +84 -0
- data/test/test_grit.rb +32 -0
- data/test/test_head.rb +47 -0
- data/test/test_index_status.rb +40 -0
- data/test/test_merge.rb +17 -0
- data/test/test_raw.rb +16 -0
- data/test/test_real.rb +19 -0
- data/test/test_reality.rb +17 -0
- data/test/test_remote.rb +14 -0
- data/test/test_repo.rb +347 -0
- data/test/test_rubygit.rb +188 -0
- data/test/test_rubygit_alt.rb +40 -0
- data/test/test_rubygit_index.rb +76 -0
- data/test/test_rubygit_iv2.rb +28 -0
- data/test/test_submodule.rb +69 -0
- data/test/test_tag.rb +67 -0
- data/test/test_tree.rb +101 -0
- 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
|
+
|