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,129 @@
1
+ require 'zlib'
2
+
3
+ module Amp
4
+ module Mercurial
5
+ module RevlogSupport
6
+
7
+ class RevlogError < StandardError; end
8
+ class LookupError < StandardError; end
9
+
10
+ module Support
11
+ extend self
12
+
13
+ # Old version of the revlog file format
14
+ REVLOG_VERSION_0 = 0
15
+ # Current version of the revlog file format
16
+ REVLOG_VERSION_NG = 1
17
+ # A flag marking that the data is stored with the index
18
+ REVLOG_NG_INLINE_DATA = (1 << 16)
19
+ # Default flags - always start inline (turn off inline if file is huge)
20
+ REVLOG_DEFAULT_FLAGS = REVLOG_NG_INLINE_DATA
21
+ # Default format - the most recent
22
+ REVLOG_DEFAULT_FORMAT = REVLOG_VERSION_NG
23
+ # Default version in general
24
+ REVLOG_DEFAULT_VERSION = REVLOG_DEFAULT_FORMAT | REVLOG_DEFAULT_FLAGS
25
+
26
+ ##
27
+ # This bears some explanation.
28
+ #
29
+ # Rather than simply having a 4-byte header for the index file format, the
30
+ # Mercurial format takes the first entry in the index, and stores the header
31
+ # in its offset field. (The offset field is a 64-bit unsigned integer which
32
+ # stores the offset into the data or index of the associated record's data)
33
+ # They take advantage of the fact that the first entry's offset will always
34
+ # be 0. As such, its offset field is always going to be zero, so it's safe
35
+ # to store data there.
36
+ #
37
+ # The format is ((flags << 16) | (version)), where +flags+ is a bitmask (up to 48
38
+ # bits) and +version+ is a 16-bit unsigned short.
39
+ #
40
+ # The worst part is, EVERY SINGLE ENTRY has its offset shifted 16 bits to the left,
41
+ # apparently all because of this. It fucking baffles my mind.
42
+ #
43
+ # So yeah. offset = value >> 16.
44
+ def get_offset(o); o >> 16; end
45
+ # And yeah. version = value && 0xFFFF (last 16 bits)
46
+ def get_version(t); t & 0xFFFF; end
47
+
48
+ # Combine an offset and a version to spit this baby out
49
+ def offset_version(offset,type)
50
+ (offset << 16) | type
51
+ end
52
+
53
+ ##
54
+ # generate a hash from the given text and its parent hashes
55
+ #
56
+ # This hash combines both the current file contents and its history
57
+ # in a manner that makes it easy to distinguish nodes with the same
58
+ # content in the revision graph.
59
+ #
60
+ # since an entry in a revlog is pretty
61
+ # much [parent1, parent2, text], we use a hash of the previous entry
62
+ # as a reference to that previous entry. To create a reference to this
63
+ # entry, we make a hash of the first parent (which is just its ID), the
64
+ # second parent, and the text.
65
+ #
66
+ # @return [String] the digest of the two parents and the extra text
67
+ def history_hash(text, p1, p2)
68
+ list = [p1, p2].sort
69
+ s = list[0].sha1
70
+ s.update list[1]
71
+ s.update text
72
+ s.digest
73
+ end
74
+
75
+ ##
76
+ # returns the possibly-compressed version of the text, in a hash:
77
+ #
78
+ # @return [Hash] :compression => 'u' or ''
79
+ def compress(text)
80
+ return {:compression => "", :text => text} if text.empty?
81
+ size = text.size
82
+ binary = nil
83
+ if size < 44
84
+ elsif size > 1000000 #big ole file
85
+ deflater = Zlib::Deflate.new
86
+ parts = []
87
+ position = 0
88
+ while position < size
89
+ newposition = position + 2**20
90
+ parts << deflater.deflate(text[position..(newposition-1)], Zlib::NO_FLUSH)
91
+ position = newposition
92
+ end
93
+ parts << deflater.flush
94
+ binary = parts.join if parts.map {|e| e.size}.sum < size # only add it if
95
+ # compression made it smaller
96
+ else #tiny, just compress it
97
+ binary = Zlib::Deflate.deflate text
98
+ end
99
+
100
+ if binary.nil? || binary.size > size
101
+ return {:compression => "", :text => text} if text[0,1] == "\0"
102
+ return {:compression => 'u', :text => text}
103
+ end
104
+ {:compression => "", :text => binary}
105
+ end
106
+
107
+ ##
108
+ # Decompresses the given binary text. The binary text could be
109
+ # uncompressed, in which case, we'll figure that out. Don't worry.
110
+ #
111
+ # @param [String] binary the text to (possibly) decompress
112
+ # @return [String] the text decompressed
113
+ def decompress(binary)
114
+ return binary if binary.empty?
115
+ case binary[0,1]
116
+ when "\0"
117
+ binary #we're just stored as binary
118
+ when "x"
119
+ Zlib::Inflate.inflate(binary) #we're zlibbed
120
+ when "u"
121
+ binary[1..-1] #we're uncompressed text
122
+ else
123
+ raise LookupError.new("Unknown compression type #{binary[0,1]}")
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,597 @@
1
+ module Amp
2
+ module Mercurial
3
+
4
+ ##
5
+ # This class allows you to access a file at a given revision in the repo's
6
+ # history. You can compare them, sort them, access the changeset, and
7
+ # all sorts of stuff.
8
+ class VersionedFile < Amp::Repositories::AbstractVersionedFile
9
+ include Mercurial::RevlogSupport::Node
10
+
11
+ attr_accessor :file_id
12
+ attr_accessor :change_id
13
+ attr_accessor :path
14
+ attr_accessor :repo
15
+
16
+ ##
17
+ # Creates a new {VersionedFile}. You need to pass in the repo and the path
18
+ # to the file, as well as one of the following: a revision index/ID, the
19
+ # node_id of the file's revision in the filelog, or a changeset at a given
20
+ # index.
21
+ #
22
+ # Oh, and just FYI because it might interest you, there are three ways to
23
+ # specify the revision of a VFile: change_id (revision in changelog),
24
+ # file_id (revision in file_log), and an actual changeset. Instead of
25
+ # sticking with one way, favoring one way, or converting everything
26
+ # to a single form, they do everything
27
+ # three different ways. It's not particularly difficult to deal with, per se,
28
+ # but it's just fucking stupid. like seriously wtf. with that said, mad props
29
+ # to the mercurial team because they put out a rock solid piece of code that
30
+ # has inspired me.
31
+ #
32
+ # @param [Repository] repo The repo we're working with
33
+ # @param [String] path the path to the file
34
+ # @param [Hash] opts the options to customize how we load this file
35
+ # @option [FileLog] opts :file_log (nil) The FileLog to use for loading data
36
+ # @option [String] opts :change_id (nil) The revision ID/index to use to
37
+ # figure out which revision we're working with
38
+ # @option [Changeset] opts :changeset (nil) the changeset to use to figure
39
+ # which revision we're working with
40
+ # @option [String] opts :file_id (nil) perhaps the ID of the revision in
41
+ # the file_log to use?
42
+ def initialize(repo, path, opts={})
43
+ @repo, @path = repo, path
44
+ raise StandardError.new("specify a revision!") unless opts[:change_id] ||
45
+ opts[:file_id] ||
46
+ opts[:changeset]
47
+ @file_log = opts[:file_log] || nil
48
+ @change_id = opts[:change_id] || nil
49
+ @changeset = opts[:changeset] || nil
50
+ @file_id = opts[:file_id] || nil
51
+ end
52
+
53
+ ##
54
+ # Commit this {VersionedFile} as part of a larger transaction. This will
55
+ # not commit anything if the file hasn't actually been changed.
56
+ #
57
+ # commit_file:
58
+ # if file_has_been_copied:
59
+ # get_new_file_pointers # for the old file_log, since it needs to be transfered
60
+ # elsif file_has_been_merged:
61
+ # deal_with_merges
62
+ # end
63
+ #
64
+ # add_file_log_entry
65
+ #
66
+ # @param [Hash] opts
67
+ # @option opts [Array<Amp::Mercurial::ManifestEntry>] manifests the manifests of
68
+ # the parents
69
+ # @option opts [String] link_revision the revision index we'll be adding
70
+ # this under
71
+ # @option opts [Amp::Mercurial::Journal] journal the journal for aborting
72
+ # failed commits
73
+ # @option opts [Array<String>] changed the running tally of changed files
74
+ # @return [String] the file_id (where this revision is in its file log)
75
+ def commit(opts={})
76
+ f_log = repo.file_log @path # :: FileLog
77
+ copied = renamed? # [ path, ]
78
+ meta_data = {}
79
+
80
+ fp1 = opts[:manifests][0][path] || NULL_ID # :: String (file_id)
81
+ fp2 = opts[:manifests][1][path] || NULL_ID # :: String (file_id)
82
+
83
+ # DEALIN' WITH MERGES AN' COPIES!
84
+ if copied && copied[0] != @path
85
+ # COPIES!!!
86
+ #
87
+ # we need new pointers because when we deal with merges, we need
88
+ # to know if we have more work to do. If fp1 is NULL_ID, then it means
89
+ # we need to look up the copy data so we can get the old file log data.
90
+ fp1, fp2, meta_data = *determine_new_file_pointers(fp1, fp2, copied[0], opts)
91
+ elsif fp2 != NULL_ID
92
+ # MERGES
93
+ fpa = f_log.ancestor fp1, fp2
94
+
95
+ fp1, fp2 = fp2, NULL_ID if fpa == fp1
96
+ fp2 = NULL_ID if fpa != fp2 && fpa == fp2
97
+ end
98
+
99
+ # Else, if there is no second parent and the file hasn't been copied
100
+ # and nothing has changed in the file (that's the last little
101
+ # comparison), then we just return fp1 because there's nothing to write.
102
+ if fp2 == NULL_ID && meta_data.empty? && !(f_log.cmp(fp1, data))
103
+ return fp1
104
+ end
105
+
106
+ # Add it to the list of CHANGED files. If we've made it this far,
107
+ # we have a file that has changed.
108
+ opts[:changed] << @path
109
+
110
+ # Add the motherfucking file log entry. Have a nice day.
111
+ f_log.add data, meta_data, opts[:journal], opts[:link_revision], fp1, fp2
112
+ end
113
+
114
+ ##
115
+ # Determine the new file indices of the {FileLog} for this
116
+ # {VersionedFile}. This deals with merges and copies and anything else
117
+ # that might stymie the standard process.
118
+ #
119
+ # @param [String] fp1 the file index of the first parent
120
+ # @param [String] fp2 the file index of the second parent
121
+ # @param [Hash<Symbol => Object>] opts
122
+ # @option opts [Array<ManifestEntry>] :manifests the manifests of the
123
+ # first and second parents, respectively.
124
+ # @return [(String, String, Hash<String => String>)] the first file index,
125
+ # the second file index, and any meta data
126
+ def determine_new_file_pointers(fp1, fp2, copied_file, opts={})
127
+ # Mark the new revision of this file as a copy of another
128
+ # file. This copy data will effectively act as a parent
129
+ # of this new revision. If this is a merge, the first
130
+ # parent will be the nullid (meaning "look up the copy data")
131
+ # and the second one will be the other parent. For example:
132
+ #
133
+ # 0 --- 1 --- 3 rev1 changes file foo
134
+ # \ / rev2 renames foo to bar and changes it
135
+ # \- 2 -/ rev3 should have bar with all changes and
136
+ # should record that bar descends from
137
+ # bar in rev2 and foo in rev1
138
+ #
139
+ # this allows this merge to succeed:
140
+ #
141
+ # 0 --- 1 --- 3 rev4 reverts the content change from rev2
142
+ # \ / merging rev3 and rev4 should use bar@rev2
143
+ # \- 2 --- 4 as the merge base
144
+
145
+ copied_revision = opts[:manifests][0][copied_file]
146
+ new_fp = fp2
147
+
148
+ if opts[:manifests][1] # branch merge
149
+ # copied on remote side
150
+ if fp2 == NULL_ID || copied_revision == nil
151
+ if opts[:manifests][1][copied_file]
152
+ copied_revision = opts[:manifests][1][copied_file]
153
+ new_fp = fp1
154
+ end
155
+ end
156
+ end
157
+
158
+ if copied_revision.nil? || copied_revision.empty?
159
+ ancestor = repo["."].ancestors.detect {|ancestor| ancestor[copied_file] }
160
+ copied_revision = ancestor[copied_file].file_node
161
+ end
162
+
163
+ UI::say "#{@path}: copy #{copied_file}:#{copied_revision.hexlify}"
164
+
165
+ meta_data = {}
166
+ meta_data["copy"] = copied_file
167
+ meta_data["copyrev"] = copied_revision.hexlify
168
+ fp1, fp2 = NULL_ID, new_fp
169
+
170
+ [fp1, fp2, meta_data]
171
+ end
172
+
173
+ ##
174
+ # Is it more recent than +other+?
175
+ def <=>(other)
176
+ change_id <=> other.change_id
177
+ end
178
+
179
+ ##
180
+ # Returns the changeset that this file belongs to
181
+ #
182
+ # @return [Changeset] the changeset this file belongs to
183
+ def changeset
184
+ @changeset ||= Changeset.new @repo, change_id
185
+ end
186
+
187
+ ##
188
+ # Dunno why this is here
189
+ #
190
+ # @return [String]
191
+ def repo_path
192
+ @path
193
+ end
194
+
195
+ ##
196
+ # The file log that tracks this file
197
+ #
198
+ # @return [FileLog] The revision log tracking this file
199
+ def file_log
200
+ @file_log ||= @repo.file_log @path
201
+ end
202
+
203
+ ##
204
+ # The revision of the repository that this {VersionedFile} belongs to.
205
+ def change_id
206
+ @change_id ||= @changeset.revision if @changeset
207
+ @change_id ||= file_log[file_rev].link_rev unless @changeset
208
+ @change_id
209
+ end
210
+
211
+ ##
212
+ # The version of the file in its own history.
213
+ def file_node
214
+ @file_node ||= file_log.lookup_id(@file_id) if @file_id
215
+ @file_node ||= changeset.file_node(@path) unless @file_id
216
+ @file_node ||= NULL_ID
217
+ end
218
+
219
+ ##
220
+ # Returns the index into the file log's history for this file
221
+ def file_rev
222
+ @file_rev ||= file_log.rev(file_node)
223
+ end
224
+
225
+ ##
226
+ # Is this a null version?
227
+ def nil?
228
+ file_node.nil?
229
+ end
230
+
231
+ ##
232
+ # String representation.
233
+ def to_s
234
+ "#{path}@#{node.hexlify[0..11]}"
235
+ end
236
+
237
+ ##
238
+ # IRB Inspector string representation
239
+ def inspect
240
+ "#<HG Versioned File: #{to_s}>"
241
+ end
242
+
243
+ ##
244
+ # Hash value for sticking this fucker in a hash
245
+ def hash
246
+ return (path + file_id.to_s).hash
247
+ end
248
+
249
+ ##
250
+ # Equality! Compares paths and revision indexes
251
+ def ==(other)
252
+ return false unless @path && @file_id && other.path && other.file_id
253
+ @path == other.path && @file_id == other.file_id
254
+ end
255
+
256
+ ##
257
+ # Retrieves the file with a different ID
258
+ #
259
+ # @param [String] file_id a new file ID revision in file_log
260
+ def file(file_id)
261
+ self.class.new @repo, @path, :file_id => file_id, :file_log => file_log
262
+ end
263
+
264
+ # Gets the flags for this file (x, l, or empty string)
265
+ def flags; changeset.flags(@path); end
266
+ # The manifest that this file revision is from
267
+ def manifest_entry; changeset.manifest_entry; end
268
+ # Node ID for this file's revision
269
+ def node; changeset.node; end
270
+ # All files in this changeset that this revision of this file was committed
271
+ def files; changeset.all_files; end
272
+ # The data in this file
273
+ def data; file_log.read(file_node); end
274
+ # The path to this file
275
+ def path; @path; end
276
+ # The size of this file
277
+ def size; file_log.size(file_rev); end
278
+
279
+ # Returns the revision index
280
+ def revision
281
+ return changeset.rev if @changeset || @change_id
282
+ link_rev
283
+ end
284
+
285
+ # Link-revision index
286
+ def link_rev; file_log[file_rev].link_rev; end
287
+
288
+ ##
289
+ # Compares to a bit of text.
290
+ # Returns true if different, false for the same.
291
+ # (much like <=> == 0 for the same)
292
+ def cmp(text)
293
+ file_log.cmp(file_node, text)
294
+ end
295
+
296
+ ##
297
+ # Has this file been renamed? If so give some good info. Returns
298
+ # the new location and the flags ('x', 'l', '')
299
+ #
300
+ # @return [Array<String, String>] [new_path, flags]
301
+ def renamed?
302
+ renamed = file_log.renamed?(file_node)
303
+ return renamed unless renamed
304
+
305
+ return renamed if rev == link_rev
306
+
307
+ name = path
308
+ fnode = file_node
309
+ changeset.parents.each do |p|
310
+ pnode = p.filenode(name)
311
+ next if pnode.nil?
312
+
313
+ # Why the fuck does this method return nil. This could fuck things
314
+ # up down the line. There better be a good fucking reason for this.
315
+ # Sorry I'm so irritated. I just need some food.
316
+ return nil if fnode == pnode
317
+ end
318
+ renamed
319
+ end
320
+
321
+ ##
322
+ # What are this revised file's parents? Return them as {VersionedFile}s.
323
+ def parents
324
+ p = @path
325
+ fl = file_log
326
+ pl = file_log.parents(file_node).map {|n| [p, n, fl] }
327
+
328
+ r = file_log.renamed?(file_node)
329
+ pl[0] = [r[0], r[1], nil] if r
330
+
331
+ pl.select {|parent,n,l| n != NULL_ID}.map do |parent, n, l|
332
+ VersionedFile.new(@repo, parent, :file_id => n, :file_log => l)
333
+ end
334
+ end
335
+
336
+ ##
337
+ # What are this file's children?
338
+ def children
339
+ c = file_log.children(file_node)
340
+ c.map do |x|
341
+ VersionedFile.new(@repo, @path, :file_id => x, :file_log => file_log)
342
+ end
343
+ end
344
+
345
+ def annotate_decorate(text, revision, line_number = false)
346
+ if line_number
347
+ size = text.split("\n").size
348
+ retarr = [nil,text]
349
+ retarr[0] = (1..size).map {|i| [revision, i]}
350
+ else
351
+ retarr = [nil, text]
352
+ retarr[0] = [[revision, false]] * text.split("\n").size
353
+ end
354
+ retarr
355
+ end
356
+
357
+ def annotate_diff_pair(parent, child)
358
+ Diffs::BinaryDiff.blocks_as_array(parent[1], child[1]).each do |a1,a2,b1,b2|
359
+ child[0][b1..(b2-1)] = parent[0][a1..(a2-1)]
360
+ end
361
+ child
362
+ end
363
+
364
+ def annotate_get_file(path, file_id)
365
+ log = path == @path ? file_log : @repo.get_file(path)
366
+ return VersionedFile.new(@repo, path, :file_id => file_id, :file_log => log)
367
+ end
368
+
369
+ def annotate_parents_helper(file, follow_copies = false)
370
+ path = file.path
371
+ if file.file_rev.nil?
372
+ parent_list = file.parents.map {|n| [n.path, n.file_rev]}
373
+ else
374
+ parent_list = file.file_log.parent_indices_for_index(file.file_rev)
375
+ parent_list.map! {|n| [path, n]}
376
+ end
377
+ if follow_copies
378
+ r = file.renamed?
379
+ pl[0] = [r[0], @repo.get_file(r[0]).revision(r[1])] if r
380
+ end
381
+ return parent_list.select {|p, n| n != NULL_REV}.
382
+ map {|p, n| annotate_get_file(p, n)}
383
+ end
384
+
385
+ def annotate(follow_copies = false, line_number = false)
386
+ base = (revision != link_rev) ? file(file_rev) : self
387
+
388
+ needed = {base => 1}
389
+ counters = {(base.path + base.file_id.to_s) => 1}
390
+ visit = [base]
391
+ files = [base.path]
392
+
393
+ while visit.any?
394
+ file = visit.shift
395
+ annotate_parents_helper(file).each do |p|
396
+ unless needed.include? p
397
+ needed[p] = 1
398
+ counters[p.path + p.file_id.to_s] = 1
399
+ visit << p
400
+ files << p.path unless files.include? p.path
401
+ end
402
+ end
403
+ end
404
+
405
+ visit = []
406
+ files.each do |f|
407
+ filenames = needed.keys.select {|k| k.path == f}.map {|n| [n.revision, n]}
408
+ visit += filenames
409
+ end
410
+
411
+ hist = {}
412
+ lastfile = ""
413
+ visit.sort.each do |rev, file_ann|
414
+ curr = annotate_decorate(file_ann.data, file_ann, line_number)
415
+ annotate_parents_helper(file_ann).each do |p|
416
+ next if p.file_id == NULL_ID
417
+ curr = annotate_diff_pair(hist[p.path + p.file_id.to_s], curr)
418
+ counters[p.path + p.file_id.to_s] -= 1
419
+ hist.delete(p.path + p.file_id.to_s) if counters[p.path + p.file_id.to_s] == 0
420
+ end
421
+ hist[file_ann.path+file_ann.file_id.to_s] = curr
422
+ lastfile = file_ann
423
+ end
424
+ returnarr = []
425
+ hist[lastfile.path+lastfile.file_id.to_s].inspect # force all lazy-loading to stoppeth
426
+ ret = hist[lastfile.path+lastfile.file_id.to_s][0].each_with_index do |obj, i|
427
+ returnarr << obj + [hist[lastfile.path+lastfile.file_id.to_s][1].split_newlines[i]]
428
+ end
429
+ # hist[lastfile.path+lastfile.file_id.to_s][0][i] + hist[lastfile.path+lastfile.file_id.to_s][1].split_newlines[i]
430
+ # end
431
+ ret = hist[lastfile.path+lastfile.file_id.to_s][0].zip(hist[lastfile.path+lastfile.file_id.to_s][1].split_newlines)
432
+ returnarr
433
+ end
434
+
435
+ def get_parents_helper(vertex, ancestor_cache, filelog_cache)
436
+ return ancestor_cache[vertex] if ancestor_cache[vertex]
437
+ file, node = vertex
438
+ filelog_cache[file] = @repo.get_file(file) unless filelog_cache[file]
439
+
440
+ filelog = filelog_cache[file]
441
+ parent_list = filelog.parents(node).select {|p| p != NULL_ID}.map {|p| [file, p]}
442
+
443
+ has_renamed = filelog.renamed?(node)
444
+
445
+ parent_list << has_renamed if has_renamed
446
+ ancestor_cache[vertex] = parent_list
447
+ parent_list
448
+ end
449
+
450
+ def ancestor(file_2)
451
+ ancestor_cache = {}
452
+ [self, file_2].each do |c|
453
+ if c.file_rev == NULL_REV || c.file_rev.nil?
454
+ parent_list = c.parents.map {|n| [n.path, n.file_node]}
455
+ ancestor_cache[[c.path, c.file_node]] = parent_list
456
+ end
457
+ end
458
+
459
+ filelog_cache = {repo_path => file_log, file_2.repo_path => file_2.file_log}
460
+ a, b = [path, file_node], [file_2.path, file_2.file_node]
461
+ parents_proc = proc {|vertex| get_parents_helper(vertex, ancestor_cache, filelog_cache)}
462
+
463
+ v = Graphs::AncestorCalculator.ancestors(a, b, parents_proc)
464
+ if v
465
+ file, node = v
466
+ return VersionedFile.new(@repo, file, :file_id => node, :file_log => filelog_cache[file])
467
+ end
468
+ return nil
469
+ end
470
+
471
+ end
472
+
473
+ ##
474
+ # This is a VersionedFile, except it's in the working directory, so its data
475
+ # is stored on disk in the actual file. Other than that, it's basically the
476
+ # same in its interface!
477
+ class VersionedWorkingFile < VersionedFile
478
+
479
+ ##
480
+ # Initializes a new working dir file - slightly different semantics here
481
+ def initialize(repo, path, opts={})
482
+ @repo, @path = repo, path
483
+ @change_id = nil
484
+ @file_rev, @file_node = nil, nil
485
+
486
+ @file_log = opts[:file_log] if opts[:file_log]
487
+ @changeset = opts[:working_changeset]
488
+ end
489
+
490
+ ##
491
+ # Gets the working directory changeset
492
+ def changeset
493
+ @changeset ||= WorkingDirectoryChangeset.new(@repo)
494
+ end
495
+
496
+ ##
497
+ # Dunno why this is here
498
+ def repo_path
499
+ @repo.dirstate.copy_map[@path] || @path
500
+ end
501
+
502
+ ##
503
+ # Gets the file log?
504
+ #
505
+ # @todo add a @file_log ||= in the front?
506
+ def file_log
507
+ @repo.file_log repo_path
508
+ end
509
+
510
+ ##
511
+ # String representation
512
+ def to_s
513
+ "#{path}@#{@changeset}"
514
+ end
515
+
516
+ ##
517
+ # Returns the file at a different revision
518
+ def file(file_id)
519
+ VersionedFile.new(@repo, repo_path, :file_id => file_id, :file_log => file_log)
520
+ end
521
+
522
+ ##
523
+ # Get what revision this is
524
+ def revision
525
+ return @changeset.revision if @changeset
526
+ file_log[@file_rev].link_rev
527
+ end
528
+
529
+ ##
530
+ # Get the contents of this file
531
+ def data
532
+ data = @repo.working_read(@path)
533
+ data
534
+ end
535
+
536
+ ##
537
+ # Has this file been renamed? If so give some good info. Returns
538
+ # the new location and the flags ('x', 'l', '')
539
+ #
540
+ # @return [Array<String, String>] [new_path, flags]
541
+ def renamed?
542
+ rp = repo_path
543
+ return nil if rp == @path
544
+ [rp, (self.changeset.parents[0].manifest_entry[rp] || NULL_ID)]
545
+ end
546
+
547
+ ##
548
+ # The working directory's parents are the heads, so get this file in
549
+ # the previous revision.
550
+ def parents
551
+ p = @path
552
+ rp = repo_path
553
+ pcl = @changeset.parents
554
+ fl = file_log
555
+ pl = [[rp, pcl[0].manifest_entry[rp] || NULL_ID, fl]]
556
+ if pcl.size > 1
557
+ if rp != p
558
+ fl = nil
559
+ end
560
+ pl << [p, pcl[1].manifest_entry[p] || NULL_ID, fl]
561
+ end
562
+ pl.select {|_, n, __| n != NULL_ID}.map do |parent, n, l|
563
+ VersionedFile.new(@repo, parent, :file_id => n, :file_log => l)
564
+ end
565
+ end
566
+
567
+ ##
568
+ # Returns the current size of the file
569
+ #
570
+ def size
571
+ File.stat(@repo.join(@path)).size
572
+ end
573
+
574
+ ##
575
+ # Returns the date that this file was last modified.
576
+ def date
577
+ t, tz = changeset.date
578
+ begin
579
+ return [FileUtils.lstat(@repo.join(@path)).mtime, tz]
580
+ rescue Errno::ENOENT
581
+ return [t, tz]
582
+ end
583
+ end
584
+
585
+ ##
586
+ # Compares to the given text. Overridden because this file is
587
+ # stored on disk in the actual working directory.
588
+ #
589
+ # @param [String] text the text to compare to
590
+ # @return [Boolean] true if the two are different
591
+ def cmp(text)
592
+ @repo.working_read(@path) != text
593
+ end
594
+
595
+ end
596
+ end
597
+ end