amp 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (295) hide show
  1. data/.gitignore +1 -0
  2. data/.hgignore +26 -0
  3. data/AUTHORS +2 -0
  4. data/History.txt +6 -0
  5. data/LICENSE +37 -0
  6. data/MANIFESTO +7 -0
  7. data/Manifest.txt +294 -0
  8. data/README.md +129 -0
  9. data/Rakefile +102 -0
  10. data/SCHEDULE.markdown +12 -0
  11. data/STYLE +27 -0
  12. data/TODO.markdown +149 -0
  13. data/ampfile.rb +47 -0
  14. data/bin/amp +30 -0
  15. data/bin/amp1.9 +30 -0
  16. data/ext/amp/bz2/README.txt +39 -0
  17. data/ext/amp/bz2/bz2.c +1582 -0
  18. data/ext/amp/bz2/extconf.rb +77 -0
  19. data/ext/amp/bz2/mkmf.log +29 -0
  20. data/ext/amp/mercurial_patch/extconf.rb +5 -0
  21. data/ext/amp/mercurial_patch/mpatch.c +405 -0
  22. data/ext/amp/priority_queue/extconf.rb +5 -0
  23. data/ext/amp/priority_queue/priority_queue.c +947 -0
  24. data/ext/amp/support/extconf.rb +5 -0
  25. data/ext/amp/support/support.c +250 -0
  26. data/lib/amp.rb +200 -0
  27. data/lib/amp/commands/command.rb +507 -0
  28. data/lib/amp/commands/command_support.rb +137 -0
  29. data/lib/amp/commands/commands/config.rb +143 -0
  30. data/lib/amp/commands/commands/help.rb +29 -0
  31. data/lib/amp/commands/commands/init.rb +10 -0
  32. data/lib/amp/commands/commands/templates.rb +137 -0
  33. data/lib/amp/commands/commands/version.rb +7 -0
  34. data/lib/amp/commands/commands/workflow.rb +28 -0
  35. data/lib/amp/commands/commands/workflows/git/add.rb +65 -0
  36. data/lib/amp/commands/commands/workflows/git/copy.rb +27 -0
  37. data/lib/amp/commands/commands/workflows/git/mv.rb +23 -0
  38. data/lib/amp/commands/commands/workflows/git/rm.rb +60 -0
  39. data/lib/amp/commands/commands/workflows/hg/add.rb +53 -0
  40. data/lib/amp/commands/commands/workflows/hg/addremove.rb +86 -0
  41. data/lib/amp/commands/commands/workflows/hg/annotate.rb +46 -0
  42. data/lib/amp/commands/commands/workflows/hg/archive.rb +126 -0
  43. data/lib/amp/commands/commands/workflows/hg/branch.rb +28 -0
  44. data/lib/amp/commands/commands/workflows/hg/branches.rb +30 -0
  45. data/lib/amp/commands/commands/workflows/hg/bundle.rb +115 -0
  46. data/lib/amp/commands/commands/workflows/hg/clone.rb +95 -0
  47. data/lib/amp/commands/commands/workflows/hg/commit.rb +42 -0
  48. data/lib/amp/commands/commands/workflows/hg/copy.rb +31 -0
  49. data/lib/amp/commands/commands/workflows/hg/debug/dirstate.rb +32 -0
  50. data/lib/amp/commands/commands/workflows/hg/debug/index.rb +36 -0
  51. data/lib/amp/commands/commands/workflows/hg/default.rb +9 -0
  52. data/lib/amp/commands/commands/workflows/hg/diff.rb +30 -0
  53. data/lib/amp/commands/commands/workflows/hg/forget.rb +11 -0
  54. data/lib/amp/commands/commands/workflows/hg/heads.rb +25 -0
  55. data/lib/amp/commands/commands/workflows/hg/identify.rb +23 -0
  56. data/lib/amp/commands/commands/workflows/hg/import.rb +135 -0
  57. data/lib/amp/commands/commands/workflows/hg/incoming.rb +85 -0
  58. data/lib/amp/commands/commands/workflows/hg/info.rb +18 -0
  59. data/lib/amp/commands/commands/workflows/hg/log.rb +21 -0
  60. data/lib/amp/commands/commands/workflows/hg/manifest.rb +13 -0
  61. data/lib/amp/commands/commands/workflows/hg/merge.rb +53 -0
  62. data/lib/amp/commands/commands/workflows/hg/move.rb +28 -0
  63. data/lib/amp/commands/commands/workflows/hg/outgoing.rb +61 -0
  64. data/lib/amp/commands/commands/workflows/hg/pull.rb +74 -0
  65. data/lib/amp/commands/commands/workflows/hg/push.rb +20 -0
  66. data/lib/amp/commands/commands/workflows/hg/remove.rb +45 -0
  67. data/lib/amp/commands/commands/workflows/hg/resolve.rb +83 -0
  68. data/lib/amp/commands/commands/workflows/hg/revert.rb +53 -0
  69. data/lib/amp/commands/commands/workflows/hg/root.rb +13 -0
  70. data/lib/amp/commands/commands/workflows/hg/serve.rb +38 -0
  71. data/lib/amp/commands/commands/workflows/hg/status.rb +116 -0
  72. data/lib/amp/commands/commands/workflows/hg/tag.rb +69 -0
  73. data/lib/amp/commands/commands/workflows/hg/tags.rb +27 -0
  74. data/lib/amp/commands/commands/workflows/hg/tip.rb +13 -0
  75. data/lib/amp/commands/commands/workflows/hg/update.rb +27 -0
  76. data/lib/amp/commands/commands/workflows/hg/verify.rb +9 -0
  77. data/lib/amp/commands/commands/workflows/hg/view.rb +36 -0
  78. data/lib/amp/commands/dispatch.rb +181 -0
  79. data/lib/amp/commands/hooks.rb +81 -0
  80. data/lib/amp/dependencies/amp_support.rb +1 -0
  81. data/lib/amp/dependencies/amp_support/ruby_amp_support.rb +103 -0
  82. data/lib/amp/dependencies/minitar.rb +979 -0
  83. data/lib/amp/dependencies/priority_queue.rb +18 -0
  84. data/lib/amp/dependencies/priority_queue/c_priority_queue.rb +1 -0
  85. data/lib/amp/dependencies/priority_queue/poor_priority_queue.rb +46 -0
  86. data/lib/amp/dependencies/priority_queue/ruby_priority_queue.rb +525 -0
  87. data/lib/amp/dependencies/python_config.rb +211 -0
  88. data/lib/amp/dependencies/trollop.rb +713 -0
  89. data/lib/amp/dependencies/zip/ioextras.rb +155 -0
  90. data/lib/amp/dependencies/zip/stdrubyext.rb +111 -0
  91. data/lib/amp/dependencies/zip/tempfile_bugfixed.rb +186 -0
  92. data/lib/amp/dependencies/zip/zip.rb +1850 -0
  93. data/lib/amp/dependencies/zip/zipfilesystem.rb +609 -0
  94. data/lib/amp/dependencies/zip/ziprequire.rb +90 -0
  95. data/lib/amp/encoding/base85.rb +97 -0
  96. data/lib/amp/encoding/binary_diff.rb +82 -0
  97. data/lib/amp/encoding/difflib.rb +166 -0
  98. data/lib/amp/encoding/mercurial_diff.rb +378 -0
  99. data/lib/amp/encoding/mercurial_patch.rb +1 -0
  100. data/lib/amp/encoding/patch.rb +292 -0
  101. data/lib/amp/encoding/pure_ruby/ruby_mercurial_patch.rb +123 -0
  102. data/lib/amp/extensions/ditz.rb +41 -0
  103. data/lib/amp/extensions/lighthouse.rb +167 -0
  104. data/lib/amp/graphs/ancestor.rb +147 -0
  105. data/lib/amp/graphs/copies.rb +261 -0
  106. data/lib/amp/merges/merge_state.rb +164 -0
  107. data/lib/amp/merges/merge_ui.rb +322 -0
  108. data/lib/amp/merges/simple_merge.rb +450 -0
  109. data/lib/amp/profiling_hacks.rb +36 -0
  110. data/lib/amp/repository/branch_manager.rb +234 -0
  111. data/lib/amp/repository/dir_state.rb +950 -0
  112. data/lib/amp/repository/journal.rb +203 -0
  113. data/lib/amp/repository/lock.rb +207 -0
  114. data/lib/amp/repository/repositories/bundle_repository.rb +214 -0
  115. data/lib/amp/repository/repositories/http_repository.rb +377 -0
  116. data/lib/amp/repository/repositories/local_repository.rb +2661 -0
  117. data/lib/amp/repository/repository.rb +94 -0
  118. data/lib/amp/repository/store.rb +485 -0
  119. data/lib/amp/repository/tag_manager.rb +319 -0
  120. data/lib/amp/repository/updatable.rb +532 -0
  121. data/lib/amp/repository/verification.rb +431 -0
  122. data/lib/amp/repository/versioned_file.rb +475 -0
  123. data/lib/amp/revlogs/bundle_revlogs.rb +246 -0
  124. data/lib/amp/revlogs/changegroup.rb +217 -0
  125. data/lib/amp/revlogs/changelog.rb +338 -0
  126. data/lib/amp/revlogs/changeset.rb +521 -0
  127. data/lib/amp/revlogs/file_log.rb +165 -0
  128. data/lib/amp/revlogs/index.rb +493 -0
  129. data/lib/amp/revlogs/manifest.rb +195 -0
  130. data/lib/amp/revlogs/node.rb +18 -0
  131. data/lib/amp/revlogs/revlog.rb +1032 -0
  132. data/lib/amp/revlogs/revlog_support.rb +126 -0
  133. data/lib/amp/server/amp_user.rb +44 -0
  134. data/lib/amp/server/extension/amp_extension.rb +396 -0
  135. data/lib/amp/server/extension/authorization.rb +201 -0
  136. data/lib/amp/server/fancy_http_server.rb +252 -0
  137. data/lib/amp/server/fancy_views/_browser.haml +28 -0
  138. data/lib/amp/server/fancy_views/_diff_file.haml +13 -0
  139. data/lib/amp/server/fancy_views/_navbar.haml +17 -0
  140. data/lib/amp/server/fancy_views/changeset.haml +31 -0
  141. data/lib/amp/server/fancy_views/commits.haml +32 -0
  142. data/lib/amp/server/fancy_views/file.haml +35 -0
  143. data/lib/amp/server/fancy_views/file_diff.haml +23 -0
  144. data/lib/amp/server/fancy_views/harshcss/all_hallows_eve.css +72 -0
  145. data/lib/amp/server/fancy_views/harshcss/amy.css +147 -0
  146. data/lib/amp/server/fancy_views/harshcss/twilight.css +138 -0
  147. data/lib/amp/server/fancy_views/stylesheet.sass +175 -0
  148. data/lib/amp/server/http_server.rb +140 -0
  149. data/lib/amp/server/repo_user_management.rb +287 -0
  150. data/lib/amp/support/amp_config.rb +164 -0
  151. data/lib/amp/support/amp_ui.rb +287 -0
  152. data/lib/amp/support/docs.rb +54 -0
  153. data/lib/amp/support/generator.rb +78 -0
  154. data/lib/amp/support/ignore.rb +144 -0
  155. data/lib/amp/support/loaders.rb +93 -0
  156. data/lib/amp/support/logger.rb +103 -0
  157. data/lib/amp/support/match.rb +151 -0
  158. data/lib/amp/support/multi_io.rb +87 -0
  159. data/lib/amp/support/openers.rb +121 -0
  160. data/lib/amp/support/ruby_19_compatibility.rb +66 -0
  161. data/lib/amp/support/support.rb +1095 -0
  162. data/lib/amp/templates/blank.commit.erb +23 -0
  163. data/lib/amp/templates/blank.log.erb +18 -0
  164. data/lib/amp/templates/default.commit.erb +23 -0
  165. data/lib/amp/templates/default.log.erb +26 -0
  166. data/lib/amp/templates/template.rb +165 -0
  167. data/site/Rakefile +24 -0
  168. data/site/src/about/ampfile.haml +57 -0
  169. data/site/src/about/commands.haml +106 -0
  170. data/site/src/about/index.haml +33 -0
  171. data/site/src/about/performance.haml +31 -0
  172. data/site/src/about/workflows.haml +34 -0
  173. data/site/src/contribute/index.haml +65 -0
  174. data/site/src/contribute/style.haml +297 -0
  175. data/site/src/css/active4d.css +114 -0
  176. data/site/src/css/all_hallows_eve.css +72 -0
  177. data/site/src/css/all_themes.css +3299 -0
  178. data/site/src/css/amp.css +260 -0
  179. data/site/src/css/amy.css +147 -0
  180. data/site/src/css/blackboard.css +88 -0
  181. data/site/src/css/brilliance_black.css +605 -0
  182. data/site/src/css/brilliance_dull.css +599 -0
  183. data/site/src/css/cobalt.css +149 -0
  184. data/site/src/css/cur_amp.css +185 -0
  185. data/site/src/css/dawn.css +121 -0
  186. data/site/src/css/eiffel.css +121 -0
  187. data/site/src/css/espresso_libre.css +109 -0
  188. data/site/src/css/idle.css +62 -0
  189. data/site/src/css/iplastic.css +80 -0
  190. data/site/src/css/lazy.css +73 -0
  191. data/site/src/css/mac_classic.css +123 -0
  192. data/site/src/css/magicwb_amiga.css +104 -0
  193. data/site/src/css/pastels_on_dark.css +188 -0
  194. data/site/src/css/reset.css +55 -0
  195. data/site/src/css/slush_poppies.css +85 -0
  196. data/site/src/css/spacecadet.css +51 -0
  197. data/site/src/css/sunburst.css +180 -0
  198. data/site/src/css/twilight.css +137 -0
  199. data/site/src/css/zenburnesque.css +91 -0
  200. data/site/src/get/index.haml +32 -0
  201. data/site/src/helpers.rb +121 -0
  202. data/site/src/images/amp_logo.png +0 -0
  203. data/site/src/images/carbonica.png +0 -0
  204. data/site/src/images/revolution.png +0 -0
  205. data/site/src/images/tab-bg.png +0 -0
  206. data/site/src/images/tab-sliding-left.png +0 -0
  207. data/site/src/images/tab-sliding-right.png +0 -0
  208. data/site/src/include/_footer.haml +22 -0
  209. data/site/src/include/_header.haml +17 -0
  210. data/site/src/index.haml +104 -0
  211. data/site/src/learn/index.haml +46 -0
  212. data/site/src/scripts/jquery-1.3.2.min.js +19 -0
  213. data/site/src/scripts/jquery.cookie.js +96 -0
  214. data/tasks/stats.rake +155 -0
  215. data/tasks/yard.rake +171 -0
  216. data/test/dirstate_tests/dirstate +0 -0
  217. data/test/dirstate_tests/hgrc +5 -0
  218. data/test/dirstate_tests/test_dir_state.rb +192 -0
  219. data/test/functional_tests/resources/.hgignore +2 -0
  220. data/test/functional_tests/resources/STYLE.txt +25 -0
  221. data/test/functional_tests/resources/command.rb +372 -0
  222. data/test/functional_tests/resources/commands/annotate.rb +57 -0
  223. data/test/functional_tests/resources/commands/experimental/lolcats.rb +17 -0
  224. data/test/functional_tests/resources/commands/heads.rb +22 -0
  225. data/test/functional_tests/resources/commands/manifest.rb +12 -0
  226. data/test/functional_tests/resources/commands/status.rb +90 -0
  227. data/test/functional_tests/resources/version2/.hgignore +5 -0
  228. data/test/functional_tests/resources/version2/STYLE.txt +25 -0
  229. data/test/functional_tests/resources/version2/command.rb +372 -0
  230. data/test/functional_tests/resources/version2/commands/annotate.rb +45 -0
  231. data/test/functional_tests/resources/version2/commands/experimental/lolcats.rb +17 -0
  232. data/test/functional_tests/resources/version2/commands/heads.rb +22 -0
  233. data/test/functional_tests/resources/version2/commands/manifest.rb +12 -0
  234. data/test/functional_tests/resources/version2/commands/status.rb +90 -0
  235. data/test/functional_tests/resources/version3/.hgignore +5 -0
  236. data/test/functional_tests/resources/version3/STYLE.txt +31 -0
  237. data/test/functional_tests/resources/version3/command.rb +376 -0
  238. data/test/functional_tests/resources/version3/commands/annotate.rb +45 -0
  239. data/test/functional_tests/resources/version3/commands/experimental/lolcats.rb +17 -0
  240. data/test/functional_tests/resources/version3/commands/heads.rb +22 -0
  241. data/test/functional_tests/resources/version3/commands/manifest.rb +12 -0
  242. data/test/functional_tests/resources/version3/commands/status.rb +90 -0
  243. data/test/functional_tests/resources/version4/.hgignore +5 -0
  244. data/test/functional_tests/resources/version4/STYLE.txt +31 -0
  245. data/test/functional_tests/resources/version4/command.rb +376 -0
  246. data/test/functional_tests/resources/version4/commands/experimental/lolcats.rb +17 -0
  247. data/test/functional_tests/resources/version4/commands/heads.rb +22 -0
  248. data/test/functional_tests/resources/version4/commands/manifest.rb +12 -0
  249. data/test/functional_tests/resources/version4/commands/stats.rb +25 -0
  250. data/test/functional_tests/resources/version4/commands/status.rb +90 -0
  251. data/test/functional_tests/resources/version5_1/.hgignore +5 -0
  252. data/test/functional_tests/resources/version5_1/STYLE.txt +2 -0
  253. data/test/functional_tests/resources/version5_1/command.rb +374 -0
  254. data/test/functional_tests/resources/version5_1/commands/experimental/lolcats.rb +17 -0
  255. data/test/functional_tests/resources/version5_1/commands/heads.rb +22 -0
  256. data/test/functional_tests/resources/version5_1/commands/manifest.rb +12 -0
  257. data/test/functional_tests/resources/version5_1/commands/stats.rb +25 -0
  258. data/test/functional_tests/resources/version5_1/commands/status.rb +90 -0
  259. data/test/functional_tests/resources/version5_2/.hgignore +5 -0
  260. data/test/functional_tests/resources/version5_2/STYLE.txt +14 -0
  261. data/test/functional_tests/resources/version5_2/command.rb +376 -0
  262. data/test/functional_tests/resources/version5_2/commands/experimental/lolcats.rb +17 -0
  263. data/test/functional_tests/resources/version5_2/commands/manifest.rb +12 -0
  264. data/test/functional_tests/resources/version5_2/commands/newz.rb +12 -0
  265. data/test/functional_tests/resources/version5_2/commands/stats.rb +25 -0
  266. data/test/functional_tests/resources/version5_2/commands/status.rb +90 -0
  267. data/test/functional_tests/test_functional.rb +604 -0
  268. data/test/localrepo_tests/test_local_repo.rb +121 -0
  269. data/test/localrepo_tests/testrepo.tar.gz +0 -0
  270. data/test/manifest_tests/00manifest.i +0 -0
  271. data/test/manifest_tests/test_manifest.rb +72 -0
  272. data/test/merge_tests/base.txt +10 -0
  273. data/test/merge_tests/expected.local.txt +16 -0
  274. data/test/merge_tests/local.txt +11 -0
  275. data/test/merge_tests/remote.txt +11 -0
  276. data/test/merge_tests/test_merge.rb +26 -0
  277. data/test/revlog_tests/00changelog.i +0 -0
  278. data/test/revlog_tests/revision_added_changelog.i +0 -0
  279. data/test/revlog_tests/test_adding_index.i +0 -0
  280. data/test/revlog_tests/test_revlog.rb +333 -0
  281. data/test/revlog_tests/testindex.i +0 -0
  282. data/test/store_tests/store.tar.gz +0 -0
  283. data/test/store_tests/test_fncache_store.rb +122 -0
  284. data/test/test_amp.rb +9 -0
  285. data/test/test_base85.rb +14 -0
  286. data/test/test_bdiff.rb +42 -0
  287. data/test/test_commands.rb +122 -0
  288. data/test/test_difflib.rb +50 -0
  289. data/test/test_helper.rb +15 -0
  290. data/test/test_journal.rb +29 -0
  291. data/test/test_match.rb +134 -0
  292. data/test/test_mdiff.rb +74 -0
  293. data/test/test_mpatch.rb +14 -0
  294. data/test/test_support.rb +24 -0
  295. metadata +385 -0
