amp 0.5.2 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
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