Class: Amp::VersionedFile

Inherits:
Object show all
Includes:
RevlogSupport::Node
Defined in:
lib/amp/repository/versioned_file.rb

Overview

This class allows you to access a file at a given revision in the repo’s history. You can compare them, sort them, access the changeset, and all sorts of stuff.

Direct Known Subclasses

VersionedWorkingFile

Constant Summary

Constants included from RevlogSupport::Node

RevlogSupport::Node::NULL_ID, RevlogSupport::Node::NULL_REV

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from RevlogSupport::Node

#short

Constructor Details

#initialize(repo, path, opts = {}) ⇒ VersionedFile

Creates a new Amp::VersionedFile. You need to pass in the repo and the path to the file, as well as one of the following: a revision index/ID, the node_id of the file’s revision in the filelog, or a changeset at a given index.

Parameters:

  • repo (Repository)

    The repo we’re working with

  • path (String)

    the path to the file

  • opts (Hash) (defaults to: {})

    the options to customize how we load this file

  • [FileLog] (Hash)

    a customizable set of options

  • [String] (Hash)

    a customizable set of options

  • [Changeset] (Hash)

    a customizable set of options

Raises:

  • (StandardError)


31
32
33
34
35
36
37
38
39
40
41
# File 'lib/amp/repository/versioned_file.rb', line 31

def initialize(repo, path, opts={})
  @repo, @path = repo, path
  raise StandardError.new("specify a revision!") unless opts[:change_id] ||
                                                        opts[:file_id]   ||
                                                        opts[:changeset]
  @file_log  = opts[:file_log]  if opts[:file_log]
  @change_id = opts[:change_id] if opts[:change_id]
  @changeset = opts[:changeset] if opts[:changeset]
  @file_id   = opts[:file_id]   if opts[:file_id]
  
end

Instance Attribute Details

#change_idObject

The revision index into the history of the repository. Could also be a node_id



77
78
79
80
81
# File 'lib/amp/repository/versioned_file.rb', line 77

def change_id
  @change_id ||= @changeset.revision         if @changeset
  @change_id ||= file_log[file_rev].link_rev unless @changeset
  @change_id
end

#file_idObject

Returns the value of attribute file_id.



11
12
13
# File 'lib/amp/repository/versioned_file.rb', line 11

def file_id
  @file_id
end

#pathObject

The path to this file



162
# File 'lib/amp/repository/versioned_file.rb', line 162

def path; @path; end

Instance Method Details

#<=>(other) ⇒ Object



43
44
45
# File 'lib/amp/repository/versioned_file.rb', line 43

def <=>(other)
  to_i <=> other.to_i
end

#==(other) ⇒ Object

Equality! Compares paths and revision indexes



121
122
123
124
# File 'lib/amp/repository/versioned_file.rb', line 121

def ==(other)
  return false unless @path && @file_id && other.path && other.file_id
  @path == other.path && @file_id == other.file_id
end

#===(other) ⇒ Boolean

Just the opposite of #cmp

Parameters:

Returns:

  • (Boolean)

    true if the two are the same



179
180
181
# File 'lib/amp/repository/versioned_file.rb', line 179

def ===(other)
  !self.cmp(other.data)
end

#ancestor(file_2) ⇒ Object



330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
# File 'lib/amp/repository/versioned_file.rb', line 330

def ancestor(file_2)
  ancestor_cache = {}
  [self, file_2].each do |c|
    if c.file_rev == NULL_REV || c.file_rev.nil?
      parent_list = c.parents.map {|n| [n.path, n.file_node]}
      ancestor_cache[[c.path, c.file_node]] = parent_list
    end
  end
  
  filelog_cache = {repo_path => file_log, file_2.repo_path => file_2.file_log}
  a, b = [path, file_node], [file_2.path, file_2.file_node]
  parents_proc = proc {|vertex| get_parents_helper(vertex, ancestor_cache, filelog_cache)}
  
  v = Graphs::AncestorCalculator.ancestors(a, b, parents_proc)
  if v
    file, node = v
    return VersionedFile.new(@repo, file, :file_id => node, :file_log => filelog_cache[file])
  end
  return nil
end

#annotate(follow_copies = false, line_number = false) ⇒ Object



265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
# File 'lib/amp/repository/versioned_file.rb', line 265

