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,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