Module: Amp::Repositories::CommonLocalRepoMethods

Included in:
AbstractLocalRepository
Defined in:
lib/amp/repository/abstract/common_methods/local_repo.rb

Overview

CommonLocalRepoMethods

These methods are common to all repositories, and this module is mixed into the AbstractLocalRepository class. This guarantees that all repositories will have these methods.

No methods should be placed into this module unless it relies on methods in the general API for repositories.

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#configObject

Returns the value of attribute config.



15
16
17
# File 'lib/amp/repository/abstract/common_methods/local_repo.rb', line 15

def config
  @config
end

Instance Method Details

#add(*paths) ⇒ Array<String>

Adds a list of file paths to the repository for the next commit.

Parameters:

  • paths (String, Array<String>)

    the paths of the files we need to add to the next commit

Returns:



80
81
82
# File 'lib/amp/repository/abstract/common_methods/local_repo.rb', line 80

def add(*paths)
  staging_area.add(*paths)
end

#each { ... } ⇒ Object

Iterates over each changeset in the repository, from oldest to newest.

Yields:

  • each changeset in the repository is yielded to the caller, in order from oldest to newest. (Actually, lowest revision # to highest revision #)



122
123
124
# File 'lib/amp/repository/abstract/common_methods/local_repo.rb', line 122

def each(&block)
  0.upto(size - 1) { |i| yield self[i] }
end

#fix_files(lookup, node1, node2) ⇒ [String]

Look up the files in lookup to make sure they’re either the same or not. Normally, we can just tell if two files are the same by looking at their sizes. But sometimes, we can’t! That’s where this method comes into play; it hashes the files to verify integrity.

Parameters:

  • lookup (String)

    files to look up

  • node1
  • node2

Returns:



239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/amp/repository/abstract/common_methods/local_repo.rb', line 239

def fix_files(lookup, node1, node2)
  write_dirstate = false # this gets returned
  modified = [] # and this
  fixup    = [] # fixup are files that haven't changed so they're being
                # marked wrong in the dirstate. this gets returned
  
  lookup.each do |file|
    # this checks to see if the file has been modified after doing
    # hashes/flag checks
    tests = [ node1.include?(file)                   ,
              node2.flags(file) == node1.flags(file) ,
              node1[file]      === node2[file]       ]
    
    unless tests.all?
      modified << file
    else
      fixup << file # mark the file as clean
    end
  end
  
  
  # mark every fixup'd file as clean in the dirstate
  begin
    lock_working do
      staging_area.normal *fixup  
      fixup.each do |file|
        modified.delete file
      end
    end
  rescue LockError
  end
  
  # the fixups are actually clean
  [fixup, modified]
end

#init(config = @config) ⇒ Object

Initializes a new repository in the given directory. We recommend calling this at some point in your repository subclass as it will do amp-specific initialization, though you will need to do all the hard stuff yourself.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/amp/repository/abstract/common_methods/local_repo.rb', line 37

def init(config=@config)
  FileUtils.makedirs root
  working_write "Ampfile", <<-EOF
# Any ruby code here will be executed before Amp loads a repository and
# dispatches a command.
#
# Example command:
#
# command "echo" do |c|
#    c.opt :"no-newline", "Don't print a trailing newline character", :short => "-n"
#    c.on_run do |opts, args|
#        print args.join(" ")
#        print "\\n" unless opts[:"no-newline"]
#    end
# end
EOF
  
end

#initialize(path = "", create = false, config = nil) ⇒ Object

Initializes a new directory to the given path, and with the current configuration.

Parameters:

  • path (String) (defaults to: "")

    a path to the Repository.

  • create (Boolean) (defaults to: false)

    Should we create a new one? Usually for the “amp init” command.

  • config (Amp::AmpConfig) (defaults to: nil)

    the configuration loaded from the user’s system. Will have some settings overwritten by the repo’s hgrc.



26
27
28
29
30
# File 'lib/amp/repository/abstract/common_methods/local_repo.rb', line 26

def initialize(path="", create=false, config=nil)
  @capabilities = {}
  @root         = File.expand_path path.chomp("/")
  @config       = config
end

#relative_join(file, cur_dir = FileUtils.pwd) ⇒ Object



101
102
103
104
# File 'lib/amp/repository/abstract/common_methods/local_repo.rb', line 101

def relative_join(file, cur_dir=FileUtils.pwd)
  @root_pathname ||= Pathname.new(root)
  Pathname.new(File.expand_path(File.join(cur_dir, file))).relative_path_from(@root_pathname).to_s
end

#remove(*args) ⇒ Boolean

Removes the file (or files) from the repository. Marks them as removed in the DirState, and if the :unlink option is provided, the files are deleted from the filesystem.

Parameters:

  • list

    the list of files. Could also just be 1 file as a string. should be paths.

  • opts

    the options for this removal. Must be last argument or will mess things up.

  • [Boolean] (Hash)

    a customizable set of options

Returns:



97
98
99
# File 'lib/amp/repository/abstract/common_methods/local_repo.rb', line 97

def remove(*args)
  staging_area.remove(*args)
end

#run_hook(call, opts = {:throw => false}) ⇒ Object

Call the hooks that run under call

Parameters:

  • call (Symbol)

    the location in the system where the hooks are to be called



70
71
72
# File 'lib/amp/repository/abstract/common_methods/local_repo.rb', line 70

def run_hook(call, opts={:throw => false})
  Hook.run_hook(call, opts)
end

#status(opts = {:node_1 => '.'}) ⇒ Hash<Symbol => Array<String>>

This gives the status of the repository, comparing 2 node in its history. Now, with no parameters, it’s going to compare the last revision with the working directory, which is the most common usage - that answers “what is the current status of the repository, compared to the last time a commit happened?”. However, given any two revisions, it can compare them.

Examples:

@repo.status # => => [‘code/smthng.rb’], :added => [], …

Parameters:

  • opts (Hash) (defaults to: {:node_1 => '.'})

    the options for this command. there’s a bunch.

  • [String, (Hash)

    a customizable set of options

  • [Proc] (Hash)

    a customizable set of options

  • [Boolean] (Hash)

    a customizable set of options

Returns:

  • (Hash<Symbol => Array<String>>)

    no, I’m not kidding. the keys are: :modified, :added, :removed, :deleted, :unknown, :ignored, :clean, and :delta. The keys are the type of change, and the values are arrays of filenames (local to the root) that are under each key.



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/amp/repository/abstract/common_methods/local_repo.rb', line 154

def status(opts={:node_1 => '.'})
  run_hook :status
  
  opts[:delta] ||= true
  node1, node2, match = opts[:node_1], opts[:node_2], opts[:match]
  
  match = Match.create({}) { true } unless match
  
  node1 = self[node1] unless node1.kind_of? Repositories::AbstractChangeset # get changeset objects
  node2 = self[node2] unless node2.kind_of? Repositories::AbstractChangeset
  
  # are we working with working directories?
  working = node2.working?
  comparing_to_tip = working && node2.parents.include?(node1)
  
  status = Hash.new {|h, k| h[k] = k == :delta ? 0 : [] }
  
  if working
    # get the dirstate's latest status
    status.merge! staging_area.status(opts[:ignored], opts[:clean], opts[:unknown], match)
    
    # this case is run about 99% of the time
    # do we need to do hashes on any files to see if they've changed?
    if comparing_to_tip && status[:lookup].any?
      clean, modified = fix_files(status[:lookup], node1, node2)
      
      status[:clean].concat clean
      status[:modified].concat modified
    end
  end
  # if we're working with old revisions...
  unless comparing_to_tip
    # get the older revision manifest            
    node1_file_list = node1.all_files.dup
    node2_file_list = node2.all_files.dup
    if working
      # remove any files we've marked as removed them from the '.' manifest
      status[:removed].each {|file| node2_file_list.delete file }
    end
    
    # Every file in the later revision (or working directory)
    node2.all_files.each do |file|
      # Does it exist in the old manifest? If so, it wasn't added.
      if node1.include? file
        # It's in the old manifest, so lets check if its been changed
        # Else, it must be unchanged
        if file_modified? file, :node1 => node1, :node2 => node2 # tests.any?
          status[:modified] << file
        elsif opts[:clean]
          status[:clean]    << file
        end
        # Remove that file from the old manifest, since we've checked it
        node1_file_list.delete file
      else
        # if it's not in the old manifest, it's been added
        status[:added] << file
      end
    end
    
    # Anything left in the old manifest is a file we've removed since the
    # first revision.
    status[:removed] = node1_file_list
  end
  
  # We're done!
  status.delete :lookup # because nobody cares about it
  delta = status.delete :delta
  
  status.each {|k, v| v.sort! } # sort dem fuckers
  status[:delta] = delta if opts[:delta]
  status.each {|k, _| status.delete k if opts[:only] && !opts[:only].include?(k) }
  status
end

#walk(node = nil, match = Match.create({}) { true }) ⇒ Object

Walk recursively through the directory tree (or a changeset) finding all files matched by the match function

Parameters:

  • node (String, Integer) (defaults to: nil)

    selects which changeset to walk

  • match (Amp::Match) (defaults to: Match.create({}) { true })

    the matcher decides how to pick the files

  • an (Array<String>)

    array of filenames



113
114
115
# File 'lib/amp/repository/abstract/common_methods/local_repo.rb', line 113

def walk(node=nil, match = Match.create({}) { true })
  self[node].walk match # calls Changeset#walk
end

#working_join(path) ⇒ String

Joins the path to the repo’s root (not .hg, the working dir root)

Parameters:

  • path

    the path we’re joining

Returns:

  • (String)

    the path joined to the working directory’s root



61
62
63
# File 'lib/amp/repository/abstract/common_methods/local_repo.rb', line 61

def working_join(path)
  File.join(root, path)
end