amp 0.5.2 → 0.5.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +12 -0
- data/.hgignore +3 -0
- data/AUTHORS +1 -1
- data/Manifest.txt +99 -38
- data/README.md +3 -3
- data/Rakefile +53 -18
- data/SCHEDULE.markdown +5 -1
- data/TODO.markdown +120 -149
- data/ampfile.rb +3 -1
- data/bin/amp +4 -1
- data/ext/amp/bz2/extconf.rb +1 -1
- data/ext/amp/mercurial_patch/extconf.rb +1 -1
- data/ext/amp/mercurial_patch/mpatch.c +4 -3
- data/ext/amp/priority_queue/extconf.rb +1 -1
- data/ext/amp/support/extconf.rb +1 -1
- data/ext/amp/support/support.c +1 -1
- data/lib/amp.rb +125 -67
- data/lib/amp/commands/command.rb +12 -10
- data/lib/amp/commands/command_support.rb +8 -1
- data/lib/amp/commands/commands/help.rb +2 -20
- data/lib/amp/commands/commands/init.rb +14 -2
- data/lib/amp/commands/commands/templates.rb +6 -4
- data/lib/amp/commands/commands/version.rb +15 -1
- data/lib/amp/commands/commands/workflow.rb +3 -3
- data/lib/amp/commands/commands/workflows/git/add.rb +3 -3
- data/lib/amp/commands/commands/workflows/git/copy.rb +1 -1
- data/lib/amp/commands/commands/workflows/git/rm.rb +4 -2
- data/lib/amp/commands/commands/workflows/hg/add.rb +1 -1
- data/lib/amp/commands/commands/workflows/hg/addremove.rb +2 -2
- data/lib/amp/commands/commands/workflows/hg/annotate.rb +8 -2
- data/lib/amp/commands/commands/workflows/hg/bisect.rb +253 -0
- data/lib/amp/commands/commands/workflows/hg/branch.rb +1 -1
- data/lib/amp/commands/commands/workflows/hg/branches.rb +3 -3
- data/lib/amp/commands/commands/workflows/hg/bundle.rb +3 -3
- data/lib/amp/commands/commands/workflows/hg/clone.rb +4 -5
- data/lib/amp/commands/commands/workflows/hg/commit.rb +37 -1
- data/lib/amp/commands/commands/workflows/hg/copy.rb +2 -1
- data/lib/amp/commands/commands/workflows/hg/debug/index.rb +1 -1
- data/lib/amp/commands/commands/workflows/hg/diff.rb +3 -8
- data/lib/amp/commands/commands/workflows/hg/forget.rb +5 -4
- data/lib/amp/commands/commands/workflows/hg/identify.rb +6 -6
- data/lib/amp/commands/commands/workflows/hg/import.rb +1 -1
- data/lib/amp/commands/commands/workflows/hg/incoming.rb +2 -2
- data/lib/amp/commands/commands/workflows/hg/log.rb +5 -4
- data/lib/amp/commands/commands/workflows/hg/merge.rb +1 -1
- data/lib/amp/commands/commands/workflows/hg/move.rb +5 -3
- data/lib/amp/commands/commands/workflows/hg/outgoing.rb +1 -1
- data/lib/amp/commands/commands/workflows/hg/push.rb +6 -7
- data/lib/amp/commands/commands/workflows/hg/remove.rb +2 -2
- data/lib/amp/commands/commands/workflows/hg/resolve.rb +6 -23
- data/lib/amp/commands/commands/workflows/hg/root.rb +1 -2
- data/lib/amp/commands/commands/workflows/hg/status.rb +21 -12
- data/lib/amp/commands/commands/workflows/hg/tag.rb +2 -2
- data/lib/amp/commands/commands/workflows/hg/untrack.rb +12 -0
- data/lib/amp/commands/commands/workflows/hg/verify.rb +13 -3
- data/lib/amp/commands/commands/workflows/hg/what_changed.rb +18 -0
- data/lib/amp/commands/dispatch.rb +12 -13
- data/lib/amp/dependencies/amp_support.rb +1 -1
- data/lib/amp/dependencies/amp_support/ruby_amp_support.rb +1 -0
- data/lib/amp/dependencies/maruku.rb +136 -0
- data/lib/amp/dependencies/maruku/attributes.rb +227 -0
- data/lib/amp/dependencies/maruku/defaults.rb +71 -0
- data/lib/amp/dependencies/maruku/errors_management.rb +92 -0
- data/lib/amp/dependencies/maruku/helpers.rb +260 -0
- data/lib/amp/dependencies/maruku/input/charsource.rb +326 -0
- data/lib/amp/dependencies/maruku/input/extensions.rb +69 -0
- data/lib/amp/dependencies/maruku/input/html_helper.rb +189 -0
- data/lib/amp/dependencies/maruku/input/linesource.rb +111 -0
- data/lib/amp/dependencies/maruku/input/parse_block.rb +615 -0
- data/lib/amp/dependencies/maruku/input/parse_doc.rb +234 -0
- data/lib/amp/dependencies/maruku/input/parse_span_better.rb +746 -0
- data/lib/amp/dependencies/maruku/input/rubypants.rb +225 -0
- data/lib/amp/dependencies/maruku/input/type_detection.rb +147 -0
- data/lib/amp/dependencies/maruku/input_textile2/t2_parser.rb +163 -0
- data/lib/amp/dependencies/maruku/maruku.rb +33 -0
- data/lib/amp/dependencies/maruku/output/to_ansi.rb +223 -0
- data/lib/amp/dependencies/maruku/output/to_html.rb +991 -0
- data/lib/amp/dependencies/maruku/output/to_markdown.rb +164 -0
- data/lib/amp/dependencies/maruku/output/to_s.rb +56 -0
- data/lib/amp/dependencies/maruku/string_utils.rb +191 -0
- data/lib/amp/dependencies/maruku/structures.rb +167 -0
- data/lib/amp/dependencies/maruku/structures_inspect.rb +87 -0
- data/lib/amp/dependencies/maruku/structures_iterators.rb +61 -0
- data/lib/amp/dependencies/maruku/textile2.rb +1 -0
- data/lib/amp/dependencies/maruku/toc.rb +199 -0
- data/lib/amp/dependencies/maruku/usage/example1.rb +33 -0
- data/lib/amp/dependencies/maruku/version.rb +40 -0
- data/lib/amp/dependencies/priority_queue.rb +2 -1
- data/lib/amp/dependencies/python_config.rb +2 -1
- data/lib/amp/graphs/ancestor.rb +2 -1
- data/lib/amp/graphs/copies.rb +236 -233
- data/lib/amp/help/entries/__default__.erb +31 -0
- data/lib/amp/help/entries/commands.erb +6 -0
- data/lib/amp/help/entries/mdtest.md +35 -0
- data/lib/amp/help/entries/silly +3 -0
- data/lib/amp/help/help.rb +288 -0
- data/lib/amp/profiling_hacks.rb +5 -3
- data/lib/amp/repository/abstract/abstract_changeset.rb +97 -0
- data/lib/amp/repository/abstract/abstract_local_repo.rb +181 -0
- data/lib/amp/repository/abstract/abstract_staging_area.rb +180 -0
- data/lib/amp/repository/abstract/abstract_versioned_file.rb +100 -0
- data/lib/amp/repository/abstract/common_methods/changeset.rb +75 -0
- data/lib/amp/repository/abstract/common_methods/local_repo.rb +277 -0
- data/lib/amp/repository/abstract/common_methods/staging_area.rb +233 -0
- data/lib/amp/repository/abstract/common_methods/versioned_file.rb +71 -0
- data/lib/amp/repository/generic_repo_picker.rb +78 -0
- data/lib/amp/repository/git/repo_format/changeset.rb +336 -0
- data/lib/amp/repository/git/repo_format/staging_area.rb +192 -0
- data/lib/amp/repository/git/repo_format/versioned_file.rb +119 -0
- data/lib/amp/repository/git/repositories/local_repository.rb +164 -0
- data/lib/amp/repository/git/repository.rb +41 -0
- data/lib/amp/repository/mercurial/encoding/mercurial_diff.rb +382 -0
- data/lib/amp/repository/mercurial/encoding/mercurial_patch.rb +1 -0
- data/lib/amp/repository/mercurial/encoding/patch.rb +294 -0
- data/lib/amp/repository/mercurial/encoding/pure_ruby/ruby_mercurial_patch.rb +124 -0
- data/lib/amp/repository/mercurial/merging/merge_ui.rb +327 -0
- data/lib/amp/repository/mercurial/merging/simple_merge.rb +452 -0
- data/lib/amp/repository/mercurial/repo_format/branch_manager.rb +266 -0
- data/lib/amp/repository/mercurial/repo_format/changeset.rb +768 -0
- data/lib/amp/repository/mercurial/repo_format/dir_state.rb +716 -0
- data/lib/amp/repository/mercurial/repo_format/journal.rb +218 -0
- data/lib/amp/repository/mercurial/repo_format/lock.rb +210 -0
- data/lib/amp/repository/mercurial/repo_format/merge_state.rb +228 -0
- data/lib/amp/repository/mercurial/repo_format/staging_area.rb +367 -0
- data/lib/amp/repository/mercurial/repo_format/store.rb +487 -0
- data/lib/amp/repository/mercurial/repo_format/tag_manager.rb +322 -0
- data/lib/amp/repository/mercurial/repo_format/updatable.rb +543 -0
- data/lib/amp/repository/mercurial/repo_format/updater.rb +848 -0
- data/lib/amp/repository/mercurial/repo_format/verification.rb +433 -0
- data/lib/amp/repository/mercurial/repositories/bundle_repository.rb +216 -0
- data/lib/amp/repository/mercurial/repositories/http_repository.rb +386 -0
- data/lib/amp/repository/mercurial/repositories/local_repository.rb +2034 -0
- data/lib/amp/repository/mercurial/repository.rb +119 -0
- data/lib/amp/repository/mercurial/revlogs/bundle_revlogs.rb +249 -0
- data/lib/amp/repository/mercurial/revlogs/changegroup.rb +217 -0
- data/lib/amp/repository/mercurial/revlogs/changelog.rb +339 -0
- data/lib/amp/repository/mercurial/revlogs/file_log.rb +152 -0
- data/lib/amp/repository/mercurial/revlogs/index.rb +500 -0
- data/lib/amp/repository/mercurial/revlogs/manifest.rb +201 -0
- data/lib/amp/repository/mercurial/revlogs/node.rb +20 -0
- data/lib/amp/repository/mercurial/revlogs/revlog.rb +1026 -0
- data/lib/amp/repository/mercurial/revlogs/revlog_support.rb +129 -0
- data/lib/amp/repository/mercurial/revlogs/versioned_file.rb +597 -0
- data/lib/amp/repository/repository.rb +11 -88
- data/lib/amp/server/extension/amp_extension.rb +3 -3
- data/lib/amp/server/fancy_http_server.rb +1 -1
- data/lib/amp/server/fancy_views/_browser.haml +1 -1
- data/lib/amp/server/fancy_views/_diff_file.haml +1 -8
- data/lib/amp/server/fancy_views/changeset.haml +2 -2
- data/lib/amp/server/fancy_views/file.haml +1 -1
- data/lib/amp/server/fancy_views/file_diff.haml +1 -1
- data/lib/amp/support/amp_ui.rb +13 -29
- data/lib/amp/support/generator.rb +1 -1
- data/lib/amp/support/loaders.rb +1 -2
- data/lib/amp/support/logger.rb +10 -16
- data/lib/amp/support/match.rb +18 -4
- data/lib/amp/support/mercurial/ignore.rb +151 -0
- data/lib/amp/support/openers.rb +8 -3
- data/lib/amp/support/support.rb +91 -46
- data/lib/amp/templates/{blank.commit.erb → mercurial/blank.commit.erb} +0 -0
- data/lib/amp/templates/{blank.log.erb → mercurial/blank.log.erb} +0 -0
- data/lib/amp/templates/{default.commit.erb → mercurial/default.commit.erb} +0 -0
- data/lib/amp/templates/{default.log.erb → mercurial/default.log.erb} +0 -0
- data/lib/amp/templates/template.rb +18 -18
- data/man/amp.1 +51 -0
- data/site/src/about/commands.haml +1 -1
- data/site/src/css/amp.css +1 -1
- data/site/src/index.haml +3 -3
- data/tasks/man.rake +39 -0
- data/tasks/stats.rake +1 -10
- data/tasks/yard.rake +1 -50
- data/test/dirstate_tests/test_dir_state.rb +10 -8
- data/test/functional_tests/annotate.out +31 -0
- data/test/functional_tests/test_functional.rb +155 -63
- data/test/localrepo_tests/ampfile.rb +12 -0
- data/test/localrepo_tests/test_local_repo.rb +56 -57
- data/test/manifest_tests/test_manifest.rb +3 -5
- data/test/merge_tests/test_merge.rb +3 -3
- data/test/revlog_tests/test_revlog.rb +14 -6
- data/test/store_tests/test_fncache_store.rb +19 -19
- data/test/test_19_compatibility.rb +46 -0
- data/test/test_base85.rb +2 -2
- data/test/test_bdiff.rb +2 -2
- data/test/test_changegroup.rb +59 -0
- data/test/test_commands.rb +2 -2
- data/test/test_difflib.rb +2 -2
- data/test/test_generator.rb +34 -0
- data/test/test_ignore.rb +203 -0
- data/test/test_journal.rb +18 -13
- data/test/test_match.rb +2 -2
- data/test/test_mdiff.rb +3 -3
- data/test/test_mpatch.rb +3 -3
- data/test/test_multi_io.rb +40 -0
- data/test/test_support.rb +18 -2
- data/test/test_templates.rb +38 -0
- data/test/test_ui.rb +79 -0
- data/test/testutilities.rb +56 -0
- metadata +168 -49
- data/ext/amp/bz2/mkmf.log +0 -38
- data/lib/amp/encoding/mercurial_diff.rb +0 -378
- data/lib/amp/encoding/mercurial_patch.rb +0 -1
- data/lib/amp/encoding/patch.rb +0 -292
- data/lib/amp/encoding/pure_ruby/ruby_mercurial_patch.rb +0 -123
- data/lib/amp/merges/merge_state.rb +0 -164
- data/lib/amp/merges/merge_ui.rb +0 -322
- data/lib/amp/merges/simple_merge.rb +0 -450
- data/lib/amp/repository/branch_manager.rb +0 -234
- data/lib/amp/repository/dir_state.rb +0 -950
- data/lib/amp/repository/journal.rb +0 -203
- data/lib/amp/repository/lock.rb +0 -207
- data/lib/amp/repository/repositories/bundle_repository.rb +0 -214
- data/lib/amp/repository/repositories/http_repository.rb +0 -377
- data/lib/amp/repository/repositories/local_repository.rb +0 -2661
- data/lib/amp/repository/store.rb +0 -485
- data/lib/amp/repository/tag_manager.rb +0 -319
- data/lib/amp/repository/updatable.rb +0 -532
- data/lib/amp/repository/verification.rb +0 -431
- data/lib/amp/repository/versioned_file.rb +0 -475
- data/lib/amp/revlogs/bundle_revlogs.rb +0 -246
- data/lib/amp/revlogs/changegroup.rb +0 -217
- data/lib/amp/revlogs/changelog.rb +0 -338
- data/lib/amp/revlogs/changeset.rb +0 -521
- data/lib/amp/revlogs/file_log.rb +0 -165
- data/lib/amp/revlogs/index.rb +0 -493
- data/lib/amp/revlogs/manifest.rb +0 -195
- data/lib/amp/revlogs/node.rb +0 -18
- data/lib/amp/revlogs/revlog.rb +0 -1045
- data/lib/amp/revlogs/revlog_support.rb +0 -126
- data/lib/amp/support/ignore.rb +0 -144
- data/site/Rakefile +0 -38
- data/test/test_amp.rb +0 -9
- data/test/test_helper.rb +0 -15
@@ -0,0 +1,75 @@
|
|
1
|
+
module Amp
|
2
|
+
module Repositories
|
3
|
+
|
4
|
+
##
|
5
|
+
# = CommonVersionedFileMethods
|
6
|
+
#
|
7
|
+
# These methods are common to all repositories, and this module is mixed into
|
8
|
+
# the AbstractLocalRepository class. This guarantees that all repositories will
|
9
|
+
# have these methods.
|
10
|
+
#
|
11
|
+
# No methods should be placed into this module unless it relies on methods in the
|
12
|
+
# general API for repositories.
|
13
|
+
module CommonChangesetMethods
|
14
|
+
|
15
|
+
##
|
16
|
+
# A hash of the files that have been changed and their most recent diffs.
|
17
|
+
# The diffs are lazily made upon access. To just get the files, use #altered_files
|
18
|
+
# or #changed_files.keys
|
19
|
+
# Checks whether this changeset included a given file or not.
|
20
|
+
#
|
21
|
+
# @return [Hash<String => String>] a hash of {filename => the diff}
|
22
|
+
def changed_files
|
23
|
+
h = {}
|
24
|
+
class << h
|
25
|
+
def [](*args)
|
26
|
+
super(*args).call # we expect a proc
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
altered_files.inject(h) do |s, k|
|
31
|
+
s[k] = proc do
|
32
|
+
other = parents.first[k]
|
33
|
+
self[k].unified_diff other
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# Is +file+ being tracked at this point in time?
|
40
|
+
#
|
41
|
+
# @param [String] file the file to lookup
|
42
|
+
# @return [Boolean] whether the file is in this changeset's manifest
|
43
|
+
def include?(file)
|
44
|
+
all_files.include? file
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# recursively walk
|
49
|
+
#
|
50
|
+
# @param [Amp::Matcher] match this is a custom object that knows files
|
51
|
+
# magically. Not your grampa's proc!
|
52
|
+
def walk(match)
|
53
|
+
# just make it so the keys are there
|
54
|
+
results = []
|
55
|
+
|
56
|
+
hash = Hash.with_keys match.files
|
57
|
+
hash.delete '.'
|
58
|
+
|
59
|
+
each do |file|
|
60
|
+
hash.each {|f, val| (hash.delete file and break) if f == file }
|
61
|
+
|
62
|
+
results << file if match.call file # yield file if match.call file
|
63
|
+
end
|
64
|
+
|
65
|
+
hash.keys.sort.each do |file|
|
66
|
+
if match.bad file, "No such file in revision #{revision}" and match[file]
|
67
|
+
results << file # yield file
|
68
|
+
end
|
69
|
+
end
|
70
|
+
results
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,277 @@
|
|
1
|
+
module Amp
|
2
|
+
module Repositories
|
3
|
+
|
4
|
+
##
|
5
|
+
# = CommonLocalRepoMethods
|
6
|
+
#
|
7
|
+
# These methods are common to all repositories, and this module is mixed into
|
8
|
+
# the AbstractLocalRepository class. This guarantees that all repositories will
|
9
|
+
# have these methods.
|
10
|
+
#
|
11
|
+
# No methods should be placed into this module unless it relies on methods in the
|
12
|
+
# general API for repositories.
|
13
|
+
module CommonLocalRepoMethods
|
14
|
+
|
15
|
+
attr_accessor :config
|
16
|
+
|
17
|
+
##
|
18
|
+
# Initializes a new directory to the given path, and with the current
|
19
|
+
# configuration.
|
20
|
+
#
|
21
|
+
# @param [String] path a path to the Repository.
|
22
|
+
# @param [Boolean] create Should we create a new one? Usually for
|
23
|
+
# the "amp init" command.
|
24
|
+
# @param [Amp::AmpConfig] config the configuration loaded from the user's
|
25
|
+
# system. Will have some settings overwritten by the repo's hgrc.
|
26
|
+
def initialize(path="", create=false, config=nil)
|
27
|
+
@capabilities = {}
|
28
|
+
@root = File.expand_path path.chomp("/")
|
29
|
+
@config = config
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
33
|
+
# Initializes a new repository in the given directory. We recommend
|
34
|
+
# calling this at some point in your repository subclass as it will
|
35
|
+
# do amp-specific initialization, though you will need to do all the
|
36
|
+
# hard stuff yourself.
|
37
|
+
def init(config=@config)
|
38
|
+
FileUtils.makedirs root
|
39
|
+
working_write "Ampfile", <<-EOF
|
40
|
+
# Any ruby code here will be executed before Amp loads a repository and
|
41
|
+
# dispatches a command.
|
42
|
+
#
|
43
|
+
# Example command:
|
44
|
+
#
|
45
|
+
# command "echo" do |c|
|
46
|
+
# c.opt :"no-newline", "Don't print a trailing newline character", :short => "-n"
|
47
|
+
# c.on_run do |opts, args|
|
48
|
+
# print args.join(" ")
|
49
|
+
# print "\\n" unless opts[:"no-newline"]
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
EOF
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# Joins the path to the repo's root (not .hg, the working dir root)
|
58
|
+
#
|
59
|
+
# @param path the path we're joining
|
60
|
+
# @return [String] the path joined to the working directory's root
|
61
|
+
def working_join(path)
|
62
|
+
File.join(root, path)
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# Call the hooks that run under +call+
|
67
|
+
#
|
68
|
+
# @param [Symbol] call the location in the system where the hooks
|
69
|
+
# are to be called
|
70
|
+
def run_hook(call, opts={:throw => false})
|
71
|
+
Hook.run_hook(call, opts)
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# Adds a list of file paths to the repository for the next commit.
|
76
|
+
#
|
77
|
+
# @param [String, Array<String>] paths the paths of the files we need to
|
78
|
+
# add to the next commit
|
79
|
+
# @return [Array<String>] which files WEREN'T added
|
80
|
+
def add(*paths)
|
81
|
+
staging_area.add(*paths)
|
82
|
+
end
|
83
|
+
|
84
|
+
##
|
85
|
+
# Removes the file (or files) from the repository. Marks them as removed
|
86
|
+
# in the DirState, and if the :unlink option is provided, the files are
|
87
|
+
# deleted from the filesystem.
|
88
|
+
#
|
89
|
+
# @param list the list of files. Could also just be 1 file as a string.
|
90
|
+
# should be paths.
|
91
|
+
# @param opts the options for this removal. Must be last argument or will mess
|
92
|
+
# things up.
|
93
|
+
# @option [Boolean] opts :unlink (false) whether or not to delete the
|
94
|
+
# files from the filesystem after marking them as removed from the
|
95
|
+
# DirState.
|
96
|
+
# @return [Boolean] success?
|
97
|
+
def remove(*args)
|
98
|
+
staging_area.remove(*args)
|
99
|
+
end
|
100
|
+
|
101
|
+
def relative_join(file, cur_dir=FileUtils.pwd)
|
102
|
+
@root_pathname ||= Pathname.new(root)
|
103
|
+
Pathname.new(File.expand_path(File.join(cur_dir, file))).relative_path_from(@root_pathname).to_s
|
104
|
+
end
|
105
|
+
|
106
|
+
##
|
107
|
+
# Walk recursively through the directory tree (or a changeset)
|
108
|
+
# finding all files matched by the match function
|
109
|
+
#
|
110
|
+
# @param [String, Integer] node selects which changeset to walk
|
111
|
+
# @param [Amp::Match] match the matcher decides how to pick the files
|
112
|
+
# @param [Array<String>] an array of filenames
|
113
|
+
def walk(node=nil, match = Match.create({}) { true })
|
114
|
+
self[node].walk match # calls Changeset#walk
|
115
|
+
end
|
116
|
+
|
117
|
+
##
|
118
|
+
# Iterates over each changeset in the repository, from oldest to newest.
|
119
|
+
#
|
120
|
+
# @yield each changeset in the repository is yielded to the caller, in order
|
121
|
+
# from oldest to newest. (Actually, lowest revision # to highest revision #)
|
122
|
+
def each(&block)
|
123
|
+
0.upto(size - 1) { |i| yield self[i] }
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
##
|
128
|
+
# This gives the status of the repository, comparing 2 node in
|
129
|
+
# its history. Now, with no parameters, it's going to compare the
|
130
|
+
# last revision with the working directory, which is the most common
|
131
|
+
# usage - that answers "what is the current status of the repository,
|
132
|
+
# compared to the last time a commit happened?". However, given any
|
133
|
+
# two revisions, it can compare them.
|
134
|
+
#
|
135
|
+
# @example @repo.status # => {:unknown => ['code/smthng.rb'], :added => [], ...}
|
136
|
+
# @param [Hash] opts the options for this command. there's a bunch.
|
137
|
+
# @option [String, Integer] opts :node_1 (".") an identifier for the starting
|
138
|
+
# revision
|
139
|
+
# @option [String, Integer] opts :node_2 (nil) an identifier for the ending
|
140
|
+
# revision. Defaults to the working directory.
|
141
|
+
# @option [Proc] opts :match (proc { true }) a proc that will match
|
142
|
+
# a file, so we know if we're interested in it.
|
143
|
+
# @option [Boolean] opts :ignored (false) do we want to see files we're
|
144
|
+
# ignoring?
|
145
|
+
# @option [Boolean] opts :clean (false) do we want to see files that are
|
146
|
+
# totally unchanged?
|
147
|
+
# @option [Boolean] opts :unknown (false) do we want to see files we've
|
148
|
+
# never seen before (i.e. files the user forgot to add to the repo)?
|
149
|
+
# @option [Boolean] opts :delta (false) do we want to see the overall delta?
|
150
|
+
# @return [Hash<Symbol => Array<String>>] no, I'm not kidding. the keys are:
|
151
|
+
# :modified, :added, :removed, :deleted, :unknown, :ignored, :clean, and :delta. The
|
152
|
+
# keys are the type of change, and the values are arrays of filenames
|
153
|
+
# (local to the root) that are under each key.
|
154
|
+
def status(opts={:node_1 => '.'})
|
155
|
+
run_hook :status
|
156
|
+
|
157
|
+
opts[:delta] ||= true
|
158
|
+
node1, node2, match = opts[:node_1], opts[:node_2], opts[:match]
|
159
|
+
|
160
|
+
match = Match.create({}) { true } unless match
|
161
|
+
|
162
|
+
node1 = self[node1] unless node1.kind_of? Repositories::AbstractChangeset # get changeset objects
|
163
|
+
node2 = self[node2] unless node2.kind_of? Repositories::AbstractChangeset
|
164
|
+
|
165
|
+
# are we working with working directories?
|
166
|
+
working = node2.working?
|
167
|
+
comparing_to_tip = working && node2.parents.include?(node1)
|
168
|
+
|
169
|
+
status = Hash.new {|h, k| h[k] = k == :delta ? 0 : [] }
|
170
|
+
|
171
|
+
if working
|
172
|
+
# get the dirstate's latest status
|
173
|
+
status.merge! staging_area.status(opts[:ignored], opts[:clean], opts[:unknown], match)
|
174
|
+
|
175
|
+
# this case is run about 99% of the time
|
176
|
+
# do we need to do hashes on any files to see if they've changed?
|
177
|
+
if comparing_to_tip && status[:lookup].any?
|
178
|
+
clean, modified = fix_files(status[:lookup], node1, node2)
|
179
|
+
|
180
|
+
status[:clean].concat clean
|
181
|
+
status[:modified].concat modified
|
182
|
+
end
|
183
|
+
end
|
184
|
+
# if we're working with old revisions...
|
185
|
+
unless comparing_to_tip
|
186
|
+
# get the older revision manifest
|
187
|
+
node1_file_list = node1.all_files.dup
|
188
|
+
node2_file_list = node2.all_files.dup
|
189
|
+
if working
|
190
|
+
# remove any files we've marked as removed them from the '.' manifest
|
191
|
+
status[:removed].each {|file| node2_file_list.delete file }
|
192
|
+
end
|
193
|
+
|
194
|
+
# Every file in the later revision (or working directory)
|
195
|
+
node2.all_files.each do |file|
|
196
|
+
# Does it exist in the old manifest? If so, it wasn't added.
|
197
|
+
if node1.include? file
|
198
|
+
# It's in the old manifest, so lets check if its been changed
|
199
|
+
# Else, it must be unchanged
|
200
|
+
if file_modified? file, :node1 => node1, :node2 => node2 # tests.any?
|
201
|
+
status[:modified] << file
|
202
|
+
elsif opts[:clean]
|
203
|
+
status[:clean] << file
|
204
|
+
end
|
205
|
+
# Remove that file from the old manifest, since we've checked it
|
206
|
+
node1_file_list.delete file
|
207
|
+
else
|
208
|
+
# if it's not in the old manifest, it's been added
|
209
|
+
status[:added] << file
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# Anything left in the old manifest is a file we've removed since the
|
214
|
+
# first revision.
|
215
|
+
status[:removed] = node1_file_list
|
216
|
+
end
|
217
|
+
|
218
|
+
# We're done!
|
219
|
+
status.delete :lookup # because nobody cares about it
|
220
|
+
delta = status.delete :delta
|
221
|
+
|
222
|
+
status.each {|k, v| v.sort! } # sort dem fuckers
|
223
|
+
status[:delta] = delta if opts[:delta]
|
224
|
+
status.each {|k, _| status.delete k if opts[:only] && !opts[:only].include?(k) }
|
225
|
+
status
|
226
|
+
end
|
227
|
+
|
228
|
+
##
|
229
|
+
# Look up the files in +lookup+ to make sure
|
230
|
+
# they're either the same or not. Normally, we can
|
231
|
+
# just tell if two files are the same by looking at their sizes. But
|
232
|
+
# sometimes, we can't! That's where this method comes into play; it
|
233
|
+
# hashes the files to verify integrity.
|
234
|
+
#
|
235
|
+
# @param [String] lookup files to look up
|
236
|
+
# @param node1
|
237
|
+
# @param node2
|
238
|
+
# @return [[String], [String]] clean files and modified files
|
239
|
+
def fix_files(lookup, node1, node2)
|
240
|
+
write_dirstate = false # this gets returned
|
241
|
+
modified = [] # and this
|
242
|
+
fixup = [] # fixup are files that haven't changed so they're being
|
243
|
+
# marked wrong in the dirstate. this gets returned
|
244
|
+
|
245
|
+
lookup.each do |file|
|
246
|
+
# this checks to see if the file has been modified after doing
|
247
|
+
# hashes/flag checks
|
248
|
+
tests = [ node1.include?(file) ,
|
249
|
+
node2.flags(file) == node1.flags(file) ,
|
250
|
+
node1[file] === node2[file] ]
|
251
|
+
|
252
|
+
unless tests.all?
|
253
|
+
modified << file
|
254
|
+
else
|
255
|
+
fixup << file # mark the file as clean
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
|
260
|
+
# mark every fixup'd file as clean in the dirstate
|
261
|
+
begin
|
262
|
+
lock_working do
|
263
|
+
staging_area.normal *fixup
|
264
|
+
fixup.each do |file|
|
265
|
+
modified.delete file
|
266
|
+
end
|
267
|
+
end
|
268
|
+
rescue LockError
|
269
|
+
end
|
270
|
+
|
271
|
+
# the fixups are actually clean
|
272
|
+
[fixup, modified]
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
end
|
277
|
+
end
|
@@ -0,0 +1,233 @@
|
|
1
|
+
module Amp
|
2
|
+
module Repositories
|
3
|
+
|
4
|
+
##
|
5
|
+
# = CommonStagingAreaMethods
|
6
|
+
#
|
7
|
+
# These methods are common to all staging areas, and this module is mixed into
|
8
|
+
# the AbstractStagingArea class. This guarantees that all staging areas will
|
9
|
+
# have these methods.
|
10
|
+
#
|
11
|
+
# No methods should be placed into this module unless it relies on methods in the
|
12
|
+
# general API for staging areas.
|
13
|
+
module CommonStagingAreaMethods
|
14
|
+
|
15
|
+
##
|
16
|
+
# Returns whether or not the repository is tracking the given file.
|
17
|
+
#
|
18
|
+
# @api
|
19
|
+
# @param [String] filename the file to look up
|
20
|
+
# @return [Boolean] are we tracking the given file?
|
21
|
+
def tracking?(filename)
|
22
|
+
file_status(filename) != :untracked
|
23
|
+
end
|
24
|
+
|
25
|
+
##
|
26
|
+
# Helper method that filters out a list of explicit filenames that do not
|
27
|
+
# belong in the repository. It then stores the File stats of the files
|
28
|
+
# it keeps, and returns any explicitly named directories.
|
29
|
+
#
|
30
|
+
# @todo this is still tied to hg
|
31
|
+
# @param [Array<String>] files the files to examine
|
32
|
+
# @param [Amp::Match] match the matcher object
|
33
|
+
# @return [Array<Hash, Array<String>>] returns an array: [file_stats, directories]
|
34
|
+
def examine_named_files(files, match)
|
35
|
+
results, work = {vcs_dir => true}, [] # ignore the .hg
|
36
|
+
files.reject {|f| results[f] || f == ""}.sort.each do |file|
|
37
|
+
path = File.join(repo.root, file)
|
38
|
+
|
39
|
+
if File.exist?(path)
|
40
|
+
# we'll take it! but only if it's a directory, which means we have
|
41
|
+
# more work to do...
|
42
|
+
if File.directory?(path)
|
43
|
+
# add it to the list of dirs we have to search in
|
44
|
+
work << File.join(repo.root, file) unless ignoring_directory? file
|
45
|
+
elsif File.file?(path) || File.symlink?(path)
|
46
|
+
# ARGH WE FOUND ZE BOOTY
|
47
|
+
results[file] = File.lstat path
|
48
|
+
else
|
49
|
+
# user you are a fuckup in life please exit the world
|
50
|
+
UI::warn "#{file}: unsupported file type (type is #{File.ftype file})"
|
51
|
+
results[file] = nil if tracking? file
|
52
|
+
end
|
53
|
+
else
|
54
|
+
prefix = file + '/'
|
55
|
+
|
56
|
+
unless all_files.find { |f, _| f == file || f.start_with?(prefix) }
|
57
|
+
bad_type[file]
|
58
|
+
results[file] = nil if (tracking?(file) || !ignoring_file?(file)) && match.call(file)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
[results, work]
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# Helper method that runs match's patterns on every non-ignored file in
|
67
|
+
# the repository's directory.
|
68
|
+
#
|
69
|
+
# @param [Hash] found_files the already found files (we don't want to search them
|
70
|
+
# again)
|
71
|
+
# @param [Array<String>] dirs the directories to search
|
72
|
+
# @param [Amp::Match] match the matcher object that runs patterns against
|
73
|
+
# filenames
|
74
|
+
# @return [Hash] the updated found_files hash
|
75
|
+
def find_with_patterns(found_files, dirs, match)
|
76
|
+
results = found_files
|
77
|
+
Find.find(*dirs) do |f|
|
78
|
+
tf = f[(repo.root.size+1)..-1]
|
79
|
+
Find.prune if results[tf]
|
80
|
+
|
81
|
+
stats = File.lstat f
|
82
|
+
match_result = match.call tf
|
83
|
+
tracked = tracking? tf
|
84
|
+
|
85
|
+
if File.directory? f
|
86
|
+
Find.prune if ignoring_file? tf
|
87
|
+
results[tf] = nil if tracked && match_result
|
88
|
+
elsif File.file?(f) || File.symlink?(f)
|
89
|
+
if match_result && (tracked || !ignoring_file?(tf))
|
90
|
+
results[tf] = stats
|
91
|
+
end
|
92
|
+
elsif tracked && match_result
|
93
|
+
results[tf] = nil
|
94
|
+
end
|
95
|
+
end
|
96
|
+
results
|
97
|
+
end
|
98
|
+
|
99
|
+
##
|
100
|
+
# Walk recursively through the directory tree, finding all
|
101
|
+
# files matched by the regexp in match.
|
102
|
+
#
|
103
|
+
# Step 1: find all explicit files
|
104
|
+
# Step 2: visit subdirectories
|
105
|
+
# Step 3: report unseen items in the @files hash
|
106
|
+
#
|
107
|
+
# @todo this is still tied to hg
|
108
|
+
# @param [Boolean] unknown
|
109
|
+
# @param [Boolean] ignored
|
110
|
+
# @return [Hash<String => [NilClass, File::Stat]>] nil for directories and
|
111
|
+
# stuff, File::Stat for files and links
|
112
|
+
def walk(unknown, ignored, match = Amp::Match.new { true })
|
113
|
+
if ignored
|
114
|
+
@ignore_all = false
|
115
|
+
elsif not unknown
|
116
|
+
@ignore_all = true
|
117
|
+
end
|
118
|
+
|
119
|
+
files = (match.files || []).uniq
|
120
|
+
|
121
|
+
# why do we overwrite the entire array if it includes the current dir?
|
122
|
+
# we even kill posisbly good things
|
123
|
+
files = [''] if files.include?('.') # strange thing to do
|
124
|
+
|
125
|
+
# Step 1: find all explicit files
|
126
|
+
results, found_directories = examine_named_files files, match
|
127
|
+
work = [repo.root] + found_directories
|
128
|
+
|
129
|
+
# Run the patterns
|
130
|
+
results = find_with_patterns(results, work, match)
|
131
|
+
|
132
|
+
# step 3: report unseen items in @files
|
133
|
+
visit = all_files.select {|f| !results[f] && match.call(f) }.sort
|
134
|
+
|
135
|
+
visit.each do |file|
|
136
|
+
path = File.join(repo.root, file)
|
137
|
+
keep = File.exist?(path) && (File.file?(path) || File.symlink(path))
|
138
|
+
results[file] = keep ? File.lstat(path) : nil
|
139
|
+
end
|
140
|
+
|
141
|
+
results.delete vcs_dir
|
142
|
+
@ignore_all = nil # reset this
|
143
|
+
results
|
144
|
+
end
|
145
|
+
|
146
|
+
##
|
147
|
+
# what's the current state of life, man!
|
148
|
+
# Splits up all the files into modified, clean,
|
149
|
+
# added, deleted, unknown, ignored, or lookup-needed.
|
150
|
+
#
|
151
|
+
# @param [Boolean] ignored do we collect the ignore files?
|
152
|
+
# @param [Boolean] clean do we collect the clean files?
|
153
|
+
# @param [Boolean] unknown do we collect the unknown files?
|
154
|
+
# @param [Amp::Match] match the matcher
|
155
|
+
# @return [Hash<Symbol => Array<String>>] a hash of the filestatuses and their files
|
156
|
+
def status(ignored, clean, unknown, match = Match.new { true })
|
157
|
+
list_ignored, list_clean, list_unknown = ignored, clean, unknown
|
158
|
+
lookup, modified, added, unknown, ignored = [], [], [], [], []
|
159
|
+
moved, copied, removed, deleted, clean = [], [], [], [], []
|
160
|
+
delta = 0
|
161
|
+
|
162
|
+
walk(list_unknown, list_ignored, match).each do |file, st|
|
163
|
+
next if file.nil?
|
164
|
+
|
165
|
+
unless tracking?(file)
|
166
|
+
if list_ignored && ignoring_directory?(file)
|
167
|
+
ignored << file
|
168
|
+
elsif list_unknown
|
169
|
+
unknown << file unless ignoring_file?(file)
|
170
|
+
end
|
171
|
+
|
172
|
+
next # on to the next one, don't do the rest
|
173
|
+
end
|
174
|
+
|
175
|
+
# here's where we split up the files
|
176
|
+
state = file_status file
|
177
|
+
|
178
|
+
delta += calculate_delta(file, st)
|
179
|
+
if !st && [:normal, :modified, :added].include?(state)
|
180
|
+
# add it to the deleted folder if it should be here but isn't
|
181
|
+
deleted << file
|
182
|
+
elsif state == :normal
|
183
|
+
case file_precise_status(file, st)
|
184
|
+
when :modified
|
185
|
+
modified << file
|
186
|
+
when :lookup
|
187
|
+
lookup << file
|
188
|
+
when :clean
|
189
|
+
clean << file if list_clean
|
190
|
+
end
|
191
|
+
|
192
|
+
elsif state == :merged
|
193
|
+
modified << file
|
194
|
+
elsif state == :added
|
195
|
+
added << file
|
196
|
+
elsif state == :removed
|
197
|
+
removed << file
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
# # This code creates the copied and moved arrays
|
202
|
+
# #
|
203
|
+
# # ugh this should be optimized
|
204
|
+
# # as in, built into the code above ^^^^^
|
205
|
+
# dirstate.copy_map.each do |dst, src|
|
206
|
+
# # assume that if +src+ is in +removed+ then +dst+ is in +added+
|
207
|
+
# # we know that this part will be COPIES
|
208
|
+
# if removed.include? src
|
209
|
+
# removed.delete src
|
210
|
+
# added.delete dst
|
211
|
+
# copied << [src, dst]
|
212
|
+
# elsif added.include? dst # these are the MOVES
|
213
|
+
# added.delete dst
|
214
|
+
# moved << [src, dst]
|
215
|
+
# end
|
216
|
+
# end
|
217
|
+
|
218
|
+
r = { :modified => modified.sort , # those that have clearly been modified
|
219
|
+
:added => added.sort , # those that are marked for adding
|
220
|
+
:removed => removed.sort , # those that are marked for removal
|
221
|
+
:deleted => deleted.sort , # those that should be here but aren't
|
222
|
+
:unknown => unknown.sort , # those that aren't being tracked
|
223
|
+
:ignored => ignored.sort , # those that are being deliberately ignored
|
224
|
+
:clean => clean.sort , # those that haven't changed
|
225
|
+
:lookup => lookup.sort , # those that need to be content-checked to see if they've changed
|
226
|
+
#:copied => copied.sort_by {|a| a[0] }, # those that have been copied
|
227
|
+
#:moved => moved.sort_by {|a| a[0] }, # those that have been moved
|
228
|
+
:delta => delta # how many bytes have been added or removed from files (not bytes that have been changed)
|
229
|
+
}
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|