def annotate(follow_copies = false, line_number = false)
  base = (revision != linkrev) ? file(file_rev) : self
  
  needed = {base => 1}
  counters = {(base.path + base.file_id.to_s) => 1}
  visit = [base]
  files = [base.path]
  
  while visit.any?
    file = visit.shift
    annotate_parents_helper(file).each do |p|
      unless needed.include? p
        needed[p] = 1
        counters[p.path + p.file_id.to_s] = 1
        visit << p
        files << p.path unless files.include? p.path
      end
    end
  end
  
  visit = []
  files.each do |f|
    filenames = needed.keys.select {|k| k.path == f}.map {|n| [n.revision, n]}
    visit += filenames
  end
  
  hist = {}
  lastfile = ""
  visit.sort.each do |rev, file_ann|
    curr = annotate_decorate(file_ann.data, file_ann, line_number)
    annotate_parents_helper(file_ann).each do |p|
      next if p.file_id == NULL_ID
      curr = annotate_diff_pair(hist[p.path + p.file_id.to_s], curr)
      counters[p.path + p.file_id.to_s] -= 1
      hist.delete(p.path + p.file_id.to_s) if counters[p.path + p.file_id.to_s] == 0
    end
    hist[file_ann.path+file_ann.file_id.to_s] = curr
    lastfile = file_ann
  end
  returnarr = []
  hist[lastfile.path+lastfile.file_id.to_s].inspect # force all lazy-loading to stoppeth
  ret = hist[lastfile.path+lastfile.file_id.to_s][0].each_with_index do |obj, i|
    returnarr << obj + [hist[lastfile.path+lastfile.file_id.to_s][1].split_newlines[i]]
  end
  #   hist[lastfile.path+lastfile.file_id.to_s][0][i] + hist[lastfile.path+lastfile.file_id.to_s][1].split_newlines[i]
  # end
  ret = hist[lastfile.path+lastfile.file_id.to_s][0].zip(hist[lastfile.path+lastfile.file_id.to_s][1].split_newlines)
  returnarr
end

#annotate_decorate(text, revision, line_number = false) ⇒ Object



225
226
227
228
229
230
231
232
233
234
235
# File 'lib/amp/repository/versioned_file.rb', line 225

def annotate_decorate(text, revision, line_number = false)
  if line_number
    size = text.split("\n").size
    retarr = [nil,text]
    retarr[0] = (1..size).map {|i| [revision, i]}
  else
    retarr = [nil, text]
    retarr[0] = [[revision, false]] * text.split("\n").size
  end
  retarr
end

#annotate_diff_pair(parent, child) ⇒ Object



237
238
239
240
241
242
# File 'lib/amp/repository/versioned_file.rb', line 237

def annotate_diff_pair(parent, child)
  Diffs::BinaryDiff.blocks_as_array(parent[1], child[1]).each do |a1,a2,b1,b2|
    child[0][b1..(b2-1)] = parent[0][a1..(a2-1)]
  end
  child
end

#annotate_get_file(path, file_id) ⇒ Object



244
245
246
247
# File 'lib/amp/repository/versioned_file.rb', line 244

def annotate_get_file(path, file_id)
  log = (path == @path) ? file_log : @repo.get_file(path)
  return VersionedFile.new(@repo, path, :file_id => file_id, :file_log => log)
end

#annotate_parents_helper(file, follow_copies = false) ⇒ Object



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/amp/repository/versioned_file.rb', line 249

def annotate_parents_helper(file, follow_copies = false)
  path = file.path
  if file.file_rev.nil?
    parent_list = file.parents.map {|n| [n.path, n.file_rev]}
  else
    parent_list = file.file_log.parent_indices_for_index(file.file_rev)
    parent_list.map! {|n| [path, n]}
  end
  if follow_copies
    r = file.renamed
    pl[0] = [r[0], @repo.get_file(r[0]).revision(r[1])] if r
  end
  return parent_list.select {|p, n| n != NULL_REV}.
                     map {|p, n| annotate_get_file(p, n)}
end

#branchObject

The branch this tracked file belongs to



156
# File 'lib/amp/repository/versioned_file.rb', line 156

def branch; changeset.branch; end

#changesetChangeset

Returns the changeset that this file belongs to

Returns:

  • (Changeset)

    the changeset this file belongs to



55
56
57
# File 'lib/amp/repository/versioned_file.rb', line 55

def changeset
  @changeset ||= Changeset.new @repo, change_id
end

#childrenObject

What are this file’s children?



218
219
220
221
222
223
# File 'lib/amp/repository/versioned_file.rb', line 218

def children
  c = file_log.children(file_node)
  c.map do |x|
    VersionedFile.new(@repo, @path, :file_id => x, :file_log => file_log)
  end  
end

#cmp(text) ⇒ Object

Compares to a bit of text. Returns true if different, false for the same. (much like <=> == 0 for the same)



170
171
172
# File 'lib/amp/repository/versioned_file.rb', line 170

def cmp(text)
  file_log.cmp(file_node, text)
end

#dataObject

The data in this file



160
# File 'lib/amp/repository/versioned_file.rb', line 160

def data; file_log.read(file_node); end

#dateObject

Date this revision to this file was committed



150
# File 'lib/amp/repository/versioned_file.rb', line 150

def date; changeset.date; end

#descriptionObject

The description of the commit that contained this file revision



154
# File 'lib/amp/repository/versioned_file.rb', line 154

def description; changeset.description; end

#file(file_id) ⇒ Object

Retrieves the file with a different ID

Parameters:

  • file_id

    a new file ID… still not sure what a file_id is



130
131
132
# File 'lib/amp/repository/versioned_file.rb', line 130

