amp-core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|