amp-core 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +23 -0
- data/Gemfile +11 -0
- data/LICENSE +20 -0
- data/README.rdoc +17 -0
- data/Rakefile +67 -0
- data/VERSION +1 -0
- data/features/amp-core.feature +9 -0
- data/features/step_definitions/amp-core_steps.rb +0 -0
- data/features/support/env.rb +4 -0
- data/lib/amp-core.rb +53 -0
- data/lib/amp-core/command_ext/repository_loading.rb +31 -0
- data/lib/amp-core/repository/abstract/abstract_changeset.rb +113 -0
- data/lib/amp-core/repository/abstract/abstract_local_repo.rb +208 -0
- data/lib/amp-core/repository/abstract/abstract_staging_area.rb +202 -0
- data/lib/amp-core/repository/abstract/abstract_versioned_file.rb +116 -0
- data/lib/amp-core/repository/abstract/common_methods/changeset.rb +185 -0
- data/lib/amp-core/repository/abstract/common_methods/local_repo.rb +293 -0
- data/lib/amp-core/repository/abstract/common_methods/staging_area.rb +248 -0
- data/lib/amp-core/repository/abstract/common_methods/versioned_file.rb +87 -0
- data/lib/amp-core/repository/generic_repo_picker.rb +94 -0
- data/lib/amp-core/repository/repository.rb +41 -0
- data/lib/amp-core/support/encoding_utils.rb +46 -0
- data/lib/amp-core/support/platform_utils.rb +92 -0
- data/lib/amp-core/support/rooted_opener.rb +143 -0
- data/lib/amp-core/support/string_utils.rb +86 -0
- data/lib/amp-core/templates/git/blank.log.erb +18 -0
- data/lib/amp-core/templates/git/default.log.erb +18 -0
- data/lib/amp-core/templates/mercurial/blank.commit.erb +23 -0
- data/lib/amp-core/templates/mercurial/blank.log.erb +18 -0
- data/lib/amp-core/templates/mercurial/default.commit.erb +23 -0
- data/lib/amp-core/templates/mercurial/default.log.erb +26 -0
- data/lib/amp-core/templates/template.rb +202 -0
- data/spec/amp-core_spec.rb +11 -0
- data/spec/command_ext_specs/repository_loading_spec.rb +64 -0
- data/spec/command_ext_specs/spec_helper.rb +1 -0
- data/spec/repository_specs/repository_spec.rb +41 -0
- data/spec/repository_specs/spec_helper.rb +1 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/support_specs/encoding_utils_spec.rb +69 -0
- data/spec/support_specs/platform_utils_spec.rb +33 -0
- data/spec/support_specs/spec_helper.rb +1 -0
- data/spec/support_specs/string_utils_spec.rb +44 -0
- data/test/test_templates.rb +81 -0
- metadata +157 -0
@@ -0,0 +1,293 @@
|
|
1
|
+
##################################################################
|
2
|
+
# Licensing Information #
|
3
|
+
# #
|
4
|
+
# The following code is licensed, as standalone code, under #
|
5
|
+
# the Ruby License, unless otherwise directed within the code. #
|
6
|
+
# #
|
7
|
+
# For information on the license of this code when distributed #
|
8
|
+
# with and used in conjunction with the other modules in the #
|
9
|
+
# Amp project, please see the root-level LICENSE file. #
|
10
|
+
# #
|
11
|
+
# © Michael J. Edgar and Ari Brown, 2009-2010 #
|
12
|
+
# #
|
13
|
+
##################################################################
|
14
|
+
|
15
|
+
module Amp
|
16
|
+
module Core
|
17
|
+
module Repositories
|
18
|
+
|
19
|
+
##
|
20
|
+
# = CommonLocalRepoMethods
|
21
|
+
#
|
22
|
+
# These methods are common to all repositories, and this module is mixed into
|
23
|
+
# the AbstractLocalRepository class. This guarantees that all repositories will
|
24
|
+
# have these methods.
|
25
|
+
#
|
26
|
+
# No methods should be placed into this module unless it relies on methods in the
|
27
|
+
# general API for repositories.
|
28
|
+
module CommonLocalRepoMethods
|
29
|
+
include Enumerable
|
30
|
+
attr_accessor :config
|
31
|
+
|
32
|
+
##
|
33
|
+
# Initializes a new directory to the given path, and with the current
|
34
|
+
# configuration.
|
35
|
+
#
|
36
|
+
# @param [String] path a path to the Repository.
|
37
|
+
# @param [Boolean] create Should we create a new one? Usually for
|
38
|
+
# the "amp init" command.
|
39
|
+
# @param [Amp::AmpConfig] config the configuration loaded from the user's
|
40
|
+
# system. Will have some settings overwritten by the repo's hgrc.
|
41
|
+
def initialize(path="", create=false, config=nil)
|
42
|
+
@capabilities = {}
|
43
|
+
@root = File.expand_path path.chomp("/")
|
44
|
+
@config = config
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Initializes a new repository in the given directory. We recommend
|
49
|
+
# calling this at some point in your repository subclass as it will
|
50
|
+
# do amp-specific initialization, though you will need to do all the
|
51
|
+
# hard stuff yourself.
|
52
|
+
def init(config=@config)
|
53
|
+
FileUtils.makedirs root
|
54
|
+
working_write "Ampfile", <<-EOF
|
55
|
+
# Any ruby code here will be executed before Amp loads a repository and
|
56
|
+
# dispatches a command.
|
57
|
+
#
|
58
|
+
# Example command:
|
59
|
+
#
|
60
|
+
# command "echo" do |c|
|
61
|
+
# c.opt :"no-newline", "Don't print a trailing newline character", :short => "-n"
|
62
|
+
# c.on_run do |opts, args|
|
63
|
+
# print args.join(" ")
|
64
|
+
# print "\\n" unless opts[:"no-newline"]
|
65
|
+
# end
|
66
|
+
# end
|
67
|
+
EOF
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# Joins the path to the repo's root (not .hg, the working dir root)
|
73
|
+
#
|
74
|
+
# @param path the path we're joining
|
75
|
+
# @return [String] the path joined to the working directory's root
|
76
|
+
def working_join(path)
|
77
|
+
File.join(root, path)
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Call the hooks that run under +call+
|
82
|
+
#
|
83
|
+
# @param [Symbol] call the location in the system where the hooks
|
84
|
+
# are to be called
|
85
|
+
def run_hook(call, opts={:throw => false})
|
86
|
+
Hook.run_hook(call, opts)
|
87
|
+
end
|
88
|
+
|
89
|
+
##
|
90
|
+
# Adds a list of file paths to the repository for the next commit.
|
91
|
+
#
|
92
|
+
# @param [String, Array<String>] paths the paths of the files we need to
|
93
|
+
# add to the next commit
|
94
|
+
# @return [Array<String>] which files WEREN'T added
|
95
|
+
def add(*paths)
|
96
|
+
staging_area.add(*paths)
|
97
|
+
end
|
98
|
+
|
99
|
+
##
|
100
|
+
# Removes the file (or files) from the repository. Marks them as removed
|
101
|
+
# in the DirState, and if the :unlink option is provided, the files are
|
102
|
+
# deleted from the filesystem.
|
103
|
+
#
|
104
|
+
# @param list the list of files. Could also just be 1 file as a string.
|
105
|
+
# should be paths.
|
106
|
+
# @param opts the options for this removal. Must be last argument or will mess
|
107
|
+
# things up.
|
108
|
+
# @option opts [Boolean] :unlink (false) whether or not to delete the
|
109
|
+
# files from the filesystem after marking them as removed from the
|
110
|
+
# DirState.
|
111
|
+
# @return [Boolean] success?
|
112
|
+
def remove(*args)
|
113
|
+
staging_area.remove(*args)
|
114
|
+
end
|
115
|
+
|
116
|
+
def relative_join(file, cur_dir=FileUtils.pwd)
|
117
|
+
@root_pathname ||= Pathname.new(root)
|
118
|
+
Pathname.new(File.expand_path(File.join(cur_dir, file))).relative_path_from(@root_pathname).to_s
|
119
|
+
end
|
120
|
+
|
121
|
+
##
|
122
|
+
# Walk recursively through the directory tree (or a changeset)
|
123
|
+
# finding all files matched by the match function
|
124
|
+
#
|
125
|
+
# @param [String, Integer] node selects which changeset to walk
|
126
|
+
# @param [Amp::Match] match the matcher decides how to pick the files
|
127
|
+
# @param [Array<String>] an array of filenames
|
128
|
+
def walk(node=nil, match = Match.create({}) { true })
|
129
|
+
self[node].walk match # calls Changeset#walk
|
130
|
+
end
|
131
|
+
|
132
|
+
##
|
133
|
+
# Iterates over each changeset in the repository, from oldest to newest.
|
134
|
+
#
|
135
|
+
# @yield each changeset in the repository is yielded to the caller, in order
|
136
|
+
# from oldest to newest. (Actually, lowest revision # to highest revision #)
|
137
|
+
def each(&block)
|
138
|
+
0.upto(size - 1) { |i| yield self[i] }
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
##
|
143
|
+
# This gives the status of the repository, comparing 2 node in
|
144
|
+
# its history. Now, with no parameters, it's going to compare the
|
145
|
+
# last revision with the working directory, which is the most common
|
146
|
+
# usage - that answers "what is the current status of the repository,
|
147
|
+
# compared to the last time a commit happened?". However, given any
|
148
|
+
# two revisions, it can compare them.
|
149
|
+
#
|
150
|
+
# @example @repo.status # => {:unknown => ['code/smthng.rb'], :added => [], ...}
|
151
|
+
# @param [Hash] opts the options for this command. there's a bunch.
|
152
|
+
# @option opts [String, Integer] :node_1 (".") an identifier for the starting
|
153
|
+
# revision
|
154
|
+
# @option opts [String, Integer] :node_2 (nil) an identifier for the ending
|
155
|
+
# revision. Defaults to the working directory.
|
156
|
+
# @option opts [Proc] :match (proc { true }) a proc that will match
|
157
|
+
# a file, so we know if we're interested in it.
|
158
|
+
# @option opts [Boolean] :ignored (false) do we want to see files we're
|
159
|
+
# ignoring?
|
160
|
+
# @option opts [Boolean] :clean (false) do we want to see files that are
|
161
|
+
# totally unchanged?
|
162
|
+
# @option opts [Boolean] :unknown (false) do we want to see files we've
|
163
|
+
# never seen before (i.e. files the user forgot to add to the repo)?
|
164
|
+
# @option opts [Boolean] :delta (false) do we want to see the overall delta?
|
165
|
+
# @return [Hash{Symbol => Array<String>}] no, I'm not kidding. the keys are:
|
166
|
+
# :modified, :added, :removed, :deleted, :unknown, :ignored, :clean, and :delta. The
|
167
|
+
# keys are the type of change, and the values are arrays of filenames
|
168
|
+
# (local to the root) that are under each key.
|
169
|
+
def status(opts={:node_1 => '.'})
|
170
|
+
run_hook :status
|
171
|
+
|
172
|
+
opts[:delta] ||= true
|
173
|
+
node1, node2, match = opts[:node_1], opts[:node_2], opts[:match]
|
174
|
+
|
175
|
+
match = Match.create({}) { true } unless match
|
176
|
+
|
177
|
+
node1 = self[node1] unless node1.kind_of? Repositories::AbstractChangeset # get changeset objects
|
178
|
+
node2 = self[node2] unless node2.kind_of? Repositories::AbstractChangeset
|
179
|
+
|
180
|
+
# are we working with working directories?
|
181
|
+
working = node2.working?
|
182
|
+
comparing_to_tip = working && node2.parents.include?(node1)
|
183
|
+
|
184
|
+
status = Hash.new {|h, k| h[k] = k == :delta ? 0 : [] }
|
185
|
+
|
186
|
+
if working
|
187
|
+
# get the dirstate's latest status
|
188
|
+
status.merge! staging_area.status(opts[:ignored], opts[:clean], opts[:unknown], match)
|
189
|
+
|
190
|
+
# this case is run about 99% of the time
|
191
|
+
# do we need to do hashes on any files to see if they've changed?
|
192
|
+
if comparing_to_tip && status[:lookup].any?
|
193
|
+
clean, modified = fix_files(status[:lookup], node1, node2)
|
194
|
+
|
195
|
+
status[:clean].concat clean
|
196
|
+
status[:modified].concat modified
|
197
|
+
end
|
198
|
+
end
|
199
|
+
# if we're working with old revisions...
|
200
|
+
unless comparing_to_tip
|
201
|
+
# get the older revision manifest
|
202
|
+
node1_file_list = node1.all_files.dup
|
203
|
+
node2_file_list = node2.all_files.dup
|
204
|
+
if working
|
205
|
+
# remove any files we've marked as removed them from the '.' manifest
|
206
|
+
status[:removed].each {|file| node2_file_list.delete file }
|
207
|
+
end
|
208
|
+
|
209
|
+
# Every file in the later revision (or working directory)
|
210
|
+
node2.all_files.each do |file|
|
211
|
+
# Does it exist in the old manifest? If so, it wasn't added.
|
212
|
+
if node1.include? file
|
213
|
+
# It's in the old manifest, so lets check if its been changed
|
214
|
+
# Else, it must be unchanged
|
215
|
+
if file_modified? file, :node1 => node1, :node2 => node2 # tests.any?
|
216
|
+
status[:modified] << file
|
217
|
+
elsif opts[:clean]
|
218
|
+
status[:clean] << file
|
219
|
+
end
|
220
|
+
# Remove that file from the old manifest, since we've checked it
|
221
|
+
node1_file_list.delete file
|
222
|
+
else
|
223
|
+
# if it's not in the old manifest, it's been added
|
224
|
+
status[:added] << file
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
# Anything left in the old manifest is a file we've removed since the
|
229
|
+
# first revision.
|
230
|
+
status[:removed] = node1_file_list
|
231
|
+
end
|
232
|
+
|
233
|
+
# We're done!
|
234
|
+
status.delete :lookup # because nobody cares about it
|
235
|
+
delta = status.delete :delta
|
236
|
+
|
237
|
+
status.each {|k, v| v.sort! } # sort dem fuckers
|
238
|
+
status[:delta] = delta if opts[:delta]
|
239
|
+
status.each {|k, _| status.delete k if opts[:only] && !opts[:only].include?(k) }
|
240
|
+
status
|
241
|
+
end
|
242
|
+
|
243
|
+
##
|
244
|
+
# Look up the files in +lookup+ to make sure
|
245
|
+
# they're either the same or not. Normally, we can
|
246
|
+
# just tell if two files are the same by looking at their sizes. But
|
247
|
+
# sometimes, we can't! That's where this method comes into play; it
|
248
|
+
# hashes the files to verify integrity.
|
249
|
+
#
|
250
|
+
# @param [String] lookup files to look up
|
251
|
+
# @param node1
|
252
|
+
# @param node2
|
253
|
+
# @return [[String], [String]] clean files and modified files
|
254
|
+
def fix_files(lookup, node1, node2)
|
255
|
+
write_dirstate = false # this gets returned
|
256
|
+
modified = [] # and this
|
257
|
+
fixup = [] # fixup are files that haven't changed so they're being
|
258
|
+
# marked wrong in the dirstate. this gets returned
|
259
|
+
|
260
|
+
lookup.each do |file|
|
261
|
+
# this checks to see if the file has been modified after doing
|
262
|
+
# hashes/flag checks
|
263
|
+
tests = [ node1.include?(file) ,
|
264
|
+
node2.flags(file) == node1.flags(file) ,
|
265
|
+
node1[file] === node2[file] ]
|
266
|
+
|
267
|
+
unless tests.all?
|
268
|
+
modified << file
|
269
|
+
else
|
270
|
+
fixup << file # mark the file as clean
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
|
275
|
+
# mark every fixup'd file as clean in the dirstate
|
276
|
+
begin
|
277
|
+
lock_working do
|
278
|
+
staging_area.normal *fixup
|
279
|
+
fixup.each do |file|
|
280
|
+
modified.delete file
|
281
|
+
end
|
282
|
+
end
|
283
|
+
rescue LockError
|
284
|
+
end
|
285
|
+
|
286
|
+
# the fixups are actually clean
|
287
|
+
[fixup, modified]
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
@@ -0,0 +1,248 @@
|
|
1
|
+
##################################################################
|
2
|
+
# Licensing Information #
|
3
|
+
# #
|
4
|
+
# The following code is licensed, as standalone code, under #
|
5
|
+
# the Ruby License, unless otherwise directed within the code. #
|
6
|
+
# #
|
7
|
+
# For information on the license of this code when distributed #
|
8
|
+
# with and used in conjunction with the other modules in the #
|
9
|
+
# Amp project, please see the root-level LICENSE file. #
|
10
|
+
# #
|
11
|
+
# © Michael J. Edgar and Ari Brown, 2009-2010 #
|
12
|
+
# #
|
13
|
+
##################################################################
|
14
|
+
|
15
|
+
module Amp
|
16
|
+
module Core
|
17
|
+
module Repositories
|
18
|
+
|
19
|
+
##
|
20
|
+
# = CommonStagingAreaMethods
|
21
|
+
#
|
22
|
+
# These methods are common to all staging areas, and this module is mixed into
|
23
|
+
# the AbstractStagingArea class. This guarantees that all staging areas will
|
24
|
+
# have these methods.
|
25
|
+
#
|
26
|
+
# No methods should be placed into this module unless it relies on methods in the
|
27
|
+
# general API for staging areas.
|
28
|
+
module CommonStagingAreaMethods
|
29
|
+
|
30
|
+
##
|
31
|
+
# Returns whether or not the repository is tracking the given file.
|
32
|
+
#
|
33
|
+
# @api
|
34
|
+
# @param [String] filename the file to look up
|
35
|
+
# @return [Boolean] are we tracking the given file?
|
36
|
+
def tracking?(filename)
|
37
|
+
file_status(filename) != :untracked
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# Helper method that filters out a list of explicit filenames that do not
|
42
|
+
# belong in the repository. It then stores the File stats of the files
|
43
|
+
# it keeps, and returns any explicitly named directories.
|
44
|
+
#
|
45
|
+
# @param [Array<String>] files the files to examine
|
46
|
+
# @param [Amp::Match] match the matcher object
|
47
|
+
# @return [Array<Hash, Array<String>>] returns an array: [file_stats, directories]
|
48
|
+
def examine_named_files(files, match)
|
49
|
+
results, work = {vcs_dir => true}, [] # ignore the .hg
|
50
|
+
files.reject {|f| results[f] || f == ""}.sort.each do |file|
|
51
|
+
path = File.join(repo.root, file)
|
52
|
+
|
53
|
+
if File.exist?(path)
|
54
|
+
# we'll take it! but only if it's a directory, which means we have
|
55
|
+
# more work to do...
|
56
|
+
if File.directory?(path)
|
57
|
+
# add it to the list of dirs we have to search in
|
58
|
+
work << File.join(repo.root, file) unless ignoring_directory? file
|
59
|
+
elsif File.file?(path) || File.symlink?(path)
|
60
|
+
# ARGH WE FOUND ZE BOOTY
|
61
|
+
results[file] = File.lstat path
|
62
|
+
else
|
63
|
+
# user you are a fuckup in life please exit the world
|
64
|
+
UI::warn "#{file}: unsupported file type (type is #{File.ftype file})"
|
65
|
+
results[file] = nil if tracking? file
|
66
|
+
end
|
67
|
+
else
|
68
|
+
prefix = file + '/'
|
69
|
+
|
70
|
+
unless all_files.find { |f, _| f == file || f.start_with?(prefix) }
|
71
|
+
bad_type[file]
|
72
|
+
results[file] = nil if (tracking?(file) || !ignoring_file?(file)) && match.call(file)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
[results, work]
|
77
|
+
end
|
78
|
+
|
79
|
+
##
|
80
|
+
# Helper method that runs match's patterns on every non-ignored file in
|
81
|
+
# the repository's directory.
|
82
|
+
#
|
83
|
+
# @param [Hash] found_files the already found files (we don't want to search them
|
84
|
+
# again)
|
85
|
+
# @param [Array<String>] dirs the directories to search
|
86
|
+
# @param [Amp::Match] match the matcher object that runs patterns against
|
87
|
+
# filenames
|
88
|
+
# @return [Hash] the updated found_files hash
|
89
|
+
def find_with_patterns(found_files, dirs, match)
|
90
|
+
results = found_files
|
91
|
+
Find.find(*dirs) do |f|
|
92
|
+
tf = f[(repo.root.size+1)..-1]
|
93
|
+
Find.prune if results[tf]
|
94
|
+
|
95
|
+
stats = File.lstat f
|
96
|
+
match_result = match.call tf
|
97
|
+
tracked = tracking? tf
|
98
|
+
|
99
|
+
if File.directory? f
|
100
|
+
Find.prune if ignoring_file? tf
|
101
|
+
results[tf] = nil if tracked && match_result
|
102
|
+
elsif File.file?(f) || File.symlink?(f)
|
103
|
+
if match_result && (tracked || !ignoring_file?(tf))
|
104
|
+
results[tf] = stats
|
105
|
+
end
|
106
|
+
elsif tracked && match_result
|
107
|
+
results[tf] = nil
|
108
|
+
end
|
109
|
+
end
|
110
|
+
results
|
111
|
+
end
|
112
|
+
|
113
|
+
##
|
114
|
+
# Walk recursively through the directory tree, finding all
|
115
|
+
# files matched by the regexp in match.
|
116
|
+
#
|
117
|
+
# Step 1: find all explicit files
|
118
|
+
# Step 2: visit subdirectories
|
119
|
+
# Step 3: report unseen items in the @files hash
|
120
|
+
#
|
121
|
+
# @todo this is still tied to hg
|
122
|
+
# @param [Boolean] unknown
|
123
|
+
# @param [Boolean] ignored
|
124
|
+
# @return [Hash{String => [NilClass, File::Stat]}] nil for directories and
|
125
|
+
# stuff, File::Stat for files and links
|
126
|
+
def walk(unknown, ignored, match = Amp::Match.new { true })
|
127
|
+
if ignored
|
128
|
+
@ignore_all = false
|
129
|
+
elsif not unknown
|
130
|
+
@ignore_all = true
|
131
|
+
end
|
132
|
+
|
133
|
+
files = (match.files || []).uniq
|
134
|
+
|
135
|
+
# why do we overwrite the entire array if it includes the current dir?
|
136
|
+
# we even kill posisbly good things
|
137
|
+
files = [''] if files.include?('.') # strange thing to do
|
138
|
+
|
139
|
+
# Step 1: find all explicit files
|
140
|
+
results, found_directories = examine_named_files files, match
|
141
|
+
work = [repo.root] + found_directories
|
142
|
+
|
143
|
+
# Run the patterns
|
144
|
+
results = find_with_patterns(results, work, match)
|
145
|
+
|
146
|
+
# step 3: report unseen items in @files
|
147
|
+
visit = all_files.select {|f| !results[f] && match.call(f) }.sort
|
148
|
+
|
149
|
+
visit.each do |file|
|
150
|
+
path = File.join(repo.root, file)
|
151
|
+
keep = File.exist?(path) && (File.file?(path) || File.symlink(path))
|
152
|
+
results[file] = keep ? File.lstat(path) : nil
|
153
|
+
end
|
154
|
+
|
155
|
+
results.delete vcs_dir
|
156
|
+
@ignore_all = nil # reset this
|
157
|
+
results
|
158
|
+
end
|
159
|
+
|
160
|
+
##
|
161
|
+
# what's the current state of life, man!
|
162
|
+
# Splits up all the files into modified, clean,
|
163
|
+
# added, deleted, unknown, ignored, or lookup-needed.
|
164
|
+
#
|
165
|
+
# @param [Boolean] ignored do we collect the ignore files?
|
166
|
+
# @param [Boolean] clean do we collect the clean files?
|
167
|
+
# @param [Boolean] unknown do we collect the unknown files?
|
168
|
+
# @param [Amp::Match] match the matcher
|
169
|
+
# @return [Hash{Symbol => Array<String>}] a hash of the filestatuses and their files
|
170
|
+
def status(ignored, clean, unknown, match = Match.new { true })
|
171
|
+
list_ignored, list_clean, list_unknown = ignored, clean, unknown
|
172
|
+
lookup, modified, added, unknown, ignored = [], [], [], [], []
|
173
|
+
moved, copied, removed, deleted, clean = [], [], [], [], []
|
174
|
+
delta = 0
|
175
|
+
|
176
|
+
walk(list_unknown, list_ignored, match).each do |file, st|
|
177
|
+
next if file.nil?
|
178
|
+
|
179
|
+
unless tracking?(file)
|
180
|
+
if list_ignored && ignoring_directory?(file)
|
181
|
+
ignored << file
|
182
|
+
elsif list_unknown
|
183
|
+
unknown << file unless ignoring_file?(file)
|
184
|
+
end
|
185
|
+
|
186
|
+
next # on to the next one, don't do the rest
|
187
|
+
end
|
188
|
+
|
189
|
+
# here's where we split up the files
|
190
|
+
state = file_status file
|
191
|
+
|
192
|
+
delta += calculate_delta(file, st)
|
193
|
+
if !st && [:normal, :modified, :added].include?(state)
|
194
|
+
# add it to the deleted folder if it should be here but isn't
|
195
|
+
deleted << file
|
196
|
+
elsif state == :normal
|
197
|
+
case file_precise_status(file, st)
|
198
|
+
when :modified
|
199
|
+
modified << file
|
200
|
+
when :lookup
|
201
|
+
lookup << file
|
202
|
+
when :clean
|
203
|
+
clean << file if list_clean
|
204
|
+
end
|
205
|
+
|
206
|
+
elsif state == :merged
|
207
|
+
modified << file
|
208
|
+
elsif state == :added
|
209
|
+
added << file
|
210
|
+
elsif state == :removed
|
211
|
+
removed << file
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
# # This code creates the copied and moved arrays
|
216
|
+
# #
|
217
|
+
# # ugh this should be optimized
|
218
|
+
# # as in, built into the code above ^^^^^
|
219
|
+
# dirstate.copy_map.each do |dst, src|
|
220
|
+
# # assume that if +src+ is in +removed+ then +dst+ is in +added+
|
221
|
+
# # we know that this part will be COPIES
|
222
|
+
# if removed.include? src
|
223
|
+
# removed.delete src
|
224
|
+
# added.delete dst
|
225
|
+
# copied << [src, dst]
|
226
|
+
# elsif added.include? dst # these are the MOVES
|
227
|
+
# added.delete dst
|
228
|
+
# moved << [src, dst]
|
229
|
+
# end
|
230
|
+
# end
|
231
|
+
|
232
|
+
r = { :modified => modified.sort , # those that have clearly been modified
|
233
|
+
:added => added.sort , # those that are marked for adding
|
234
|
+
:removed => removed.sort , # those that are marked for removal
|
235
|
+
:deleted => deleted.sort , # those that should be here but aren't
|
236
|
+
:unknown => unknown.sort , # those that aren't being tracked
|
237
|
+
:ignored => ignored.sort , # those that are being deliberately ignored
|
238
|
+
:clean => clean.sort , # those that haven't changed
|
239
|
+
:lookup => lookup.sort , # those that need to be content-checked to see if they've changed
|
240
|
+
#:copied => copied.sort_by {|a| a[0] }, # those that have been copied
|
241
|
+
#:moved => moved.sort_by {|a| a[0] }, # those that have been moved
|
242
|
+
:delta => delta # how many bytes have been added or removed from files (not bytes that have been changed)
|
243
|
+
}
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|