@@ -0,0 +1,338 @@
1
+ module Amp
2
+
3
+ ##
4
+ # This is used for faking data writing, until we're totally done
5
+ # accumulating data.
6
+ class FakeFileAppender
7
+
8
+ ##
9
+ # Initializes the fake file with the pointer to the real deal,
10
+ # and also the current data
11
+ def initialize(fp, buffer, size)
12
+ @data = buffer
13
+ @fp = fp
14
+ @offset = fp.tell
15
+ @size = size
16
+ end
17
+
18
+ ##
19
+ # Returns the endpoint of the data in the (fake) file
20
+ def endpt
21
+ @size + @data.join.size
22
+ end
23
+
24
+ ##
25
+ # Returns the current offset of the (fake) file
26
+ def tell
27
+ @offset
28
+ end
29
+
30
+ ##
31
+ # Nothing, since we don't flush, we just sit here
32
+ def flush; end
33
+
34
+ ##
35
+ # Closes the real file pointer for this fake file
36
+ def close
37
+ @fp.close
38
+ end
39
+
40
+ ##
41
+ # Seeks to the position requested. We're faking this, but it's a little
42
+ # tougher, because if we break the fake file down like this:
43
+ #
44
+ # [ actual file size = 10 | pending data size = 10 ]
45
+ #
46
+ # If the user seeks to 15, we need to make sure we don't try to seek to
47
+ # 15 in the file, as that would cause errors.
48
+ def seek(offset, whence)
49
+ if whence == IO::SEEK_SET
50
+ @offset = offset
51
+ elsif whence == IO::SEEK_CUR
52
+ @offset += offset
53
+ elsif whence == IO::SEEK_END
54
+ @offset = self.endpt + offset
55
+ end
56
+ if @offset < @size
57
+ @fp.seek @offset
58
+ end
59
+ end
60
+
61
+ ##
62
+ # Reads from the fake file, for _count_ bytes. This is a little sketchy
63
+ # because we might be reading some real data and some fake data, or all
64
+ # real, or all fake.
65
+ #
66
+ # @param [Integer] count how much to read. -1 will read it all.
67
+ # @return [String] the contents
68
+ def read(count=-1)
69
+ ret = ""
70
+
71
+ if @offset < @size
72
+ s = @fp.read(count)
73
+ ret = s
74
+ @offset += s.size
75
+ if count > 0
76
+ count -= s.size
77
+ end
78
+ end
79
+
80
+ unless count.zero?
81
+ doff = @offset - @size
82
+ @data.unshift @data.join
83
+ @data.delete_range(1..-1)
84
+ s = @data[0][doff..(doff+count-1)]
85
+ @offset += s.size
86
+ ret += s
87
+ end
88
+
89
+ ret
90
+ end
91
+
92
+ ##
93
+ # Writes to the fake file. Notice - this will only work because we only
94
+ # append to the file - we don't write in the middle anywhere, or this
95
+ # scheme would fail.
96
+ #
97
+ # @param [#to_s] s data to write
98
+ def write(s)
99
+ @data << s.to_s
100
+ @offset += s.size
101
+ end
102
+ end
103
+
104
+ class DelayedOpener
105
+ attr_accessor :default
106
+
107
+ ##
108
+ # Initializes the delayed opener, needing the real deal, and the owner
109
+ # of the delayed opener.
110
+ #
111
+ # @param [Opener] realopener the actual opener. Since the DelayedOpener
112
+ # doesn't know how to open files, we'll need this!
113
+ # @param [ChangeLog] owner the owner of the delayed opener. This reference
114
+ # is needed because we need to check on the state of the delayed_buffer
115
+ # and what not.
116
+ def initialize(realopener, owner)
117
+ @real_opener, @owner = realopener, owner
118
+ end
119
+
120
+ ##
121
+ # Specialized opener that will fake writing when asked to. We need to
122
+ # know who our owner is and we need a real opener so we can actually
123
+ # open files. The DelayedOpener doesn't know how to do real IO.
124
+ def open(name, mode="r")
125
+ fp = @real_opener.open(name, mode)
126
+ return fp if name != @owner.index_file
127
+
128
+ # Are we holding off on writing? if so, set up the file where we
129
+ # writing pending data.
130
+
131
+ if @owner.delay_count == 0
132
+ @owner.delay_name = File.amp_name(fp)
133
+ mode.gsub!(/a/, 'w') if @owner.empty?
134
+ return @real_opener.open(name+".a", mode) # See what I did there?
135
+ end
136
+
137
+ # FakeFileAppender = fake file so we don't do any real writing yet
138
+ size = File.stat(@real_opener.join(name)).size
139
+ ffa = FakeFileAppender.new(fp, @owner.delay_buffer, size)
140
+
141
+ return ffa
142
+ end
143
+ end
144
+
145
+ ##
146
+ # A ChangeLog is a special revision log that stores the actual commit data,
147
+ # including usernames, dates, messages, all kinds of stuff.
148
+ #
149
+ # This version of the revision log is special though, because sometimes
150
+ # we have to hold off on writing until all other updates are done, for
151
+ # example during merges that might fail. So we have to actually have
152
+ # a real Opener and a fake one, which will save the data in memory.
153
+ # When you call #finalize, the fake file will replace the real deal.
154
+ class ChangeLog < Amp::Revlog
155
+ attr_accessor :delay_count, :delay_name, :index_file, :delay_buffer, :node_map
156
+
157
+ ##
158
+ # Initializes the revision log. Just pass in an Opener. Amp::Opener.new(path)
159
+ # will do just fine.
160
+ #
161
+ # @param [Amp::Opener] opener an object that knows how to open
162
+ # and return files based on a root directory.
163
+ def initialize(opener)
164
+ super(opener, "00changelog.i")
165
+ @node_map = @index.node_map
166
+ end
167
+ alias_method :changelog_initialize, :initialize
168
+
169
+ ##
170
+ # Tells the changelog to stop writing updates directly to the file,
171
+ # and start saving any new info to memory/other files. Used when the
172
+ # changelog has to be the last file saved.
173
+ def delay_update
174
+ @_real_opener = @opener
175
+ @opener = DelayedOpener.new(@_real_opener, self) # Our fake Opener
176
+ @delay_count = self.size
177
+ @delay_buffer = []
178
+ @delay_name = nil
179
+ end
180
+
181
+ ##
182
+ # Finalizes the changelog by swapping out the fake file if it has to.
183
+ # If there's any other data left in the buffer, it will be written
184
+ # as well.
185
+ def finalize(journal)
186
+ if @delay_name
187
+ src = @_real_opener.join(@index_file+".a")
188
+ dest = @_real_opener.join(@index_file)
189
+ @opener = @_real_opener # switch back to normal mode....
190
+ return File.amp_force_rename(src, dest)
191
+ end
192
+
193
+ if @delay_buffer && @delay_buffer.any?
194
+ @fp = open(@index_file, "a")
195
+ @fp.write @delay_buffer.join
196
+ @fp.close
197
+ @delay_buffer = []
198
+ end
199
+ # check_inline_size journal
200
+ end
201
+
202
+ ##
203
+ # Reads while we're blocking this changelog's output.
204
+ # @param file the file to read in as a revision log
205
+ def read_pending(file)
206
+ r = Revlog.new(@opener, file)
207
+ @index = r.index
208
+ @node_map = r.index.node_map
209
+ @chunk_cache = r.chunk_cache
210
+ end
211
+
212
+ ##
213
+ # Writes our data, while being aware of the delay buffer when we're holding
214
+ # off on finalizing the changelog.
215
+ def write_pending
216
+ if @delay_buffer && @delay_buffer.size > 0
217
+ fp1 = @_real_opener.open(@index_file)
218
+ fp2 = @_real_opener.open(@index_file + ".a", "w+")
219
+ UI.debug "trying to open #{@index_file + ".a"}..."
220
+ fp2.write fp1.read
221
+ fp2.write @delay_buffer.join
222
+ fp2.close
223
+ fp1.close
224
+ @delay_buffer = []
225
+ @delay_name = @index_file
226
+ end
227
+ return true if @delay_name && @delay_name.any?
228
+ false
229
+ end
230
+
231
+ ##
232
+ # Does a check on our size, but knows enough to quit if we're still in
233
+ # delayed-writing mode.
234
+ # @param [Amp::Journal] journal the journal to use to keep track of our transaction
235
+ # @param [File] fp the file pointer to use to check our size
236
+ def check_inline_size(journal, fp=nil)
237
+ return if @opener.is_a? DelayedOpener
238
+ super(journal, fp)
239
+ end
240
+
241
+ ##
242
+ # Decodes the extra data stored with the commit, such as requirements
243
+ # or just about anything else we need to save
244
+ # @param [String] text the data in the revision, decompressed
245
+ # @return [Hash] key-value pairs, joining each file with its extra data
246
+ def decode_extra(text)
247
+ extra = {}
248
+ text.split("\0").select {|l| l.any? }.
249
+ map {|l| l.remove_slashes.split(":",2) }.
250
+ each {|k,v| extra[k]=v }
251
+ extra
252
+ end
253
+
254
+ ##
255
+ # Encodes the extra data in a format we can use for writing.
256
+ # @param [Hash] data the extra data to format
257
+ # @return [String] the encoded data
258
+ def encode_extra(data)
259
+ " " + data.sort.map {|k| "#{k}:#{data[k]}".add_slashes }.join("\0")
260
+ end
261
+
262
+ ##
263
+ # Reads the revision at the given node_id. It returns it in a format
264
+ # that tells us everything about the revision - the manifest, the user
265
+ # who committed it, timestamps, the relevant filenames, the description
266
+ # message, and any extra data.
267
+ #
268
+ # @todo Text encodings, I hate you. but i must do them
269
+ # @param [Fixnum] node the node ID to lookup into the revision log
270
+ # @return [[String, String, [Float, Integer], [String], String, Hash]]
271
+ # The format is [Manifest, Username, [Time, Timezone], [Filenames],
272
+ # Message, ExtraData].
273
+ def read(node)
274
+ text = decompress_revision node
275
+ if text.nil? || text.empty?
276
+ return [NULL_ID, "", [0,0], [], "", {"branch" => "default"}]
277
+ end
278
+ #p text
279
+ last = text.index("\n\n")
280
+ desc = text[last+2..-1] #TODO: encoding
281
+ l = text[0..last].split("\n")
282
+ manifest = l[0].unhexlify
283
+ user = l[1] #TODO: encoding
284
+ extra_data = l[2].split(' ', 3)
285
+ if extra_data.size != 3
286
+ time = extra_data.shift.to_f
287
+ timezone = extra_data.shift.to_i
288
+ extra = {}
289
+ else
290
+ time, timezone, extra = extra_data
291
+ time, timezone = time.to_f, timezone.to_i
292
+ extra = decode_extra text
293
+ end
294
+ extra["branch"] = "default" unless extra["branch"]
295
+
296
+ files = l[3..-1]
297
+
298
+ #puts(">> Ari's tipmost changeset: "+[manifest, user, [time, timezone], files, desc, extra].inspect) #killme
299
+
300
+ [manifest, user, [time, timezone], files, desc, extra]
301
+ end
302
+
303
+ ##
304
+ # Adds the given commit to the changelog.
305
+ #
306
+ # @todo Handle text encodings
307
+ # @param [Amp::Manifest] Manifest a hex-version of a node_id or something?
308
+ # @param [String] files the files relevant to the commit, to be included
309
+ # @param [String] desc the commit message from the user
310
+ # @param [Amp::Journal] journal the transaction journal to write to for rollbacks if
311
+ # something goes horribly wrong
312
+ # @param [String] p1 the first parent of this node
313
+ # @param [String] p2 the second parent of this node
314
+ # @param [Strng] user the username of the committer
315
+ # @param [Time] date the date of the commit
316
+ # @param [Hash] extra any extra data
317
+ def add(manifest, files, desc, journal, p1=nil, p2=nil, user=nil, date=nil, extra={})
318
+ user = user.strip
319
+ raise RevlogSupport::RevlogError.new("no \\n in username") if user=~ /\n/
320
+ user, desc = user, desc #TODO: encoding!
321
+
322
+ date = Time.now unless date
323
+ parsed_date = "#{date.to_i} #{-1 * date.utc_offset}"
324
+
325
+ if extra && ["default", "", nil].include?(extra["branch"])
326
+ extra.delete "branch"
327
+ end
328
+ if extra
329
+ extra = (extra.any?) ? encode_extra(extra) : ""
330
+ parsed_date = "#{parsed_date}#{extra}"
331
+ end
332
+
333
+ l = [manifest.hexlify, user, parsed_date] + files.sort + ["", desc]
334
+ text = l.join "\n"
335
+ add_revision text, journal, self.size, p1, p2
336
+ end
337
+ end
338
+ end
@@ -0,0 +1,521 @@
1
+ module Amp
2
+
3
+ ##
4
+ # A Changeset is a simple way of accessing the repository within a certain
5
+ # revision. For example, if the user specifies revision # 36, or revision
6
+ # 3adf21, then we can look those up, and work within the repository at the
7
+ # moment of that revision.
8
+ class Changeset
9
+ include RevlogSupport::Node
10
+ include Comparable
11
+ include Enumerable
12
+
13
+ attr_reader :repo
14
+ alias_method :repository, :repo
15
+
16
+ ##
17
+ # Initializes a new changeset. We need a repository to work with, and also
18
+ # a change_id. this change_id could be a revision index or a node_id for
19
+ # the revision.
20
+ #
21
+ # @param [Repository] repo a repository to work with.
22
+ # @param [Integer, String] change_id an ID or index to lookup to find this
23
+ # changeset.
24
+ #
25
+ def initialize(repo, change_id='')
26
+ change_id = '.' if change_id == ''
27
+ @repo = repo
28
+ if change_id.kind_of? Integer
29
+ @revision = change_id
30
+ @node_id = @repo.changelog.node_id_for_index change_id
31
+ else
32
+ @node_id = @repo.lookup change_id
33
+ @revision = @repo.changelog.rev @node_id
34
+ end
35
+ end
36
+
37
+ ##
38
+ # Converts the revision to a number
39
+ def to_i; @revision; end
40
+
41
+ ##
42
+ # Converts the revision to an easy-to-digest string
43
+ def to_s(opts = {})
44
+ if opts[:template]
45
+ to_templated_s(opts)
46
+ else
47
+ @node_id[0..5].hexlify
48
+ end
49
+ end
50
+
51
+ ##
52
+ #
53
+ def to_templated_s(opts={})
54
+
55
+ change_node = node
56
+ revision = self.revision
57
+ log = @repo.changelog
58
+ changes = log.read change_node
59
+ username = changes[1]
60
+ date = Time.at changes[2].first
61
+ files = changes[3]
62
+ description = changes[4]
63
+ extra = changes[5]
64
+ branch = extra["branch"]
65
+ cs_tags = tags
66
+ type = opts[:template_type].to_s || 'log'
67
+
68
+ added = opts[:added]
69
+ removed = opts[:removed]
70
+ updated = opts[:updated]
71
+ config = opts
72
+
73
+ parents = useful_parents log, revision
74
+ parents.map! {|p| [p, log.node(p)[0..5].hexlify] }
75
+
76
+ p1 = useful_parents(log, revision)[0]
77
+ p2 = useful_parents(log, revision)[1]
78
+
79
+ return "" if opts[:no_output]
80
+
81
+ config = opts
82
+
83
+ template = opts[:template]
84
+ template = "default-#{type}" if (template.nil? || template.to_s == "default")
85
+
86
+ template = Support::Template[template]
87
+ template.render({}, binding)
88
+ end
89
+
90
+ def useful_parents(log, revision)
91
+ parents = log.parent_indices_for_index revision
92
+ if parents[1] == -1
93
+ if parents[0] >= revision - 1
94
+ parents = []
95
+ else
96
+ parents = [parents[0]]
97
+ end
98
+ end
99
+ parents
100
+ end
101
+
102
+ ##
103
+ # Gives an easier way to digest this changeset while reminding us it's a
104
+ # changeset
105
+ def inspect
106
+ "#<Changeset #{to_s}>"
107
+ end
108
+
109
+ ##
110
+ # Hash function for putting these bad boys in hashes
111
+ #
112
+ # @return [Integer] a hash value.
113
+ def hash
114
+ return @revision.hash if @revision
115
+ return object_id
116
+ end
117
+
118
+ ##
119
+ # Compares 2 changesets so we can sort them and whatnot
120
+ #
121
+ # @param [Changeset] other a changeset we will compare against
122
+ # @return [Integer] -1, 0, or 1. Typical comparison.
123
+ def <=>(other)
124
+ return 0 if @revision.nil? || other.revision.nil?
125
+ @revision <=> other.revision
126
+ end
127
+
128
+ ##
129
+ # Are we a null revision?
130
+ # @return [Boolean] null?
131
+ def nil?
132
+ @revision != NULL_REV
133
+ end
134
+ alias_method :null?, :nil?
135
+
136
+ # Gets the raw changeset data for this revision. This includes
137
+ # the user who committed it, the description of the commit, and so on.
138
+ # Returns this: [manifest, user, [time, timezone], files, desc, extra]
139
+ def raw_changeset
140
+ @repo.changelog.read(@node_id)
141
+ end
142
+
143
+ ##
144
+ # Returns the {ManifestEntry} for this revision. This will give
145
+ # us info on any file we want, including flags such as executable
146
+ # or if it's a link. Sizes and so on are also included.
147
+ #
148
+ # @return [ManifestEntry] the manifest at this point in time
149
+ def manifest
150
+ @manifest ||= @repo.manifest.read(raw_changeset[0])
151
+ end
152
+
153
+ ##
154
+ # Returns the change in the manifest at this revision. I don't entirely
155
+ # know what this is yet.
156
+ def manifest_delta
157
+ @manifest_delta ||= @repo.manifest.read_delta(raw_changeset[0])
158
+ end
159
+
160
+ ##
161
+ # Returns the parents of this changeset as {Changeset}s.
162
+ #
163
+ # @return [[Changeset]] the parents of this changeset.
164
+ def parents
165
+ return @parents if @parents
166
+
167
+ p = @repo.changelog.parent_indices_for_index @revision
168
+ p = [p[0]] if p[1] == NULL_REV
169
+
170
+ @parents = p.map {|x| Changeset.new(@repo, x) }
171
+ end
172
+
173
+ ##
174
+ # Returns the children of this changeset as {Changeset}s.
175
+ #
176
+ # @return [Array<Changeset>] the children of this changeset.
177
+ def children
178
+ @repo.changelog.children(node_id).map do |node|
179
+ Changeset.new(@repo, node)
180
+ end
181
+ end
182
+
183
+ ##
184
+ # Iterates over each entry in the manifest.
185
+ def each(&block)
186
+ manifest.sort.each(&block)
187
+ end
188
+
189
+ ##
190
+ # Checks whether this changeset included a given file or not.
191
+ #
192
+ # @param [String] file the file to lookup
193
+ # @return [Boolean] whether the file is in this changeset's manifest
194
+ def include?(file)
195
+ manifest[file] != nil
196
+ end
197
+
198
+ ##
199
+ # Gets the file with the given name, as a {VersionedFile}.
200
+ # @param file the path to the file to retrieve
201
+ # @return [VersionedFile] the file at this revision
202
+ #
203
+ def [](file)
204
+ get_file(file)
205
+ end
206
+
207
+ ##
208
+ # Returns the file's info, namely it's node_id and flags it may
209
+ # have at this point in time, such as "x" for executable.
210
+ #
211
+ # @param path the path to the file
212
+ # @return [[String, String]] the [node_id, flags] pair for this file
213
+ def file_info(path)
214
+ if manifest # have we loaded our manifest yet? if so, use that sucker
215
+ result = [manifest[path], manifest.flags[path]]
216
+ if result[0].nil?
217
+ return [NULL_ID, '']
218
+ else
219
+ return result
220
+ end
221
+ end
222
+ if manifest_delta || files[path] # check if it's in the delta... i dunno
223
+ if manifest_delta[path]
224
+ return [manifest_delta[path], manifest_delta.flags[path]]
225
+ end
226
+ end
227
+ # Give us, just look it up the long way in the manifest. not fun. slow.
228
+ node, flag = @repo.manifest.find(raw_changeset[0], path)
229
+ if node.nil?
230
+ return [NULL_ID, '']
231
+ end
232
+ return [node, flag]
233
+ end
234
+
235
+ ##
236
+ # Gets the flags for the file at the given path at this revision.
237
+ # @param path the path to the file in question
238
+ # @return [String] the flags for the file, such as "x" or "l".
239
+ #
240
+ def flags(path)
241
+ info = file_info(path)[1]
242
+ return "" if info.nil?
243
+ info
244
+ end
245
+
246
+ ##
247
+ # Gets the node_id in the manifest for the file at this path, for this
248
+ # specific revision.
249
+ #
250
+ # @param path the path to the file
251
+ # @return [String] the node's ID in the manifest, which we'll use every
252
+ # where we need a node_id.
253
+ def file_node(path)
254
+ file_info(path).first[0..19]
255
+ end
256
+
257
+ ##
258
+ # Creates a versioned file for the file at the given path, for the frame
259
+ # of reference of this revision.
260
+ # @param path the path to the file
261
+ # @param [String] file_id the node_id, to save us some computation
262
+ # @param [FileLog] file_log the file_log to use, again to save us computation
263
+ # @return [VersionedFile] the file at this revision.
264
+ #
265
+ def get_file(path, file_id = nil, file_log = nil)
266
+ file_id = file_node(path) if file_id.nil?
267
+ VersionedFile.new(@repo, path, :file_id => file_id, :changeset => self,
268
+ :file_log => file_log)
269
+ end
270
+ #accessors
271
+ # revision index
272
+ def revision; @revision; end
273
+ alias_method :rev, :revision
274
+ # node_id
275
+ def node_id; @node_id; end
276
+ # @see node_id
277
+ alias_method :node, :node_id
278
+ # our node_id in sexy hexy
279
+ def hex; @node_id.hexlify; end
280
+ # the user who committed me!
281
+ def user; raw_changeset[1]; end
282
+ # the date i was committed!
283
+ def date; raw_changeset[2]; end
284
+ def easy_date; Time.at(raw_changeset[2].first); end
285
+ # the files affected in this commit!
286
+ def files; raw_changeset[3]; end
287
+ # the message with this commit
288
+ def description; raw_changeset[4]; end
289
+ # What branch i was committed onto
290
+ def branch
291
+ raw_changeset[5]["branch"]
292
+ end
293
+ # Any extra stuff I've got in me
294
+ def extra; raw_changeset[5]; end
295
+ # tags
296
+ def tags; @repo.tags_for_node node; end
297
+
298
+ ##
299
+ # recursively walk
300
+ #
301
+ # @param [Amp::Matcher] match this is a custom object that knows files
302
+ # magically. Not your grampa's proc!
303
+ def walk(match) # calls DirState#walk
304
+ # just make it so the keys are there
305
+ results = []
306
+
307
+ hash = Hash.with_keys match.files
308
+ hash.delete '.'
309
+
310
+ each do |file|
311
+ hash.each {|f, val| (hash.delete file and break) if f == file }
312
+
313
+ results << file if match.call file # yield file if match.call file
314
+ end
315
+
316
+ hash.keys.sort.each do |file|
317
+ if match.bad file, "No such file in revision #{revision}" and match[file]
318
+ results << file # yield file
319
+ end
320
+ end
321
+ results
322
+ end
323
+
324
+ def ancestor(other_changeset)
325
+ node = @repo.changelog.ancestor(self.node, other_changeset.node)
326
+ return Changeset.new(@repo, node)
327
+ end
328
+
329
+ def ancestors
330
+ results = []
331
+ @repo.changelog.ancestors(revision)
332
+ end
333
+ end
334
+
335
+ ##
336
+ # This is a special changeset that specifically works within the
337
+ # working directory. We sort of have to combine the old revision
338
+ # logs with the fact that files might be changed, and not in the
339
+ # revision logs! oh, mercy!
340
+ class WorkingDirectoryChangeset < Changeset
341
+
342
+ def initialize(repo, opts={:text => ""})
343
+ @repo = repo
344
+ @revision = nil
345
+ @node_id = nil
346
+ @text = opts[:text]
347
+ require 'time' if opts[:date].kind_of?(String)
348
+ @date = opts[:date].kind_of?(String) ? Time.parse(opts[:date]) : opts[:date]
349
+ @user = opts[:user] if opts[:user]
350
+ @parents = opts[:parents].map {|p| Changeset.new(@repo, p)} if opts[:parents]
351
+ @status = opts[:changes] if opts[:changes]
352
+
353
+ @extra = {}
354
+ @extra = opts[:extra].dup if opts[:extra]
355
+ unless @extra["branch"]
356
+ branch = @repo.dirstate.branch
357
+ # encoding - to UTF-8
358
+ @extra["branch"] = branch
359
+ end
360
+ @extra["branch"] = "default" if @extra["branch"] && @extra["branch"].empty?
361
+
362
+ end
363
+
364
+ ##
365
+ # Converts to a string.
366
+ # I'm my first parent, plus a little extra.
367
+ def to_s
368
+ parents.first.to_s + "+"
369
+ end
370
+
371
+ ##
372
+ # Am I nil? never!
373
+ def nil?; false; end
374
+
375
+ ##
376
+ # Do I include a given file? (not sure this is ever used yet)
377
+ def include?(key); "?r".include?(@repo.dirstate[key].status.to_hg_letter); end
378
+
379
+ ##
380
+ # What is the status of the working directory? This little
381
+ # method hides quite a bit of work!
382
+ def status
383
+ @status ||= @repo.status(:unknown => true)
384
+ end
385
+
386
+ ##
387
+ # Who is the user working on me?
388
+ def user
389
+ @user ||= @repo.config.username
390
+ end
391
+
392
+ ##
393
+ # Well, I guess the working directory's date is... right now!
394
+ def date
395
+ @date ||= Time.new
396
+ end
397
+
398
+ ##
399
+ # Who is the working directory's father? Is it Chef? Mr. Garrison?
400
+ # the 1989 denver broncos?
401
+ #
402
+ # hahaha mike that's hilarious
403
+ def parents
404
+ return @parents if @parents
405
+ p = @repo.dirstate.parents
406
+ p = [p[0]] if p[1] == NULL_ID
407
+ @parents = p.map {|x| Changeset.new(@repo, x) }
408
+ @parents
409
+ end
410
+
411
+ ##
412
+ # OK, so we've got the last revision's manifest, that part's simple and makes sense.
413
+ # except now, we need to get the status of the working directory, and
414
+ # add in all the other files, because they're in the "manifest" by being
415
+ # in existence. Oh, and we need to remove any files from the parent's
416
+ # manifest that don't exist anymore. Make sense?
417
+ def manifest
418
+ return @manifest if @manifest
419
+
420
+ # Start off with the last revision's manifest, that's safe.
421
+ man = parents()[0].manifest.dup
422
+ # Any copied files since the last revision?
423
+ copied = @repo.dirstate.copy_map
424
+ # Any modified, added, etc files since the last revision?
425
+ modified, added, removed = status[:modified], status[:added], status[:removed]
426
+ deleted, unknown = status[:deleted], status[:unknown]
427
+ # Merge these discoveries in!
428
+ {:a => added, :m => modified, :u => unknown}.each do |k, list|
429
+ list.each do |file|
430
+ copy_name = (copied[file] || file)
431
+ man[file] = (man.flags[copy_name] || NULL_ID) + k.to_s
432
+ man.flags[file] = @repo.dirstate.flags(file)
433
+ end
434
+ end
435
+
436
+ # Delete files from the real manifest that don't exist.
437
+ (deleted + removed).each do |file|
438
+ man.delete file if man[file]
439
+ end
440
+
441
+ man
442
+ end
443
+
444
+ ##
445
+ # Returns a {VersionedWorkingFile} to represent the file at the given
446
+ # point in time. It represents a file in the working directory, which
447
+ # obvious don't read from the history, but from the actual file in
448
+ # question.
449
+ #
450
+ # @param path the path to the file
451
+ # @param file_log the log for the file to save some computation
452
+ # @return [Amp::VersionedWorkingFile] the file object we can work with
453
+ def get_file(path, file_log=nil)
454
+ VersionedWorkingFile.new(@repo, path, :working_changeset => self,
455
+ :file_log => file_log)
456
+ end
457
+
458
+ ##
459
+ # Gets the flags for the file at current state in time
460
+ #
461
+ # @param [String] path the path to the file
462
+ # @return [String] the flags, such as "x", "l", or ""
463
+ def flags(path)
464
+ if @manifest
465
+ return manifest.flags[path] || ""
466
+ end
467
+ pnode = parents[0].raw_changeset[0]
468
+
469
+ orig = @repo.dirstate.copy_map[path] || path
470
+ node, flag = @repo.manifest.find(pnode, orig)
471
+ return @repo.dirstate.flags(@repo.working_join(path))
472
+ end
473
+
474
+ def useful_parents(log, revision)
475
+ parents = @parents.map {|p| p.revision}
476
+ if parents[1] == -1
477
+ if parents[0] >= @repo.size - 1
478
+ parents = []
479
+ else
480
+ parents = [parents[0]]
481
+ end
482
+ end
483
+ parents
484
+ end
485
+
486
+ ##
487
+ # Recursively walk the directory tree, getting all files that +match+ says
488
+ # are good.
489
+ #
490
+ # @param [Amp::Match] match how to select the files in the tree
491
+ # @param [Boolean] check_ignored (false) should we check for ignored files?
492
+ # @return [Array<String>] an array of filenames in the tree that match +match+
493
+ def walk(match, check_ignored = false)
494
+ tree = @repo.dirstate.walk true, check_ignored, match
495
+ tree.keys.sort
496
+ end
497
+
498
+ # If there's a description, ok then
499
+ def description; @text; end
500
+ # Files affected in this transaction: modified, added, removed.
501
+ def files; (status[:modified] + status[:added] + status[:removed]).sort; end
502
+ # What files have changed?
503
+ def modified; status[:modified]; end
504
+ # What files have we added?
505
+ def added; status[:added]; end
506
+ # What files have been removed?
507
+ def removed; status[:removed]; end
508
+ # What files have been deleted (but not officially)?
509
+ def deleted; status[:deleted]; end
510
+ # What files are hanging out, but untracked?
511
+ def unknown; status[:unknown]; end
512
+ # What files are pristine since the last revision?
513
+ def clean; status[:normal]; end
514
+ # What branch are we in?
515
+ def branch; @extra["branch"]; end
516
+ # Any other extra data? i'd like to hear it
517
+ def extra; @extra; end
518
+ # No children. Returns the empty array.
519
+ def children; []; end
520
+ end
521
+ end