amp 0.5.2 → 0.5.3

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.
Files changed (232) hide show
  1. data/.gitignore +12 -0
  2. data/.hgignore +3 -0
  3. data/AUTHORS +1 -1
  4. data/Manifest.txt +99 -38
  5. data/README.md +3 -3
  6. data/Rakefile +53 -18
  7. data/SCHEDULE.markdown +5 -1
  8. data/TODO.markdown +120 -149
  9. data/ampfile.rb +3 -1
  10. data/bin/amp +4 -1
  11. data/ext/amp/bz2/extconf.rb +1 -1
  12. data/ext/amp/mercurial_patch/extconf.rb +1 -1
  13. data/ext/amp/mercurial_patch/mpatch.c +4 -3
  14. data/ext/amp/priority_queue/extconf.rb +1 -1
  15. data/ext/amp/support/extconf.rb +1 -1
  16. data/ext/amp/support/support.c +1 -1
  17. data/lib/amp.rb +125 -67
  18. data/lib/amp/commands/command.rb +12 -10
  19. data/lib/amp/commands/command_support.rb +8 -1
  20. data/lib/amp/commands/commands/help.rb +2 -20
  21. data/lib/amp/commands/commands/init.rb +14 -2
  22. data/lib/amp/commands/commands/templates.rb +6 -4
  23. data/lib/amp/commands/commands/version.rb +15 -1
  24. data/lib/amp/commands/commands/workflow.rb +3 -3
  25. data/lib/amp/commands/commands/workflows/git/add.rb +3 -3
  26. data/lib/amp/commands/commands/workflows/git/copy.rb +1 -1
  27. data/lib/amp/commands/commands/workflows/git/rm.rb +4 -2
  28. data/lib/amp/commands/commands/workflows/hg/add.rb +1 -1
  29. data/lib/amp/commands/commands/workflows/hg/addremove.rb +2 -2
  30. data/lib/amp/commands/commands/workflows/hg/annotate.rb +8 -2
  31. data/lib/amp/commands/commands/workflows/hg/bisect.rb +253 -0
  32. data/lib/amp/commands/commands/workflows/hg/branch.rb +1 -1
  33. data/lib/amp/commands/commands/workflows/hg/branches.rb +3 -3
  34. data/lib/amp/commands/commands/workflows/hg/bundle.rb +3 -3
  35. data/lib/amp/commands/commands/workflows/hg/clone.rb +4 -5
  36. data/lib/amp/commands/commands/workflows/hg/commit.rb +37 -1
  37. data/lib/amp/commands/commands/workflows/hg/copy.rb +2 -1
  38. data/lib/amp/commands/commands/workflows/hg/debug/index.rb +1 -1
  39. data/lib/amp/commands/commands/workflows/hg/diff.rb +3 -8
  40. data/lib/amp/commands/commands/workflows/hg/forget.rb +5 -4
  41. data/lib/amp/commands/commands/workflows/hg/identify.rb +6 -6
  42. data/lib/amp/commands/commands/workflows/hg/import.rb +1 -1
  43. data/lib/amp/commands/commands/workflows/hg/incoming.rb +2 -2
  44. data/lib/amp/commands/commands/workflows/hg/log.rb +5 -4
  45. data/lib/amp/commands/commands/workflows/hg/merge.rb +1 -1
  46. data/lib/amp/commands/commands/workflows/hg/move.rb +5 -3
  47. data/lib/amp/commands/commands/workflows/hg/outgoing.rb +1 -1
  48. data/lib/amp/commands/commands/workflows/hg/push.rb +6 -7
  49. data/lib/amp/commands/commands/workflows/hg/remove.rb +2 -2
  50. data/lib/amp/commands/commands/workflows/hg/resolve.rb +6 -23
  51. data/lib/amp/commands/commands/workflows/hg/root.rb +1 -2
  52. data/lib/amp/commands/commands/workflows/hg/status.rb +21 -12
  53. data/lib/amp/commands/commands/workflows/hg/tag.rb +2 -2
  54. data/lib/amp/commands/commands/workflows/hg/untrack.rb +12 -0
  55. data/lib/amp/commands/commands/workflows/hg/verify.rb +13 -3
  56. data/lib/amp/commands/commands/workflows/hg/what_changed.rb +18 -0
  57. data/lib/amp/commands/dispatch.rb +12 -13
  58. data/lib/amp/dependencies/amp_support.rb +1 -1
  59. data/lib/amp/dependencies/amp_support/ruby_amp_support.rb +1 -0
  60. data/lib/amp/dependencies/maruku.rb +136 -0
  61. data/lib/amp/dependencies/maruku/attributes.rb +227 -0
  62. data/lib/amp/dependencies/maruku/defaults.rb +71 -0
  63. data/lib/amp/dependencies/maruku/errors_management.rb +92 -0
  64. data/lib/amp/dependencies/maruku/helpers.rb +260 -0
  65. data/lib/amp/dependencies/maruku/input/charsource.rb +326 -0
  66. data/lib/amp/dependencies/maruku/input/extensions.rb +69 -0
  67. data/lib/amp/dependencies/maruku/input/html_helper.rb +189 -0
  68. data/lib/amp/dependencies/maruku/input/linesource.rb +111 -0
  69. data/lib/amp/dependencies/maruku/input/parse_block.rb +615 -0
  70. data/lib/amp/dependencies/maruku/input/parse_doc.rb +234 -0
  71. data/lib/amp/dependencies/maruku/input/parse_span_better.rb +746 -0
  72. data/lib/amp/dependencies/maruku/input/rubypants.rb +225 -0
  73. data/lib/amp/dependencies/maruku/input/type_detection.rb +147 -0
  74. data/lib/amp/dependencies/maruku/input_textile2/t2_parser.rb +163 -0
  75. data/lib/amp/dependencies/maruku/maruku.rb +33 -0
  76. data/lib/amp/dependencies/maruku/output/to_ansi.rb +223 -0
  77. data/lib/amp/dependencies/maruku/output/to_html.rb +991 -0
  78. data/lib/amp/dependencies/maruku/output/to_markdown.rb +164 -0
  79. data/lib/amp/dependencies/maruku/output/to_s.rb +56 -0
  80. data/lib/amp/dependencies/maruku/string_utils.rb +191 -0
  81. data/lib/amp/dependencies/maruku/structures.rb +167 -0
  82. data/lib/amp/dependencies/maruku/structures_inspect.rb +87 -0
  83. data/lib/amp/dependencies/maruku/structures_iterators.rb +61 -0
  84. data/lib/amp/dependencies/maruku/textile2.rb +1 -0
  85. data/lib/amp/dependencies/maruku/toc.rb +199 -0
  86. data/lib/amp/dependencies/maruku/usage/example1.rb +33 -0
  87. data/lib/amp/dependencies/maruku/version.rb +40 -0
  88. data/lib/amp/dependencies/priority_queue.rb +2 -1
  89. data/lib/amp/dependencies/python_config.rb +2 -1
  90. data/lib/amp/graphs/ancestor.rb +2 -1
  91. data/lib/amp/graphs/copies.rb +236 -233
  92. data/lib/amp/help/entries/__default__.erb +31 -0
  93. data/lib/amp/help/entries/commands.erb +6 -0
  94. data/lib/amp/help/entries/mdtest.md +35 -0
  95. data/lib/amp/help/entries/silly +3 -0
  96. data/lib/amp/help/help.rb +288 -0
  97. data/lib/amp/profiling_hacks.rb +5 -3
  98. data/lib/amp/repository/abstract/abstract_changeset.rb +97 -0
  99. data/lib/amp/repository/abstract/abstract_local_repo.rb +181 -0
  100. data/lib/amp/repository/abstract/abstract_staging_area.rb +180 -0
  101. data/lib/amp/repository/abstract/abstract_versioned_file.rb +100 -0
  102. data/lib/amp/repository/abstract/common_methods/changeset.rb +75 -0
  103. data/lib/amp/repository/abstract/common_methods/local_repo.rb +277 -0
  104. data/lib/amp/repository/abstract/common_methods/staging_area.rb +233 -0
  105. data/lib/amp/repository/abstract/common_methods/versioned_file.rb +71 -0
  106. data/lib/amp/repository/generic_repo_picker.rb +78 -0
  107. data/lib/amp/repository/git/repo_format/changeset.rb +336 -0
  108. data/lib/amp/repository/git/repo_format/staging_area.rb +192 -0
  109. data/lib/amp/repository/git/repo_format/versioned_file.rb +119 -0
  110. data/lib/amp/repository/git/repositories/local_repository.rb +164 -0
  111. data/lib/amp/repository/git/repository.rb +41 -0
  112. data/lib/amp/repository/mercurial/encoding/mercurial_diff.rb +382 -0
  113. data/lib/amp/repository/mercurial/encoding/mercurial_patch.rb +1 -0
  114. data/lib/amp/repository/mercurial/encoding/patch.rb +294 -0
  115. data/lib/amp/repository/mercurial/encoding/pure_ruby/ruby_mercurial_patch.rb +124 -0
  116. data/lib/amp/repository/mercurial/merging/merge_ui.rb +327 -0
  117. data/lib/amp/repository/mercurial/merging/simple_merge.rb +452 -0
  118. data/lib/amp/repository/mercurial/repo_format/branch_manager.rb +266 -0
  119. data/lib/amp/repository/mercurial/repo_format/changeset.rb +768 -0
  120. data/lib/amp/repository/mercurial/repo_format/dir_state.rb +716 -0
  121. data/lib/amp/repository/mercurial/repo_format/journal.rb +218 -0
  122. data/lib/amp/repository/mercurial/repo_format/lock.rb +210 -0
  123. data/lib/amp/repository/mercurial/repo_format/merge_state.rb +228 -0
  124. data/lib/amp/repository/mercurial/repo_format/staging_area.rb +367 -0
  125. data/lib/amp/repository/mercurial/repo_format/store.rb +487 -0
  126. data/lib/amp/repository/mercurial/repo_format/tag_manager.rb +322 -0
  127. data/lib/amp/repository/mercurial/repo_format/updatable.rb +543 -0
  128. data/lib/amp/repository/mercurial/repo_format/updater.rb +848 -0
  129. data/lib/amp/repository/mercurial/repo_format/verification.rb +433 -0
  130. data/lib/amp/repository/mercurial/repositories/bundle_repository.rb +216 -0
  131. data/lib/amp/repository/mercurial/repositories/http_repository.rb +386 -0
  132. data/lib/amp/repository/mercurial/repositories/local_repository.rb +2034 -0
  133. data/lib/amp/repository/mercurial/repository.rb +119 -0
  134. data/lib/amp/repository/mercurial/revlogs/bundle_revlogs.rb +249 -0
  135. data/lib/amp/repository/mercurial/revlogs/changegroup.rb +217 -0
  136. data/lib/amp/repository/mercurial/revlogs/changelog.rb +339 -0
  137. data/lib/amp/repository/mercurial/revlogs/file_log.rb +152 -0
  138. data/lib/amp/repository/mercurial/revlogs/index.rb +500 -0
  139. data/lib/amp/repository/mercurial/revlogs/manifest.rb +201 -0
  140. data/lib/amp/repository/mercurial/revlogs/node.rb +20 -0
  141. data/lib/amp/repository/mercurial/revlogs/revlog.rb +1026 -0
  142. data/lib/amp/repository/mercurial/revlogs/revlog_support.rb +129 -0
  143. data/lib/amp/repository/mercurial/revlogs/versioned_file.rb +597 -0
  144. data/lib/amp/repository/repository.rb +11 -88
  145. data/lib/amp/server/extension/amp_extension.rb +3 -3
  146. data/lib/amp/server/fancy_http_server.rb +1 -1
  147. data/lib/amp/server/fancy_views/_browser.haml +1 -1
  148. data/lib/amp/server/fancy_views/_diff_file.haml +1 -8
  149. data/lib/amp/server/fancy_views/changeset.haml +2 -2
  150. data/lib/amp/server/fancy_views/file.haml +1 -1
  151. data/lib/amp/server/fancy_views/file_diff.haml +1 -1
  152. data/lib/amp/support/amp_ui.rb +13 -29
  153. data/lib/amp/support/generator.rb +1 -1
  154. data/lib/amp/support/loaders.rb +1 -2
  155. data/lib/amp/support/logger.rb +10 -16
  156. data/lib/amp/support/match.rb +18 -4
  157. data/lib/amp/support/mercurial/ignore.rb +151 -0
  158. data/lib/amp/support/openers.rb +8 -3
  159. data/lib/amp/support/support.rb +91 -46
  160. data/lib/amp/templates/{blank.commit.erb → mercurial/blank.commit.erb} +0 -0
  161. data/lib/amp/templates/{blank.log.erb → mercurial/blank.log.erb} +0 -0
  162. data/lib/amp/templates/{default.commit.erb → mercurial/default.commit.erb} +0 -0
  163. data/lib/amp/templates/{default.log.erb → mercurial/default.log.erb} +0 -0
  164. data/lib/amp/templates/template.rb +18 -18
  165. data/man/amp.1 +51 -0
  166. data/site/src/about/commands.haml +1 -1
  167. data/site/src/css/amp.css +1 -1
  168. data/site/src/index.haml +3 -3
  169. data/tasks/man.rake +39 -0
  170. data/tasks/stats.rake +1 -10
  171. data/tasks/yard.rake +1 -50
  172. data/test/dirstate_tests/test_dir_state.rb +10 -8
  173. data/test/functional_tests/annotate.out +31 -0
  174. data/test/functional_tests/test_functional.rb +155 -63
  175. data/test/localrepo_tests/ampfile.rb +12 -0
  176. data/test/localrepo_tests/test_local_repo.rb +56 -57
  177. data/test/manifest_tests/test_manifest.rb +3 -5
  178. data/test/merge_tests/test_merge.rb +3 -3
  179. data/test/revlog_tests/test_revlog.rb +14 -6
  180. data/test/store_tests/test_fncache_store.rb +19 -19
  181. data/test/test_19_compatibility.rb +46 -0
  182. data/test/test_base85.rb +2 -2
  183. data/test/test_bdiff.rb +2 -2
  184. data/test/test_changegroup.rb +59 -0
  185. data/test/test_commands.rb +2 -2
  186. data/test/test_difflib.rb +2 -2
  187. data/test/test_generator.rb +34 -0
  188. data/test/test_ignore.rb +203 -0
  189. data/test/test_journal.rb +18 -13
  190. data/test/test_match.rb +2 -2
  191. data/test/test_mdiff.rb +3 -3
  192. data/test/test_mpatch.rb +3 -3
  193. data/test/test_multi_io.rb +40 -0
  194. data/test/test_support.rb +18 -2
  195. data/test/test_templates.rb +38 -0
  196. data/test/test_ui.rb +79 -0
  197. data/test/testutilities.rb +56 -0
  198. metadata +168 -49
  199. data/ext/amp/bz2/mkmf.log +0 -38
  200. data/lib/amp/encoding/mercurial_diff.rb +0 -378
  201. data/lib/amp/encoding/mercurial_patch.rb +0 -1
  202. data/lib/amp/encoding/patch.rb +0 -292
  203. data/lib/amp/encoding/pure_ruby/ruby_mercurial_patch.rb +0 -123
  204. data/lib/amp/merges/merge_state.rb +0 -164
  205. data/lib/amp/merges/merge_ui.rb +0 -322
  206. data/lib/amp/merges/simple_merge.rb +0 -450
  207. data/lib/amp/repository/branch_manager.rb +0 -234
  208. data/lib/amp/repository/dir_state.rb +0 -950
  209. data/lib/amp/repository/journal.rb +0 -203
  210. data/lib/amp/repository/lock.rb +0 -207
  211. data/lib/amp/repository/repositories/bundle_repository.rb +0 -214
  212. data/lib/amp/repository/repositories/http_repository.rb +0 -377
  213. data/lib/amp/repository/repositories/local_repository.rb +0 -2661
  214. data/lib/amp/repository/store.rb +0 -485
  215. data/lib/amp/repository/tag_manager.rb +0 -319
  216. data/lib/amp/repository/updatable.rb +0 -532
  217. data/lib/amp/repository/verification.rb +0 -431
  218. data/lib/amp/repository/versioned_file.rb +0 -475
  219. data/lib/amp/revlogs/bundle_revlogs.rb +0 -246
  220. data/lib/amp/revlogs/changegroup.rb +0 -217
  221. data/lib/amp/revlogs/changelog.rb +0 -338
  222. data/lib/amp/revlogs/changeset.rb +0 -521
  223. data/lib/amp/revlogs/file_log.rb +0 -165
  224. data/lib/amp/revlogs/index.rb +0 -493
  225. data/lib/amp/revlogs/manifest.rb +0 -195
  226. data/lib/amp/revlogs/node.rb +0 -18
  227. data/lib/amp/revlogs/revlog.rb +0 -1045
  228. data/lib/amp/revlogs/revlog_support.rb +0 -126
  229. data/lib/amp/support/ignore.rb +0 -144
  230. data/site/Rakefile +0 -38
  231. data/test/test_amp.rb +0 -9
  232. 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