Module: Amp::Core::Repositories::CommonLocalRepoMethods

Includes:
Enumerable
Included in:
AbstractLocalRepository
Defined in:
lib/amp-core/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.



30
31
32
# File 'lib/amp-core/repository/abstract/common_methods/local_repo.rb', line 30

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:

  • (Array<String>)

    which files WEREN’T added



95
96
97
# File 'lib/amp-core/repository/abstract/common_methods/local_repo.rb', line 95

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 #)



137
138
139
# File 'lib/amp-core/repository/abstract/common_methods/local_repo.rb', line 137

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:

  • ([String], [String])

    clean files and modified files



254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
# File 'lib/amp-core/repository/abstract/common_methods/local_repo.rb', line 254

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.



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/amp-core/repository/abstract/common_methods/local_repo.rb', line 52

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.



41
42
43
44
45
# File 'lib/amp-core/repository/abstract/common_methods/local_repo.rb', line 41

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



116
117
118
119
# File 'lib/amp-core/repository/abstract/common_methods/local_repo.rb', line 116

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.

Returns:

  • (Boolean)

    success?



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

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



85
86
87
# File 'lib/amp-core/repository/abstract/common_methods/local_repo.rb', line 85

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.

Options Hash (opts):

  • :node_1 (String, Integer) — default: "."

    an identifier for the starting revision

  • :node_2 (String, Integer) — default: nil

    an identifier for the ending revision. Defaults to the working directory.

  • :match (Proc) — default: proc { true }

    a proc that will match a file, so we know if we’re interested in it.

  • :ignored (Boolean) — default: false

    do we want to see files we’re ignoring?

  • :clean (Boolean) — default: false

    do we want to see files that are totally unchanged?

  • :unknown (Boolean) — default: false

    do we want to see files we’ve never seen before (i.e. files the user forgot to add to the repo)?

  • :delta (Boolean) — default: false

    do we want to see the overall delta?

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.



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
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/amp-core/repository/abstract/common_methods/local_repo.rb', line 169

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



128
129
130
# File 'lib/amp-core/repository/abstract/common_methods/local_repo.rb', line 128

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



76
77
78
# File 'lib/amp-core/repository/abstract/common_methods/local_repo.rb', line 76

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