gitlab-grit-2.8.3/0000755000004100000410000000000013641342231013760 5ustar www-datawww-datagitlab-grit-2.8.3/README.md0000644000004100000410000001702213641342231015241 0ustar www-datawww-data## Grit. GitLab fork #### Code status [![pipeline status](https://gitlab.com/gitlab-org/gitlab-grit/badges/master/pipeline.svg)](https://gitlab.com/gitlab-org/gitlab-grit/-/commits/master) [![build status](https://secure.travis-ci.org/gitlabhq/grit.png)](https://travis-ci.org/gitlabhq/grit) [![Gem Version](https://badge.fury.io/rb/gitlab-grit.svg)](http://badge.fury.io/rb/gitlab-grit) [![Code Climate](https://codeclimate.com/github/gitlabhq/grit.png)](https://codeclimate.com/github/gitlabhq/grit) [![Coverage Status](https://coveralls.io/repos/gitlabhq/grit/badge.png?branch=master)](https://coveralls.io/r/gitlabhq/grit) ## Fork changes We patched existing grit library to use it inside GitLab * Added grep method to look for files * Fixed commits parsing for signed commits * Encoding fixes * Cleanup and improve code style * Handle filenames with spaces * Fixes symlinks omission from diff * Added Gemfile * Ruby 2.0 support * Automatically set the `--work-tree=` option for Git * Remove `chdir:` option from Grit::Git#native * and much more small fixes ## Grit Grit gives you object oriented read/write access to Git repositories via Ruby. The main goals are stability and performance. To this end, some of the interactions with Git repositories are done by shelling out to the system's `git` command, and other interactions are done with pure Ruby reimplementations of core Git functionality. This choice, however, is transparent to end users, and you need not know which method is being used. This software was developed to power GitHub, and should be considered production ready. An extensive test suite is provided to verify its correctness. Original Grit is maintained by Tom Preston-Werner, Scott Chacon, Chris Wanstrath, and PJ Hyett. This fork is maintained by GitLab.com This documentation is accurate as of Grit 2.3. ## Requirements * git (http://git-scm.com) tested with 1.7.4.6 ## Install Easiest install is via RubyGems: $ gem install grit ## Source Grit's Git repo is available on GitHub, which can be browsed at: http://github.com/gitlabhq/grit and cloned with: git clone git://github.com/gitlabhq/grit.git If you're installing from source, you can use Bundler to pick up all the gems: $ bundle install ### Contributing If you'd like to hack on Grit, follow these instructions. To get all of the dependencies, install the gem first. 1. Fork the project to your own account 1. Clone down your fork 1. Create a thoughtfully named topic branch to contain your change 1. Hack away 1. Add tests and make sure everything still passes by running `rake` 1. If you are adding new functionality, document it in README.md 1. Do not change the version number, I will do that on my end 1. If necessary, rebase your commits into logical chunks, without errors 1. Push the branch up to GitHub 1. Send a pull request for your branch ## Usage Grit gives you object model access to your Git repositories. Once you have created a `Repo` object, you can traverse it to find parent commits, trees, blobs, etc. ### Initialize a Repo object The first step is to create a `Grit::Repo` object to represent your repo. In this documentation I include the `Grit` module to reduce typing. require 'grit' repo = Grit::Repo.new("/Users/tom/dev/grit") In the above example, the directory `/Users/tom/dev/grit` is my working directory and contains the `.git` directory. You can also initialize Grit with a bare repo. repo = Repo.new("/var/git/grit.git") ### Getting a list of commits From the `Repo` object, you can get a list of commits as an array of `Commit` objects. repo.commits # => [#, #, #, #, #] Called without arguments, `Repo#commits` returns a list of up to ten commits reachable by the **master** branch (starting at the latest commit). You can ask for commits beginning at a different branch, commit, tag, etc. repo.commits('mybranch') repo.commits('40d3057d09a7a4d61059bca9dca5ae698de58cbe') repo.commits('v0.1') You can specify the maximum number of commits to return. repo.commits('master', 100) If you need paging, you can specify a number of commits to skip. repo.commits('master', 10, 20) The above will return commits 21-30 from the commit list. ### The Commit object `Commit` objects contain information about that commit. head = repo.commits.first head.id # => "e80bbd2ce67651aa18e57fb0b43618ad4baf7750" head.parents # => [#] head.tree # => # head.author # => #"> head.authored_date # => Wed Oct 24 22:02:31 -0700 2007 head.committer # => #"> head.committed_date # => Wed Oct 24 22:02:31 -0700 2007 head.message # => "add Actor inspect" You can traverse a commit's ancestry by chaining calls to `#parents`. repo.commits.first.parents[0].parents[0].parents[0] The above corresponds to **master^^^** or **master~3** in Git parlance. ### The Tree object A tree records pointers to the contents of a directory. Let's say you want the root tree of the latest commit on the **master** branch. tree = repo.commits.first.tree # => # tree.id # => "3536eb9abac69c3e4db583ad38f3d30f8db4771f" Once you have a tree, you can get the contents. contents = tree.contents # => [#, #, #, #] This tree contains two `Blob` objects and two `Tree` objects. The trees are subdirectories and the blobs are files. Trees below the root have additional attributes. contents.last.name # => "lib" contents.last.mode # => "040000" There is a convenience method that allows you to get a named sub-object from a tree. tree / "lib" # => # You can also get a tree directly from the repo if you know its name. repo.tree # => # repo.tree("91169e1f5fa4de2eaea3f176461f5dc784796769") # => # ### The Blob object A blob represents a file. Trees often contain blobs. blob = tree.contents.first # => # A blob has certain attributes. blob.id # => "4ebc8aea50e0a67e000ba29a30809d0a7b9b2666" blob.name # => "README.txt" blob.mode # => "100644" blob.size # => 7726 You can get the data of a blob as a string. blob.data # => "Grit is a library to ..." You can also get a blob directly from the repo if you know its name. repo.blob("4ebc8aea50e0a67e000ba29a30809d0a7b9b2666") # => # ### Other There are many more API methods available that are not documented here. Please reference the code for more functionality. Copyright --------- Copyright (c) 2010 Tom Preston-Werner. See LICENSE for details. gitlab-grit-2.8.3/LICENSE0000644000004100000410000000207613641342231014772 0ustar www-datawww-data(The MIT License) Copyright (c) 2007-2009 Tom Preston-Werner Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. gitlab-grit-2.8.3/lib/0000755000004100000410000000000013641342231014526 5ustar www-datawww-datagitlab-grit-2.8.3/lib/grit/0000755000004100000410000000000013641342231015473 5ustar www-datawww-datagitlab-grit-2.8.3/lib/grit/actor.rb0000644000004100000410000000220513641342231017127 0ustar www-datawww-datamodule Grit class Actor attr_reader :name attr_reader :email def initialize(name, email) @name = name @email = email end alias_method :to_s, :name # Create an Actor from a string. # # str - The String in this format: 'John Doe ' # # Returns Git::Actor. def self.from_string(str) case str when /<.+>/ m, name, email = *str.match(/(.*) <(.+?)>/) return self.new(name, email) else return self.new(str, nil) end end # Outputs an actor string for Git commits. # # actor = Actor.new('bob', 'bob@email.com') # actor.output(time) # => "bob UNIX_TIME +0700" # # time - The Time the commit was authored or committed. # # Returns a String. def output(time) offset = time.utc_offset / 60 "%s <%s> %d %+.2d%.2d" % [ @name, @email || "null", time.to_i, offset / 60, offset.abs % 60] end # Pretty object inspection def inspect %Q{#">} end end # Actor end # Grit gitlab-grit-2.8.3/lib/grit/lazy.rb0000644000004100000410000000135113641342231016777 0ustar www-datawww-data## # Allows attributes to be declared as lazy, meaning that they won't be # computed until they are asked for. # # Works by delegating each lazy_reader to a cached lazy_source method. # # class Person # lazy_reader :eyes # # def lazy_source # OpenStruct.new(:eyes => 2) # end # end # # >> Person.new.eyes # => 2 # module Lazy def self.extended(klass) klass.send(:attr_writer, :lazy_source) end def lazy_reader(*args) args.each do |arg| ivar = "@#{arg}" define_method(arg) do if instance_variable_defined?(ivar) val = instance_variable_get(ivar) return val if val end instance_variable_set(ivar, (@lazy_source ||= lazy_source).send(arg)) end end end endgitlab-grit-2.8.3/lib/grit/tag.rb0000644000004100000410000000567313641342231016606 0ustar www-datawww-datamodule Grit class Tag < Ref extend Lazy lazy_reader :message lazy_reader :tagger lazy_reader :tag_date # Writes a new tag object from a hash # +repo+ is a Grit repo # +hash+ is the hash of tag values # # Returns a hash with +sha+ and +size+ of the created object def self.create_tag_object(repo, hash, default_actor = nil) tagger = hash[:tagger] if !tagger tagger = default_actor ? default_actor : Actor.new("none", "none@none") tagger_date = Time.now else tagger_date = tagger[:date] ? Time.parse(tagger[:date]) : Time.now tagger = Actor.new(tagger[:name], tagger[:email]) end data = [] data << "object #{hash[:object]}" data << "type #{hash[:type]}" data << "tag #{hash[:tag]}" data << "tagger #{tagger.output(tagger_date)}" data << "" data << hash[:message] data = data.join("\n") sha = repo.git.put_raw_object(data, 'tag') { :sha => sha, :size => data.size } end # Parses the results from `cat-file -p` # # data - String tag object data. Example: # object 7bcc0ee821cdd133d8a53e8e7173a334fef448aa # type commit # tag v0.7.0 # tagger USER DATE # # v0.7.0 # # Returns parsed Hash. Example: # {:message => "...", :tagger => "bob", :tag_date => ...} def self.parse_tag_data(data) return unless data =~ /^object/ parsed = {} lines = data.split("\n") parsed[:object] = lines.shift.sub(/^object /, '') parsed[:type] = lines.shift.sub(/^type /, '') parsed[:tag] = lines.shift.sub(/^tag /, '') author_line = lines.shift parsed[:tagger], parsed[:tag_date] = Commit.actor(author_line) if !parsed[:tagger] || !parsed[:tagger].name parsed[:tag_date] ||= Time.utc(1970) parsed[:tagger] = Actor.from_string(author_line.sub(/^tagger /, '')) end lines.shift # blank line parsed[:message] = [] while lines.first && lines.first !~ /-----BEGIN PGP SIGNATURE-----/ parsed[:message] << lines.shift end parsed[:message] = parsed[:message] * "\n" parsed[:pgp] = [] while lines.first parsed[:pgp] << lines.shift end parsed[:pgp] = parsed[:pgp] * "\n" parsed end def lazy_source data = commit.repo.git.cat_ref({:p => true}, name) @message = commit.short_message @tagger = commit.author @tag_date = commit.authored_date return self if data.empty? if parsed = self.class.parse_tag_data(data) @message = parsed[:message] @tagger = parsed[:tagger] @tag_date = parsed[:tag_date] end self end def get_commit sha = @repo_ref.git.commit_from_sha(@commit_id) raise "Unknown object type." if sha == '' Commit.create(@repo_ref, :id => sha) end end end gitlab-grit-2.8.3/lib/grit/diff.rb0000644000004100000410000000506713641342231016740 0ustar www-datawww-datamodule Grit class Diff attr_reader :a_path, :b_path attr_reader :a_blob, :b_blob attr_reader :a_mode, :b_mode attr_reader :new_file, :deleted_file, :renamed_file attr_reader :similarity_index attr_accessor :diff def initialize(repo, a_path, b_path, a_blob, b_blob, a_mode, b_mode, new_file, deleted_file, diff, renamed_file = false, similarity_index = 0) @repo = repo @a_path = a_path @b_path = b_path @a_blob = a_blob =~ /^0{40}$/ ? nil : Blob.create(repo, :id => a_blob) @b_blob = b_blob =~ /^0{40}$/ ? nil : Blob.create(repo, :id => b_blob) @a_mode = a_mode @b_mode = b_mode @new_file = new_file || @a_blob.nil? @deleted_file = deleted_file || @b_blob.nil? @renamed_file = renamed_file @similarity_index = similarity_index.to_i @diff = diff end def self.list_from_string(repo, text) lines = text.split("\n") diffs = [] while !lines.empty? m, a_path, b_path = *lines.shift.match(%r{^diff --git "?a\/(.+?)(?>>>>>> (.*?)/.match(line) status = STATUS_BOTH section += 1 else @text[section] ||= {} @text[section][status] ||= [] @text[section][status] << line end end @text = @text.values @sections = @text.size end # Pretty object inspection def inspect %Q{#} end protected def get_commit Commit.create(@repo_ref, :id => @commit_id) end end # Ref # A Head is a named reference to a Commit. Every Head instance contains a name # and a Commit object. # # r = Grit::Repo.new("/path/to/repo") # h = r.heads.first # h.name # => "master" # h.commit # => # # h.commit.id # => "1c09f116cbc2cb4100fb6935bb162daa4723f455" class Head < Ref # Get the HEAD revision of the repo. # +repo+ is the Repo # +options+ is a Hash of options # # Returns Grit::Head (baked) def self.current(repo, options = {}) head = repo.git.fs_read('HEAD').chomp if /ref: refs\/heads\/(.*)/.match(head) id = repo.git.rev_parse(options, 'HEAD') self.new($1, repo, id) end end end # Head class Remote < Ref; end class Note < Ref; end end # Grit gitlab-grit-2.8.3/lib/grit/submodule.rb0000644000004100000410000000436613641342231020030 0ustar www-datawww-datamodule Grit class Submodule attr_reader :id attr_reader :mode attr_reader :name # Create a Submodule containing just the specified attributes # +repo+ is the Repo # +atts+ is a Hash of instance variable data # # Returns Grit::Submodule (unbaked) def self.create(repo, atts) self.allocate.create_initialize(repo, atts) end # Initializer for Submodule.create # +repo+ is the Repo # +atts+ is a Hash of instance variable data # # Returns Grit::Submodule def create_initialize(repo, atts) @repo = repo atts.each do |k, v| instance_variable_set("@#{k}".to_sym, v) end self end # The url of this submodule # +ref+ is the committish that should be used to look up the url # # Returns String def url(ref) config = self.class.config(@repo, ref) lookup = config.keys.inject({}) do |acc, key| id = config[key]['id'] acc[id] = config[key]['url'] acc end lookup[@id] end # The configuration information for the given +repo+ # +repo+ is the Repo # +ref+ is the committish (defaults to 'master') # # Returns a Hash of { => { 'url' => , 'id' => } } # Returns {} if no .gitmodules file was found def self.config(repo, ref = "master") commit = repo.commit(ref) blob = commit.tree/'.gitmodules' return {} unless blob lines = blob.data.gsub(/\r\n?/, "\n" ).split("\n") config = {} current = nil lines.each do |line| if line =~ /^\[submodule "(.+)"\]$/ current = $1 config[current] = {} submodule = (commit.tree/current.strip) config[current]['id'] = submodule.id if submodule elsif line =~ /^\t(\w+) = (.+)$/ config[current][$1] = $2 if $1 == 'path' submodule = (commit.tree/$2.strip) config[current]['id'] = submodule.id if submodule end else # ignore end end config end def basename File.basename(name) end # Pretty object inspection def inspect %Q{#} end end # Submodule end # Grit gitlab-grit-2.8.3/lib/grit/git-ruby.rb0000644000004100000410000001644713641342231017576 0ustar www-datawww-datarequire 'grit/git-ruby/repository' module Grit # the functions in this module intercept the calls to git binary # made by the grit objects and attempts to run them in pure ruby # if it will be faster, or if the git binary is not available (!!TODO!!) module GitRuby attr_accessor :ruby_git_repo, :git_file_index def init(options, *args) if options.size == 0 Grit::GitRuby::Repository.init(@git_dir) else method_missing('init', options, *args) end end def cat_file(options, sha) if options[:t] file_type(sha) elsif options[:s] file_size(sha) elsif options[:p] try_run { ruby_git.cat_file(sha) } end rescue Grit::GitRuby::Repository::NoSuchShaFound '' end def cat_ref(options, ref) sha = rev_parse({}, ref) cat_file(options, sha) end # lib/grit/tree.rb:16: output = repo.git.ls_tree({}, treeish, *paths) def ls_tree(options, treeish, *paths) sha = rev_parse({}, treeish) ruby_git.ls_tree(sha, paths.flatten, options.delete(:r)) rescue Grit::GitRuby::Repository::NoSuchShaFound '' end # git diff --full-index 'ec037431382e83c3e95d4f2b3d145afbac8ea55d' 'f1ec1aea10986159456846b8a05615b87828d6c6' def diff(options, sha1, sha2 = nil) try_run { ruby_git.diff(sha1, sha2, options) } end def rev_list(options, *refs) refs = ['master'] if refs.empty? options.delete(:skip) if options[:skip].to_i == 0 allowed_options = [:max_count, :since, :until, :pretty] # this is all I can do right now if ((options.keys - allowed_options).size > 0) || refs.size > 1 method_missing('rev-list', options, *refs) elsif (options.size == 0) # pure rev-list ref = refs.first begin file_index.commits_from(rev_parse({}, ref)).join("\n") + "\n" rescue method_missing('rev-list', options, *refs) end else ref = refs.first aref = rev_parse({:verify => true}, ref) if aref.is_a? Array method_missing('rev-list', options, *refs) else try_run { ruby_git.rev_list(aref, options) } end end end def rev_parse(options, string) raise RuntimeError, "invalid string: #{string.inspect}" unless string.is_a?(String) # Split ranges, but don't split when specifying a ref:path. # Don't split HEAD:some/path/in/repo..txt # Do split sha1..sha2 if string !~ /:/ && string =~ /\.\./ (sha1, sha2) = string.split('..') return [rev_parse({}, sha1), rev_parse({}, sha2)] end if /^[0-9a-f]{40}$/.match(string) # passing in a sha - just no-op it return string.chomp end head = File.join(@git_dir, 'refs', 'heads', string) return File.read(head).chomp if File.file?(head) head = File.join(@git_dir, 'refs', 'remotes', string) return File.read(head).chomp if File.file?(head) head = File.join(@git_dir, 'refs', 'tags', string) return File.read(head).chomp if File.file?(head) ## check packed-refs file, too packref = File.join(@git_dir, 'packed-refs') if File.file?(packref) File.readlines(packref).each do |line| if m = /^(\w{40}) refs\/.+?\/(.*?)$/.match(line) next if !Regexp.new(Regexp.escape(string) + '$').match(m[3]) return m[1].chomp end end end ## !! more - partials and such !! # revert to calling git - grr return method_missing('rev-parse', options, string).chomp end def refs(options, prefix) refs = [] already = {} orig_prefix = prefix prefix = File.join @git_dir, prefix files = Dir.glob(prefix + '/**/*') files.each do |ref| next if !File.file?(ref) id = File.read(ref).chomp name = ref.sub("#{prefix}/", '') if !already[name] refs << "#{name} #{id}" already[name] = true end end packed = File.join(@git_dir, 'packed-refs') if File.file?(packed) File.readlines(packed).each do |line| if m = /^(\w{40}) (.*?)$/.match(line) next if !Regexp.new('^' + orig_prefix).match(m[2]) name = m[2].sub("#{orig_prefix}/", '') if !already[name] refs << "#{name} #{m[1]}" already[name] = true end end end end refs.join("\n") end def tags(options, prefix) refs = [] already = {} orig_prefix = prefix prefix = File.join @git_dir, prefix files = Dir.glob(prefix + '/**/*') files.each do |ref| next if !File.file?(ref) id = File.read(ref).chomp name = ref.sub("#{prefix}/", '') if !already[name] refs << "#{name} #{id}" already[name] = true end end packed = File.join(@git_dir, 'packed-refs') if File.file?(packed) lines = File.readlines('packed-refs') lines.each_with_index do |line, i| if m = /^(\w{40}) (.*?)$/.match(line) next if !Regexp.new('^' + orig_prefix).match(m[2]) name = m[2].sub("#{orig_prefix}/", '') # Annotated tags in packed-refs include a reference # to the commit object on the following line. next_line = lines[i + 1] id = if next_line && next_line[0] == ?^ next_line[1..-1].chomp else m[1] end if !already[name] refs << "#{name} #{id}" already[name] = true end end end end refs.join("\n") end def file_size(ref) try_run { ruby_git.cat_file_size(ref).to_s } end def file_type(ref) try_run { ruby_git.cat_file_type(ref).to_s } end def ruby_git @ruby_git_repo ||= Repository.new(@git_dir) end private def try_run ret = '' Timeout.timeout(self.class.git_timeout) do ret = yield end @bytes_read += ret.size #if @bytes_read > 5242880 # 5.megabytes # bytes = @bytes_read # @bytes_read = 0 # raise Grit::Git::GitTimeout.new(command, bytes) #end ret rescue Timeout::Error => e bytes = @bytes_read @bytes_read = 0 raise Grit::Git::GitTimeout.new(command, bytes) end def looking_for(commit, path = nil) tree_sha = ruby_git.get_subtree(rev_parse({}, commit), path) looking_for = [] ruby_git.get_object_by_sha1(tree_sha).entry.each do |e| if path && !(path == '' || path == '.' || path == './') file = File.join(path, e.name) else file = e.name end file += '/' if e.type == :directory looking_for << file end looking_for end def clean_paths(commit_array) new_commits = {} commit_array.each do |file, sha| file = file.chop if file[file.size - 1 , 1] == '/' new_commits[file] = sha end new_commits end # TODO # git grep -n 'foo' 'master' # git log --pretty='raw' --max-count='1' 'master' -- 'LICENSE' # git log --pretty='raw' --max-count='1' 'master' -- 'test' end end gitlab-grit-2.8.3/lib/grit/repo.rb0000644000004100000410000006202513641342231016772 0ustar www-datawww-datamodule Grit class Repo include POSIX::Spawn DAEMON_EXPORT_FILE = 'git-daemon-export-ok' BATCH_PARSERS = { 'commit' => ::Grit::Commit } # Public: The String path of the Git repo. attr_accessor :path # Public: The String path to the working directory of the repo, or nil if # there is no working directory. attr_accessor :working_dir # Public: The Boolean of whether or not the repo is bare. attr_reader :bare # Public: The Grit::Git command line interface object. attr_accessor :git # Public: Create a new Repo instance. # # path - The String path to either the root git directory or the bare # git repo. Bare repos are expected to end with ".git". # options - A Hash of options (default: {}): # :is_bare - Boolean whether to consider the repo as bare even # if the repo name does not end with ".git". # # Examples # # r = Repo.new("/Users/tom/dev/normal") # r = Repo.new("/Users/tom/public/bare.git") # r = Repo.new("/Users/tom/public/bare", {:is_bare => true}) # # Returns a newly initialized Grit::Repo. # Raises Grit::InvalidGitRepositoryError if the path exists but is not # a Git repository. # Raises Grit::NoSuchPathError if the path does not exist. def initialize(path, options = {}) epath = File.expand_path(path) if File.exist?(File.join(epath, '.git')) self.working_dir = epath self.path = File.join(epath, '.git') @bare = false elsif File.exist?(epath) && (epath =~ /\.git$/ || options[:is_bare]) self.path = epath @bare = true elsif File.exist?(epath) raise InvalidGitRepositoryError.new(epath) else raise NoSuchPathError.new(epath) end self.git = Git.new(self.path, work_tree: self.working_dir) end # Public: Initialize a git repository (create it on the filesystem). By # default, the newly created repository will contain a working directory. # If you would like to create a bare repo, use Grit::Repo.init_bare. # # path - The String full path to the repo. Traditionally ends with # "/.git". # git_options - A Hash of additional options to the git init command # (default: {}). # repo_options - A Hash of additional options to the Grit::Repo.new call # (default: {}). # # Examples # # Grit::Repo.init('/var/git/myrepo.git') # # Returns the newly created Grit::Repo. def self.init(path, git_options = {}, repo_options = {}) git_options = {:base => false}.merge(git_options) git = Git.new(path) git.fs_mkdir('..') git.init(git_options, path) self.new(path, repo_options) end # Public: Initialize a bare git repository (create it on the filesystem). # # path - The String full path to the repo. Traditionally ends with # "/.git". # git_options - A Hash of additional options to the git init command # (default: {}). # repo_options - A Hash of additional options to the Grit::Repo.new call # (default: {}). # # Examples # # Grit::Repo.init_bare('/var/git/myrepo.git') # # Returns the newly created Grit::Repo. def self.init_bare(path, git_options = {}, repo_options = {}) git_options = {:bare => true}.merge(git_options) git = Git.new(path) git.fs_mkdir('..') git.init(git_options) repo_options = {:is_bare => true}.merge(repo_options) self.new(path, repo_options) end # Public: Initialize a bare Git repository (create it on the filesystem) # or, if the repo already exists, simply return it. # # path - The String full path to the repo. Traditionally ends with # "/.git". # git_options - A Hash of additional options to the git init command # (default: {}). # repo_options - A Hash of additional options to the Grit::Repo.new call # (default: {}). # # Returns the new or existing Grit::Repo. def self.init_bare_or_open(path, git_options = {}, repo_options = {}) git = Git.new(path) unless git.exist? git.fs_mkdir(path) git.init(git_options) end self.new(path, repo_options) end # Public: Create a bare fork of this repository. # # path - The String full path of where to create the new fork. # Traditionally ends with "/.git". # options - The Hash of additional options to the git clone command. # These options will be merged on top of the default Hash: # {:bare => true, :shared => true}. # # Returns the newly forked Grit::Repo. def fork_bare(path, options = {}) default_options = {:bare => true, :shared => true} real_options = default_options.merge(options) Git.new(path).fs_mkdir('..') self.git.clone(real_options, self.path, path) Repo.new(path) end # Public: Fork a bare git repository from another repo. # # path - The String full path of the repo from which to fork.. # Traditionally ends with "/.git". # options - The Hash of additional options to the git clone command. # These options will be merged on top of the default Hash: # {:bare => true, :shared => true}. # # Returns the newly forked Grit::Repo. def fork_bare_from(path, options = {}) default_options = {:bare => true, :shared => true} real_options = default_options.merge(options) Git.new(self.path).fs_mkdir('..') self.git.clone(real_options, path, self.path) Repo.new(self.path) end # Public: Return the full Git objects from the given SHAs. Only Commit # objects are parsed for now. # # *shas - Array of String SHAs. # # Returns an Array of Grit objects (Grit::Commit). def batch(*shas) shas.flatten! text = git.native(:cat_file, {:batch => true, :input => (shas * "\n")}) parse_batch(text) end # Parses `git cat-file --batch` output, returning an array of Grit objects. # # text - Raw String output. # # Returns an Array of Grit objects (Grit::Commit). def parse_batch(text) io = StringIO.new(text) objects = [] while line = io.gets sha, type, size = line.split(" ", 3) parser = BATCH_PARSERS[type] if type == 'missing' || !parser io.seek(size.to_i + 1, IO::SEEK_CUR) objects << nil next end object = io.read(size.to_i + 1) objects << parser.parse_batch(self, sha, size, object) end objects end # The project's description. Taken verbatim from GIT_REPO/description # # Returns String def description self.git.fs_read('description').chomp end def blame(file, commit = nil) Blame.new(self, file, commit) end # An array of Head objects representing the branch heads in # this repo # # Returns Grit::Head[] (baked) def heads Head.find_all(self) end def head_count Head.count_all(self) end alias_method :branches, :heads alias_method :branch_count, :head_count def get_head(head_name) heads.find { |h| h.name == head_name } end def is_head?(head_name) get_head(head_name) end # Object reprsenting the current repo head. # # Returns Grit::Head (baked) def head Head.current(self) end # Commits current index # # Returns true/false if commit worked def commit_index(message) self.git.commit({}, '-m', message) end # Commits all tracked and modified files # # Returns true/false if commit worked def commit_all(message) self.git.commit({}, '-a', '-m', message) end # Adds files to the index def add(*files) self.git.add({}, *files.flatten) end # Remove files from the index def remove(*files) self.git.rm({}, *files.flatten) end def blame_tree(commit, path = nil) commit_array = self.git.blame_tree(commit, path) final_array = {} commit_array.each do |file, sha| final_array[file] = commit(sha) end final_array end def status Status.new(self) end # An array of Tag objects that are available in this repo # # Returns Grit::Tag[] (baked) def tags Tag.find_all(self) end def tag_count Tag.count_all(self) end # Finds the most recent annotated tag name that is reachable from a commit. # # @repo.recent_tag_name('master') # # => "v1.0-0-abcdef" # # committish - optional commit SHA, branch, or tag name. # options - optional hash of options to pass to git. # Default: {:always => true} # :tags => true # use lightweight tags too. # :abbrev => Integer # number of hex digits to form the unique # name. Defaults to 7. # :long => true # always output tag + commit sha # # see `git describe` docs for more options. # # Returns the String tag name, or just the commit if no tag is # found. If there have been updates since the tag was made, a # suffix is added with the number of commits since the tag, and # the abbreviated object name of the most recent commit. # Returns nil if the committish value is not found. def recent_tag_name(committish = nil, options = {}) value = git.describe({:always => true}.update(options), committish.to_s).to_s.strip value.size.zero? ? nil : value end # An array of Remote objects representing the remote branches in # this repo # # Returns Grit::Remote[] (baked) def remotes Remote.find_all(self) end def remote_count Remote.count_all(self) end def remote_list self.git.list_remotes end def remote_add(name, url) self.git.remote({}, 'add', name, url) end def remote_fetch(name) self.git.fetch({}, name) end # takes an array of remote names and last pushed dates # fetches from all of the remotes where the local fetch # date is earlier than the passed date, then records the # last fetched date # # { 'origin' => date, # 'peter => date, # } def remotes_fetch_needed(remotes) remotes.each do |remote, date| # TODO: check against date self.remote_fetch(remote) end end # An array of Ref objects representing the refs in # this repo # # Returns Grit::Ref[] (baked) def refs [ Head.find_all(self), Tag.find_all(self), Remote.find_all(self) ].flatten end # returns an array of hashes representing all references def refs_list refs = self.git.for_each_ref refarr = refs.split("\n").map do |line| shatype, ref = line.split("\t") sha, type = shatype.split(' ') [ref, sha, type] end refarr end def delete_ref(ref) self.git.native(:update_ref, {:d => true}, ref) end def commit_stats(start = 'master', max_count = 10, skip = 0) options = {:max_count => max_count, :skip => skip} CommitStats.find_all(self, start, options) end # An array of Commit objects representing the history of a given ref/commit # +start+ is the branch/commit name (default 'master') # +max_count+ is the maximum number of commits to return (default 10, use +false+ for all) # +skip+ is the number of commits to skip (default 0) # # Returns Grit::Commit[] (baked) def commits(start = 'master', max_count = 10, skip = 0) options = {:max_count => max_count, :skip => skip} Commit.find_all(self, start, options) end # The Commits objects that are reachable via +to+ but not via +from+ # Commits are returned in chronological order. # +from+ is the branch/commit name of the younger item # +to+ is the branch/commit name of the older item # # Returns Grit::Commit[] (baked) def commits_between(from, to) Commit.find_all(self, "#{from}..#{to}").reverse end def fast_forwardable?(to, from) mb = self.git.native(:merge_base, {}, [to, from]).strip mb == from end # The Commits objects that are newer than the specified date. # Commits are returned in chronological order. # +start+ is the branch/commit name (default 'master') # +since+ is a string representing a date/time # +extra_options+ is a hash of extra options # # Returns Grit::Commit[] (baked) def commits_since(start = 'master', since = '1970-01-01', extra_options = {}) options = {:since => since}.merge(extra_options) Commit.find_all(self, start, options) end # The number of commits reachable by the given branch/commit # +start+ is the branch/commit name (default 'master') # # Returns Integer def commit_count(start = 'master') Commit.count(self, start) end # The Commit object for the specified id # +id+ is the SHA1 identifier of the commit # # Returns Grit::Commit (baked) def commit(id) options = {:max_count => 1} Commit.find_all(self, id, options).first end # Returns a list of commits that is in +other_repo+ but not in self # # Returns Grit::Commit[] def commit_deltas_from(other_repo, ref = "master", other_ref = "master") # TODO: we should be able to figure out the branch point, rather than # rev-list'ing the whole thing repo_refs = self.git.rev_list({}, ref).strip.split("\n") other_repo_refs = other_repo.git.rev_list({}, other_ref).strip.split("\n") (other_repo_refs - repo_refs).map do |refn| Commit.find_all(other_repo, refn, {:max_count => 1}).first end end def objects(refs) refs = refs.split(/\s+/) if refs.respond_to?(:to_str) self.git.rev_list({:objects => true, :timeout => false}, *refs). split("\n").map { |a| a[0, 40] } end def commit_objects(refs) refs = refs.split(/\s+/) if refs.respond_to?(:to_str) self.git.rev_list({:timeout => false}, *refs).split("\n").map { |a| a[0, 40] } end def objects_between(ref1, ref2 = nil) if ref2 refs = "#{ref2}..#{ref1}" else refs = ref1 end self.objects(refs) end def diff_objects(commit_sha, parents = true) revs = [] Grit.no_quote = true if parents # PARENTS: revs = self.git.diff_tree({:timeout => false, :r => true, :t => true, :m => true}, commit_sha). strip.split("\n").map{ |a| r = a.split(' '); r[3] if r[1] != '160000' } else # NO PARENTS: revs = self.git.native(:ls_tree, {:timeout => false, :r => true, :t => true}, commit_sha). split("\n").map{ |a| a.split("\t").first.split(' ')[2] } end revs << self.commit(commit_sha).tree.id Grit.no_quote = false return revs.uniq.compact end # The Tree object for the given treeish reference # +treeish+ is the reference (default 'master') # +paths+ is an optional Array of directory paths to restrict the tree (default []) # # Examples # repo.tree('master', ['lib/']) # # Returns Grit::Tree (baked) def tree(treeish = 'master', paths = []) Tree.construct(self, treeish, paths) end # quick way to get a simple array of hashes of the entries # of a single tree or recursive tree listing from a given # sha or reference # +treeish+ is the reference (default 'master') # +options+ is a hash or options - currently only takes :recursive # # Examples # repo.lstree('master', :recursive => true) # # Returns array of hashes - one per tree entry def lstree(treeish = 'master', options = {}) # check recursive option opts = {:timeout => false, :l => true, :t => true, :z => true} if options[:recursive] opts[:r] = true end # mode, type, sha, size, path revs = self.git.native(:ls_tree, opts, treeish) lines = revs.split("\0") revs = lines.map do |a| stuff, path = a.split("\t") mode, type, sha, size = stuff.split(" ") entry = {:mode => mode, :type => type, :sha => sha, :path => path} entry[:size] = size.strip.to_i if size.strip != '-' entry end revs end def object(sha) obj = git.get_git_object(sha) raw = Grit::GitRuby::Internal::RawObject.new(obj[:type], obj[:content]) object = Grit::GitRuby::GitObject.from_raw(raw) object.sha = sha object end # The Blob object for the given id # +id+ is the SHA1 id of the blob # # Returns Grit::Blob (unbaked) def blob(id) Blob.create(self, :id => id) end # The commit log for a treeish # # Returns Grit::Commit[] def log(commit = 'master', path = nil, options = {}) default_options = {:pretty => "raw"} actual_options = default_options.merge(options) arg = path ? [commit, '--', path] : [commit] commits = self.git.log(actual_options, *arg) Commit.list_from_string(self, commits) end # The diff from commit +a+ to commit +b+, optionally restricted to the given file(s) # +a+ is the base commit # +b+ is the other commit # +paths+ is an optional list of file paths on which to restrict the diff def diff(a, b, *paths) diff = self.git.native('diff', {}, a, b, '--', *paths) if diff =~ /diff --git a/ diff = diff.sub(/.*?(diff --git a)/m, '\1') else diff = '' end Diff.list_from_string(self, diff) end # The commit diff for the given commit # +commit+ is the commit name/id # # Returns Grit::Diff[] def commit_diff(commit) Commit.diff(self, commit) end # Write an archive directly to a file # +treeish+ is the treeish name/id (default 'master') # +prefix+ is the optional prefix (default nil) # +filename+ is the name of the file (default 'archive.tar.gz') # +format+ is the optional format (default nil) # +compress_cmd+ is the command to run the output through (default ['gzip']) # # Returns nothing def archive_to_file(treeish = 'master', prefix = nil, filename = 'archive.tar.gz', format = nil, compress_cmd = %W(gzip)) git_archive_cmd = %W(#{Git.git_binary} --git-dir=#{self.git.git_dir} archive) git_archive_cmd << "--prefix=#{prefix}" if prefix git_archive_cmd << "--format=#{format}" if format git_archive_cmd += %W(-- #{treeish}) open(filename, 'w') do |file| pipe_rd, pipe_wr = IO.pipe git_archive_pid = spawn(*git_archive_cmd, :out => pipe_wr) pipe_wr.close compress_pid = spawn(*compress_cmd, :in => pipe_rd, :out => file) pipe_rd.close Process.waitpid(git_archive_pid) Process.waitpid(compress_pid) end end # Enable git-daemon serving of this repository by writing the # git-daemon-export-ok file to its git directory # # Returns nothing def enable_daemon_serve self.git.fs_write(DAEMON_EXPORT_FILE, '') end # Disable git-daemon serving of this repository by ensuring there is no # git-daemon-export-ok file in its git directory # # Returns nothing def disable_daemon_serve self.git.fs_delete(DAEMON_EXPORT_FILE) end def gc_auto self.git.gc({:auto => true}) end # The list of alternates for this repo # # Returns Array[String] (pathnames of alternates) def alternates alternates_path = "objects/info/alternates" self.git.fs_read(alternates_path).strip.split("\n") rescue Errno::ENOENT [] end # Sets the alternates # +alts+ is the Array of String paths representing the alternates # # Returns nothing def alternates=(alts) alts.each do |alt| unless File.exist?(alt) raise "Could not set alternates. Alternate path #{alt} must exist" end end if alts.empty? self.git.fs_write('objects/info/alternates', '') else self.git.fs_write('objects/info/alternates', alts.join("\n")) end end def config @config ||= Config.new(self) end def index Index.new(self) end def update_ref(head, commit_sha) return nil if !commit_sha || (commit_sha.size != 40) self.git.fs_write("refs/heads/#{head}", commit_sha) commit_sha end # Rename the current repository directory. # +name+ is the new name # # Returns nothing def rename(name) if @bare self.git.fs_move('/', "../#{name}") else self.git.fs_move('/', "../../#{name}") end end def grep(searchtext, contextlines = 3, branch = 'master') context_arg = '-C ' + contextlines.to_s result = git.native(:grep, {}, '-n', '-E', '-i', '-z', '--heading', '--break', context_arg, searchtext, branch).encode('UTF-8', invalid: :replace, undef: :replace, replace: '') greps = [] filematches = result.split("\n\n") filematches.each do |filematch| binary = false file = '' matches = filematch.split("--\n") matches.each_with_index do |match, i| content = [] startline = 0 lines = match.split("\n") if i == 0 text = lines.first lines.slice!(0) file = text[/^Binary file (.+) matches$/] if file binary = true else text.slice! /^#{branch}:/ file = text end end lines.each_with_index do |line, j| line.chomp! number, text = line.split("\0", 2) if j == 0 startline = number.to_i end content << text end greps << Grit::Grep.new(self, file, startline, content, binary) end end greps end def final_escape(str) str.gsub('"', '\\"') end # Accepts quoted strings and negation (-) operator in search strings def advanced_grep(searchtext, contextlines = 3, branch = 'master') # If there's not an even number of quote marks, get rid of them all searchtext = searchtext.gsub('"', '') if searchtext.count('"') % 2 == 1 # Escape the text, but allow spaces and quote marks (by unescaping them) searchtext = Shellwords.shellescape(searchtext).gsub('\ ',' ').gsub('\\"','"') # Shellwords happens to parse search terms really nicely! terms = Shellwords.split(searchtext) term_args = [] negative_args = [] # For each search term (either a word or a quoted string), add the relevant term argument to either the term # args array, or the negative args array, used for two separate calls to git grep terms.each do |term| arg_array = term_args if term[0] == '-' arg_array = negative_args term = term[1..-1] end arg_array.push '-e' arg_array.push final_escape(term) end context_arg = '-C ' + contextlines.to_s result = git.native(:grep, {pipeline: false}, '-n', '-F', '-i', '-z', '--heading', '--break', '--all-match', context_arg, *term_args, branch).encode('UTF-8', invalid: :replace, undef: :replace, replace: '') # Find files that match the negated terms; these will be excluded from the results excluded_files = Array.new unless negative_args.empty? negative = git.native(:grep, {pipeline: false}, '-F', '-i', '--files-with-matches', *negative_args, branch).encode('UTF-8', invalid: :replace, undef: :replace, replace: '') excluded_files = negative.split("\n").map {|text| text.partition(':')[2]} end greps = [] filematches = result.split("\n\n") filematches.each do |filematch| binary = false file = '' matches = filematch.split("--\n") matches.each_with_index do |match, i| content = [] startline = 0 lines = match.split("\n") if i == 0 text = lines.first lines.slice!(0) file = text[/^Binary file (.+) matches$/] if file puts "BINARY #{file}" binary = true else text.slice! /^#{branch}:/ file = text end end # Skip this result if the file is to be ignored (due to a negative match) next if excluded_files.include? file || ( excluded_files.include? text[/^Binary file (.+) matches$/, 1].partition(':')[2] ) lines.each_with_index do |line, j| line.chomp! number, text = line.split("\0", 2) if j == 0 startline = number.to_i end content << text end greps << Grit::Grep.new(self, file, startline, content, binary) end end greps end # Pretty object inspection def inspect %Q{#} end end # Repo end # Grit gitlab-grit-2.8.3/lib/grit/git-ruby/0000755000004100000410000000000013641342231017235 5ustar www-datawww-datagitlab-grit-2.8.3/lib/grit/git-ruby/git_object.rb0000644000004100000410000002007513641342231021677 0ustar www-datawww-data# # converted from the gitrb project # # authors: # Matthias Lederhofer # Simon 'corecode' Schubert # Scott Chacon # # provides native ruby access to git objects and pack files # # These classes translate the raw binary data kept in the sha encoded files # into parsed data that can then be used in another fashion require 'stringio' module Grit module GitRuby # class for author/committer/tagger lines class UserInfo attr_accessor :name, :email, :date, :offset def initialize(str) @email = '' @date = Time.now @offset = 0 m = /^(.*?) <(.*)> (\d+) ([+-])0*(\d+?)$/.match(str) if !m case str when /<.+>/ m, @name, @email = *str.match(/(.*) <(.+?)>/) else @name = str end else @name = m[1] @email = m[2] @date = Time.at(Integer(m[3])) @offset = (m[4] == "-" ? -1 : 1)*Integer(m[5]) end end def to_s "%s <%s> %s %+05d" % [@name, @email, @date.to_i, @offset] end end # base class for all git objects (blob, tree, commit, tag) class GitObject attr_accessor :repository attr_accessor :sha def GitObject.from_raw(rawobject, repository = nil) case rawobject.type when :blob return Blob.from_raw(rawobject, repository) when :tree return Tree.from_raw(rawobject, repository) when :commit return Commit.from_raw(rawobject, repository) when :tag return Tag.from_raw(rawobject, repository) else raise RuntimeError, "got invalid object-type" end end def initialize raise NotImplemented, "abstract class" end def type raise NotImplemented, "abstract class" end def raw_content raise NotImplemented, "abstract class" end def sha1 Digest::SHA1.hexdigest("%s %d\0" % \ [self.type, self.raw_content.length] + \ self.raw_content) end end class Blob < GitObject attr_accessor :content def self.from_raw(rawobject, repository) new(rawobject.content) end def initialize(content, repository=nil) @content = content @repository = repository end def type :blob end def raw_content @content end end class DirectoryEntry S_IFMT = 00170000 S_IFLNK = 0120000 S_IFREG = 0100000 S_IFDIR = 0040000 S_IFGITLINK = 0160000 attr_accessor :mode, :name, :sha1 def initialize(mode, filename, sha1o) @mode = 0 mode.each_byte do |i| @mode = (@mode << 3) | (i-'0'.getord(0)) end @name = filename @sha1 = sha1o if ![S_IFLNK, S_IFDIR, S_IFREG, S_IFGITLINK].include?(@mode & S_IFMT) raise RuntimeError, "unknown type for directory entry" end end # Filenames can have weird characters that throw grit's text parsing def safe_name name.gsub(/[\r\n\0]/, '') end def type case @mode & S_IFMT when S_IFGITLINK @type = :submodule when S_IFLNK @type = :link when S_IFDIR @type = :directory when S_IFREG @type = :file else raise RuntimeError, "unknown type for directory entry" end end def type=(type) case @type when :link @mode = (@mode & ~S_IFMT) | S_IFLNK when :directory @mode = (@mode & ~S_IFMT) | S_IFDIR when :file @mode = (@mode & ~S_IFMT) | S_IFREG when :submodule @mode = (@mode & ~S_IFMT) | S_IFGITLINK else raise RuntimeError, "invalid type" end end def format_type case type when :link 'link' when :directory 'tree' when :file 'blob' when :submodule 'commit' end end def format_mode "%06o" % @mode end def raw "%o %s\0%s" % [@mode, @name, [@sha1].pack("H*")] end end def self.read_bytes_until(io, char) string = '' if RUBY_VERSION > '1.9' while ((next_char = io.getc) != char) && !io.eof string += next_char end else while ((next_char = io.getc.chr) != char) && !io.eof string += next_char end end string end class Tree < GitObject attr_accessor :entry def self.from_raw(rawobject, repository=nil) raw = StringIO.new(rawobject.content) entries = [] while !raw.eof? mode = Grit::GitRuby.read_bytes_until(raw, ' ') file_name = Grit::GitRuby.read_bytes_until(raw, "\0") raw_sha = raw.read(20) sha = raw_sha.unpack("H*").first entries << DirectoryEntry.new(mode, file_name, sha) end new(entries, repository) end def initialize(entries=[], repository = nil) @entry = entries @repository = repository end def type :tree end def raw_content # TODO: sort correctly #@entry.sort { |a,b| a.name <=> b.name }. @entry.collect { |e| [[e.format_mode, e.format_type, e.sha1].join(' '), e.safe_name].join("\t") }.join("\n") end def actual_raw #@entry.collect { |e| e.raw.join(' '), e.name].join("\t") }.join("\n") end end class Commit < GitObject attr_accessor :author, :committer, :tree, :parent, :message, :headers def self.from_raw(rawobject, repository=nil) parent = [] tree = author = committer = nil headers, message = rawobject.content.split(/\n\n/, 2) all_headers = headers.split(/\n/).map { |header| header.split(/ /, 2) } all_headers.each do |key, value| case key when "tree" tree = value when "parent" parent.push(value) when "author" author = UserInfo.new(value) when "committer" committer = UserInfo.new(value) end end if not tree && author && committer raise RuntimeError, "incomplete raw commit object" end new(tree, parent, author, committer, message, headers, repository) end def initialize(tree, parent, author, committer, message, headers, repository=nil) @tree = tree @author = author @parent = parent @committer = committer @message = message @headers = headers @repository = repository end def type :commit end def raw_content "tree %s\n%sauthor %s\ncommitter %s\n\n" % [ @tree, @parent.collect { |i| "parent %s\n" % i }.join, @author, @committer] + @message end def raw_log(sha) output = "commit #{sha}\n" output += @headers + "\n\n" output += @message.split("\n").map { |l| ' ' + l }.join("\n") + "\n\n" end end class Tag < GitObject attr_accessor :object, :tag, :tagger, :message, :object_type attr_writer :type def self.from_raw(rawobject, repository=nil) headers, message = rawobject.content.split(/\n\n/, 2) headers = headers.split(/\n/).map { |header| header.split(' ', 2) } object = '' type = '' tag = '' tagger = '' headers.each do |key, value| case key when "object" object = value when "type" if !["blob", "tree", "commit", "tag"].include?(value) raise RuntimeError, "invalid type in tag" end type = value.to_sym when "tag" tag = value when "tagger" tagger = UserInfo.new(value) end end if not object && type && tag && tagger raise RuntimeError, "incomplete raw tag object" end new(object, type, tag, tagger, message, repository) end def initialize(object, type, tag, tagger, message, repository=nil) @object = object @type = type @object_type = type @tag = tag @tagger = tagger @repository = repository @message = message end def raw_content ("object %s\ntype %s\ntag %s\ntagger %s\n\n" % \ [@object, @type, @tag, @tagger]) + @message.to_s end def type :tag end end end end gitlab-grit-2.8.3/lib/grit/git-ruby/commit_db.rb0000644000004100000410000000270313641342231021521 0ustar www-datawww-databegin require 'sequel' module Grit class CommitDb SCHEMA_VERSION = 1 attr_accessor :db, :git def initialize(git_obj, index_location = nil) @git = git_obj db_file = File.join(index_location || @git.git_dir, 'commit_db') if !File.exists?(db_file) @db = Sequel.open "sqlite:///#{db_file}" setup_tables else @db = Sequel.open "sqlite:///#{db_file}" end end def rev_list(branch, options) end def update_db(branch = nil) # find all refs/heads, for each # add branch if not there # go though all commits in branch # add new commit_branches a # and commit_nodes for each new one # stop if reach commit that already has branch and node links end def setup_tables @db << "create table meta (meta_key text, meta_value text)" @db[:meta] << {:meta_key => 'schema', :meta_value => SCHEMA_VERSION} @db << "create table commits (id integer, sha text, author_date integer)" @db << "create table nodes (id integer, path text, type text)" @db << "create table branches (id integer, ref text, commit_id integer)" @db << "create table commit_branches (commit_id integer, branch_id integer)" @db << "create table commit_nodes (commit_id integer, node_id integer, node_sha string)" end end end rescue LoadError # no commit db end gitlab-grit-2.8.3/lib/grit/git-ruby/internal/0000755000004100000410000000000013641342231021051 5ustar www-datawww-datagitlab-grit-2.8.3/lib/grit/git-ruby/internal/pack.rb0000644000004100000410000002621413641342231022321 0ustar www-datawww-data# # converted from the gitrb project # # authors: # Matthias Lederhofer # Simon 'corecode' Schubert # Scott Chacon # # provides native ruby access to git objects and pack files # require 'zlib' require 'grit/git-ruby/internal/raw_object' require 'grit/git-ruby/internal/file_window' PACK_SIGNATURE = "PACK" PACK_IDX_SIGNATURE = "\377tOc".b module Grit module GitRuby module Internal class PackFormatError < StandardError end class PackStorage OBJ_OFS_DELTA = 6 OBJ_REF_DELTA = 7 FanOutCount = 256 SHA1Size = 20 IdxOffsetSize = 4 OffsetSize = 4 ExtendedOffsetSize = 8 CrcSize = 4 OffsetStart = FanOutCount * IdxOffsetSize SHA1Start = OffsetStart + OffsetSize EntrySize = OffsetSize + SHA1Size EntrySizeV2 = SHA1Size + CrcSize + OffsetSize def initialize(file) if file =~ /\.idx$/ file = file[0...-3] + 'pack' end @name = file @cache = {} init_pack end def with_idx(index_file = nil) index_file ||= @name[0...-4] + 'idx' begin idxfile = File.open(index_file, 'rb') rescue Errno::ENOENT => boom # file went away. bail out without yielding. return end # read header sig = idxfile.read(4) ver = idxfile.read(4).unpack("N")[0] if sig == PACK_IDX_SIGNATURE if(ver != 2) raise PackFormatError, "pack #@name has unknown pack file version #{ver}" end @version = 2 else @version = 1 end idx = FileWindow.new(idxfile, @version) yield idx idx.unmap ensure idxfile.close if idxfile end def with_packfile begin packfile = File.open(@name, 'rb') rescue Errno::ENOENT # file went away. bail out without yielding. return end yield packfile ensure packfile.close if packfile end def cache_objects @cache = {} with_packfile do |packfile| each_entry do |sha, offset| data, type = unpack_object(packfile, offset, {:caching => true}) if data @cache[sha] = RawObject.new(OBJ_TYPES[type], data) end end end end def name @name end def close # shouldnt be anything open now end # given an index file, list out the shas that it's packfile contains def get_shas shas = [] each_sha1 { |sha| shas << sha.unpack("H*")[0] } shas end def [](sha1) if obj = @cache[sha1] return obj end offset = find_object(sha1) return nil if !offset @cache[sha1] = obj = parse_object(offset) return obj end def init_pack with_idx do |idx| @offsets = [0] FanOutCount.times do |i| pos = idx[i * IdxOffsetSize,IdxOffsetSize].unpack('N')[0] if pos < @offsets[i] raise PackFormatError, "pack #@name has discontinuous index #{i}" end @offsets << pos end @size = @offsets[-1] end end def each_entry with_idx do |idx| if @version == 2 data = read_data_v2(idx) data.each do |sha1, crc, offset| yield sha1, offset end else pos = OffsetStart @size.times do offset = idx[pos,OffsetSize].unpack('N')[0] sha1 = idx[pos+OffsetSize,SHA1Size] pos += EntrySize yield sha1, offset end end end end def read_data_v2(idx) data = [] pos = OffsetStart @size.times do |i| data[i] = [idx[pos,SHA1Size], 0, 0] pos += SHA1Size end @size.times do |i| crc = idx[pos,CrcSize] data[i][1] = crc pos += CrcSize end @size.times do |i| offset = idx[pos,OffsetSize].unpack('N')[0] data[i][2] = offset pos += OffsetSize end data end private :read_data_v2 def each_sha1 with_idx do |idx| if @version == 2 data = read_data_v2(idx) data.each do |sha1, crc, offset| yield sha1 end else pos = SHA1Start @size.times do sha1 = idx[pos,SHA1Size] pos += EntrySize yield sha1 end end end end def find_object_in_index(idx, sha1) slot = sha1.getord(0) return nil if !slot first, last = @offsets[slot,2] while first < last mid = (first + last) / 2 if @version == 2 midsha1 = idx[OffsetStart + (mid * SHA1Size), SHA1Size] cmp = midsha1 <=> sha1 if cmp < 0 first = mid + 1 elsif cmp > 0 last = mid else pos = OffsetStart + (@size * (SHA1Size + CrcSize)) + (mid * OffsetSize) offset = idx[pos, OffsetSize].unpack('N')[0] if offset & 0x80000000 > 0 offset &= 0x7fffffff pos = OffsetStart + (@size * (SHA1Size + CrcSize + OffsetSize)) + (offset * ExtendedOffsetSize) words = idx[pos, ExtendedOffsetSize].unpack('NN') offset = (words[0] << 32) | words[1] end return offset end else midsha1 = idx[SHA1Start + mid * EntrySize,SHA1Size] cmp = midsha1 <=> sha1 if cmp < 0 first = mid + 1 elsif cmp > 0 last = mid else pos = OffsetStart + mid * EntrySize offset = idx[pos,OffsetSize].unpack('N')[0] return offset end end end nil end def find_object(sha1) obj = nil with_idx do |idx| obj = find_object_in_index(idx, sha1) end obj end private :find_object def parse_object(offset) obj = nil with_packfile do |packfile| data, type = unpack_object(packfile, offset) obj = RawObject.new(OBJ_TYPES[type], data) end obj end protected :parse_object def unpack_object(packfile, offset, options = {}) obj_offset = offset packfile.seek(offset) c = packfile.read(1).getord(0) size = c & 0xf type = (c >> 4) & 7 shift = 4 offset += 1 while c & 0x80 != 0 c = packfile.read(1).getord(0) size |= ((c & 0x7f) << shift) shift += 7 offset += 1 end return [false, false] if !(type == OBJ_COMMIT || type == OBJ_TREE) && options[:caching] case type when OBJ_OFS_DELTA, OBJ_REF_DELTA data, type = unpack_deltified(packfile, type, offset, obj_offset, size, options) #puts type when OBJ_COMMIT, OBJ_TREE, OBJ_BLOB, OBJ_TAG data = unpack_compressed(offset, size) else raise PackFormatError, "invalid type #{type}" end [data, type] end private :unpack_object def unpack_deltified(packfile, type, offset, obj_offset, size, options = {}) packfile.seek(offset) data = packfile.read(SHA1Size) if type == OBJ_OFS_DELTA i = 0 c = data.getord(i) base_offset = c & 0x7f while c & 0x80 != 0 c = data.getord(i += 1) base_offset += 1 base_offset <<= 7 base_offset |= c & 0x7f end base_offset = obj_offset - base_offset offset += i + 1 else base_offset = find_object(data) offset += SHA1Size end base, type = unpack_object(packfile, base_offset) return [false, false] if !(type == OBJ_COMMIT || type == OBJ_TREE) && options[:caching] delta = unpack_compressed(offset, size) [patch_delta(base, delta), type] end private :unpack_deltified def unpack_compressed(offset, destsize) outdata = "" with_packfile do |packfile| packfile.seek(offset) zstr = Zlib::Inflate.new while outdata.size < destsize indata = packfile.read(4096) if indata.size == 0 raise PackFormatError, 'error reading pack data' end outdata << zstr.inflate(indata) end if outdata.size > destsize raise PackFormatError, 'error reading pack data' end zstr.close end outdata end private :unpack_compressed def patch_delta(base, delta) src_size, pos = patch_delta_header_size(delta, 0) if src_size != base.size raise PackFormatError, 'invalid delta data' end dest_size, pos = patch_delta_header_size(delta, pos) dest = "" while pos < delta.size c = delta.getord(pos) pos += 1 if c & 0x80 != 0 pos -= 1 cp_off = cp_size = 0 cp_off = delta.getord(pos += 1) if c & 0x01 != 0 cp_off |= delta.getord(pos += 1) << 8 if c & 0x02 != 0 cp_off |= delta.getord(pos += 1) << 16 if c & 0x04 != 0 cp_off |= delta.getord(pos += 1) << 24 if c & 0x08 != 0 cp_size = delta.getord(pos += 1) if c & 0x10 != 0 cp_size |= delta.getord(pos += 1) << 8 if c & 0x20 != 0 cp_size |= delta.getord(pos += 1) << 16 if c & 0x40 != 0 cp_size = 0x10000 if cp_size == 0 pos += 1 dest << base[cp_off,cp_size] elsif c != 0 dest << delta[pos,c] pos += c else raise PackFormatError, 'invalid delta data' end end dest end private :patch_delta def patch_delta_header_size(delta, pos) size = 0 shift = 0 begin c = delta.getord(pos) if c == nil raise PackFormatError, 'invalid delta header' end pos += 1 size |= (c & 0x7f) << shift shift += 7 end while c & 0x80 != 0 [size, pos] end private :patch_delta_header_size end end end end gitlab-grit-2.8.3/lib/grit/git-ruby/internal/raw_object.rb0000644000004100000410000000153313641342231023517 0ustar www-datawww-data# # converted from the gitrb project # # authors: # Matthias Lederhofer # Simon 'corecode' Schubert # # provides native ruby access to git objects and pack files # require 'digest/sha1' module Grit module GitRuby module Internal OBJ_NONE = 0 OBJ_COMMIT = 1 OBJ_TREE = 2 OBJ_BLOB = 3 OBJ_TAG = 4 OBJ_TYPES = [nil, :commit, :tree, :blob, :tag].freeze class RawObject attr_accessor :type, :content def initialize(type, content) @type = type @content = content end def sha1 Digest::SHA1.digest("%s %d\0" % [@type, @content.length] + @content) end def to_hash { :type => @type, :content => @content } end end end end end gitlab-grit-2.8.3/lib/grit/git-ruby/internal/file_window.rb0000644000004100000410000000242613641342231023710 0ustar www-datawww-data# # converted from the gitrb project # # authors: # Matthias Lederhofer # Simon 'corecode' Schubert # Scott Chacon # # provides native ruby access to git objects and pack files # module Grit module GitRuby module Internal class FileWindow def initialize(file, version = 1) @file = file @offset = nil if version == 2 @global_offset = 8 else @global_offset = 0 end end def unmap @file = nil end def [](*idx) idx = idx[0] if idx.length == 1 case idx when Range offset = idx.first len = idx.last - idx.first + idx.exclude_end? ? 0 : 1 when Fixnum offset = idx len = nil when Array offset, len = idx else raise RuntimeError, "invalid index param: #{idx.class}" end if @offset != offset @file.seek(offset + @global_offset) end @offset = offset + len ? len : 1 if not len @file.read(1).getord(0) else @file.read(len) end end end end end end gitlab-grit-2.8.3/lib/grit/git-ruby/internal/loose.rb0000644000004100000410000000771413641342231022530 0ustar www-datawww-data# # converted from the gitrb project # # authors: # Matthias Lederhofer # Simon 'corecode' Schubert # Scott Chacon # # provides native ruby access to git objects and pack files # require 'zlib' require 'digest/sha1' require 'grit/git-ruby/internal/raw_object' module Grit module GitRuby module Internal class LooseObjectError < StandardError end class LooseStorage def initialize(directory) @directory = directory end def [](sha1) sha1 = sha1.unpack("H*")[0] begin return nil unless sha1[0...2] && sha1[2..39] path = @directory + '/' + sha1[0...2] + '/' + sha1[2..39] get_raw_object(open(path, 'rb') { |f| f.read }) rescue Errno::ENOENT nil end end def get_raw_object(buf) if buf.bytesize < 2 raise LooseObjectError, "object file too small" end if legacy_loose_object?(buf) content = Zlib::Inflate.inflate(buf) header, content = content.split(/\0/, 2) if !header || !content raise LooseObjectError, "invalid object header" end type, size = header.split(/ /, 2) if !%w(blob tree commit tag).include?(type) || size !~ /^\d+$/ raise LooseObjectError, "invalid object header" end type = type.to_sym size = size.to_i else type, size, used = unpack_object_header_gently(buf) content = Zlib::Inflate.inflate(buf[used..-1]) end raise LooseObjectError, "size mismatch" if content.bytesize != size return RawObject.new(type, content) end # currently, I'm using the legacy format because it's easier to do # this function takes content and a type and writes out the loose object and returns a sha def put_raw_object(content, type) size = content.bytesize.to_s LooseStorage.verify_header(type, size) header = "#{type} #{size}\0" store = header + content sha1 = Digest::SHA1.hexdigest(store) path = @directory+'/'+sha1[0...2]+'/'+sha1[2..40] if !File.exists?(path) content = Zlib::Deflate.deflate(store) FileUtils.mkdir_p(@directory+'/'+sha1[0...2]) File.open(path, 'wb') do |f| f.write content end end return sha1 end # simply figure out the sha def self.calculate_sha(content, type) size = content.bytesize.to_s verify_header(type, size) header = "#{type} #{size}\0" store = header + content Digest::SHA1.hexdigest(store) end def self.verify_header(type, size) if !%w(blob tree commit tag).include?(type) || size !~ /^\d+$/ raise LooseObjectError, "invalid object header" end end # private def unpack_object_header_gently(buf) used = 0 c = buf.getord(used) used += 1 type = (c >> 4) & 7; size = c & 15; shift = 4; while c & 0x80 != 0 if buf.bytesize <= used raise LooseObjectError, "object file too short" end c = buf.getord(used) used += 1 size += (c & 0x7f) << shift shift += 7 end type = OBJ_TYPES[type] if ![:blob, :tree, :commit, :tag].include?(type) raise LooseObjectError, "invalid loose object type" end return [type, size, used] end private :unpack_object_header_gently def legacy_loose_object?(buf) word = (buf.getord(0) << 8) + buf.getord(1) buf.getord(0) == 0x78 && word % 31 == 0 end private :legacy_loose_object? end end end end gitlab-grit-2.8.3/lib/grit/git-ruby/repository.rb0000644000004100000410000005675313641342231022021 0ustar www-datawww-data# # converted from the gitrb project # # authors: # Matthias Lederhofer # Simon 'corecode' Schubert # Scott Chacon # # provides native ruby access to git objects and pack files # require 'grit/git-ruby/internal/raw_object' require 'grit/git-ruby/internal/pack' require 'grit/git-ruby/internal/loose' require 'grit/git-ruby/git_object' require 'rubygems' require 'diff/lcs' require 'diff/lcs/hunk' # have to do this so it doesn't interfere with Grit::Diff module Difference include Diff end module Grit module GitRuby class Repository class NoSuchShaFound < StandardError end class NoSuchPath < StandardError end attr_accessor :git_dir, :options def initialize(git_dir, options = {}) @git_dir = git_dir @options = options @packs = [] end # returns the loose objects object lazily def loose @loose ||= initloose end # returns the array of pack list objects def packs @packs ||= initpacks end # prints out the type, shas and content of all of the pack files def show packs.each do |p| puts p.name puts p.each_sha1 do |s| puts "**#{p[s].type}**" if p[s].type.to_s == 'commit' puts s.unpack('H*') puts p[s].content end end puts end end # returns a raw object given a SHA1 def get_raw_object_by_sha1(sha1o) raise NoSuchShaFound if sha1o.nil? || sha1o.empty? || !sha1o.is_a?(String) sha1 = [sha1o.chomp].pack("H*") # try packs packs.each do |pack| o = pack[sha1] return pack[sha1] if o end # try loose storage loose.each do |lsobj| o = lsobj[sha1] return o if o end # try packs again, maybe the object got packed in the meantime initpacks packs.each do |pack| o = pack[sha1] return o if o end # puts "*#{sha1o}*" raise NoSuchShaFound end def cached(key, object, do_cache = true) object end # returns GitRuby object of any type given a SHA1 def get_object_by_sha1(sha1) r = get_raw_object_by_sha1(sha1) return nil if !r GitObject.from_raw(r) end # writes a raw object into the git repo def put_raw_object(content, type) loose.first.put_raw_object(content, type) end # returns true or false if that sha exists in the db def object_exists?(sha1) sha_hex = [sha1].pack("H*") return true if in_packs?(sha_hex) return true if in_loose?(sha_hex) initpacks return true if in_packs?(sha_hex) #maybe the object got packed in the meantime false end # returns true if the hex-packed sha is in the packfiles def in_packs?(sha_hex) # try packs packs.each do |pack| return true if pack[sha_hex] end false end # returns true if the hex-packed sha is in the loose objects def in_loose?(sha_hex) loose.each do |lsobj| return true if lsobj[sha_hex] end false end # returns the file type (as a symbol) of this sha def cat_file_type(sha) get_raw_object_by_sha1(sha).type end # returns the file size (as an int) of this sha def cat_file_size(sha) get_raw_object_by_sha1(sha).content.size end # returns the raw file contents of this sha def cat_file(sha) get_object_by_sha1(sha).raw_content end # returns a 2-d hash of the tree # ['blob']['FILENAME'] = {:mode => '100644', :sha => SHA} # ['tree']['DIRNAME'] = {:mode => '040000', :sha => SHA} def list_tree(sha) data = {'blob' => {}, 'tree' => {}, 'link' => {}, 'commit' => {}} get_object_by_sha1(sha).entry.each do |e| data[e.format_type][e.name] = {:mode => e.format_mode, :sha => e.sha1} end data end # returns the raw (cat-file) output for a tree # if given a commit sha, it will print the tree of that commit # if given a path limiter array, it will limit the output to those # if asked for recrusive trees, will traverse trees def ls_tree(sha, paths = [], recursive = false) if paths.size > 0 # pathing part = [] paths.each do |path| part += ls_tree_path(sha, path) end return part.join("\n") else get_raw_tree(sha, recursive) end end def get_raw_tree(sha, recursive = false) o = get_raw_object_by_sha1(sha) if o.type == :commit tree = get_object_by_sha1(sha).tree elsif o.type == :tag commit_sha = get_object_by_sha1(sha).object tree = get_object_by_sha1(commit_sha).tree elsif o.type == :tree tree = sha else return nil end recursive ? get_raw_trees(tree) : cat_file(tree) end # Grabs tree contents recursively, # e.g. `git ls-tree -r sha` def get_raw_trees(sha, path = '') out = '' cat_file(sha).split("\n").each do |line| mode, type, sha, name = line.split(/\s/) if type == 'tree' full_name = path.empty? ? name : "#{path}/#{name}" out << get_raw_trees(sha, full_name) elsif path.empty? out << line + "\n" else out << line.gsub(name, "#{path}/#{name}") + "\n" end end out end # return array of tree entries ## TODO : refactor this to remove the fugly def ls_tree_path(sha, path, append = nil) tree = get_raw_tree(sha) if path =~ /\// paths = path.split('/') last = path[path.size - 1, 1] if (last == '/') && (paths.size == 1) append = append ? File.join(append, paths.first) : paths.first dir_name = tree.split("\n").select { |p| p.split("\t")[1] == paths.first }.first raise NoSuchPath if !dir_name next_sha = dir_name.split(' ')[2] tree = get_raw_tree(next_sha) tree = tree.split("\n") if append mod_tree = [] tree.each do |ent| (info, fpath) = ent.split("\t") mod_tree << [info, File.join(append, fpath)].join("\t") end mod_tree else tree end else raise NoSuchPath if tree.nil? next_path = paths.shift dir_name = tree.split("\n").select { |p| p.split("\t")[1] == next_path }.first raise NoSuchPath if !dir_name next_sha = dir_name.split(' ')[2] next_path = append ? File.join(append, next_path) : next_path if (last == '/') ls_tree_path(next_sha, paths.join("/") + '/', next_path) else ls_tree_path(next_sha, paths.join("/"), next_path) end end else raise NoSuchPath if tree.nil? tree = tree.split("\n") tree = tree.select { |p| p.split("\t")[1] == path } if append mod_tree = [] tree.each do |ent| (info, fpath) = ent.split("\t") mod_tree << [info, File.join(append, fpath)].join("\t") end mod_tree else tree end end end # returns an array of GitRuby Commit objects # [ [sha, raw_output], [sha, raw_output], [sha, raw_output] ... ] # # takes the following options: # :since - Time object specifying that you don't want commits BEFORE this # :until - Time object specifying that you don't want commit AFTER this # :first_parent - tells log to only walk first parent # :path_limiter - string or array of strings to limit path # :max_count - number to limit the output def log(sha, options = {}) @already_searched = {} walk_log(sha, options) end def truncate_arr(arr, sha) new_arr = [] arr.each do |a| if a[0] == sha return new_arr end new_arr << a end return new_arr end def rev_list(sha, options) if sha.is_a? Array (end_sha, sha) = sha end log = log(sha, options) log = log.sort { |a, b| a[2] <=> b[2] }.reverse if end_sha log = truncate_arr(log, end_sha) end # shorten the list if it's longer than max_count (had to get everything in branches) if options[:max_count] if (opt_len = options[:max_count].to_i) < log.size log = log[0, opt_len] end end if options[:pretty] == 'raw' log.map {|k, v| v }.join('') else log.map {|k, v| k }.join("\n") end end # called by log() to recursively walk the tree def walk_log(sha, opts, total_size = 0) return [] if @already_searched[sha] # to prevent rechecking branches @already_searched[sha] = true array = [] if (sha) o = get_raw_object_by_sha1(sha) if o.type == :tag commit_sha = get_object_by_sha1(sha).object c = get_object_by_sha1(commit_sha) else c = GitObject.from_raw(o) end return [] if c.type != :commit add_sha = true if opts[:since] && opts[:since].is_a?(Time) && (opts[:since] > c.committer.date) add_sha = false end if opts[:until] && opts[:until].is_a?(Time) && (opts[:until] < c.committer.date) add_sha = false end # follow all parents unless '--first-parent' is specified # subarray = [] if !c.parent.first && opts[:path_limiter] # check for the last commit add_sha = false end if (!opts[:max_count] || ((array.size + total_size) < opts[:max_count])) if !opts[:path_limiter] output = c.raw_log(sha) array << [sha, output, c.committer.date] end if (opts[:max_count] && (array.size + total_size) >= opts[:max_count]) return array end c.parent.each do |psha| if psha && !files_changed?(c.tree, get_object_by_sha1(psha).tree, opts[:path_limiter]) add_sha = false end subarray += walk_log(psha, opts, (array.size + total_size)) next if opts[:first_parent] end if opts[:path_limiter] && add_sha output = c.raw_log(sha) array << [sha, output, c.committer.date] end if add_sha array += subarray end end end array end def diff(commit1, commit2, options = {}) patch = '' commit_obj1 = get_object_by_sha1(commit1) tree1 = commit_obj1.tree if commit2 tree2 = get_object_by_sha1(commit2).tree else tree2 = get_object_by_sha1(commit_obj1.parent.first).tree end qdiff = quick_diff(tree1, tree2) qdiff.sort.each do |diff_arr| path, status, treeSHA1, treeSHA2 = *diff_arr format, lines, output = :unified, 3, '' file_length_difference = 0 fileA = treeSHA1 ? cat_file(treeSHA1) : '' fileB = treeSHA2 ? cat_file(treeSHA2) : '' sha1 = treeSHA1 || '0000000000000000000000000000000000000000' sha2 = treeSHA2 || '0000000000000000000000000000000000000000' data_old = fileA.split(/\n/).map! { |e| e.chomp } data_new = fileB.split(/\n/).map! { |e| e.chomp } diffs = Difference::LCS.diff(data_old, data_new) next if diffs.empty? a_path = "a/#{path.gsub('./', '')}" b_path = "b/#{path.gsub('./', '')}" header = "diff --git #{a_path} #{b_path}" if options[:full_index] header << "\n" + 'index ' + sha1 + '..' + sha2 header << ' 100644' if treeSHA2 # hard coding this because i don't think we use it else header << "\n" + 'index ' + sha1[0,7] + '..' + sha2[0,7] header << ' 100644' if treeSHA2 # hard coding this because i don't think we use it end header << "\n--- " + (treeSHA1 ? a_path : '/dev/null') header << "\n+++ " + (treeSHA2 ? b_path : '/dev/null') header += "\n" oldhunk = hunk = nil diffs.each do |piece| begin hunk = Difference::LCS::Hunk.new(data_old, data_new, piece, lines, file_length_difference) file_length_difference = hunk.file_length_difference next unless oldhunk if lines > 0 && hunk.overlaps?(oldhunk) hunk.unshift(oldhunk) else output << oldhunk.diff(format) end ensure oldhunk = hunk output << "\n" end end output << oldhunk.diff(format) output << "\n" patch << header + output.lstrip end patch rescue '' # one of the trees was bad or lcs isn't there - no diff end def quick_what_changed(t1, t2, path, type) changed = [] t1[type].each do |file, hsh| t2_file = t2[type][file] rescue nil full = File.join(path, file) if !t2_file changed << [full, 'added', hsh[:sha], nil] # not in parent elsif (hsh[:sha] != t2_file[:sha]) changed << [full, 'modified', hsh[:sha], t2_file[:sha]] # file changed end end if t1 t2[type].each do |file, hsh| if !t1 || !t1[type][file] changed << [File.join(path, file), 'removed', nil, hsh[:sha]] end end if t2 changed end # takes 2 tree shas and recursively walks them to find out what # files or directories have been modified in them and returns an # array of changes # [ [full_path, 'added', tree1_hash, nil], # [full_path, 'removed', nil, tree2_hash], # [full_path, 'modified', tree1_hash, tree2_hash] # ] def quick_diff(tree1, tree2, path = '.', recurse = true) # handle empty trees return changed if tree1 == tree2 t1 = list_tree(tree1) if tree1 t2 = list_tree(tree2) if tree2 # finding files that are different changed = quick_what_changed(t1, t2, path, 'blob') + quick_what_changed(t1, t2, path, 'link') t1['tree'].each do |dir, hsh| t2_tree = t2['tree'][dir] rescue nil full = File.join(path, dir) if !t2_tree if recurse changed += quick_diff(hsh[:sha], nil, full, true) else changed << [full, 'added', hsh[:sha], nil] # not in parent end elsif (hsh[:sha] != t2_tree[:sha]) if recurse changed += quick_diff(hsh[:sha], t2_tree[:sha], full, true) else changed << [full, 'modified', hsh[:sha], t2_tree[:sha]] # file changed end end end if t1 t2['tree'].each do |dir, hsh| t1_tree = t1['tree'][dir] rescue nil full = File.join(path, dir) if !t1_tree if recurse changed += quick_diff(nil, hsh[:sha], full, true) else changed << [full, 'removed', nil, hsh[:sha]] end end end if t2 changed end # returns true if the files in path_limiter were changed, or no path limiter # used by the log() function when passed with a path_limiter def files_changed?(tree_sha1, tree_sha2, path_limiter = nil) if path_limiter mod = quick_diff(tree_sha1, tree_sha2) files = mod.map { |c| c.first } path_limiter.to_a.each do |filepath| if files.include?(filepath) return true end end return false end true end def get_subtree(commit_sha, path) tree_sha = get_object_by_sha1(commit_sha).tree if path && !(path == '' || path == '.' || path == './') paths = path.split('/') paths.each do |pathname| tree = get_object_by_sha1(tree_sha) if entry = tree.entry.select { |e| e.name == pathname }.first tree_sha = entry.sha1 rescue nil else return false end end end tree_sha end def blame_tree(commit_sha, path) # find subtree tree_sha = get_subtree(commit_sha, path) return {} if !tree_sha looking_for = [] get_object_by_sha1(tree_sha).entry.each do |e| looking_for << File.join('.', e.name) end @already_searched = {} commits = look_for_commits(commit_sha, path, looking_for) # cleaning up array arr = {} commits.each do |commit_array| key = commit_array[0].gsub('./', '') arr[key] = commit_array[1] end arr end def look_for_commits(commit_sha, path, looking_for, options = {}) return [] if @already_searched[commit_sha] # to prevent rechecking branches @already_searched[commit_sha] = true commit = get_object_by_sha1(commit_sha) tree_sha = get_subtree(commit_sha, path) found_data = [] # at the beginning of the branch if commit.parent.size == 0 looking_for.each do |search| # prevents the rare case of multiple branch starting points with # files that have never changed if found_data.assoc(search) found_data << [search, commit_sha] end end return found_data end # go through the parents recursively, looking for somewhere this has been changed commit.parent.each do |pc| diff = quick_diff(tree_sha, get_subtree(pc, path), '.', false) # remove anything found looking_for.each do |search| if match = diff.assoc(search) found_data << [search, commit_sha, match] looking_for.delete(search) end end if looking_for.size <= 0 # we're done return found_data end found_data += look_for_commits(pc, path, looking_for) # recurse into parent return found_data if options[:first_parent] end ## TODO : find most recent commit with change in any parent found_data end # initialize a git repository def self.init(dir, bare = true) FileUtils.mkdir_p(dir) if !File.exists?(dir) FileUtils.cd(dir) do if(File.exists?('objects')) return false # already initialized else # initialize directory create_initial_config(bare) FileUtils.mkdir_p('refs/heads') FileUtils.mkdir_p('refs/tags') FileUtils.mkdir_p('objects/info') FileUtils.mkdir_p('objects/pack') FileUtils.mkdir_p('branches') add_file('description', 'Unnamed repository; edit this file to name it for gitweb.') add_file('HEAD', "ref: refs/heads/master\n") FileUtils.mkdir_p('hooks') FileUtils.cd('hooks') do add_file('applypatch-msg', '# add shell script and make executable to enable') add_file('post-commit', '# add shell script and make executable to enable') add_file('post-receive', '# add shell script and make executable to enable') add_file('post-update', '# add shell script and make executable to enable') add_file('pre-applypatch', '# add shell script and make executable to enable') add_file('pre-commit', '# add shell script and make executable to enable') add_file('pre-rebase', '# add shell script and make executable to enable') add_file('update', '# add shell script and make executable to enable') end FileUtils.mkdir_p('info') add_file('info/exclude', "# *.[oa]\n# *~") end end end def self.create_initial_config(bare = false) bare ? bare_status = 'true' : bare_status = 'false' config = "[core]\n\trepositoryformatversion = 0\n\tfilemode = true\n\tbare = #{bare_status}\n\tlogallrefupdates = true" add_file('config', config) end def self.add_file(name, contents) path = File.join(Dir.pwd, name) raise "Invalid path: #{path}" unless File.absolute_path(path) == path File.open(path, 'w') do |f| f.write contents end end def close @packs.each do |pack| pack.close end if @packs end protected def git_path(path) return "#@git_dir/#{path}" end private def initloose @loaded = [] @loose = [] load_loose(git_path('objects')) load_alternate_loose(git_path('objects')) @loose end def each_alternate_path(path) alt = File.join(path, 'info/alternates') return if !File.exists?(alt) File.readlines(alt).each do |line| path = line.chomp if path[0, 2] == '..' yield File.expand_path(File.join(@git_dir, 'objects', path)) # XXX this is here for backward compatibility with grit < 2.3.0 # relative alternate objects paths are expanded relative to the # objects directory, not the git repository directory. yield File.expand_path(File.join(@git_dir, path)) else yield path end end end def load_alternate_loose(pathname) # load alternate loose, too each_alternate_path pathname do |path| next if @loaded.include?(path) next if !File.exist?(path) load_loose(path) load_alternate_loose(path) end end def load_loose(path) @loaded << path return if !File.exists?(path) @loose << Grit::GitRuby::Internal::LooseStorage.new(path) end def initpacks close @loaded_packs = [] @packs = [] load_packs(git_path("objects/pack")) load_alternate_packs(git_path('objects')) @packs end def load_alternate_packs(pathname) each_alternate_path pathname do |path| full_pack = File.join(path, 'pack') next if @loaded_packs.include?(full_pack) load_packs(full_pack) load_alternate_packs(path) end end def load_packs(path) @loaded_packs << path return if !File.exists?(path) Dir.open(path) do |dir| dir.each do |entry| next if !(entry =~ /\.pack$/i) pack = Grit::GitRuby::Internal::PackStorage.new(File.join(path,entry)) if @options[:map_packfile] pack.cache_objects end @packs << pack end end end end end end gitlab-grit-2.8.3/lib/grit/tree.rb0000644000004100000410000000624713641342231016770 0ustar www-datawww-datamodule Grit class Tree extend Lazy lazy_reader :contents attr_reader :id attr_reader :mode attr_reader :name # Construct the contents of the tree # +repo+ is the Repo # +treeish+ is the reference # +paths+ is an optional Array of directory paths to restrict the tree # # Returns Grit::Tree (baked) def self.construct(repo, treeish, paths = []) output = repo.git.ls_tree({:raise => true}, treeish, *paths) self.allocate.construct_initialize(repo, treeish, output) end def construct_initialize(repo, id, text) @repo = repo @id = id @contents = [] text.split("\n").each do |line| @contents << content_from_string(repo, line) end @contents.compact! self end def lazy_source Tree.construct(@repo, @id, []) end # Create an unbaked Tree containing just the specified attributes # +repo+ is the Repo # +atts+ is a Hash of instance variable data # # Returns Grit::Tree (unbaked) def self.create(repo, atts) self.allocate.create_initialize(repo, atts) end # Initializer for Tree.create # +repo+ is the Repo # +atts+ is a Hash of instance variable data # # Returns Grit::Tree (unbaked) def create_initialize(repo, atts) @repo = repo atts.each do |k, v| instance_variable_set("@#{k}", v) end self end # Parse a content item and create the appropriate object # +repo+ is the Repo # +text+ is the single line containing the items data in `git ls-tree` format # # Returns Grit::Blob or Grit::Tree def content_from_string(repo, text) mode, type, id, name = text.split(/ |\t/, 4) case type when "tree" Tree.create(repo, :id => id, :mode => mode, :name => name) when "blob" Blob.create(repo, :id => id, :mode => mode, :name => name) when "link" Blob.create(repo, :id => id, :mode => mode, :name => name) when "commit" Submodule.create(repo, :id => id, :mode => mode, :name => name) else raise Grit::InvalidObjectType, type end end # Find the named object in this tree's contents # # Examples # Repo.new('/path/to/grit').tree/'lib' # # => # # Repo.new('/path/to/grit').tree/'README.txt' # # => # # # Returns Grit::Blob or Grit::Tree or nil if not found def /(file) if file =~ /\// file.split("/").inject(self) { |acc, x| acc/x } rescue nil else self.contents.find { |c| c.name == file } end end def basename File.basename(name) end # Pretty object inspection def inspect %Q{#} end # Find only Tree objects from contents def trees contents.select {|v| v.kind_of? Tree} end # Find only Blob objects from contents def blobs contents.select {|v| v.kind_of? Blob} end # Compares trees by name def <=>(other) name <=> other.name end end # Tree end # Grit gitlab-grit-2.8.3/lib/grit/status.rb0000644000004100000410000000756313641342231017356 0ustar www-datawww-datamodule Grit class Status include Enumerable attr_reader :files @base = nil @files = nil def initialize(base) @base = base construct_status end def changed @files.select { |k, f| f.type == 'M' } end def added @files.select { |k, f| f.type == 'A' } end def deleted @files.select { |k, f| f.type == 'D' } end def untracked @files.select { |k, f| f.untracked } end def pretty out = '' self.each do |file| out << file.path out << "\n\tsha(r) " + file.sha_repo.to_s + ' ' + file.mode_repo.to_s out << "\n\tsha(i) " + file.sha_index.to_s + ' ' + file.mode_index.to_s out << "\n\ttype " + file.type.to_s out << "\n\tstage " + file.stage.to_s out << "\n\tuntrac " + file.untracked.to_s out << "\n" end out << "\n" out end # enumerable method def [](file) @files[file] end def each @files.each do |k, file| yield file end end class StatusFile attr_accessor :path, :type, :stage, :untracked attr_accessor :mode_index, :mode_repo attr_accessor :sha_index, :sha_repo @base = nil def initialize(base, hash) @base = base @path = hash[:path] @type = hash[:type] @stage = hash[:stage] @mode_index = hash[:mode_index] @mode_repo = hash[:mode_repo] @sha_index = hash[:sha_index] @sha_repo = hash[:sha_repo] @untracked = hash[:untracked] end def blob(type = :index) if type == :repo @base.object(@sha_repo) else @base.object(@sha_index) rescue @base.object(@sha_repo) end end end private def construct_status @files = ls_files # find untracked in working dir Dir.glob(File.join(@base.working_dir, '**/*')) do |full_file| file = full_file.gsub "#{@base.working_dir}/", "" if !@files[file] @files[file] = {:path => file, :untracked => true} if !File.directory?(file) end end # find modified in tree diff_files.each do |path, data| @files[path] ? @files[path].merge!(data) : @files[path] = data end # find added but not committed - new files diff_index('HEAD').each do |path, data| @files[path] ? @files[path].merge!(data) : @files[path] = data end @files.each do |k, file_hash| @files[k] = StatusFile.new(@base, file_hash) end end # compares the index and the working directory def diff_files hsh = {} @base.git.diff_files({}).split("\n").each do |line| (info, file) = line.split("\t") (mode_src, mode_dest, sha_src, sha_dest, type) = info.split hsh[file] = {:path => file, :mode_file => mode_src.to_s[1, 7], :mode_index => mode_dest, :sha_file => sha_src, :sha_index => sha_dest, :type => type} end hsh end # compares the index and the repository def diff_index(treeish) hsh = {} @base.git.diff_index({}, treeish).split("\n").each do |line| (info, file) = line.split("\t") (mode_src, mode_dest, sha_src, sha_dest, type) = info.split hsh[file] = {:path => file, :mode_repo => mode_src.to_s[1, 7], :mode_index => mode_dest, :sha_repo => sha_src, :sha_index => sha_dest, :type => type} end hsh end def ls_files hsh = {} lines = @base.git.ls_files({:stage => true}) lines.split("\n").each do |line| (info, file) = line.split("\t") (mode, sha, stage) = info.split hsh[file] = {:path => file, :mode_index => mode, :sha_index => sha, :stage => stage} end hsh end end end gitlab-grit-2.8.3/lib/grit/index.rb0000644000004100000410000001644013641342231017134 0ustar www-datawww-datamodule Grit class Index # Public: Gets/Sets the Grit::Repo to which this index belongs. attr_accessor :repo # Public: Gets/Sets the Hash tree map that holds the changes to be made # in the next commit. attr_accessor :tree # Public: Gets/Sets the Grit::Tree object representing the tree upon # which the next commit will be based. attr_accessor :current_tree # Public: if a tree or commit is written, this stores the size of that object attr_reader :last_tree_size attr_reader :last_commit_size # Initialize a new Index object. # # repo - The Grit::Repo to which the index belongs. # # Returns the newly initialized Grit::Index. def initialize(repo) self.repo = repo self.tree = {} self.current_tree = nil end # Public: Add a file to the index. # # path - The String file path including filename (no slash prefix). # data - The String binary contents of the file. # # Returns nothing. def add(path, data) path = path.split('/') filename = path.pop current = self.tree path.each do |dir| current[dir] ||= {} node = current[dir] current = node end current[filename] = data end # Public: Delete the given file from the index. # # path - The String file path including filename (no slash prefix). # # Returns nothing. def delete(path) add(path, false) end # Public: Read the contents of the given Tree into the index to use as a # starting point for the index. # # tree - The String branch/tag/sha of the Git tree object. # # Returns nothing. def read_tree(tree) self.current_tree = self.repo.tree(tree) end # Public: Commit the contents of the index. This method supports two # formats for arguments: # # message - The String commit message. # options - An optional Hash of index options. # :parents - Array of String commit SHA1s or Grit::Commit # objects to attach this commit to to form a # new head (default: nil). # :actor - The Grit::Actor details of the user making # the commit (default: nil). # :last_tree - The String SHA1 of a tree to compare with # in order to avoid making empty commits # (default: nil). # :head - The String branch name to write this head to # (default: nil). # :committed_date - The Time that the commit was made. # (Default: Time.now) # :authored_date - The Time that the commit was authored. # (Default: committed_date) # # The legacy argument style looks like: # # message - The String commit message. # parents - Array of String commit SHA1s or Grit::Commit objects to # attach this commit to to form a new head (default: nil). # actor - The Grit::Actor details of the user making the commit # (default: nil). # last_tree - The String SHA1 of a tree to compare with in order to avoid # making empty commits (default: nil). # head - The String branch name to write this head to # (default: "master"). # # Returns a String of the SHA1 of the new commit. def commit(message, parents = nil, actor = nil, last_tree = nil, head = 'master') commit_tree_sha = nil if parents.is_a?(Hash) commit_tree_sha = parents[:commit_tree_sha] actor = parents[:actor] committer = parents[:committer] author = parents[:author] last_tree = parents[:last_tree] head = parents[:head] committed_date = parents[:committed_date] authored_date = parents[:authored_date] parents = parents[:parents] end committer ||= actor author ||= committer if commit_tree_sha tree_sha1 = commit_tree_sha else tree_sha1 = write_tree(self.tree, self.current_tree) end # don't write identical commits return false if tree_sha1 == last_tree contents = [] contents << ['tree', tree_sha1].join(' ') parents.each do |p| contents << ['parent', p].join(' ') end if parents committer ||= begin config = Config.new(self.repo) Actor.new(config['user.name'], config['user.email']) end author ||= committer committed_date ||= Time.now authored_date ||= committed_date contents << ['author', author.output(authored_date)].join(' ') contents << ['committer', committer.output(committed_date)].join(' ') contents << '' contents << message contents = contents.join("\n") @last_commit_size = contents.size commit_sha1 = self.repo.git.put_raw_object(contents, 'commit') self.repo.update_ref(head, commit_sha1) if head commit_sha1 end # Recursively write a tree to the index. # # tree - The Hash tree map: # key - The String directory or filename. # val - The Hash submap or the String contents of the file. # now_tree - The Grit::Tree representing the a previous tree upon which # this tree will be based (default: nil). # # Returns the String SHA1 String of the tree. def write_tree(tree = nil, now_tree = nil) tree = self.tree if !tree tree_contents = {} # fill in original tree now_tree = read_tree(now_tree) if(now_tree && now_tree.is_a?(String)) now_tree.contents.each do |obj| sha = [obj.id].pack("H*") k = obj.name k += '/' if (obj.class == Grit::Tree) tmode = obj.mode.to_i.to_s ## remove zero-padding tree_contents[k] = "%s %s\0%s" % [tmode, obj.name, sha] end if now_tree # overwrite with new tree contents tree.each do |k, v| case v when Array sha, mode = v if sha.size == 40 # must be a sha sha = [sha].pack("H*") mode = mode.to_i.to_s # leading 0s not allowed k = k.split('/').last # slashes not allowed str = "%s %s\0%s" % [mode, k, sha] tree_contents[k] = str end when String sha = write_blob(v) sha = [sha].pack("H*") str = "%s %s\0%s" % ['100644', k, sha] tree_contents[k] = str when Hash ctree = now_tree/k if now_tree sha = write_tree(v, ctree) sha = [sha].pack("H*") str = "%s %s\0%s" % ['40000', k, sha] tree_contents[k + '/'] = str when false tree_contents.delete(k) end end tr = tree_contents.sort.map { |k, v| v }.join('') @last_tree_size = tr.size self.repo.git.put_raw_object(tr, 'tree') end # Write a blob to the index. # # data - The String data to write. # # Returns the String SHA1 of the new blob. def write_blob(data) self.repo.git.put_raw_object(data, 'blob') end end # Index end # Grit gitlab-grit-2.8.3/lib/grit/commit_stats.rb0000644000004100000410000000653013641342231020532 0ustar www-datawww-datamodule Grit class CommitStats attr_reader :id, :files, :additions, :deletions, :total # Instantiate a new CommitStats # +id+ is the id of the commit # +files+ is an array of : # [ [filename, adds, deletes, total], # [filename, adds, deletes, total], # [filename, adds, deletes, total] ] # # Returns Grit::CommitStats (baked) def initialize(repo, id, files) @repo = repo @id = id @files = files @additions = files.inject(0) { |total, a| total += a[1] } @deletions = files.inject(0) { |total, a| total += a[2] } @total = files.inject(0) { |total, a| total += a[3] } end # Find all commit stats matching the given criteria. # +repo+ is the Repo # +ref+ is the ref from which to begin (SHA1 or name) or nil for --all # +options+ is a Hash of optional arguments to git # :max_count is the maximum number of commits to fetch # :skip is the number of commits to skip # # Returns assoc array [sha, Grit::Commit[] (baked)] def self.find_all(repo, ref, options = {}) allowed_options = [:max_count, :skip, :since] default_options = {:numstat => true} actual_options = default_options.merge(options) if ref output = repo.git.log(actual_options, ref) else output = repo.git.log(actual_options.merge(:all => true)) end self.list_from_string(repo, output) end # Parse out commit information into an array of baked Commit objects # +repo+ is the Repo # +text+ is the text output from the git command (raw format) # # Returns assoc array [sha, Grit::Commit[] (baked)] def self.list_from_string(repo, text) lines = text.split("\n") commits = [] while !lines.empty? id = lines.shift.split.last lines.shift lines.shift lines.shift message_lines = [] message_lines << lines.shift[4..-1] while lines.first =~ /^ {4}/ || lines.first == '' lines.shift while lines.first && lines.first.empty? files = [] while lines.first =~ /^([-\d]+)\s+([-\d]+)\s+(.+)/ (additions, deletions, filename) = lines.shift.split(nil, 3) additions = additions.to_i deletions = deletions.to_i total = additions + deletions files << [filename, additions, deletions, total] end lines.shift while lines.first && lines.first.empty? commits << [id, CommitStats.new(repo, id, files)] end commits end # Pretty object inspection def inspect %Q{#} end # Convert to an easy-to-traverse structure def to_diffstat files.map do |metadata| DiffStat.new(*metadata) end end # private def to_hash { 'id' => id, 'files' => files, 'additions' => additions, 'deletions' => deletions, 'total' => total } end end # CommitStats class DiffStat attr_reader :filename, :additions, :deletions def initialize(filename, additions, deletions, total=nil) @filename, @additions, @deletions = filename, additions, deletions end def net additions - deletions end def inspect "#{filename}: +#{additions} -#{deletions}" end end end # Grit gitlab-grit-2.8.3/lib/grit/grep.rb0000644000004100000410000000060313641342231016754 0ustar www-datawww-datamodule Grit class Grep attr_reader :repo attr_reader :filename attr_reader :startline attr_reader :content attr_reader :is_binary def initialize(repo, filename, startline, content, is_binary) @repo = repo @filename = filename @startline = startline.to_i @content = content @is_binary = is_binary end end end gitlab-grit-2.8.3/lib/grit/commit.rb0000644000004100000410000002252013641342231017311 0ustar www-datawww-datamodule Grit class Commit extend Lazy attr_reader :id attr_reader :repo lazy_reader :parents lazy_reader :tree lazy_reader :author lazy_reader :authored_date lazy_reader :committer lazy_reader :committed_date lazy_reader :message lazy_reader :short_message # Parses output from the `git-cat-file --batch'. # # repo - Grit::Repo instance. # sha - String SHA of the Commit. # size - Fixnum size of the object. # object - Parsed String output from `git cat-file --batch`. # # Returns an Array of Grit::Commit objects. def self.parse_batch(repo, sha, size, object) info, message = object.split("\n\n", 2) lines = info.split("\n") tree = lines.shift.split(' ', 2).last parents = [] parents << lines.shift[7..-1] while lines.first[0, 6] == 'parent' author, authored_date = Grit::Commit.actor(lines.shift) committer, committed_date = Grit::Commit.actor(lines.shift) Grit::Commit.new( repo, sha, parents, tree, author, authored_date, committer, committed_date, message.to_s.split("\n")) end # Instantiate a new Commit # +id+ is the id of the commit # +parents+ is an array of commit ids (will be converted into Commit instances) # +tree+ is the correspdonding tree id (will be converted into a Tree object) # +author+ is the author string # +authored_date+ is the authored Time # +committer+ is the committer string # +committed_date+ is the committed Time # +message+ is an array of commit message lines # # Returns Grit::Commit (baked) def initialize(repo, id, parents, tree, author, authored_date, committer, committed_date, message) @repo = repo @id = id @parents = parents.map { |p| Commit.create(repo, :id => p) } @tree = Tree.create(repo, :id => tree) @author = author @authored_date = authored_date @committer = committer @committed_date = committed_date @message = message.join("\n") @short_message = message.find { |x| !x.strip.empty? } || '' end def id_abbrev @id_abbrev ||= @repo.git.rev_parse({}, self.id).chomp[0, 7] end # Create an unbaked Commit containing just the specified attributes # +repo+ is the Repo # +atts+ is a Hash of instance variable data # # Returns Grit::Commit (unbaked) def self.create(repo, atts) self.allocate.create_initialize(repo, atts) end # Initializer for Commit.create # +repo+ is the Repo # +atts+ is a Hash of instance variable data # # Returns Grit::Commit (unbaked) def create_initialize(repo, atts) @repo = repo atts.each do |k, v| instance_variable_set("@#{k}", v) end self end def lazy_source self.class.find_all(@repo, @id, {:max_count => 1}).first end # Count the number of commits reachable from this ref # +repo+ is the Repo # +ref+ is the ref from which to begin (SHA1 or name) # # Returns Integer def self.count(repo, ref) repo.git.rev_list({}, ref).size / 41 end # Find all commits matching the given criteria. # +repo+ is the Repo # +ref+ is the ref from which to begin (SHA1 or name) or nil for --all # +options+ is a Hash of optional arguments to git # :max_count is the maximum number of commits to fetch # :skip is the number of commits to skip # # Returns Grit::Commit[] (baked) def self.find_all(repo, ref, options = {}) allowed_options = [:max_count, :skip, :since] default_options = {:pretty => "raw"} actual_options = default_options.merge(options) if ref output = repo.git.rev_list(actual_options, ref) else output = repo.git.rev_list(actual_options.merge(:all => true)) end self.list_from_string(repo, output) rescue Grit::GitRuby::Repository::NoSuchShaFound [] end # Parse out commit information into an array of baked Commit objects # +repo+ is the Repo # +text+ is the text output from the git command (raw format) # # Returns Grit::Commit[] (baked) # # really should re-write this to be more accepting of non-standard commit messages # - it broke when 'encoding' was introduced - not sure what else might show up # def self.list_from_string(repo, text) text_gpgless = text.gsub(/gpgsig -----BEGIN PGP SIGNATURE-----[\n\r](.*[\n\r])*? -----END PGP SIGNATURE-----[\n\r]/, "") lines = text_gpgless.split("\n") commits = [] while !lines.empty? # GITLAB patch # Skip all garbage unless we get real commit while !lines.empty? && lines.first !~ /^commit [a-zA-Z0-9]*$/ lines.shift end id = lines.shift.split.last tree = lines.shift.split.last parents = [] parents << lines.shift.split.last while lines.first =~ /^parent/ author_line = lines.shift author_line << lines.shift if lines[0] !~ /^committer / author, authored_date = self.actor(author_line) committer_line = lines.shift committer_line << lines.shift if lines[0] && lines[0] != '' && lines[0] !~ /^encoding/ committer, committed_date = self.actor(committer_line) # not doing anything with this yet, but it's sometimes there encoding = lines.shift.split.last if lines.first =~ /^encoding/ # GITLAB patch # Skip Signature and other raw data lines.shift while lines.first =~ /^ / lines.shift message_lines = [] message_lines << lines.shift[4..-1] while lines.first =~ /^ {4}/ lines.shift while lines.first && lines.first.empty? commits << Commit.new(repo, id, parents, tree, author, authored_date, committer, committed_date, message_lines) end commits end # Show diffs between two trees. # # repo - The current Grit::Repo instance. # a - A String named commit. # b - An optional String named commit. Passing an array assumes you # wish to omit the second named commit and limit the diff to the # given paths. # paths - An optional Array of paths to limit the diff. # options - An optional Hash of options. Merged into {:full_index => true}. # # Returns Grit::Diff[] (baked) def self.diff(repo, a, b = nil, paths = [], options = {}) if b.is_a?(Array) paths = b b = nil end paths.unshift("--") unless paths.empty? paths.unshift(b) unless b.nil? paths.unshift(a) options = {:full_index => true}.update(options) text = repo.git.diff(options, *paths) Diff.list_from_string(repo, text) end def show if parents.size > 1 diff = @repo.git.native(:diff, {:full_index => true}, "#{parents[0].id}...#{parents[1].id}") else diff = @repo.git.show({:full_index => true, :pretty => 'raw'}, @id) end if diff =~ /diff --git a/ diff = diff.sub(/.*?(diff --git a)/m, '\1') else diff = '' end Diff.list_from_string(@repo, diff) end # Shows diffs between the commit's parent and the commit. # # options - An optional Hash of options, passed to Grit::Commit.diff. # # Returns Grit::Diff[] (baked) def diffs(options = {}) show end def stats stats = @repo.commit_stats(self.sha, 1)[0][-1] end # Convert this Commit to a String which is just the SHA1 id def to_s @id end def sha @id end def date @committed_date end def to_patch @repo.git.format_patch({'1' => true, :stdout => true}, to_s) end def notes ret = {} notes = Note.find_all(@repo) notes.each do |note| if n = note.commit.tree/(self.id) ret[note.name] = n.data end end ret end # Calculates the commit's Patch ID. The Patch ID is essentially the SHA1 # of the diff that the commit is introducing. # # Returns the 40 character hex String if a patch-id could be calculated # or nil otherwise. def patch_id show = @repo.git.show({}, @id) patch_line = @repo.git.native(:patch_id, :input => show) if patch_line =~ /^([0-9a-f]{40}) [0-9a-f]{40}\n$/ $1 else nil end end # Pretty object inspection def inspect %Q{#} end # private # Parse out the actor (author or committer) info # # Returns [String (actor name and email), Time (acted at time)] def self.actor(line) m, actor, epoch = *line.match(/^.+? (.*) (\d+) .*$/) [Actor.from_string(actor), Time.at(epoch.to_i)] end def author_string "%s <%s> %s %+05d" % [author.name, author.email, authored_date.to_i, 800] end def to_hash { 'id' => id, 'parents' => parents.map { |p| { 'id' => p.id } }, 'tree' => tree.id, 'message' => message, 'author' => { 'name' => author.name, 'email' => author.email }, 'committer' => { 'name' => committer.name, 'email' => committer.email }, 'authored_date' => authored_date.xmlschema, 'committed_date' => committed_date.xmlschema, } end end # Commit end # Grit gitlab-grit-2.8.3/lib/grit/blame.rb0000644000004100000410000000315413641342231017103 0ustar www-datawww-datamodule Grit class Blame attr_reader :lines def initialize(repo, file, commit, lines=nil) @repo = repo @file = file @commit = commit if lines.nil? @lines = [] load_blame else @lines = lines end end def load_blame output = @repo.git.blame({'p' => true}, @commit, '--', @file) process_raw_blame(output) end def process_raw_blame(output) lines, final = [], [] info, commits = {}, {} # process the output output.split("\n").each do |line| if line[0, 1] == "\t" lines << line[1, line.size] elsif m = /^(\w{40}) (\d+) (\d+)/.match(line) commit_id, old_lineno, lineno = m[1], m[2].to_i, m[3].to_i commits[commit_id] = nil if !commits.key?(commit_id) info[lineno] = [commit_id, old_lineno] end end # load all commits in single call @repo.batch(*commits.keys).each do |commit| commits[commit.id] = commit end # get it together info.sort.each do |lineno, (commit_id, old_lineno)| commit = commits[commit_id] final << BlameLine.new(lineno, old_lineno, commit, lines[lineno - 1]) end @lines = final end # Pretty object inspection def inspect %Q{#">} end class BlameLine attr_accessor :lineno, :oldlineno, :commit, :line def initialize(lineno, oldlineno, commit, line) @lineno = lineno @oldlineno = oldlineno @commit = commit @line = line end end end # Blame end # Grit gitlab-grit-2.8.3/lib/grit/git.rb0000644000004100000410000003452513641342231016614 0ustar www-datawww-datarequire 'tempfile' require 'posix-spawn' module Grit class Git include POSIX::Spawn class GitTimeout < RuntimeError attr_accessor :command attr_accessor :bytes_read def initialize(command = nil, bytes_read = nil) @command = command @bytes_read = bytes_read end end # Raised when a native git command exits with non-zero. class CommandFailed < StandardError # The full git command that failed as a String. attr_reader :command # The integer exit status. attr_reader :exitstatus # Everything output on the command's stdout as a String. attr_reader :out # Everything output on the command's stderr as a String. attr_reader :err def initialize(command, exitstatus=nil, err='', out='') if exitstatus @command = command @exitstatus = exitstatus @err = err @out = out message = "Command failed [#{exitstatus}]: #{command}" message << "\n\n" << err unless err.nil? || err.empty? super message else super command end end end undef_method :clone include GitRuby def exist? File.exist?(self.git_dir) end def put_raw_object(content, type) ruby_git.put_raw_object(content, type) end def get_raw_object(object_id) ruby_git.get_raw_object_by_sha1(object_id).content end def get_git_object(object_id) ruby_git.get_raw_object_by_sha1(object_id).to_hash end def object_exists?(object_id) ruby_git.object_exists?(object_id) end def select_existing_objects(object_ids) object_ids.select do |object_id| object_exists?(object_id) end end class << self attr_accessor :git_timeout, :git_max_size def git_binary @git_binary ||= ENV['PATH'].split(':'). map { |p| File.join(p, 'git') }. find { |p| File.exist?(p) } end attr_writer :git_binary end self.git_timeout = 10 self.git_max_size = 5242880 # 5.megabytes def self.with_timeout(timeout = 10) old_timeout = Grit::Git.git_timeout begin Grit::Git.git_timeout = timeout yield ensure Grit::Git.git_timeout = old_timeout end end attr_accessor :git_dir, :bytes_read, :work_tree def initialize(git_dir, options={}) self.git_dir = git_dir self.work_tree = options[:work_tree] self.bytes_read = 0 end def shell_escape(str) str.to_s.gsub("'", "\\\\'").gsub(";", '\\;') end alias_method :e, :shell_escape # Check if a normal file exists on the filesystem # +file+ is the relative path from the Git dir # # Returns Boolean def fs_exist?(file) path = File.join(self.git_dir, file) raise "Invalid path: #{path}" unless File.absolute_path(path) == path File.exist?(path) end # Read a normal file from the filesystem. # +file+ is the relative path from the Git dir # # Returns the String contents of the file def fs_read(file) path = File.join(self.git_dir, file) raise "Invalid path: #{path}" unless File.absolute_path(path) == path File.read(path) end # Write a normal file to the filesystem. # +file+ is the relative path from the Git dir # +contents+ is the String content to be written # # Returns nothing def fs_write(file, contents) path = File.join(self.git_dir, file) raise "Invalid path: #{path}" unless File.absolute_path(path) == path FileUtils.mkdir_p(File.dirname(path)) File.open(path, 'w') do |f| f.write(contents) end end # Delete a normal file from the filesystem # +file+ is the relative path from the Git dir # # Returns nothing def fs_delete(file) FileUtils.rm_rf(File.join(self.git_dir, file)) end # Move a normal file # +from+ is the relative path to the current file # +to+ is the relative path to the destination file # # Returns nothing def fs_move(from, to) FileUtils.mv(File.join(self.git_dir, from), File.join(self.git_dir, to)) end # Make a directory # +dir+ is the relative path to the directory to create # # Returns nothing def fs_mkdir(dir) FileUtils.mkdir_p(File.join(self.git_dir, dir)) end # Chmod the the file or dir and everything beneath # +file+ is the relative path from the Git dir # # Returns nothing def fs_chmod(mode, file = '/') FileUtils.chmod_R(mode, File.join(self.git_dir, file)) end def list_remotes Dir.glob(File.join(self.git_dir, 'refs/remotes/*')) rescue [] end def create_tempfile(seed, unlink = false) path = Tempfile.new(seed).path File.unlink(path) if unlink return path end def commit_from_sha(id) git_ruby_repo = GitRuby::Repository.new(self.git_dir) object = git_ruby_repo.get_object_by_sha1(id) if object.type == :commit id elsif object.type == :tag object.object else '' end end # Checks if the patch of a commit can be applied to the given head. # # options - grit command options hash # head_sha - String SHA or ref to check the patch against. # applies_sha - String SHA of the patch. The patch itself is retrieved # with #get_patch. # # Returns 0 if the patch applies cleanly (according to `git apply`), or # an Integer that is the sum of the failed exit statuses. def check_applies(options={}, head_sha=nil, applies_sha=nil) options, head_sha, applies_sha = {}, options, head_sha if !options.is_a?(Hash) options = options.dup options[:env] &&= options[:env].dup git_index = create_tempfile('index', true) (options[:env] ||= {}).merge!('GIT_INDEX_FILE' => git_index) options[:raise] = true status = 0 begin native(:read_tree, options.dup, head_sha) stdin = native(:diff, options.dup, "#{applies_sha}^", applies_sha) native(:apply, options.merge(:check => true, :cached => true, :input => stdin)) rescue CommandFailed => fail status += fail.exitstatus end status end # Gets a patch for a given SHA using `git diff`. # # options - grit command options hash # applies_sha - String SHA to get the patch from, using this command: # `git diff #{applies_sha}^ #{applies_sha}` # # Returns the String patch from `git diff`. def get_patch(options={}, applies_sha=nil) options, applies_sha = {}, options if !options.is_a?(Hash) options = options.dup options[:env] &&= options[:env].dup git_index = create_tempfile('index', true) (options[:env] ||= {}).merge!('GIT_INDEX_FILE' => git_index) native(:diff, options, "#{applies_sha}^", applies_sha) end # Applies the given patch against the given SHA of the current repo. # # options - grit command hash # head_sha - String SHA or ref to apply the patch to. # patch - The String patch to apply. Get this from #get_patch. # # Returns the String Tree SHA on a successful patch application, or false. def apply_patch(options={}, head_sha=nil, patch=nil) options, head_sha, patch = {}, options, head_sha if !options.is_a?(Hash) options = options.dup options[:env] &&= options[:env].dup options[:raise] = true git_index = create_tempfile('index', true) (options[:env] ||= {}).merge!('GIT_INDEX_FILE' => git_index) begin native(:read_tree, options.dup, head_sha) native(:apply, options.merge(:cached => true, :input => patch)) rescue CommandFailed return false end native(:write_tree, :env => options[:env]).to_s.chomp! end # Execute a git command, bypassing any library implementation. # # cmd - The name of the git command as a Symbol. Underscores are # converted to dashes as in :rev_parse => 'rev-parse'. # options - Command line option arguments passed to the git command. # Single char keys are converted to short options (:a => -a). # Multi-char keys are converted to long options (:arg => '--arg'). # Underscores in keys are converted to dashes. These special options # are used to control command execution and are not passed in command # invocation: # :timeout - Maximum amount of time the command can run for before # being aborted. When true, use Grit::Git.git_timeout; when numeric, # use that number of seconds; when false or 0, disable timeout. # :base - Set false to avoid passing the --git-dir argument when # invoking the git command. # :env - Hash of environment variable key/values that are set on the # child process. # :raise - When set true, commands that exit with a non-zero status # raise a CommandFailed exception. This option is available only on # platforms that support fork(2). # :process_info - By default, a single string with output written to # the process's stdout is returned. Setting this option to true # results in a [exitstatus, out, err] tuple being returned instead. # args - Non-option arguments passed on the command line. # # Optionally yields to the block an IO object attached to the child # process's STDIN. # # Examples # git.native(:rev_list, {:max_count => 10, :header => true}, "master") # # Returns a String with all output written to the child process's stdout # when the :process_info option is not set. # Returns a [exitstatus, out, err] tuple when the :process_info option is # set. The exitstatus is an small integer that was the process's exit # status. The out and err elements are the data written to stdout and # stderr as Strings. # Raises Grit::Git::GitTimeout when the timeout is exceeded or when more # than Grit::Git.git_max_size bytes are output. # Raises Grit::Git::CommandFailed when the :raise option is set true and the # git command exits with a non-zero exit status. The CommandFailed's #command, # #exitstatus, and #err attributes can be used to retrieve additional # detail about the error. def native(cmd, options = {}, *args, &block) args = args.first if args.size == 1 && args[0].is_a?(Array) args.map! { |a| a.to_s } args.reject! { |a| a.empty? } # special option arguments env = options.delete(:env) || {} raise_errors = options.delete(:raise) process_info = options.delete(:process_info) # more options input = options.delete(:input) timeout = options.delete(:timeout); timeout = true if timeout.nil? base = options.delete(:base); base = true if base.nil? # build up the git process argv argv = [] argv << Git.git_binary argv << "--git-dir=#{git_dir}" if base argv << "--work-tree=#{work_tree}" if work_tree argv << cmd.to_s.tr('_', '-') argv.concat(options_to_argv(options)) argv.concat(args) # run it and deal with fallout Grit.log(argv.join(' ')) if Grit.debug process = Child.new(env, *(argv + [{ :input => input, :timeout => (Grit::Git.git_timeout if timeout == true), :max => (Grit::Git.git_max_size if timeout == true) }])) Grit.log(process.out) if Grit.debug Grit.log(process.err) if Grit.debug status = process.status if raise_errors && !status.success? raise CommandFailed.new(argv.join(' '), status.exitstatus, process.err, process.out) elsif process_info [status.exitstatus, process.out, process.err] else process.out end rescue TimeoutExceeded, MaximumOutputExceeded raise GitTimeout, argv.join(' ') end # Methods not defined by a library implementation execute the git command # using #native, passing the method name as the git command name. # # Examples: # git.rev_list({:max_count => 10, :header => true}, "master") def method_missing(cmd, options={}, *args, &block) native(cmd, options, *args, &block) end # Transform a ruby-style options hash to command-line arguments sutiable for # use with Kernel::exec. No shell escaping is performed. # # Returns an Array of String option arguments. def options_to_argv(options) argv = [] options.each do |key, val| if key.to_s.size == 1 if val == true argv << "-#{key}" elsif val == false # ignore else argv << "-#{key}" argv << val.to_s end else if val == true argv << "--#{key.to_s.tr('_', '-')}" elsif val == false # ignore else argv << "--#{key.to_s.tr('_', '-')}=#{val}" end end end argv end # Simple wrapper around Timeout::timeout. # # seconds - Float number of seconds before a Timeout::Error is raised. When # true, the Grit::Git.git_timeout value is used. When the timeout is less # than or equal to 0, no timeout is established. # # Raises Timeout::Error when the timeout has elapsed. def timeout_after(seconds) seconds = self.class.git_timeout if seconds == true if seconds && seconds > 0 Timeout.timeout(seconds) { yield } else yield end end # Transform Ruby style options into git command line options # +options+ is a hash of Ruby style options # # Returns String[] # e.g. ["--max-count=10", "--header"] def transform_options(options) args = [] options.keys.each do |opt| if opt.to_s.size == 1 if options[opt] == true args << "-#{opt}" elsif options[opt] == false # ignore else val = options.delete(opt) args << "-#{opt.to_s} '#{e(val)}'" end else if options[opt] == true args << "--#{opt.to_s.gsub(/_/, '-')}" elsif options[opt] == false # ignore else val = options.delete(opt) args << "--#{opt.to_s.gsub(/_/, '-')}='#{e(val)}'" end end end args end end # Git end # Grit gitlab-grit-2.8.3/lib/grit/blob.rb0000644000004100000410000000715713641342231016750 0ustar www-datawww-datamodule Grit class Blob DEFAULT_MIME_TYPE = "text/plain" attr_reader :id attr_reader :mode attr_reader :name # Create an unbaked Blob containing just the specified attributes # +repo+ is the Repo # +atts+ is a Hash of instance variable data # # Returns Grit::Blob (unbaked) def self.create(repo, atts) self.allocate.create_initialize(repo, atts) end # Initializer for Blob.create # +repo+ is the Repo # +atts+ is a Hash of instance variable data # # Returns Grit::Blob (unbaked) def create_initialize(repo, atts) @repo = repo atts.each do |k, v| instance_variable_set("@#{k}".to_sym, v) end self end # The size of this blob in bytes # # Returns Integer def size @size ||= @repo.git.cat_file({:s => true}, id).chomp.to_i end # The binary contents of this blob. # # Returns String def data @data ||= @repo.git.cat_file({:p => true}, id) end # The mime type of this file (based on the filename) # # Returns String def mime_type guesses = MIME::Types.type_for(self.name) rescue [] guesses.first ? guesses.first.simplified : DEFAULT_MIME_TYPE end # The blame information for the given file at the given commit # # Returns Array: [Grit::Commit, Array: []] def self.blame(repo, commit, file) data = repo.git.blame({:p => true}, commit, '--', file) commits = {} blames = [] info = nil data.split("\n").each do |line| parts = line.split(/\s+/, 2) case parts.first when /^[0-9A-Fa-f]{40}$/ case line when /^([0-9A-Fa-f]{40}) (\d+) (\d+) (\d+)$/ _, id, origin_line, final_line, group_lines = *line.match(/^([0-9A-Fa-f]{40}) (\d+) (\d+) (\d+)$/) info = {:id => id} blames << [nil, []] when /^([0-9A-Fa-f]{40}) (\d+) (\d+)$/ _, id, origin_line, final_line = *line.match(/^([0-9A-Fa-f]{40}) (\d+) (\d+)$/) info = {:id => id} end when /^(author|committer)/ case parts.first when /^(.+)-mail$/ info["#{$1}_email".intern] = parts.last when /^(.+)-time$/ info["#{$1}_date".intern] = Time.at(parts.last.to_i) when /^(author|committer)$/ info[$1.intern] = parts.last end when /^filename/ info[:filename] = parts.last when /^summary/ info[:summary] = parts.last when '' c = commits[info[:id]] unless c c = Commit.create(repo, :id => info[:id], :author => Actor.from_string(info[:author] + ' ' + info[:author_email]), :authored_date => info[:author_date], :committer => Actor.from_string(info[:committer] + ' ' + info[:committer_email]), :committed_date => info[:committer_date], :message => info[:summary]) commits[info[:id]] = c end _, text = *line.match(/^\t(.*)$/) blames.last[0] = c blames.last[1] << text info = nil end end blames end def basename File.basename(name) end # Pretty object inspection def inspect %Q{#} end # Compares blobs by name def <=>(other) name <=> other.name end end # Blob end # Gritgitlab-grit-2.8.3/lib/grit/config.rb0000644000004100000410000000132413641342231017265 0ustar www-datawww-datamodule Grit class Config def initialize(repo) @repo = repo end def []=(key, value) @repo.git.config({}, key, value) @data = nil end def [](key) data[key] end def fetch(key, default = nil) data[key] || default || raise(IndexError.new("key not found")) end def keys data.keys end protected def data @data ||= load_config end def load_config hash = {} config_lines.map do |line| key, value = line.split(/=/, 2) hash[key] = value end hash end def config_lines @repo.git.config(:list => true).split(/\n/) end end # Config end # Gritgitlab-grit-2.8.3/lib/grit/ruby1.9.rb0000644000004100000410000000050313641342231017227 0ustar www-datawww-dataclass String if self.method_defined?(:ord) def getord(offset); self[offset].ord; end else alias :getord :[] end unless self.method_defined?(:b) if self.method_defined?(:force_encoding) def b; self.dup.force_encoding(Encoding::ASCII_8BIT); end else def b; self.dup; end end end endgitlab-grit-2.8.3/lib/grit_ext.rb0000644000004100000410000000213713641342231016703 0ustar www-datawww-datarequire "charlock_holmes" require "grit_ext/actor" require "grit_ext/blob" require "grit_ext/commit" require "grit_ext/tree" require "grit_ext/diff" require "grit_ext/version" module GritExt extend self def encode!(message) return nil unless message.respond_to? :force_encoding # if message is utf-8 encoding, just return it message.force_encoding("UTF-8") return message if message.valid_encoding? # return message if message type is binary detect = CharlockHolmes::EncodingDetector.detect(message) return message.force_encoding("BINARY") if detect && detect[:type] == :binary # encoding message to detect encoding if detect && detect[:encoding] message.force_encoding(detect[:encoding]) end # encode and clean the bad chars message.replace clean(message) rescue encoding = detect ? detect[:encoding] : "unknown" "--broken encoding: #{encoding}" end def clean(message) message.encode("UTF-16BE", :undef => :replace, :invalid => :replace, :replace => "") .encode("UTF-8") .gsub("\0".encode("UTF-8"), "") end end gitlab-grit-2.8.3/lib/grit.rb0000644000004100000410000000311113641342231016014 0ustar www-datawww-data$:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed # core require 'fileutils' require 'time' # stdlib require 'timeout' require 'logger' require 'digest/sha1' # third party begin require 'mime/types' require 'rubygems' rescue LoadError require 'rubygems' begin gem "mime-types", ">=0" require 'mime/types' rescue Gem::LoadError => e puts "WARNING: Gem LoadError: #{e.message}" end end # ruby 1.9 compatibility require 'grit/ruby1.9' # internal requires require 'grit/lazy' require 'grit/errors' require 'grit/git-ruby' require 'grit/git' unless defined? Grit::Git require 'grit/ref' require 'grit/tag' require 'grit/commit' require 'grit/commit_stats' require 'grit/tree' require 'grit/blob' require 'grit/actor' require 'grit/diff' require 'grit/config' require 'grit/repo' require 'grit/index' require 'grit/status' require 'grit/submodule' require 'grit/blame' require 'grit/merge' require 'grit/grep' module Grit VERSION = File.read(File.expand_path("../../VERSION", __FILE__)).chomp.strip class << self # Set +debug+ to true to log all git calls and responses attr_accessor :debug attr_accessor :use_git_ruby attr_accessor :no_quote # The standard +logger+ for debugging git calls - this defaults to a plain STDOUT logger attr_accessor :logger def log(str) logger.debug { str } end end self.debug = false self.use_git_ruby = true self.no_quote = false @logger ||= ::Logger.new(STDOUT) def self.version VERSION end end # Include grit_ext require 'charlock_holmes' require 'grit_ext' gitlab-grit-2.8.3/lib/grit_ext/0000755000004100000410000000000013641342231016353 5ustar www-datawww-datagitlab-grit-2.8.3/lib/grit_ext/actor.rb0000644000004100000410000000032713641342231020012 0ustar www-datawww-datamodule Grit class Actor alias_method :old_name, :name alias_method :old_email, :email def name GritExt.encode! old_name end def email GritExt.encode! old_email end end end gitlab-grit-2.8.3/lib/grit_ext/version.rb0000644000004100000410000000010213641342231020356 0ustar www-datawww-datamodule GritExt extend self def version "0.8.1" end end gitlab-grit-2.8.3/lib/grit_ext/tag.rb0000644000004100000410000000020613641342231017451 0ustar www-datawww-datamodule Grit class Tag alias_method :old_message, :message def message GritExt.encode! old_message end end end gitlab-grit-2.8.3/lib/grit_ext/diff.rb0000644000004100000410000000100313641342231017602 0ustar www-datawww-datamodule Grit class Diff def old_path GritExt.encode! @a_path end def new_path GritExt.encode! @b_path end def diff if @diff.nil? @diff = "" else lines = @diff.lines.to_a path = GritExt.encode! lines.shift(2).join body = GritExt.encode! lines.join begin @diff = path + body rescue Encoding::CompatibilityError @diff = GritExt.clean(path) + GritExt.clean(body) end end end end end gitlab-grit-2.8.3/lib/grit_ext/tree.rb0000644000004100000410000000017313641342231017640 0ustar www-datawww-datamodule Grit class Tree alias_method :old_name, :name def name GritExt.encode! old_name end end end gitlab-grit-2.8.3/lib/grit_ext/commit.rb0000644000004100000410000000040413641342231020166 0ustar www-datawww-datamodule Grit class Commit alias_method :old_message, :message alias_method :old_short_message, :short_message def message GritExt.encode! old_message end def short_message GritExt.encode! old_short_message end end end gitlab-grit-2.8.3/lib/grit_ext/blob.rb0000644000004100000410000000070313641342231017616 0ustar www-datawww-datamodule Grit class Blob alias_method :old_name, :name alias_method :old_data, :data def name GritExt.encode! old_name end def data GritExt.encode! old_data end class << self alias_method :old_blame, :blame def blame(repo, commit, file) old_blame(repo, commit, file).map do |b,lines| [b, GritExt.encode!(lines.join('\n')).split('\n')] end end end end end gitlab-grit-2.8.3/gitlab-grit.gemspec0000644000004100000410000000677213641342231017546 0ustar www-datawww-data######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: gitlab-grit 2.8.3 ruby lib Gem::Specification.new do |s| s.name = "gitlab-grit".freeze s.version = "2.8.3" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.require_paths = ["lib".freeze] s.authors = ["Tom Preston-Werner".freeze, "Scott Chacon".freeze, "Dmitriy Zaporozhets".freeze] s.date = "2020-02-25" s.description = "Grit is a Ruby library for extracting information from a git repository in an object oriented manner. GitLab fork".freeze s.email = "m@gitlabhq.com".freeze s.extra_rdoc_files = ["LICENSE".freeze, "README.md".freeze] s.files = ["LICENSE".freeze, "README.md".freeze, "VERSION".freeze, "lib/grit.rb".freeze, "lib/grit/actor.rb".freeze, "lib/grit/blame.rb".freeze, "lib/grit/blob.rb".freeze, "lib/grit/commit.rb".freeze, "lib/grit/commit_stats.rb".freeze, "lib/grit/config.rb".freeze, "lib/grit/diff.rb".freeze, "lib/grit/errors.rb".freeze, "lib/grit/git-ruby.rb".freeze, "lib/grit/git-ruby/commit_db.rb".freeze, "lib/grit/git-ruby/git_object.rb".freeze, "lib/grit/git-ruby/internal/file_window.rb".freeze, "lib/grit/git-ruby/internal/loose.rb".freeze, "lib/grit/git-ruby/internal/pack.rb".freeze, "lib/grit/git-ruby/internal/raw_object.rb".freeze, "lib/grit/git-ruby/repository.rb".freeze, "lib/grit/git.rb".freeze, "lib/grit/grep.rb".freeze, "lib/grit/index.rb".freeze, "lib/grit/lazy.rb".freeze, "lib/grit/merge.rb".freeze, "lib/grit/ref.rb".freeze, "lib/grit/repo.rb".freeze, "lib/grit/ruby1.9.rb".freeze, "lib/grit/status.rb".freeze, "lib/grit/submodule.rb".freeze, "lib/grit/tag.rb".freeze, "lib/grit/tree.rb".freeze, "lib/grit_ext.rb".freeze, "lib/grit_ext/actor.rb".freeze, "lib/grit_ext/blob.rb".freeze, "lib/grit_ext/commit.rb".freeze, "lib/grit_ext/diff.rb".freeze, "lib/grit_ext/tag.rb".freeze, "lib/grit_ext/tree.rb".freeze, "lib/grit_ext/version.rb".freeze] s.homepage = "http://github.com/gitlabhq/grit".freeze s.licenses = ["MIT".freeze] s.rdoc_options = ["--charset=UTF-8".freeze] s.rubygems_version = "2.5.2.1".freeze s.summary = "Ruby Git bindings.".freeze if s.respond_to? :specification_version then s.specification_version = 4 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_runtime_dependency(%q.freeze, ["~> 0.7"]) s.add_runtime_dependency(%q.freeze, ["~> 1.1"]) s.add_runtime_dependency(%q.freeze, ["< 3", ">= 1.16"]) s.add_development_dependency(%q.freeze, [">= 0"]) s.add_runtime_dependency(%q.freeze, ["~> 0.3"]) s.add_development_dependency(%q.freeze, [">= 0"]) else s.add_dependency(%q.freeze, ["~> 0.7"]) s.add_dependency(%q.freeze, ["~> 1.1"]) s.add_dependency(%q.freeze, ["< 3", ">= 1.16"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, ["~> 0.3"]) s.add_dependency(%q.freeze, [">= 0"]) end else s.add_dependency(%q.freeze, ["~> 0.7"]) s.add_dependency(%q.freeze, ["~> 1.1"]) s.add_dependency(%q.freeze, ["< 3", ">= 1.16"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, ["~> 0.3"]) s.add_dependency(%q.freeze, [">= 0"]) end end gitlab-grit-2.8.3/VERSION0000644000004100000410000000000613641342231015024 0ustar www-datawww-data2.8.3