def file(file_id)
  self.class.new @repo, @path, :file_id => file_id, :file_log => file_log
end

#file_logFileLog

The file log that tracks this file

Returns:

  • (FileLog)

    The revision log tracking this file



70
71
72
# File 'lib/amp/repository/versioned_file.rb', line 70

def file_log
  @file_log ||= @repo.file @path
end

#file_nodeObject



83
84
85
86
87
# File 'lib/amp/repository/versioned_file.rb', line 83

def file_node
  @file_node ||= file_log.lookup_id(@file_id) if @file_id
  @file_node ||= changeset.file_node(@path)   unless @file_id
  @file_node ||= NULL_ID
end

#file_revObject

Returns the index into the file log’s history for this file



91
92
93
# File 'lib/amp/repository/versioned_file.rb', line 91

def file_rev
  @file_rev ||= file_log.rev(file_node)
end

#filesObject

All files in this changeset that this revision of this file was committed



152
# File 'lib/amp/repository/versioned_file.rb', line 152

def files; changeset.files; end

#flagsObject

Gets the flags for this file (x and l)



135
# File 'lib/amp/repository/versioned_file.rb', line 135

def flags; changeset.flags(@path); end

#get_parents_helper(vertex, ancestor_cache, filelog_cache) ⇒ Object



315
316
317
318
319
320
321
322
323
324
325
326
327
328
# File 'lib/amp/repository/versioned_file.rb', line 315

def get_parents_helper(vertex, ancestor_cache, filelog_cache)
  return ancestor_cache[vertex] if ancestor_cache[vertex]
  file, node = vertex
  filelog_cache[file] = @repo.get_file(file) unless filelog_cache[file]
  
  filelog = filelog_cache[file]
  parent_list = filelog.parents(node).select {|p| p != NULL_ID}.map {|p| [file, p]}
  
  has_renamed = filelog.renamed(node)
  
  parent_list << has_renamed if has_renamed
  ancestor_cache[vertex] = parent_list
  parent_list
end

#hashObject

Hash value for sticking this fucker in a hash



115
116
117
# File 'lib/amp/repository/versioned_file.rb', line 115

def hash
  return (path + file_id.to_s).hash
end

#inspectObject

IRB Inspector string representation



109
110
111
# File 'lib/amp/repository/versioned_file.rb', line 109

def inspect
  "#<Versioned File: #{to_s}>"
end

#linkrevObject

Link-revision index



144
# File 'lib/amp/repository/versioned_file.rb', line 144

def linkrev; file_log[file_rev].link_rev; end

#manifestObject

THe manifest that this file revision is from



158
# File 'lib/amp/repository/versioned_file.rb', line 158

def manifest; changeset.manifest; end

#nil?Boolean

Is this a null version?

Returns:



97
98
99
# File 'lib/amp/repository/versioned_file.rb', line 97

def nil?
  file_node.nil?
end

#nodeObject

Node ID for this file’s revision



146
# File 'lib/amp/repository/versioned_file.rb', line 146

def node; changeset.node; end

#parentsObject

What are this revised file’s parents? Return them as Amp::VersionedFiles.



203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/amp/repository/versioned_file.rb', line 203

def parents
  p = @path
  fl = file_log
  pl = file_log.parents(file_node).map {|n| [p, n, fl]}
  
  r = file_log.renamed(file_node)
  pl[0] = [r[0], r[1], nil] if r
  
  pl.select {|parent,n,l| n != NULL_ID}.map do |parent, n, l|
    VersionedFile.new(@repo, parent, :file_id => n, :file_log => l)
  end
end

#renamedObject

Has this file been renamed? If so, return some useful info



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/amp/repository/versioned_file.rb', line 185

def renamed
  renamed = file_log.renamed(file_node)
  return renamed unless renamed
  
  return renamed if rev == linkrev
  
  name = path
  fnode = file_node
  changeset.parents.each do |p|
    pnode = p.filenode(name)
    next if pnode.nil?
    return nil if fnode == pnode
  end
  renamed
end

#repo_pathObject

Dunno why this is here



62
63
64
# File 'lib/amp/repository/versioned_file.rb', line 62

def repo_path
  @path
end

#revisionObject

Returns the revision index



138
139
140
141
# File 'lib/amp/repository/versioned_file.rb', line 138

def revision
  return changeset.rev if @changeset || @change_id
  file_log[file_rev].link_rev
end

#sizeObject

The size of this file



164
# File 'lib/amp/repository/versioned_file.rb', line 164

def size; file_log.size(file_rev); end

#to_iObject



47
48
49
# File 'lib/amp/repository/versioned_file.rb', line 47

def to_i
  change_id
end

#to_sObject

String representation.



103
104
105
# File 'lib/amp/repository/versioned_file.rb', line 103

def to_s
  "#{path}@#{node.hexlify[0..11]}"
end

#userObject

User who committed this revision to this file



148
# File 'lib/amp/repository/versioned_file.rb', line 148

def user; changeset.user; end