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
@@ -1,319 +0,0 @@
1
- module Amp
2
- module Repositories
3
- ##
4
- # = TagManager
5
- # This module handles all tag-related (but not branch tag) functionality
6
- # of the repository.
7
- module TagManager
8
- include Amp::RevlogSupport::Node
9
-
10
- TAG_FORBIDDEN_LETTERS = ":\r\n"
11
- ##
12
- # Returns a list of all the tags as a hash, mapping each tag to the tip-most
13
- # changeset it applies to.
14
- #
15
- # @return [Hash] a hash, sorted by revision index (i.e. its order in the commit
16
- # history), with the keys:
17
- # :revision => the revision index of the changeset,
18
- # :tag => the name of the tag,
19
- # :node => the node-id of the changeset
20
- def tag_list
21
- list = []
22
- tags.each do |tag, node|
23
- begin
24
- r = changelog.revision(node)
25
- rescue
26
- r = -2
27
- end
28
- list << {:revision => r, :tag => tag, :node => node}
29
- end
30
- list.sort {|i1, i2| i1[:revision] <=> i2[:revision] }
31
- end
32
-
33
- ##
34
- # Returns the tag-type of the given tag. This could be "local", which means it is
35
- # not shared among repositories.
36
- #
37
- # @param [String] tag_name the name of the tag to lookup, such as "tip"
38
- # @return [String] the type of the requested tag, such as "local".
39
- def tag_type(tag_name)
40
- tags #load the tags
41
- @tags_type_cache[tag_name]
42
- end
43
-
44
- ##
45
- # Returns the tags for a given revision (by ID).
46
- #
47
- # @param [String] node the node-ID, in binary form.
48
- # @return [Array<String>] a list of tags for the given node.
49
- def tags_for_node(node)
50
- return (@tags_for_node_cache[node] || []) if @tags_for_node_cache
51
- @tags_for_node_cache = {}
52
- tags.each do |tag, tag_node|
53
- @tags_for_node_cache[tag_node] ||= [] # make sure there's an array
54
- @tags_for_node_cache[tag_node] << tag # add the tag to it
55
- end
56
- @tags_for_node_cache[node] || []
57
- end
58
-
59
- ##
60
- # Invalidates the tag cache. Removes all ivars relating to tags.
61
- def invalidate_tag_cache!
62
- @tags_for_node_cache = nil
63
- @tags_cache = nil
64
- @tags_type_cache = nil
65
- end
66
-
67
- ##
68
- # Loads all of the tags from the tag-cache file stored as .hgtags. Returns
69
- # a hash, mapping tag names to node-IDs.
70
- #
71
- # @return [Hash] a hash mapping tags to node-IDs.
72
- def tags
73
- return @tags_cache if @tags_cache
74
-
75
- global_tags, tag_types = {}, {}
76
-
77
- file = nil
78
- # For each current .hgtags file in our history (including multi-heads), read in
79
- # the tags
80
- hg_tags_nodes.each do |rev, node, file_node|
81
- # get the file
82
- f = (f && f.file(file_node)) || self.versioned_file(".hgtags", :file_id => file_node.file_node)
83
- # read the tags, as global, because they're versioned.
84
- read_tags(f.data.split("\n"), f, "global", global_tags, tag_types)
85
- end
86
-
87
- # Now do locally stored tags, that aren't committed/versioned
88
- begin
89
- # get the local file, stored in .hg/
90
- data = @hg_opener.read("localtags")
91
- # Read the tags as local, because they are not versioned
92
- read_tags(data.split_newlines,"local_tags","local",global_tags, tag_types)
93
- rescue Errno::ENOENT
94
- # do nothing. most people don't have this file.
95
- end
96
- # Save our tags for use later. Use ivars.
97
- @tags_cache = {}
98
- @tags_type_cache = {}
99
- # Go through the global tags to store them in the cache
100
- global_tags.each do |k, nh|
101
- # the node ID is the first part of the stored data
102
- n = nh.first
103
-
104
- # update the cache
105
- @tags_cache[k] = n unless n == NULL_ID
106
- @tags_type_cache[k] = tag_types[k]
107
- end
108
-
109
- # tip = special tag
110
- @tags_cache["tip"] = self.changelog.tip
111
-
112
- # return our tags
113
- @tags_cache
114
- end
115
-
116
- ##
117
- # Adds the given tag to a given changeset, and commits to preserve it.
118
- #
119
- # @param [String, Array] names a list of tags (or just 1 tag) to apply to
120
- # the changeset
121
- # @param [String, Integer] node the node to apply the tag to
122
- # @param [Hash] opts the opts for tagging
123
- # @option [String] opts message ("added tag _tag_ to changeset _node_")
124
- # the commit message to use.
125
- # @option [Boolean] opts local (false) is the tag a local one? I.E., will it be
126
- # shared across repos?
127
- # @option [String] opts user ($USER) the username to apply for the commit
128
- # @option [Time] opts time (Time.now) what should the commit-time be marked as?
129
- # @option [String] opts parent (nil) The parent revision of the one we
130
- # are tagging. or something.
131
- # @option [Hash] opts extra ({}) the extra data to apply for the commit.
132
- def apply_tag(names, node, opts={})
133
- use_dirstate = opts[:parent].nil?
134
- all_letters = names.kind_of?(Array) ? names.join : names
135
- (TAG_FORBIDDEN_LETTERS.size-1).downto 0 do |i|
136
- if all_letters.include? TAG_FORBIDDEN_LETTERS[i, 1]
137
- raise abort("#{TAG_FORBIDDEN_LETTERS[i,1]} not allowed in a tag name!")
138
- end
139
- end
140
-
141
- prev_tags = ""
142
- # If it's a local tag, we just write the file and bounce. mad easy yo.
143
- if opts[:local]
144
- @hg_opener.open("localtags","r+") do |fp|
145
- prev_tags = fp.read
146
- write_tags(fp,names, nil, prev_tags)
147
- end
148
- return
149
- end
150
-
151
- # ok, it's a global tag. now we have to handle versioning and shit.
152
- if use_dirstate
153
- prev_tags = working_read(".hgtags") rescue ""
154
- file = @file_opener.open(".hgtags","a")
155
- else
156
- prev_tags = versioned_file(".hgtags", :change_id => parent).data
157
- file = @file_opener.open(".hgtags","w")
158
-
159
- file.write prev_tags if prev_tags && prev_tags.any?
160
- end
161
-
162
- write_tags(file, node, names, prev_tags)
163
- file.close
164
- if use_dirstate && dirstate[".hgtags"].status == :untracked
165
- self.add([".hgtags"])
166
- end
167
-
168
- tag_node = commit :files => [".hgtags"],
169
- :message => opts[:message],
170
- :user => opts[:user],
171
- :date => opts[:date],
172
- :p1 => opts[:parent],
173
- :extra => opts[:extra]
174
-
175
- tag_node
176
- end
177
-
178
- private
179
-
180
- ##
181
- # Goes through all the heads in the repository, getting the state of the .hgtags
182
- # file at each head. We then return a list, with each entry mapping revision index
183
- # and node ID to the .hgtags file at that head. If two different heads have the same
184
- # .hgtags file, only 1 is returned with it.
185
- #
186
- # @return [[Fixnum, String, VersionedFile]] each head with a different .hgtags file
187
- # at that point. That way we have the most recent copy of .hgtags, even if the file
188
- # differs on different heads.
189
- def hg_tags_nodes
190
- heads = self.heads.reverse
191
- last = {}
192
- return_list = []
193
- heads.each do |node|
194
- changeset = self[node]
195
- rev = changeset.revision
196
- begin
197
- file_node = changeset.get_file(".hgtags")
198
- rescue
199
- next
200
- end
201
- return_list << [rev, node, file_node]
202
- return_list[last[file_node]] = nil if last[file_node] # replace old head
203
-
204
- last[file_node] = return_list.size - 1
205
- end
206
- return return_list.reject {|item| item.nil?}
207
- end
208
-
209
- ##
210
- # Writes the tags to the given stream. This method must be aware of previously
211
- # written tags. Also, any new tags must state what the node to use for writing is.
212
- #
213
- # @param [IO, #write] file the output stream to write to. Could be a file, or any IO.
214
- # @param [String] node a binary node ID for any newly-added tags
215
- # @param [Array] names A list of all the tag names to write
216
- # @param [Hash] prev_tags the previously written string (or something)
217
- def write_tags(file, node, names, prev_tags)
218
- file.seek(0, IO::SEEK_END)
219
- if prev_tags && prev_tags.any? && prev_tags[-1,1] != "\n"
220
- file.write "\n"
221
- end
222
- names.each do |name|
223
- if @tags_type_cache && @tags_type_cache[name]
224
- old = @tags_cache[name] || NULL_ID
225
- file.write("#{old.hexlify} #{name}\n")
226
- end
227
- file.write("#{node.hexlify} #{name}\n")
228
- end
229
- end
230
-
231
- ##
232
- # Reads in an .hgtags file and parses it, while respecting global tags.
233
- # This is where things get kinda messy, because otherwise we'd just be parsing
234
- # a simple text file. Anyway, global_tags are tags like "tip" -> the current tip
235
- # - they're programmatically assigned tags.
236
- #
237
- # @param [Array<String>] lines the file, split into lines
238
- # @param [String] fn the file that we are parsing, only for debug purposes
239
- # @param [String] tag_type what kind of tag are we looking at? usually "local"
240
- # or "global" or nothing. For example, a local-only tag isn't committed - these
241
- # need to be treated differently.
242
- # @param [Hash] global_tags maps nodes to global tags, such as "tip".
243
- # @param [Hash] tag_types maps nodes to what type of tag they are
244
- # @return [Hash] the list of tags we have read in.
245
- #
246
- # @todo encodings, handle local encodings
247
- def read_tags(lines, fn, tag_type, global_tags, tag_types)
248
- # This is our tag list we'll be building.
249
- file_tags = {}
250
-
251
- # Each line is of the format:
252
- # [char * 40, node ID] [tag]
253
- # 0123456789012345678901234567890123456789 crazymerge
254
- lines.each_with_index do |line, count|
255
- # skip if we have no text to parse
256
- next if line.nil? || line.empty?
257
-
258
- # split once, so we could theoretically have spaces in tag names
259
- s = line.split(" ", 2)
260
- # make sure we parsed the tag entry alright
261
- if s.size != 2
262
- UI::warn "Can't parse entry, filename #{fn} line #{count}"
263
- next
264
- end
265
-
266
- # Node comes first, tag comes second
267
- node, tag = s
268
- tag.strip! #TODO: encodings, handle local encodings
269
-
270
- # Convert to binary so we can look it up in our repo
271
- bin_node = node.unhexlify
272
-
273
- # Is it in our repo? if not, skip to the next tag.
274
- unless self.changelog.node_map[bin_node]
275
- UI::warn "Tag #{key} refers to unknown node"
276
- next
277
- end
278
-
279
- # heads is a list of the nodes that have this same tag
280
- heads = []
281
- # have we already seen this tag?
282
- if file_tags[tag]
283
- # pull out the old data
284
- n, heads = file_tags[tag]
285
- # add our new node to the list for this tag
286
- heads << n
287
- end
288
- # update our tag list
289
- file_tags[tag] = [bin_node, heads]
290
- end
291
-
292
- # For each tag that we have...
293
- file_tags.each do |k, nh|
294
- # Is this a reserved, global tag? Or, just one that's been used already?
295
- # like "tip"? if not, we're ok
296
- unless global_tags[k]
297
- # update global_tags with our new tag
298
- global_tags[k] = nh
299
- # set the tag_types hash as well
300
- tag_types[k] = tag_type
301
- next
302
- end
303
- # we prefer the global tag if:
304
- # it supercedes us OR
305
- # mutual supercedes and it has a higher rank
306
- # otherwise we win because we're tip-most
307
- an, ah = nh
308
- bn, bh = global_tags[k]
309
- if [bn != an, bh[an], (!ah[bn] || bh.size > ah.size)].all?
310
- an = bn
311
- end
312
- ah += bh.select {|n| !ah[n]}
313
- global_tags[k] = an, ah
314
- tag_types[k] = tag_type
315
- end
316
- end
317
- end
318
- end
319
- end
@@ -1,532 +0,0 @@
1
- module Amp
2
- module Repositories
3
-
4
- ##
5
- # This module contains all the code that makes a repository able to
6
- # update its working directory.
7
- module Updatable
8
- include Amp::RevlogSupport::Node
9
-
10
- ##
11
- # Updates the repository to the given node. One of the major operations on a repository.
12
- # This will update the working directory to the given node.
13
- #
14
- # @todo add lock
15
- # @param [String, Integer] node the revision to which we are updating the repository. Can
16
- # be either nil, a node ID, or an integer. If it is nil, it will update
17
- # to the latest revision.
18
- # @param [Boolean] branch_merge whether to merge between branches
19
- # @param [Boolean] force whether to force branch merging or file overwriting
20
- # @param [Proc, #call] partial a function to filter file lists (dirstate not updated if this
21
- # is passed)
22
- # @return [Array<Integer>] a set of statistics about the update. In the form:
23
- # [updated, merged, removed, unresolved] where each entry is the # of files in that category.
24
- def update(node=nil, branch_merge=false, force=false, partial=nil)
25
- # debugger
26
- self.status(:node_1 => self.dirstate.parents.first, :node_2 => nil)
27
- working_changeset = self[nil]
28
- # tip of current branch
29
- node ||= branch_tags[working_changeset.branch]
30
- node = self.lookup("tip") if node.nil? && working_changeset.branch == "default"
31
- if node.nil?
32
- raise abort("branch #{working_changeset.branch} not found")
33
- end
34
-
35
- overwrite = force && !branch_merge
36
- parent_list = working_changeset.parents
37
- parent1, parent2 = parent_list.first, self[node]
38
- parent_ancestor = parent1.ancestor(parent2)
39
-
40
- fp1, fp2, xp1, xp2 = parent1.node, parent2.node, parent1.to_s, parent2.to_s
41
- fast_forward = false
42
-
43
- ## In this section, we make sure that we can actually do an update.
44
- ## No use starting an udpate if we can't finish!
45
-
46
- if !overwrite && parent_list.size > 1
47
- raise abort("outstanding uncommitted merges")
48
- end
49
-
50
- if branch_merge
51
- if parent_ancestor == parent2
52
- raise abort("can't merge with ancestor")
53
- elsif parent_ancestor == parent1
54
- if parent1.branch != parent2.branch
55
- fast_forward = true
56
- else
57
- raise abort("nothing to merge (use 'hg update' or check"+
58
- " 'hg heads')")
59
- end
60
- end
61
- if !force && (working_changeset.files.any? || working_changeset.deleted.any?)
62
- raise abort("oustanding uncommitted changes")
63
- end
64
- elsif !overwrite
65
- if parent_ancestor == parent1 || parent_ancestor == parent2
66
- # do nothing
67
- elsif parent1.branch == parent2.branch
68
- if working_changeset.files.any? || working_changeset.deleted.any?
69
- raise abort("crosses branches (use 'hg merge' or "+
70
- "'hg update -C' to discard changes)")
71
- end
72
- raise abort("crosses branches (use 'hg merge' or 'hg update -C')")
73
- elsif working_changeset.files.any? || working_changeset.deleted.any?
74
- raise abort("crosses named branches (use 'hg update -C'"+
75
- " to discard changes)")
76
- else
77
- overwrite = true
78
- end
79
- end
80
-
81
- ## Alright, now let's figure out exactly what we have to do to make this update.
82
- ## Shall we?
83
-
84
- actions = []
85
- check_unknown(working_changeset, parent2) if force
86
- check_collision(parent2) if false # case-sensitive file-system? seriously?
87
-
88
- actions += forget_removed(working_changeset, parent2, branch_merge)
89
- actions += manifest_merge(working_changeset, parent2, parent_ancestor,
90
- overwrite, partial)
91
-
92
- ## Apply phase - apply the changes we just generated.
93
- unless branch_merge # just jump to the new revision
94
- fp1, fp2, xp1, xp2 = fp2, NULL_ID, xp2, ''
95
- end
96
- unless partial
97
- run_hook :preupdate, :throw => true, :parent1 => xp1, :parent2 => xp2
98
- end
99
-
100
- stats = apply_updates(actions, working_changeset, parent2)
101
-
102
- unless partial
103
- record_updates(actions, branch_merge)
104
- dirstate.parents = [fp1, fp2]
105
- if !branch_merge && !fast_forward
106
- dirstate.branch = parent2.branch
107
- end
108
- run_hook :update, :parent1 => xp1, :parent2 => xp2, :error => stats[3]
109
- dirstate.write
110
- end
111
-
112
- return stats
113
-
114
- end
115
-
116
- ##
117
- # Merge two heads
118
- def merge(node, force=false)
119
- update node, true, force, false
120
- end
121
-
122
- ##
123
- # Updates the repository to the given node, clobbering (removing) changes
124
- # along the way. This has the effect of turning the working directory into
125
- # a pristine copy of the requested changeset. Really just a nice way of
126
- # skipping some arguments for the caller.
127
- #
128
- # @param [String] node the requested node
129
- def clean(node)
130
- update node, false, true, nil
131
- end
132
-
133
- private
134
-
135
- ##
136
- # This method will make sure that there are no differences between
137
- # untracked files in the working directory, and tracked files in
138
- # the new changeset.
139
- #
140
- # @param [WorkingDirectoryChangeset] working_changeset the current working directory
141
- # @param [Changeset] target_changeset the destination changeset (that we're updating to)
142
- # @raise [AbortError] if an untracked file in the working directory is different from
143
- # a tracked file in the target changeset, this abort error will be raised.
144
- def check_unknown(working_changeset, target_changeset)
145
- working_changeset.unknown.each do |file|
146
- if target_changeset[file] && target_changeset[file].cmp(working_changeset[file].data())
147
- raise abort("Untracked file in the working directory differs from "+
148
- "a tracked file in the requested revision: #{file}")
149
- end
150
- end
151
- end
152
-
153
- ##
154
- # This method will check if the target changeset will cause name collisions
155
- # when filenames are changed to all lower-case. This is important because
156
- # in the store, the file-logs are all changed to lower-case.
157
- #
158
- # @param [Changeset] target_changeset the destination changeset (that we're updating to)
159
- # @raise [AbortError] If two files have the same lower-case name, in 1 changeset,
160
- # this error will be thrown.
161
- def check_collision(target_changeset)
162
- folded_names = {}
163
- target_changeset.each do |file|
164
- folded = file.downcase
165
- if folded_names[folded]
166
- raise abort("Case-folding name collision between #{folded_names[folded]} and #{file}.")
167
- end
168
- folded_names[folded] = file
169
- end
170
- end
171
-
172
- ##
173
- # Forget removed files (docs ripped from mercurial)
174
- #
175
- # If we're jumping between revisions (as opposed to merging), and if
176
- # neither the working directory nor the target rev has the file,
177
- # then we need to remove it from the dirstate, to prevent the
178
- # dirstate from listing the file when it is no longer in the
179
- # manifest.
180
- #
181
- # If we're merging, and the other revision has removed a file
182
- # that is not present in the working directory, we need to mark it
183
- # as removed.
184
- #
185
- # @param [WorkingDirectoryChangeset] working_changeset the current working directory
186
- # @param [Changeset] target_changeset the destination changeset (that we're updating to)
187
- # @param [Boolean] branch_merge whether or not to delete working files
188
- # @return [[String, Symbol]] a list of actions that should be taken to complete
189
- # a successful transition from working_changeset to target_changeset.
190
- def forget_removed(working_changeset, target_changeset, branch_merge)
191
- action_list = []
192
- action = branch_merge ? :remove : :forget
193
- working_changeset.deleted.each do |file|
194
- action_list << [file, action] unless target_changeset[file]
195
- end
196
-
197
- unless branch_merge
198
- working_changeset.removed.each do |file|
199
- action_list << [file, :forget] unless target_changeset[file]
200
- end
201
- end
202
-
203
- action_list
204
- end
205
-
206
- ##
207
- # Merge the local working changeset (local), and the target changeset (remote),
208
- # using the common ancestor (ancestor). Generates a merge action list to update
209
- # the manifest.
210
- #
211
- # @param [Changeset] local The working-directory changeset we're merging from
212
- # @param [Changeset] remote The target changeset we need to merge to
213
- # @param [Changeset] ancestor A common ancestor between the 2 parents
214
- # @param [Boolean] overwrite Can we delete working files?
215
- # @param [Proc] partial a function to filter file lists
216
- # @return [[String, Symbol]] A list of actions that should be taken to complete
217
- # a successful transition from local to remote.
218
- def manifest_merge(local, remote, ancestor, overwrite, partial)
219
- UI::status("resolving manifests")
220
- UI::debug(" overwrite #{overwrite} partial #{partial}")
221
- UI::debug(" ancestor #{ancestor} local #{local} remote #{remote}")
222
-
223
- local_manifest = local.manifest
224
- remote_manifest = remote.manifest
225
- ancestor_manifest = ancestor.manifest
226
- backwards = (ancestor == remote)
227
- action_list = []
228
- copy, copied, diverge = {}, {}, {}
229
-
230
- flag_merge = lambda do |file_local, file_remote, file_ancestor|
231
- file_remote = file_ancestor = file_local unless file_remote
232
-
233
- a = ancestor_manifest.flags[file_ancestor]
234
- m = local_manifest.flags[file_local]
235
- n = remote_manifest.flags[file_remote]
236
-
237
- return m if m == n # flags are identical, we're fine
238
-
239
- if m.any? && n.any?
240
- unless a.any? # i'm so confused, ask the user what the flag should be!
241
- r = UI.ask("conflicting flags for #{file_local} (n)one, e(x)ec, or "+
242
- "sym(l)ink?")
243
- return (r != "n") ? r : ''
244
- end
245
- return n if m == a # changed from m to n
246
- return m # changed from n to m
247
- end
248
-
249
- return m if m.any? && m != a # changed from a to m
250
- return n if n.any? && n != a # changed from a to n
251
- return '' #no more flag
252
- end
253
-
254
- act = lambda do |message, move, file, *args|
255
- UI::debug(" #{file}: #{message} -> #{move}")
256
- action_list << [file, move] + args
257
- end
258
-
259
- if ancestor && !(backwards || overwrite)
260
- if @config["merge", "followcopies", Boolean, true]
261
- dirs = @config["merge", "followdirs", Boolean, false] # don't track directory renames
262
- copy, diverge = Amp::Graphs::CopyCalculator.find_copies(self, local, remote, ancestor, dirs)
263
- end
264
- copied = Hash.with_keys(copy.values)
265
- diverge.each do |of, fl|
266
- act["divergent renames", :divergent_rename, of, fl]
267
- end
268
- end
269
-
270
- # Compare manifests
271
- local_manifest.each do |file, node|
272
- next if partial && !partial[file]
273
-
274
- if remote_manifest[file]
275
- rflags = (overwrite || backwards) ? remote_manifest.flags[file] : flag_merge[file,nil,nil]
276
- # Are files different?
277
- if node != remote_manifest[file]
278
- anc_node = ancestor_manifest[file] || NULL_ID
279
-
280
- if overwrite # Can we kill the file?
281
- act["clobbering", :get, file, rflags]
282
- elsif backwards # Or are we going back in time and cleaning?
283
- if !(node[20..-1]) || !(remote[file].cmp(local[file].data))
284
- act["reverting", :get, file, rflags]
285
- end
286
- elsif node != anc_node && remote_manifest[file] != anc_node
287
- # are both nodes different from the ancestor?
288
- act["versions differ", :merge, file, file, file, rflags, false]
289
- elsif remote_manifest[file] != anc_node
290
- # is remote's version newer?
291
- act["remote is newer", :get, file, rflags]
292
- elsif local_manifest.flags[file] != rflags
293
- # local is newer, not overwrite, check mode bits (wtf does this mean)
294
- act["update permissions", :exec, file, rflags]
295
- end
296
- elsif local_manifest.flags[file] != rflags
297
- act["update permissions", :exec, file, rflags]
298
- end
299
- elsif copied[file]
300
- next
301
- elsif copy[file]
302
- file2 = copy[file]
303
- if !remote_manifest[file2] #directory rename
304
- act["remote renamed directory to #{file2}", :d, file, nil, file2, local_manifest.flags[file]]
305
- elsif local_manifest[file2] # case 2 A,B/B/B
306
- act["local copied to #{file2}", :merge, file, file2, file,
307
- flag_merge[file, file2, file2], false]
308
- else # case 4,21 A/B/B
309
- act["local moved to #{file2}", :merge, file, file2, file,
310
- flag_merge[file, file2, file2], false]
311
- end
312
- elsif ancestor_manifest[file]
313
- if node != ancestor_manifest[file] && !overwrite
314
- if UI.ask("local changed #{file} which remote deleted\n" +
315
- "use (c)hanged version or (d)elete?") == "d"
316
- act["prompt delete", :remove, file]
317
- else
318
- act["prompt keep", :add, file]
319
- end
320
- else
321
- act["other deleted", :remove, file]
322
- end
323
- else
324
- if (overwrite && node[20..-1] != "u") || (backwards && node[20..-1].empty?)
325
- act["remote deleted", :remove, file]
326
- end
327
- end
328
- end
329
-
330
- remote_manifest.each do |file, node|
331
- next if partial && !(partial[file])
332
- next if local_manifest[file]
333
- next if copied[file]
334
- if copy[file]
335
- file2 = copy[file]
336
- if !(local_manifest[file2])
337
- act["local renamed directory to #{file2}", :directory, nil, file,
338
- file2, remote_manifest.flags[file]]
339
- elsif remote_manifest[file2]
340
- act["remote copied to #{file}", :merge, file2, file, file,
341
- flag_merge[file2, file, file2], false]
342
- else
343
- act["remote moved to #{file}", :merge, file2, file, file,
344
- flag_merge[file2, file, file2], true]
345
- end
346
- elsif ancestor_manifest[file]
347
- if overwrite || backwards
348
- act["recreating", :get, file, remote_manifest.flags[file]]
349
- elsif node != ancestor_manifest[file]
350
- if UI.ask("remote changed #{file} which local deleted\n" +
351
- "use (c)hanged version or leave (d)eleted?") == "c"
352
- act["prompt recreating", :get, file, remote_manifest.flags[file]]
353
- end
354
- end
355
- else
356
- act["remote created", :get, file, remote_manifest.flags[file]]
357
- end
358
- end
359
-
360
- action_list
361
- end
362
-
363
- def action_cmp(action1, action2)
364
- move1 = action1[1] # action out of the tuple
365
- move2 = action2[1] # action out of the tuple
366
-
367
- return action1 <=> action2 if move1 == move2
368
- return -1 if move1 == :remove
369
- return 1 if move2 == :remove
370
- return action1 <=> action2
371
- end
372
-
373
- ##
374
- # Apply the merge action list to the working directory, in order to migrate from
375
- # working_changeset to target_changeset.
376
- #
377
- # @todo add path auditor
378
- # @param [Array<Array>] actions list of actions to take to migrate from {working_changeset} to
379
- # {target_changeset}.
380
- # @param [WorkingDirectoryChangeset] working_changeset the current changeset in the repository
381
- # @param [Changeset] target_changeset the changeset we are updating the working directory to.
382
- # @return [Hash] Statistics about the update. Keys are:
383
- # :updated => files that were changed
384
- # :merged => files that were merged
385
- # :removed => files that were removed
386
- # :unresolved => files that had conflicts when merging that we couldn't fix
387
- def apply_updates(actions, working_changeset, target_changeset)
388
- updated, merged, removed, unresolved = [], [], [], []
389
- merge_state.reset(working_changeset.parents.first.node)
390
-
391
- moves = []
392
- actions.sort! {|a1, a2| action_cmp a1, a2 }
393
-
394
- # prescan for merges in the list of actions.
395
- actions.each do |a|
396
- file, action = a[0], a[1]
397
- if action == :merge # ah ha! a merge.
398
- file2, filename_dest, flags, move = a[2..-1] # grab some info about it
399
- UI.debug("preserving #{file} for resolve of #{filename_dest}")
400
- vf_local = working_changeset[file] # look up our changesets we'll need later
401
- vf_other = target_changeset[file2]
402
- vf_base = vf_local.ancestor(vf_other) || versioned_file(file, :file_id => NULL_REV)
403
- merge_state.add(vf_local, vf_other, vf_base, filename_dest, flags) # track this merge!
404
-
405
- moves << file if file != filename_dest && move
406
- end
407
- end
408
-
409
- # If we're moving any files, we can remove renamed ones now
410
- moves.each do |file|
411
- if File.amp_lexist?(working_join(file))
412
- UI.debug("removing #{file}")
413
- File.unlink(working_join(file))
414
- end
415
- end
416
-
417
- # TODO: add path auditor
418
-
419
- actions.each do |action|
420
- file, choice = action[0], action[1]
421
- next if file && file[0,1] == "/"
422
- case choice
423
- when :remove
424
- UI.note "removing #{file}"
425
- File.unlink(working_join(file))
426
- removed << file
427
- when :merge
428
- file2, file_dest, flags, move = action[2..-1]
429
- result = merge_state.resolve(file_dest, working_changeset, target_changeset)
430
-
431
- unresolved << file if result
432
- updated << file if result.nil?
433
- merged << file if result == false
434
-
435
- File.amp_set_executable(working_join(file_dest), flags && flags.include?('x'))
436
- if (file != file_dest && move && File.amp_lexist?(working_join(file)))
437
- UI.debug("removing #{file}")
438
- File.unlink(working_join(file))
439
- end
440
- when :get
441
- flags = action[2]
442
- UI.note("getting #{file}")
443
- data = target_changeset.get_file(file).data
444
- working_write(file, data, flags)
445
- updated << file
446
- when :directory
447
- file2, file_dest, flags = action[2..-1]
448
- if file && file.any?
449
- UI.note("moving #{file} to #{file_dest}")
450
- File.move(file, file_dest)
451
- end
452
- if file2 && file2.any?
453
- UI.note("getting #{file2} to #{file_dest}")
454
- data = target_changeset.get_file(file2).data
455
- working_write(file_dest, data, flags)
456
- end
457
- updated << file
458
- when :divergent_rename
459
- filelist = action[2]
460
- UI.warn("detected divergent renames of #{f} to:")
461
- filelist.each {|fn| UI.warn fn }
462
- when :exec
463
- flags = action[2]
464
- File.amp_set_executable(working_join(file), flags.include?('x'))
465
- end
466
-
467
- end
468
-
469
- hash = {:updated => updated ,
470
- :merged => merged ,
471
- :removed => removed ,
472
- :unresolved => unresolved }
473
-
474
- class << hash
475
- def success?; self[:unresolved].empty?; end
476
- end
477
-
478
- hash
479
- end
480
-
481
- ##
482
- # Records all the updates being made while merging to the new working directory.
483
- # It records them by writing to the dirstate.
484
- #
485
- # @param [Array<Array>] actions a list of actions to take
486
- # @param [Boolean] branch_merge is this a branch merge?
487
- def record_updates(actions, branch_merge)
488
- actions.each do |action|
489
- file, choice = action[0], action[1]
490
- case choice
491
- when :remove
492
- branch_merge and dirstate.remove(file) or dirstate.forget(file)
493
- when :add
494
- dirstate.add file unless branch_merge
495
- when :forget
496
- dirstate.forget file
497
- when :get
498
- branch_merge and dirstate.dirty(file) or dirstate.normal(file)
499
- when :merge
500
- file2, file_dest, flag, move = action[2..-1]
501
- if branch_merge
502
- dirstate.merge(file_dest)
503
- if file != file2 #copy/rename
504
- dirstate.remove file if move
505
- dirstate.copy(file, file_dest) if file != file_dest
506
- dirstate.copy(file2, file_dest) if file == file_dest
507
- end
508
- else
509
- dirstate.maybe_dirty(file_dest)
510
- dirstate.forget(file) if move
511
- end
512
- when :directory
513
- file2, file_dest, flag = action[2..-1]
514
- next if !file2 && !(dirstate.include?(file))
515
-
516
- if branch_merge
517
- dirstate.add file_dest
518
- if file && file.any?
519
- dirstate.remove file
520
- dirstate.copy file, file_dest
521
- end
522
- dirstate.copy file2, file_dest if file2 && file2.any?
523
- else
524
- dirstate.normal file_dest
525
- dirstate.forget file if file && file.any?
526
- end
527
- end
528
- end
529
- end
530
- end
531
- end
532
- end