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,768 @@
1
+ module Amp
2
+ module Mercurial
3
+
4
+ ##
5
+ # A Changeset is a simple way of accessing the repository within a certain
6
+ # revision. For example, if the user specifies revision # 36, or revision
7
+ # 3adf21, then we can look those up, and work within the repository at the
8
+ # moment of that revision.
9
+ class Changeset < Amp::Repositories::AbstractChangeset
10
+ include Mercurial::RevlogSupport::Node
11
+
12
+ attr_reader :repo
13
+ alias_method :repository, :repo
14
+
15
+ ##
16
+ # Initializes a new changeset. We need a repository to work with, and also
17
+ # a change_id. this change_id could be a revision index or a node_id for
18
+ # the revision.
19
+ #
20
+ # @param [Repository] repo a repository to work with.
21
+ # @param [Integer, String] change_id an ID or index to lookup to find this
22
+ # changeset.
23
+ #
24
+ def initialize(repo, change_id='')
25
+ change_id = '.' if change_id == ''
26
+ @repo = repo
27
+ if change_id.kind_of? Integer
28
+ @revision = change_id
29
+ @node_id = @repo.changelog.node_id_for_index change_id
30
+ else
31
+ @node_id = @repo.lookup change_id
32
+ @revision = @repo.changelog.rev @node_id
33
+ end
34
+ @parents = nil
35
+ end
36
+
37
+ ##
38
+ # Converts the revision to a number
39
+ def to_i; @revision; end
40
+
41
+ ##
42
+ # Converts the revision to an easy-to-digest string
43
+ def to_s(opts = {})
44
+ if opts[:template]
45
+ to_templated_s(opts)
46
+ else
47
+ @node_id[0..5].hexlify
48
+ end
49
+ end
50
+
51
+ ##
52
+ #
53
+ def to_templated_s(opts={})
54
+
55
+ change_node = node
56
+ revision = self.revision
57
+ log = @repo.changelog
58
+ changes = log.read change_node
59
+ username = changes[1]
60
+ date = Time.at changes[2].first
61
+ files = changes[3]
62
+ description = changes[4]
63
+ extra = changes[5]
64
+ branch = extra["branch"]
65
+ cs_tags = tags
66
+ type = opts[:template_type] || 'log'
67
+
68
+ added = opts[:added] || []
69
+ removed = opts[:removed] || []
70
+ updated = opts[:updated] || []
71
+ config = opts
72
+
73
+ parents = useful_parents log, revision
74
+ parents.map! {|p| [p, log.node(p)[0..5].hexlify] }
75
+
76
+ p1 = useful_parents(log, revision)[0]
77
+ p2 = useful_parents(log, revision)[1]
78
+
79
+ return "" if opts[:no_output]
80
+
81
+ config = opts
82
+
83
+ template = opts[:template]
84
+ template = "default-#{type}" if template.nil? || template.to_s == "default"
85
+
86
+ template = Support::Template['mercurial', template]
87
+ template.render({}, binding)
88
+ end
89
+
90
+ def useful_parents(log, revision)
91
+ parents = log.parent_indices_for_index revision
92
+ if parents[1] == -1
93
+ if parents[0] >= revision - 1
94
+ parents = []
95
+ else
96
+ parents = [parents[0]]
97
+ end
98
+ end
99
+ parents
100
+ end
101
+
102
+ ##
103
+ # Commits the given changeset to the repository.
104
+ #
105
+ # commit_changeset:
106
+ # foreach file in commit:
107
+ # commit_file file
108
+ # end
109
+ # add_manifest_entry
110
+ # add_changelog_entry
111
+ # Is this changeset a working changeset?
112
+ #
113
+ # @param changeset the changeset to commit. Could be working dir, for
114
+ # example.
115
+ # @param opts the options for committing the changeset.
116
+ # @option [Boolean] opts :force (false) force the commit, even though
117
+ # nothing has changed.
118
+ # @option [Boolean] opts :force_editor (false) force the user to open
119
+ # their editor, even though they provided a message already
120
+ # @option [Boolean] opts :empty_ok (false) is it ok if they have no
121
+ # description of the commit?
122
+ # @option [Boolean] opts :use_dirstate (true) use the DirState for this
123
+ # commit? Used if you're committing the working directory (typical)
124
+ # @option [Boolean] opts :update_dirstate (true) should we update the
125
+ # DirState after the commit? Used if you're committing the working
126
+ # directory.
127
+ # @return [String] the digest referring to this entry in the revlog
128
+ def commit(opts = {:use_dirstate => true, :update_dirstate => true})
129
+ valid = false # don't update the DirState if this is set!
130
+
131
+ commit = ((modified || []) + (added || [])).sort
132
+ remove = removed
133
+ xtra = extra.dup
134
+ branchname = xtra["branch"]
135
+ text = description
136
+
137
+ p1, p2 = parents.map {|p| p.node }
138
+ c1 = repo.changelog.read(p1) # 1 parent's changeset as an array
139
+ c2 = repo.changelog.read(p2) # 2nd parent's changeset as an array
140
+ m1 = repo.manifest.read(c1[0]).dup # 1st parent's manifest
141
+ m2 = repo.manifest.read(c2[0]) # 2nd parent's manifest
142
+
143
+ if opts[:use_dirstate]
144
+ oldname = c1[5]["branch"]
145
+ tests = [ commit.empty?, remove.empty?, ! opts[:force],
146
+ p2 == NULL_ID, branchname == oldname ]
147
+
148
+ if tests.all?
149
+ UI::status "nothing changed"
150
+ return nil
151
+ end
152
+ end
153
+
154
+ xp1 = p1.hexlify
155
+ xp2 = p2 == NULL_ID ? "" : p2.hexlify
156
+
157
+ Hook.run_hook :pre_commit
158
+ journal = Amp::Mercurial::Journal.new(:opener => repo.store_opener)
159
+
160
+ fresh = {} # new = reserved haha i don't know why someone wrote "haha"
161
+ changed = []
162
+ link_rev = repo.size
163
+
164
+ (commit + (remove || [])).each {|file| UI::status file }
165
+
166
+ # foreach file in commit:
167
+ # commit_file file
168
+ # end
169
+ commit.each do |file|
170
+ versioned_file = self[file]
171
+ fresh[file] = versioned_file.commit :manifests => [m1, m2],
172
+ :link_revision => link_rev,
173
+ :journal => journal ,
174
+ :changed => changed
175
+
176
+ new_flags = versioned_file.flags
177
+
178
+ # TODO
179
+ # Clean this shit up
180
+ if [ changed.empty? || changed.last != file,
181
+ m2[file] != fresh[file]
182
+ ].all?
183
+ changed << file if m1.flags[file] != new_flags
184
+ end
185
+ m1.flags[file] = new_flags
186
+
187
+ repo.staging_area.normal file if opts[:use_dirstate]
188
+ end
189
+
190
+ # add_manifest_entry
191
+ man_entry, updated, added = *add_manifest_entry(:manifests => [m1, m2],
192
+ :changesets => [c1, c2],
193
+ :journal => journal ,
194
+ :link_rev => link_rev,
195
+ :fresh => fresh ,
196
+ :remove => remove ,
197
+ :changed => changed )
198
+
199
+ # get_commit_text
200
+ text = get_commit_text text, :added => added, :updated => updated,
201
+ :removed => removed, :user => user ,
202
+ :empty_ok => opts[:empty_ok] ,
203
+ :use_dirstate => opts[:use_dirstate]
204
+
205
+ # atomically write to the changelog
206
+ # add_changelog_entry
207
+ # for the unenlightened, rents = 'rents = parents
208
+ new_rents = add_changelog_entry :manifest_entry => man_entry,
209
+ :files => (changed + removed),
210
+ :text => text,
211
+ :journal => journal,
212
+ :parents => [p1, p2],
213
+ :user => user,
214
+ :date => date,
215
+ :extra => xtra
216
+
217
+ # Write the dirstate if it needs to be updated
218
+ # basically just bring it up to speed
219
+ if opts[:use_dirstate] || opts[:update_dirstate]
220
+ repo.dirstate.parents = new_rents
221
+ removed.each {|f| repo.dirstate.forget(f) } if opts[:use_dirstate]
222
+ repo.dirstate.write
223
+ end
224
+
225
+ # The journal and dirstates are awesome. Leave them be.
226
+ valid = true
227
+ journal.close
228
+
229
+ # if an error and we've gotten this far, then the journal is complete
230
+ # and it deserves to stay (if an error is thrown and journal isn't nil,
231
+ # the rescue will destroy it)
232
+ journal = nil
233
+
234
+ # Run any hooks
235
+ Hook.run_hook :post_commit, :added => added, :modified => updated, :removed => removed,
236
+ :user => user, :date => date, :text => text,
237
+ :revision => repo.changelog.index_size
238
+ return new_rents
239
+ rescue StandardError => e
240
+ if !valid
241
+ repo.dirstate.invalidate!
242
+ end
243
+ if e.kind_of?(AbortError)
244
+ UI::warn "Abort: #{e}"
245
+ else
246
+ UI::warn "Got exception while committing. #{e}"
247
+ UI::warn e.backtrace.join("\n")
248
+ end
249
+
250
+ # the journal is a vestigial and incomplete file.
251
+ # destroyzzzzzzzzzzz
252
+ journal.delete if journal
253
+ end
254
+
255
+ ##
256
+ # Add an entry to the changelog (the final receipt of the commit).
257
+ #
258
+ # @param [Hash] opts
259
+ # @return [String] the changelog id as to where the revision is in
260
+ # the changelog
261
+ def add_changelog_entry(opts={})
262
+ repo.changelog.delay_update
263
+ new_parents = repo.changelog.add opts[:manifest_entry],
264
+ opts[:files],
265
+ opts[:text],
266
+ opts[:journal],
267
+ opts[:parents][0],
268
+ opts[:parents][1],
269
+ opts[:user],
270
+ opts[:date],
271
+ opts[:extra]
272
+
273
+ repo.changelog.write_pending
274
+ repo.changelog.finalize opts[:journal]
275
+ new_parents
276
+ end
277
+
278
+ ##
279
+ # Get the commit text. Ask for it if none is given.
280
+ #
281
+ # @param [String, NilClass] text (optional) the commit message
282
+ # @param [Hash] opts
283
+ def get_commit_text(text=nil, opts={})
284
+ user = opts.delete :user
285
+
286
+ unless opts[:empty_ok] || (text && !text.empty?)
287
+ edit_text = to_templated_s :added => added, :updated => modified,
288
+ :removed => removed, :template_type => :commit
289
+ text = UI::edit edit_text, user
290
+ end
291
+
292
+ lines = text.rstrip.split("\n").map {|r| r.rstrip }.reject {|l| l.empty? }
293
+ raise abort("empty commit message") if lines.empty? && opts[:use_dirstate]
294
+ lines.join("\n")
295
+ end
296
+
297
+ def add_manifest_entry(opts={})
298
+ # changed, m1, m2, c1, c2, fresh, remove, journal, link_rev
299
+ updated, added = [], []
300
+
301
+ fresh = opts[:fresh]
302
+ remove = opts[:remove]
303
+ changed = opts[:changed]
304
+
305
+ changesets = opts[:changesets]
306
+ manifests = opts[:manifests]
307
+
308
+ changed.sort.each do |file|
309
+ if manifests[0][file] || manifests[1][file]
310
+ updated << file
311
+ else
312
+ added << file
313
+ end
314
+ end
315
+
316
+ manifests[0].merge! fresh
317
+
318
+ remove.sort!
319
+ remove.reject! {|f| not manifests[0][f] }
320
+ remove.each {|f| manifests[0].delete f }
321
+
322
+ UI::debug "before adding manifest entry"
323
+
324
+ # sorry for making this destructive
325
+ # but it's clean and memory efficient
326
+ # GHC's GC goes like 3 times per second, so STFU
327
+ # I don't have that kind of luxury
328
+ fresh.replace fresh.inject([]) {|a, (k, v)| v ? a << k : a }
329
+ man_entry = repo.manifest.add manifests[0], opts[:journal],
330
+ opts[:link_rev], changesets[0][0], changesets[1][0], [fresh, remove]
331
+ [man_entry, updated, added]
332
+ end
333
+
334
+ ##
335
+ # @return [Boolean] is the changeset representing the working directory?
336
+ def working?
337
+ false
338
+ end
339
+
340
+ ##
341
+ # Gives an easier way to digest this changeset while reminding us it's a
342
+ # changeset
343
+ def inspect
344
+ "#<Changeset #{to_s}>"
345
+ end
346
+
347
+ ##
348
+ # Hash function for putting these bad boys in hashes
349
+ #
350
+ # @return [Integer] a hash value.
351
+ def hash
352
+ return @revision.hash if @revision
353
+ return object_id
354
+ end
355
+
356
+ ##
357
+ # Compares 2 changesets so we can sort them and whatnot
358
+ #
359
+ # @param [Changeset] other a changeset we will compare against
360
+ # @return [Integer] -1, 0, or 1. Typical comparison.
361
+ def <=>(other)
362
+ return 0 if @revision.nil? || other.revision.nil?
363
+ @revision <=> other.revision
364
+ end
365
+
366
+ ##
367
+ # Are we a null revision?
368
+ # @return [Boolean] null?
369
+ def nil?
370
+ @revision != NULL_REV
371
+ end
372
+ alias_method :null?, :nil?
373
+
374
+ # Gets the raw changeset data for this revision. This includes
375
+ # the user who committed it, the description of the commit, and so on.
376
+ # Returns this: [manifest, user, [time, timezone], files, desc, extra]
377
+ def raw_changeset
378
+ @repo.changelog.read(@node_id)
379
+ end
380
+
381
+ ##
382
+ # Returns the {ManifestEntry} for this revision. This will give
383
+ # us info on any file we want, including flags such as executable
384
+ # or if it's a link. Sizes and so on are also included.
385
+ #
386
+ # @return [ManifestEntry] the manifest at this point in time
387
+ def manifest_entry
388
+ @manifest_entry ||= @repo.manifest.read(raw_changeset[0])
389
+ end
390
+
391
+ ##
392
+ # Provides access to all the tracked files in the changeset. Needed
393
+ # for API compatibility.
394
+ #
395
+ # @return [Array<String>] all the files tracked in this changeset.
396
+ def all_files
397
+ return manifest_entry.files
398
+ end
399
+
400
+ ##
401
+ # Returns the change in the manifest at this revision. I don't entirely
402
+ # know what this is yet.
403
+ def manifest_delta
404
+ @manifest_entry_delta ||= @repo.manifest.read_delta(raw_changeset[0])
405
+ end
406
+
407
+ ##
408
+ # Returns the parents of this changeset as {Changeset}s.
409
+ #
410
+ # @return [[Changeset]] the parents of this changeset.
411
+ def parents
412
+ return @parents if @parents
413
+
414
+ p = @repo.changelog.parent_indices_for_index @revision
415
+ p = [p[0]] if p[1] == NULL_REV
416
+
417
+ @parents = p.map {|x| Changeset.new(@repo, x) }
418
+ end
419
+
420
+ ##
421
+ # Returns the children of this changeset as {Changeset}s.
422
+ #
423
+ # @return [Array<Changeset>] the children of this changeset.
424
+ def children
425
+ @repo.changelog.children(node_id).map do |node|
426
+ Changeset.new(@repo, node)
427
+ end
428
+ end
429
+
430
+ ##
431
+ # Iterates over each entry in the manifest entry.
432
+ #
433
+ # @return [Changeset] self, because that's how #each works
434
+ def each(&block)
435
+ manifest_entry.sort.each(&block)
436
+ self
437
+ end
438
+
439
+ ##
440
+ # Checks whether this changeset included a given file or not.
441
+ #
442
+ # @param [String] file the file to lookup
443
+ # @return [Boolean] whether the file is in this changeset's manifest
444
+ def include?(file)
445
+ manifest_entry[file] != nil
446
+ end
447
+
448
+ ##
449
+ # Gets the file with the given name, as a {VersionedFile}.
450
+ # @param file the path to the file to retrieve
451
+ # @return [VersionedFile] the file at this revision
452
+ #
453
+ def [](file)
454
+ get_file(file)
455
+ end
456
+
457
+ ##
458
+ # Returns the file's info, namely it's node_id and flags it may
459
+ # have at this point in time, such as "x" for executable.
460
+ #
461
+ # @param path the path to the file
462
+ # @return [[String, String]] the [node_id, flags] pair for this file
463
+ def file_info(path)
464
+ if manifest_entry # have we loaded our manifest yet? if so, use that sucker
465
+ result = [manifest_entry[path], manifest_entry.flags[path]]
466
+ if result[0].nil?
467
+ return [NULL_ID, '']
468
+ else
469
+ return result
470
+ end
471
+ end
472
+ if manifest_delta || files[path] # check if it's in the delta... i dunno
473
+ if manifest_delta[path]
474
+ return [manifest_delta[path], manifest_delta.flags[path]]
475
+ end
476
+ end
477
+ # Give us, just look it up the long way in the manifest. not fun. slow.
478
+ node, flag = @repo.manifest.find(raw_changeset[0], path)
479
+ if node.nil?
480
+ return [NULL_ID, '']
481
+ end
482
+ return [node, flag]
483
+ end
484
+
485
+ ##
486
+ # Gets the flags for the file at the given path at this revision.
487
+ # @param path the path to the file in question
488
+ # @return [String] the flags for the file, such as "x", "l", or "".
489
+ #
490
+ def flags(path)
491
+ info = file_info(path)[1]
492
+ return "" if info.nil?
493
+ info
494
+ end
495
+
496
+ ##
497
+ # Gets the node_id in the manifest_entry for the file at this path, for this
498
+ # specific revision.
499
+ #
500
+ # @param path the path to the file
501
+ # @return [String] the node's ID in the manifest_entry, which we'll use every
502
+ # where we need a node_id.
503
+ def file_node(path)
504
+ file_info(path).first[0..19]
505
+ end
506
+
507
+ ##
508
+ # Creates a versioned file for the file at the given path, for the frame
509
+ # of reference of this revision.
510
+ # @param path the path to the file
511
+ # @param [String] file_id the node_id, to save us some computation
512
+ # @param [FileLog] file_log the file_log to use, again to save us computation
513
+ # @return [VersionedFile] the file at this revision.
514
+ #
515
+ def get_file(path, file_id = nil, file_log = nil)
516
+ file_id = file_node(path) if file_id.nil?
517
+ VersionedFile.new(@repo, path, :file_id => file_id, :changeset => self,
518
+ :file_log => file_log)
519
+ end
520
+ #accessors
521
+ # revision index
522
+ def revision; @revision; end
523
+ alias_method :rev, :revision
524
+ # node_id
525
+ def node_id; @node_id; end
526
+ # @see node_id
527
+ alias_method :node, :node_id
528
+ # our node_id in sexy hexy
529
+ def hex; @node_id.hexlify; end
530
+ # the user who committed me!
531
+ def user; raw_changeset[1]; end
532
+ # the date i was committed!
533
+ def date; raw_changeset[2]; end
534
+ def easy_date; Time.at(raw_changeset[2].first); end
535
+ # the files affected in this commit!
536
+ def altered_files; raw_changeset[3]; end
537
+ # pre-API compatibility
538
+ alias_method :files, :altered_files
539
+
540
+ # the message with this commit
541
+ def description; raw_changeset[4]; end
542
+ # What branch i was committed onto
543
+ def branch
544
+ extra["branch"]
545
+ end
546
+ # Any extra stuff I've got in me
547
+ def extra; raw_changeset[5]; end
548
+ # tags
549
+ def tags; @repo.tags_for_node node; end
550
+
551
+ def ancestor(other_changeset)
552
+ node = @repo.changelog.ancestor(self.node, other_changeset.node)
553
+ return Changeset.new(@repo, node)
554
+ end
555
+
556
+ def ancestors
557
+ results = []
558
+ @repo.changelog.ancestors(revision)
559
+ end
560
+ end
561
+
562
+ ##
563
+ # This is a special changeset that specifically works within the
564
+ # working directory. We sort of have to combine the old revision
565
+ # logs with the fact that files might be changed, and not in the
566
+ # revision logs! oh, mercy!
567
+ class WorkingDirectoryChangeset < Changeset
568
+
569
+ def initialize(repo, opts={:text => ""})
570
+ @repo = repo
571
+ @revision = nil
572
+ @parents = nil
573
+ @node_id = nil
574
+ @text = opts[:text]
575
+ require 'time' if opts[:date].kind_of?(String)
576
+ @date = opts[:date].kind_of?(String) ? Time.parse(opts[:date]) : opts[:date]
577
+ @user = opts[:user] if opts[:user]
578
+ @parents = opts[:parents].map {|p| Changeset.new(@repo, p)} if opts[:parents]
579
+ @status = opts[:changes] if opts[:changes]
580
+ @manifest = nil
581
+ @extra = opts[:extra] ? opts[:extra].dup : {}
582
+ unless @extra["branch"]
583
+ branch = @repo.dirstate.branch
584
+ # encoding - to UTF-8
585
+ @extra["branch"] = branch
586
+ end
587
+ @extra["branch"] = "default" if @extra["branch"] && @extra["branch"].empty?
588
+
589
+ end
590
+
591
+ ##
592
+ # Is this changeset a working changeset?
593
+ #
594
+ # @return [Boolean] is the changeset representing the working directory?
595
+ def working?
596
+ true
597
+ end
598
+
599
+ ##
600
+ # Converts to a string.
601
+ # I'm my first parent, plus a little extra.
602
+ # "I am my own grandpa"
603
+ #
604
+ # @return [String]
605
+ def to_s
606
+ parents.first.to_s + "+"
607
+ end
608
+
609
+
610
+ ##
611
+ # Do I include a given file? (not sure this is ever used yet)
612
+ def include?(key)
613
+ status = @repo.staging_area.file_status(key)
614
+ ![:unknown, :removed].include?(status)
615
+ end
616
+
617
+ def all_files
618
+ repo.staging_area.all_files
619
+ end
620
+
621
+ ##
622
+ # Am I nil? never!
623
+ def nil?; false; end
624
+
625
+ ##
626
+ # What is the status of the working directory? This little
627
+ # method hides quite a bit of work!
628
+ def status
629
+ @status ||= @repo.status(:unknown => true)
630
+ end
631
+
632
+ ##
633
+ # Who is the user working on me?
634
+ def user
635
+ @user ||= @repo.config.username
636
+ end
637
+
638
+ ##
639
+ # Well, I guess the working directory's date is... right now!
640
+ def date
641
+ @date ||= Time.new
642
+ end
643
+
644
+ ##
645
+ # Who is the working directory's father? Is it Chef? Mr. Garrison?
646
+ # the 1989 denver broncos?
647
+ #
648
+ # hahaha mike that's hilarious
649
+ def parents
650
+ @parents ||= begin
651
+ p = @repo.dirstate.parents
652
+ p = [p[0]] if p[1] == NULL_ID
653
+ p.map {|x| Changeset.new(@repo, x) }
654
+ end
655
+ end
656
+
657
+ ##
658
+ # OK, so we've got the last revision's manifest entry, that part's simple and makes sense.
659
+ # except now, we need to get the status of the working directory, and
660
+ # add in all the other files, because they're in the "manifest entry" by being
661
+ # in existence. Oh, and we need to remove any files from the parent's
662
+ # manifest entry that don't exist anymore. Make sense?
663
+ def manifest_entry
664
+ return @manifest_entry if @manifest_entry
665
+
666
+ # Start off with the last revision's manifest_entry, that's safe.
667
+ man = parents()[0].manifest_entry.dup
668
+ # Any copied files since the last revision?
669
+ copied = @repo.dirstate.copy_map
670
+ # Any modified, added, etc files since the last revision?
671
+ modified, added, removed = status[:modified], status[:added], status[:removed]
672
+ deleted, unknown = status[:deleted], status[:unknown]
673
+ # Merge these discoveries in!
674
+ {:a => added, :m => modified, :u => unknown}.each do |k, list|
675
+ list.each do |file|
676
+ copy_name = (copied[file] || file)
677
+ man[file] = (man.flags[copy_name] || NULL_ID) + k.to_s
678
+ man.flags[file] = @repo.dirstate.flags(file)
679
+ end
680
+ end
681
+
682
+ # Delete files from the real manifest entry that don't exist.
683
+ (deleted + removed).each do |file|
684
+ man.delete file if man[file]
685
+ end
686
+
687
+ man
688
+ end
689
+
690
+ ##
691
+ # Returns a {VersionedWorkingFile} to represent the file at the given
692
+ # point in time. It represents a file in the working directory, which
693
+ # obvious don't read from the history, but from the actual file in
694
+ # question.
695
+ #
696
+ # @param path the path to the file
697
+ # @param file_log the log for the file to save some computation
698
+ # @return [Amp::VersionedWorkingFile] the file object we can work with
699
+ def get_file(path, file_log=nil)
700
+ VersionedWorkingFile.new(@repo, path, :working_changeset => self,
701
+ :file_log => file_log)
702
+ end
703
+
704
+ ##
705
+ # Gets the flags for the file at current state in time
706
+ #
707
+ # @param [String] path the path to the file
708
+ # @return [String] the flags, such as "x", "l", or ""
709
+ def flags(path)
710
+ if @manifest_entry ||= nil
711
+ return manifest_entry.flags[path] || ""
712
+ end
713
+ pnode = parents[0].raw_changeset[0]
714
+
715
+ orig = @repo.dirstate.copy_map[path] || path
716
+ node, flag = @repo.manifest.find(pnode, orig)
717
+ return @repo.dirstate.flags(@repo.working_join(path))
718
+ end
719
+
720
+ def useful_parents(log, revision)
721
+ parents = @parents.map {|p| p.revision}
722
+ if parents[1] == -1
723
+ if parents[0] >= @repo.size - 1
724
+ parents = []
725
+ else
726
+ parents = [parents[0]]
727
+ end
728
+ end
729
+ parents
730
+ end
731
+
732
+ ##
733
+ # Recursively walk the directory tree, getting all files that +match+ says
734
+ # are good.
735
+ #
736
+ # @param [Amp::Match] match how to select the files in the tree
737
+ # @param [Boolean] check_ignored (false) should we check for ignored files?
738
+ # @return [Array<String>] an array of filenames in the tree that match +match+
739
+ def walk(match, check_ignored = false)
740
+ tree = @repo.staging_area.walk true, check_ignored, match
741
+ tree.keys.sort
742
+ end
743
+
744
+ # If there's a description, ok then
745
+ def description; @text; end
746
+ # Files affected in this transaction: modified, added, removed.
747
+ def files; (status[:modified] + status[:added] + status[:removed]).sort; end
748
+ # What files have changed?
749
+ def modified; status[:modified]; end
750
+ # What files have we added?
751
+ def added; status[:added]; end
752
+ # What files have been removed?
753
+ def removed; status[:removed]; end
754
+ # What files have been deleted (but not officially)?
755
+ def deleted; status[:deleted]; end
756
+ # What files are hanging out, but untracked?
757
+ def unknown; status[:unknown]; end
758
+ # What files are pristine since the last revision?
759
+ def clean; status[:normal]; end
760
+ # What branch are we in?
761
+ def branch; @extra["branch"]; end
762
+ # Any other extra data? i'd like to hear it
763
+ def extra; @extra; end
764
+ # No children. Returns the empty array.
765
+ def children; []; end
766
+ end
767
+ end
768
+ end