- Softwrap mode:
- Toggle
Blob of vendor/grit/lib/grit/repo.rb
(raw blob data)
| 1 | require "enumerator" |
| 2 | |
| 3 | module Grit |
| 4 | |
| 5 | class Repo |
| 6 | DAEMON_EXPORT_FILE = 'git-daemon-export-ok' |
| 7 | |
| 8 | # The path of the git repo as a String |
| 9 | attr_accessor :path |
| 10 | attr_reader :bare |
| 11 | |
| 12 | # The git command line interface object |
| 13 | attr_accessor :git |
| 14 | |
| 15 | # Create a new Repo instance |
| 16 | # +path+ is the path to either the root git directory or the bare git repo |
| 17 | # |
| 18 | # Examples |
| 19 | # g = Repo.new("/Users/tom/dev/grit") |
| 20 | # g = Repo.new("/Users/tom/public/grit.git") |
| 21 | # |
| 22 | # Returns Grit::Repo |
| 23 | def initialize(path) |
| 24 | epath = File.expand_path(path) |
| 25 | |
| 26 | if File.exist?(File.join(epath, '.git')) |
| 27 | self.path = File.join(epath, '.git') |
| 28 | @bare = false |
| 29 | elsif File.exist?(epath) && epath =~ /\.git$/ |
| 30 | self.path = epath |
| 31 | @bare = true |
| 32 | elsif File.exist?(epath) |
| 33 | raise InvalidGitRepositoryError.new(epath) |
| 34 | else |
| 35 | raise NoSuchPathError.new(epath) |
| 36 | end |
| 37 | |
| 38 | self.git = Git.new(self.path) |
| 39 | end |
| 40 | |
| 41 | # The project's description. Taken verbatim from GIT_REPO/description |
| 42 | # |
| 43 | # Returns String |
| 44 | def description |
| 45 | File.open(File.join(self.path, 'description')).read.chomp |
| 46 | end |
| 47 | |
| 48 | # An array of Head objects representing the branch heads in |
| 49 | # this repo |
| 50 | # |
| 51 | # Returns Grit::Head[] (baked) |
| 52 | def heads |
| 53 | Head.find_all(self) |
| 54 | end |
| 55 | |
| 56 | alias_method :branches, :heads |
| 57 | |
| 58 | # An array of Tag objects that are available in this repo |
| 59 | # |
| 60 | # Returns Grit::Tag[] (baked) |
| 61 | def tags |
| 62 | Tag.find_all(self) |
| 63 | end |
| 64 | |
| 65 | # An array of Commit objects representing the history of a given ref/commit |
| 66 | # +start+ is the branch/commit name (default 'master') |
| 67 | # +max_count+ is the maximum number of commits to return (default 10) |
| 68 | # +skip+ is the number of commits to skip (default 0) |
| 69 | # |
| 70 | # Returns Grit::Commit[] (baked) |
| 71 | def commits(start = 'master', max_count = 10, skip = 0) |
| 72 | options = {:max_count => max_count, |
| 73 | :skip => skip} |
| 74 | |
| 75 | Commit.find_all(self, start, options) |
| 76 | end |
| 77 | |
| 78 | # The Commits objects that are reachable via +to+ but not via +from+ |
| 79 | # Commits are returned in chronological order. |
| 80 | # +from+ is the branch/commit name of the younger item |
| 81 | # +to+ is the branch/commit name of the older item |
| 82 | # |
| 83 | # Returns Grit::Commit[] (baked) |
| 84 | def commits_between(from, to) |
| 85 | Commit.find_all(self, "#{from}..#{to}").reverse |
| 86 | end |
| 87 | |
| 88 | # The Commits objects that are newer than the specified date. |
| 89 | # Commits are returned in chronological order. |
| 90 | # +start+ is the branch/commit name (default 'master') |
| 91 | # +since+ is a string represeting a date/time |
| 92 | # +extra_options+ is a hash of extra options |
| 93 | # |
| 94 | # Returns Grit::Commit[] (baked) |
| 95 | def commits_since(start = 'master', since = '1970-01-01', extra_options = {}) |
| 96 | options = {:since => since}.merge(extra_options) |
| 97 | |
| 98 | Commit.find_all(self, start, options) |
| 99 | end |
| 100 | |
| 101 | # The number of commits reachable by the given branch/commit |
| 102 | # +start+ is the branch/commit name (default 'master') |
| 103 | # |
| 104 | # Returns Integer |
| 105 | def commit_count(start = 'master') |
| 106 | Commit.count(self, start) |
| 107 | end |
| 108 | |
| 109 | # The Commit object for the specified id |
| 110 | # +id+ is the SHA1 identifier of the commit |
| 111 | # |
| 112 | # Returns Grit::Commit (baked) |
| 113 | def commit(id) |
| 114 | options = {:max_count => 1} |
| 115 | |
| 116 | Commit.find_all(self, id, options).first |
| 117 | end |
| 118 | |
| 119 | # Returns a list of commits that is in +other_repo+ but not in self |
| 120 | # |
| 121 | # Returns Grit::Commit[] |
| 122 | def commit_deltas_from(other_repo, ref = "master", other_ref = "master") |
| 123 | repo_refs = self.git.rev_list({}, ref).strip.split("\n") |
| 124 | other_repo_refs = other_repo.git.rev_list({}, other_ref).strip.split("\n") |
| 125 | |
| 126 | (other_repo_refs - repo_refs).map do |ref| |
| 127 | Commit.find_all(other_repo, ref, {:max_count => 1}).first |
| 128 | end |
| 129 | |
| 130 | # commits = [] |
| 131 | # (other_repo_refs - repo_refs).each_slice(5) do |refs| # due to cmdline arg length |
| 132 | # commits.concat Commit.find_all(self, refs.join(" "), {:max_count => refs.size}) |
| 133 | # end |
| 134 | # commits |
| 135 | end |
| 136 | |
| 137 | # The Tree object for the given treeish reference |
| 138 | # +treeish+ is the reference (default 'master') |
| 139 | # +paths+ is an optional Array of directory paths to restrict the tree (deafult []) |
| 140 | # |
| 141 | # Examples |
| 142 | # repo.tree('master', ['lib/']) |
| 143 | # |
| 144 | # Returns Grit::Tree (baked) |
| 145 | def tree(treeish = 'master', paths = []) |
| 146 | Tree.construct(self, treeish, paths) |
| 147 | end |
| 148 | |
| 149 | # The Blob object for the given id |
| 150 | # +id+ is the SHA1 id of the blob |
| 151 | # |
| 152 | # Returns Grit::Blob (unbaked) |
| 153 | def blob(id) |
| 154 | Blob.create(self, :id => id) |
| 155 | end |
| 156 | |
| 157 | # The commit log for a treeish |
| 158 | # |
| 159 | # Returns Grit::Commit[] |
| 160 | def log(commit = 'master', path = nil, options = {}) |
| 161 | default_options = {:pretty => "raw"} |
| 162 | actual_options = default_options.merge(options) |
| 163 | arg = path ? [commit, '--', path] : [commit] |
| 164 | commits = self.git.log(actual_options, *arg) |
| 165 | Commit.list_from_string(self, commits) |
| 166 | end |
| 167 | |
| 168 | # The diff from commit +a+ to commit +b+, optionally restricted to the given file(s) |
| 169 | # +a+ is the base commit |
| 170 | # +b+ is the other commit |
| 171 | # +paths+ is an optional list of file paths on which to restrict the diff |
| 172 | def diff(a, b, *paths) |
| 173 | self.git.diff({}, a, b, '--', *paths) |
| 174 | end |
| 175 | |
| 176 | # The commit diff for the given commit |
| 177 | # +commit+ is the commit name/id |
| 178 | # |
| 179 | # Returns Grit::Diff[] |
| 180 | def commit_diff(commit) |
| 181 | Commit.diff(self, commit) |
| 182 | end |
| 183 | |
| 184 | # Initialize a bare git repository at the given path |
| 185 | # +path+ is the full path to the repo (traditionally ends with /<name>.git) |
| 186 | # +options+ is any additional options to the git init command |
| 187 | # |
| 188 | # Examples |
| 189 | # Grit::Repo.init_bare('/var/git/myrepo.git') |
| 190 | # |
| 191 | # Returns Grit::Repo (the newly created repo) |
| 192 | def self.init_bare(path, options = {}) |
| 193 | git = Git.new(path) |
| 194 | git.init(options) |
| 195 | self.new(path) |
| 196 | end |
| 197 | |
| 198 | # Fork a bare git repository from this repo |
| 199 | # +path+ is the full path of the new repo (traditionally ends with /<name>.git) |
| 200 | # +options+ is any additional options to the git clone command |
| 201 | # |
| 202 | # Returns Grit::Repo (the newly forked repo) |
| 203 | def fork_bare(path, options = {}) |
| 204 | default_options = {:bare => true, :shared => false} |
| 205 | real_options = default_options.merge(options) |
| 206 | self.git.clone(real_options, self.path, path) |
| 207 | Repo.new(path) |
| 208 | end |
| 209 | |
| 210 | # Archive the given treeish |
| 211 | # +treeish+ is the treeish name/id (default 'master') |
| 212 | # +prefix+ is the optional prefix |
| 213 | # |
| 214 | # Examples |
| 215 | # repo.archive_tar |
| 216 | # # => <String containing tar archive> |
| 217 | # |
| 218 | # repo.archive_tar('a87ff14') |
| 219 | # # => <String containing tar archive for commit a87ff14> |
| 220 | # |
| 221 | # repo.archive_tar('master', 'myproject/') |
| 222 | # # => <String containing tar archive and prefixed with 'myproject/'> |
| 223 | # |
| 224 | # Returns String (containing tar archive) |
| 225 | def archive_tar(treeish = 'master', prefix = nil) |
| 226 | options = {} |
| 227 | options[:prefix] = prefix if prefix |
| 228 | self.git.archive(options, treeish) |
| 229 | end |
| 230 | |
| 231 | # Archive and gzip the given treeish |
| 232 | # +treeish+ is the treeish name/id (default 'master') |
| 233 | # +prefix+ is the optional prefix |
| 234 | # |
| 235 | # Examples |
| 236 | # repo.archive_tar_gz |
| 237 | # # => <String containing tar.gz archive> |
| 238 | # |
| 239 | # repo.archive_tar_gz('a87ff14') |
| 240 | # # => <String containing tar.gz archive for commit a87ff14> |
| 241 | # |
| 242 | # repo.archive_tar_gz('master', 'myproject/') |
| 243 | # # => <String containing tar.gz archive and prefixed with 'myproject/'> |
| 244 | # |
| 245 | # Returns String (containing tar.gz archive) |
| 246 | def archive_tar_gz(treeish = 'master', prefix = nil) |
| 247 | options = {} |
| 248 | options[:prefix] = prefix if prefix |
| 249 | self.git.archive(options, treeish, "| gzip") |
| 250 | end |
| 251 | |
| 252 | # Enable git-daemon serving of this repository by writing the |
| 253 | # git-daemon-export-ok file to its git directory |
| 254 | # |
| 255 | # Returns nothing |
| 256 | def enable_daemon_serve |
| 257 | FileUtils.touch(File.join(self.path, DAEMON_EXPORT_FILE)) |
| 258 | end |
| 259 | |
| 260 | # Disable git-daemon serving of this repository by ensuring there is no |
| 261 | # git-daemon-export-ok file in its git directory |
| 262 | # |
| 263 | # Returns nothing |
| 264 | def disable_daemon_serve |
| 265 | FileUtils.rm_f(File.join(self.path, DAEMON_EXPORT_FILE)) |
| 266 | end |
| 267 | |
| 268 | # The list of alternates for this repo |
| 269 | # |
| 270 | # Returns Array[String] (pathnames of alternates) |
| 271 | def alternates |
| 272 | alternates_path = File.join(self.path, *%w{objects info alternates}) |
| 273 | |
| 274 | if File.exist?(alternates_path) |
| 275 | File.read(alternates_path).strip.split("\n") |
| 276 | else |
| 277 | [] |
| 278 | end |
| 279 | end |
| 280 | |
| 281 | # Sets the alternates |
| 282 | # +alts+ is the Array of String paths representing the alternates |
| 283 | # |
| 284 | # Returns nothing |
| 285 | def alternates=(alts) |
| 286 | alts.each do |alt| |
| 287 | unless File.exist?(alt) |
| 288 | raise "Could not set alternates. Alternate path #{alt} must exist" |
| 289 | end |
| 290 | end |
| 291 | |
| 292 | if alts.empty? |
| 293 | File.delete(File.join(self.path, *%w{objects info alternates})) |
| 294 | else |
| 295 | File.open(File.join(self.path, *%w{objects info alternates}), 'w') do |f| |
| 296 | f.write alts.join("\n") |
| 297 | end |
| 298 | end |
| 299 | end |
| 300 | |
| 301 | def config |
| 302 | @config ||= Config.new(self) |
| 303 | end |
| 304 | |
| 305 | # Pretty object inspection |
| 306 | def inspect |
| 307 | %Q{#<Grit::Repo "#{@path}">} |
| 308 | end |
| 309 | end # Repo |
| 310 | |
| 311 | end # Grit |
