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,950 +0,0 @@
1
- module Amp
2
- module Repositories
3
-
4
- ##
5
- # An entry in the dirstate. Similar to IndexEntry for revlogs. Simple struct, that's
6
- # all.
7
- class DirStateEntry < Struct.new(:status, :mode, :size, :mtime)
8
-
9
- ##
10
- # shortcuts!
11
- def removed?; self.status == :removed; end
12
- def added?; self.status == :added; end
13
- def untracked?; self.status == :untracked; end
14
- def modified?; self.status == :modified; end
15
- def merged?; self.status == :merged; end
16
- def normal?; self.status == :normal; end
17
- def forgotten?; self.status == :forgotten; end
18
-
19
- ##
20
- # Do I represent a dirty object?
21
- #
22
- # @return [Boolean] does this array represent a dirty object in a DirState?
23
- def dirty?
24
- self[-2] == -2 && self[-1] == -1 && self.normal?
25
- end
26
-
27
- ##
28
- # Do I possibly represent a dirty object?
29
- #
30
- # @return [Boolean] does this array possibly represent a dirty object in a DirState?
31
- def maybe_dirty?
32
- self[-2] == -1 && self[-1] == -1 && self.normal?
33
- end
34
- end
35
-
36
- ##
37
- # = DirState
38
- # This class handles parsing and manipulating the "dirstate" file, which is stored
39
- # in the .hg folder. This file handles which files are marked for addition, removal,
40
- # copies, and so on. The structure of each entry is below.
41
- #
42
- #
43
- # class DirStateEntry < BitStruct
44
- # default_options :endian => :network
45
- #
46
- # char :status , 8, "the state of the file"
47
- # signed :mode , 32, "mode"
48
- # signed :size , 32, "size"
49
- # signed :mtime , 32, "mtime"
50
- # signed :fname_size , 32, "filename size"
51
- #
52
- # end
53
- class DirState
54
- include Ignore
55
- include RevlogSupport::Node
56
-
57
- UNKNOWN = DirStateEntry.new(:untracked, 0, 0, 0)
58
- FORMAT = "cNNNN"
59
-
60
- class FileNotInRootError < StandardError; end
61
- class AbsolutePathNeededError < StandardError; end
62
-
63
- # The parents of the current state. If there's been an uncommitted merge,
64
- # it will be two. Otherwise it will just be one parent and +NULL_ID+
65
- attr_reader :parents
66
-
67
- # The number of directories in each base ["dir" => #_of_dirs]
68
- attr_reader :dirs
69
-
70
- # The files mapped to their stats (state, mode, size, mtime)
71
- # [state, mode, size, mtime]
72
- attr_reader :files
73
-
74
- # A map of files to be copied, because we want to preserve their history
75
- # "source" => "dest"
76
- attr_reader :copy_map
77
-
78
- # I still don't know what this does
79
- attr_reader :folds
80
-
81
- # The conglomerate config object of global configs and the repo
82
- # specific config.
83
- attr_reader :config
84
-
85
- # The root of the repository
86
- attr_reader :root
87
-
88
- # The opener to access files. The only files that will be touched lie
89
- # in the .hg/ directory, so the default MUST be +:open_hg+.
90
- attr_reader :opener
91
-
92
- ##
93
- # Creates a DirState object. This is used to represent, in memory (and
94
- # occasionally on file) how the repository is being changed.
95
- # It's really simple, and it is really the basis for _using_ the repo
96
- # (contrary to how Revlog is the basis for _saving_ the repo).
97
- #
98
- # @param [String] root the absolute path to the root of the repository
99
- # @param [Amp::AmpConfig] config the config file of hgrc
100
- # @param [Amp::Opener] opener the opener to open files with
101
- def initialize(root, config, opener)
102
- unless root[0, 1] == "/"
103
- raise AbsolutePathNeededError, "#{root} is not an absolute path!"
104
- end
105
-
106
- # root must be an aboslute path with no ending slash
107
- @root = root[-1, 1] == "/" ? root[0..-2] : root # the root of the repo
108
- @config = config # the config file where we get defaults
109
- @opener = opener # opener to retrieve files (default: open_hg)
110
- @dirty = false # has something changed, and do we need to write?
111
- @dirty_parents = false
112
- @parents = [NULL_ID, NULL_ID] # the parent revisions
113
- @dirs = {} # number of directories in each base ["dir" => #_of_dirs]
114
- @files = {} # the files mapped to their statistics
115
- @copy_map = {} # src => dest
116
- @ignore = [] # dirs and files to ignore
117
- @folds = []
118
- @check_exec = nil
119
- generate_ignore
120
- end
121
-
122
- ##
123
- # Retrieve a file's status from +@files+. If it's not there
124
- # then return :untracked
125
- #
126
- # @param [String] key the path of the file
127
- # @return [Symbol] status of the file, either :removed, :added, :untracked,
128
- # :merged, :normal, :forgotten, or :untracked
129
- def [](key)
130
- lookup = @files[key]
131
- lookup || DirStateEntry.new(:untracked)
132
- end
133
-
134
- ##
135
- # Determine if +path+ is a link or an executable.
136
- #
137
- # @param [String] path the path to the file
138
- # @return [String] either 'l' for a link and 'x' for an executable. Returns
139
- # '' if neither
140
- def flags(path)
141
- return 'l' if File.ftype(path) == 'link'
142
- return 'x' if File.executable? path
143
- ''
144
- end
145
-
146
- ##
147
- # just a lil' reader to find if the repo is dirty or not
148
- # by dirty i mean "no longer in sync with the cache"
149
- #
150
- # @return [Boolean] is the dirstate no longer in sync with the cache located
151
- # at .hg/branch.cache
152
- def dirty?
153
- @dirty
154
- end
155
-
156
- ##
157
- # The directories and path matches that we're ignoringzorz. It will call
158
- # the ignorer generated by .hgignore, but only if @ignore_all is nil (really
159
- # only if @ignore_all isn't a Boolean value, but we set it to nil)
160
- #
161
- # @param [String] file the path to the file that will be checked by
162
- # the .hgignore file
163
- # @return [Boolean] whether we're ignoring the path or not
164
- def ignore(file)
165
- return true if @ignore_all == true
166
- return false if @ignore_all == false
167
- @ignore_matches ||= parse_ignore @root, @ignore
168
- @ignore_matches.call file
169
- end
170
-
171
- ##
172
- # Gets the current branch.
173
- #
174
- # @return [String] the current branch in the working directory
175
- def branch
176
- text = @opener.read('branch').strip
177
- @branch ||= text.empty? ? "default" : text
178
- rescue
179
- @branch = "default"
180
- end
181
-
182
- ##
183
- # Set the branch to +branch+.
184
- #
185
- # @param [#to_s] brnch the branch to switch to
186
- # @return [String] +brnch+.to_s
187
- def branch=(brnch)
188
- @branch = brnch.to_s
189
-
190
- @opener.open 'branch', 'w' do |f|
191
- f.puts brnch.to_s
192
- end
193
- @branch
194
- end
195
-
196
- ##
197
- # Set the parents to +p+
198
- #
199
- # @param [Array<String>] p the parents as binary strings
200
- # @return [Array<String>] the parents, as will be used by the dirstate
201
- def parents=(p)
202
- @parents = if p.is_a? Array
203
- p.size == 1 ? p + [NULL_ID] : p[0..1]
204
- else
205
- [p, NULL_ID]
206
- end
207
-
208
- @dirty_parents = true
209
- @dirty = true
210
- @parents # return this
211
- end
212
- alias_method :parent, :parents
213
-
214
- ##
215
- # Set the file as "to be added".
216
- #
217
- # @param [String] file the path of the file to add
218
- # @return [Boolean] a success marker
219
- def add(file)
220
- add_path file, true
221
-
222
- @dirty = true
223
- @files[file] = DirStateEntry.new(:added, 0, -1, -1)
224
- @copy_map.delete file
225
- true # success
226
- end
227
-
228
- ##
229
- # Set the file as "normal", meaning no changes. This is the same
230
- # as dirstate.normal in dirstate.py, for those referencing both.
231
- #
232
- # @param [String] file the path of the file to clean
233
- # @return [Boolean] a success marker
234
- def normal(file)
235
- @dirty = true
236
- add_path file, true
237
-
238
- f = File.lstat "#{@root}/#{file}"
239
- @files[file] = DirStateEntry.new(:normal, f.mode, f.size, f.mtime.to_i)
240
- @copy_map.delete file
241
- true # success
242
- end
243
- alias_method :clean, :normal
244
-
245
- ##
246
- # Set the file as normal, but possibly dirty. It's like when you
247
- # meet a cool girl, and she seems really innocent and it's a chance
248
- # for you to maybe change yourself and make a new friend, but then
249
- # she *might* actually be a total slut. Better milk that grapevine
250
- # to find out the truth. Oddly specific, huh.
251
- #
252
- # THUS IS THE HISTORY OF THIS METHOD!
253
- #
254
- # And then one day you go to the movies with some other girl, and the
255
- # original crazy slutty girl is the cashier next to you. Unsure of
256
- # what to do, you don't do anything. Next thing you know, she's trying
257
- # to get your attention to say hey. WTF? Anyone know what's up with this
258
- # girl?
259
- #
260
- # After milking that grapevine, you find out that she's not a great person.
261
- # There's nothing interesting there and you should just move on.
262
- #
263
- # *sigh* girls.
264
- #
265
- # @param [String] file the path of the file to mark
266
- # @return [Boolean] a success marker
267
- def maybe_dirty(file)
268
- if @files[file] && @parents.last != NULL_ID
269
- # if there's a merge happening and the file was either modified
270
- # or dirty before being removed, restore that state.
271
- # I'm quoting the python with that one.
272
- # I guess what it's saying is that if a file is being removed
273
- # by a merge, but it was altered somehow beforehand on the local
274
- # repo, then play it safe and bring back the dead. Divine intervention
275
- # on the side of the local repo.
276
-
277
- # info here is a standard array of info
278
- # [action, mode, size, mtime]
279
- info = @files[file]
280
-
281
- if info.removed? and [-1, -2].member? info.size
282
- source = @copy_map[file]
283
-
284
- # do the appropriate action
285
- case info.size
286
- when -1 # either merge it
287
- merge file
288
- when -2 # or mark it as dirty
289
- dirty file
290
- end
291
-
292
- copy source => file if source
293
- return
294
- end
295
-
296
- # next step... the base case!
297
- return true if info.modified? || info.maybe_dirty? and info.size == -2
298
- end
299
-
300
- @dirty = true # make the repo dirty
301
- add_path file # add the file
302
-
303
- @files[file] = DirStateEntry.new(:normal, 0, -1, -1) # give it info
304
- @copy_map.delete file # we're not copying it since we're adding it
305
- true # success
306
- end
307
-
308
- ##
309
- # Checks whether the dirstate is tracking the given file.
310
- #
311
- # @param f the file to check for
312
- # @return [Boolean] whether or not the file is being tracked.
313
- def include?(path)
314
- not @files[path].nil?
315
- end
316
- alias_method :tracking?, :include?
317
-
318
- ##
319
- # Mark the file as "dirty"
320
- #
321
- # @param [String] file the path of the file to mark
322
- # @return [Boolean] a success marker
323
- def dirty(file)
324
- @dirty = true
325
- add_path file
326
-
327
- @files[file] = DirStateEntry.new(:normal, 0, -2, -1)
328
- @copy_map.delete file
329
- true # success
330
- end
331
-
332
- ##
333
- # Set the file as "to be removed"
334
- #
335
- # @param [String] file the path of the file to remove
336
- # @return [Boolean] a success marker
337
- def remove(file)
338
- @dirty = true
339
- drop_path file
340
-
341
- size = 0
342
- if @parents.last.null? && (info = @files[file])
343
- if info.merged?
344
- size = -1
345
- elsif info.normal? && info.size == -2
346
- size = -2
347
- end
348
- end
349
- @files[file] = DirStateEntry.new(:removed, 0, size, 0)
350
- @copy_map.delete file if size.zero?
351
- true # success
352
- end
353
-
354
- ##
355
- # Prepare the file to be merged
356
- #
357
- # @param [String] file the path of the file to merge
358
- # @return [Boolean] a success marker
359
- def merge(file)
360
- @dirty = true
361
- add_path file
362
-
363
- stats = File.lstat "#{@root}/#{file}"
364
- add_path file
365
- @files[file] = DirStateEntry.new(:merged, stats.mode, stats.size, stats.mtime.to_i)
366
- @copy_map.delete file
367
- true # success
368
- end
369
-
370
- ##
371
- # Forget the file, erase it from the repo
372
- #
373
- # @param [String] file the path of the file to forget
374
- # @return [Boolean] a success marker
375
- def forget(file)
376
- @dirty = true
377
- drop_path file
378
- @files.delete file
379
- true # success
380
- end
381
-
382
- ##
383
- # Invalidates the dirstate, making it completely unusable until it is
384
- # re-read. Should only be used in error situations.
385
- def invalidate!
386
- %w(@files @copy_map @folds @branch @parents @dirs @ignore).each do |ivar|
387
- instance_variable_set(ivar, nil)
388
- end
389
- @dirty = false
390
- end
391
-
392
- ##
393
- # Refresh the directory's state, making everything empty.
394
- # Called by #rebuild.
395
- #
396
- # This is not the same as #initialize, so we can't just run
397
- # `send :initialize` and call it a day :-(
398
- #
399
- # @return [Boolean] a success marker
400
- def clear
401
- @files = {}
402
- @dirs = {}
403
- @copy_map = {}
404
- @parents = [NULL_ID, NULL_ID]
405
- @dirty = true
406
-
407
- true # success
408
- end
409
-
410
- ##
411
- # Rebuild the directory's state. Needs Manifest, as that's
412
- # what the files really are.
413
- #
414
- # @param [String] parent the binary format of the parent
415
- # @param [ManifestEntry] files the files in a specific revision
416
- # @return [Boolean] a success marker
417
- def rebuild(parent, files)
418
- clear
419
-
420
- # alter each file according to its flags
421
- files.each do |f|
422
- mode = files.flags(f).include?('x') ? 0777 : 0666
423
- @files[f] = DirStateEntry.new(:normal, mode, -1, 0)
424
- end
425
-
426
- @parents = [parent, NULL_ID]
427
- @dirty_parents = true
428
- true # success
429
- end
430
-
431
- ##
432
- # Save the data to .hg/dirstate.
433
- # Uses mode: "w", so it overwrites everything
434
- #
435
- # @todo watch memory usage - +si+ could grow unrestrictedly which would
436
- # bog down the entire program
437
- # @return [Boolean] a success marker
438
- def write
439
- return true unless @dirty
440
- begin
441
- @opener.open "dirstate", 'w' do |state|
442
- gran = @config['dirstate']['granularity'] || 1 # self._ui.config('dirstate', 'granularity', 1)
443
-
444
- limit = 2147483647 # sorry for the literal use...
445
- limit = state.mtime - gran if gran > 0
446
-
447
- si = StringIO.new "", (ruby_19? ? "w+:ASCII-8BIT" : "w+")
448
- si.write @parents.join
449
-
450
- @files.each do |file, info|
451
- file = file.dup # so we don't corrupt vars
452
- info = info.dup.to_a # UNLIKE PYTHON
453
- info[0] = info[0].to_hg_int
454
-
455
- # I should probably do mah physics hw. nah, i'll do it
456
- # tomorrow during my break
457
- # good news - i did pretty well on my physics test by using
458
- # brian ford's name instead of my own.
459
- file = "#{file}\0#{@copy_map[file]}" if @copy_map[file]
460
- info = [info[0], 0, (-1).to_signed(32), (-1).to_signed(32)] if info[3].to_i > limit.to_i and info[0] == :normal
461
- info << file.size # the final element to make it pass, which is the length of the filename
462
- info = info.pack FORMAT # pack them their lunch
463
- si.write info # and send them off
464
- si.write file # to school
465
- end
466
-
467
- state.write si.string
468
- @dirty = false
469
- @dirty_parents = false
470
-
471
- true # success
472
- end
473
- rescue IOError
474
- false
475
- end
476
- end
477
-
478
- ##
479
- # Copies the files in h (represented as "source" => "dest").
480
- #
481
- # @param [Hash<String => String>] h the keys are sources and the values
482
- # are dests
483
- # @return [Boolean] a success marker
484
- def copy(h={})
485
- h.each do |source, dest|
486
- next if source == dest
487
- return true unless source
488
-
489
- @dirty = true
490
-
491
- if @copy_map[dest]
492
- then @copy_map.delete dest
493
- else @copy_map[dest] = source
494
- end
495
- end
496
-
497
- true # success
498
- end
499
-
500
- ##
501
- # The current directory from where the command is being called, with
502
- # the path shortened if it's within the repo.
503
- #
504
- # @return [String] effectively Dir.pwd
505
- def cwd
506
- path = Dir.pwd
507
- return '' if path == @root
508
-
509
- # return a more local path if possible...
510
- return path[@root.length..-1] if path.start_with? @root
511
- path # else we're outside the repo
512
- end
513
- alias_method :pwd, :cwd
514
-
515
- ##
516
- # Returns the relative path from +src+ to +dest+.
517
- #
518
- # @param [String] src This is a directory! If this is relative,
519
- # it is assumed to be relative to the root.
520
- # @param [String] dest This MUST be within root! It also is a file.
521
- # @return [String] the relative path
522
- def path_to(src, dest)
523
- # first, make both paths absolute, for ease of use.
524
- # @root is guarenteed to be absolute, so we're leethax here
525
- src = File.join @root, src
526
- dest = File.join @root, dest
527
-
528
- # lil' bit of error checking...
529
- [src, dest].map do |f|
530
- unless File.exist? f # does both files and directories...
531
- raise FileNotInRootError, "#{f} is not in the root, #{@root}"
532
- end
533
- end
534
-
535
- # now we find the differences
536
- # these both are now arrays!!!
537
- src = src.split '/'
538
- dest = dest.split '/'
539
-
540
- while src.first == dest.first
541
- src.shift and dest.shift
542
- end
543
-
544
- # now, src and dest are just where they differ
545
- path = ['..'] * src.size # we want to go back this many directories
546
- path += dest
547
- path.join '/' # tadah!
548
- end
549
-
550
- ##
551
- # Walk recursively through the directory tree, finding all
552
- # files matched by the regexp in match.
553
- #
554
- # Step 1: find all explicit files
555
- # Step 2: visit subdirectories
556
- # Step 3: report unseen items in the @files hash
557
- #
558
- # @param [Boolean] unknown
559
- # @param [Boolean] ignored
560
- # @return [Hash<String => [NilClass, File::Stat]>] nil for directories and
561
- # stuff, File::Stat for files and links
562
- def walk(unknown, ignored, match)
563
- files = match.files
564
-
565
- bad_type = proc do |file|
566
- UI::warn "#{file}: unsupported file type (type is #{File.ftype file})"
567
- end
568
-
569
- if ignored
570
- @ignore_all = false
571
- elsif not unknown
572
- @ignore_all = true
573
- end
574
-
575
- work = [@root]
576
-
577
- files = match.files ? match.files.uniq : [] # because [].uniq! is a major fuckup
578
-
579
- # why do we overwrite the entire array if it includes the current dir?
580
- # we even kill posisbly good things
581
- files = [''] if files.empty? || files.include?('.') # strange thing to do
582
- results = {'.hg' => true}
583
-
584
- # Step 1: find all explicit files
585
- files.sort.each do |file|
586
- next if results[file] || file == ""
587
-
588
- begin
589
- stats = File.lstat File.join(@root, file)
590
- kind = File.ftype File.join(@root, file)
591
-
592
- # we'll take it! but only if it's a directory, which means we have
593
- # more work to do...
594
- if kind == 'directory'
595
- # add it to the list of dirs we have to search in
596
- work << File.join(@root, file) unless ignoring_directory? file
597
- elsif kind == 'file' || kind == 'link'
598
- # ARGH WE FOUND ZE BOOTY
599
- results[file] = stats
600
- else
601
- # user you are a fuckup in life please exit the world
602
- bad_type[file]
603
- results[file] = nil if @files[file]
604
- end
605
- rescue => e
606
- keep = false
607
- prefix = file + '/'
608
-
609
- @files.each do |f, _|
610
- if f == file || f.start_with?(prefix)
611
- keep = true
612
- break
613
- end
614
- end
615
-
616
- unless keep
617
- bad_type[file]
618
- results[file] = nil if (@files[file] || !ignore(file)) && match.call(file)
619
- end
620
- end
621
- end
622
-
623
- # step 2: visit subdirectories in `work`
624
- until work.empty?
625
- dir = work.shift
626
- skip = nil
627
-
628
- if dir == '.'
629
- dir = ''
630
- else
631
- skip = '.hg'
632
- end
633
-
634
-
635
- dirs = Dir.glob("#{dir}/*", File::FNM_DOTMATCH) - ["#{dir}/.", "#{dir}/.."]
636
- entries = dirs.inject({}) do |h, f|
637
- h.merge f => [File.ftype(f), File.lstat(f)]
638
- end
639
-
640
-
641
- entries.each do |f, arr|
642
- tf = f[(@root.size+1)..-1]
643
- kind = arr[0]
644
- stats = arr[1]
645
- unless results[tf]
646
- if kind == 'directory'
647
- work << f unless ignore tf
648
- results[tf] = nil if @files[tf] && match.call(tf)
649
- elsif kind == 'file' || kind == 'link'
650
- if @files[tf]
651
- results[tf] = stats if match.call tf
652
- elsif match.call(tf) && !ignore(tf)
653
- results[tf] = stats
654
- end
655
- elsif @files[tf] && match.call(tf)
656
- results[tf] = nil
657
- end
658
- end
659
- end
660
- end
661
-
662
- # step 3: report unseen items in @files
663
- visit = @files.keys.select {|f| !results[f] && match.call(f) }.sort
664
-
665
- # zip it to a hash of {file_name => file_stats}
666
- hash = visit.inject({}) do |h, f|
667
- h.merge!(f => File.stat(File.join(@root,f))) rescue h.merge!(f => nil)
668
- end
669
-
670
- hash.each do |file, stat|
671
- unless stat.nil?
672
- # because filestats can't be gathered if it's, say, a directory
673
- stat = nil unless ['file', 'link'].include? File.ftype(File.join(@root, file))
674
- end
675
- results[file] = stat
676
- end
677
-
678
- results.delete ".hg"
679
- @ignore_all = nil # reset this
680
- results
681
- end
682
-
683
- ##
684
- # what's the current state of life, man!
685
- # Splits up all the files into modified, clean,
686
- # added, deleted, unknown, ignored, or lookup-needed.
687
- #
688
- # @return [Hash<Symbol => Array<String>>] a hash of the filestatuses and their files
689
- def status(ignored, clean, unknown, match = Match.new { true })
690
- list_ignored, list_clean, list_unknown = ignored, clean, unknown
691
- lookup, modified, added, unknown, ignored = [], [], [], [], []
692
- removed, deleted, clean = [], [], []
693
- delta = 0
694
-
695
- walk(list_unknown, list_ignored, match).each do |file, st|
696
- next if file.nil?
697
-
698
- unless @files[file]
699
- if list_ignored && ignoring_directory?(file)
700
- ignored << file
701
- elsif list_unknown
702
- unknown << file unless ignore(file)
703
- end
704
-
705
- next # on to the next one, don't do the rest
706
- end
707
-
708
- # here's where we split up the files
709
- state, mode, size, time = *@files[file].to_a
710
- delta += (size - st.size).abs if st && size >= 0 # increase the delta, but don't forget to check that it's not nil
711
- if !st && [:normal, :modified, :added].include?(state)
712
- # add it to the deleted folder if it should be here but isn't
713
- deleted << file
714
- elsif state == :normal
715
- if (size >= 0 && (size != st.size || ((mode ^ st.mode) & 0100 and @check_exec))) || size == -2 || @copy_map[file]
716
- modified << file
717
- elsif time != st.mtime.to_i # DOH - we have to remember that times are stored as fixnums
718
- lookup << file
719
- elsif list_clean
720
- clean << file
721
- end
722
-
723
- elsif state == :merged
724
- modified << file
725
- elsif state == :added
726
- added << file
727
- elsif state == :removed
728
- removed << file
729
- end
730
- end
731
-
732
- r = { :modified => modified.sort , # those that have clearly been modified
733
- :added => added.sort , # those that are marked for adding
734
- :removed => removed.sort , # those that are marked for removal
735
- :deleted => deleted.sort , # those that should be here but aren't
736
- :unknown => unknown.sort , # those that aren't being tracked
737
- :ignored => ignored.sort , # those that are being deliberately ignored
738
- :clean => clean.sort , # those that haven't changed
739
- :lookup => lookup.sort , # those that need to be content-checked to see if they've changed
740
- :delta => delta # how many bytes have been added or removed from files (not bytes that have been changed)
741
- }
742
- end
743
-
744
-
745
- ##
746
- # Reads the data in the .hg folder and fills in the vars
747
- #
748
- # @return [Amp::DirState] self -- chainable!
749
- def read!
750
- @parents, @files, @copy_map = parse('dirstate')
751
- self # chainable
752
- end
753
-
754
- private
755
- ##
756
- # Generates the @ignore array
757
- # The array is full of paths relative to the root, which
758
- # makes things easier for the proc-generation phase.
759
- #
760
- # @return [NilClass]
761
- def generate_ignore
762
- @ignore = @config['ui'].map do |k, v|
763
- @ignore << "#{v}" if k == "ignore"
764
- end
765
-
766
- @ignore << ".hgignore"
767
- @ignore.compact
768
-
769
- nil
770
- end
771
-
772
- ##
773
- # Perform various checks on the file before upping the content count
774
- # for all of its parent directories. It checks for:
775
- # * filenames containing "\n" or "\r" (newlines and carriage returns)
776
- # * filenames with the same names as directories
777
- # * clashing filenames
778
- #
779
- # It only increments the dirs' file count if the file is untracked or
780
- # being removed.
781
- #
782
- # @param [String] f Should be formatted like ["action", mode, size, mtime]
783
- # @param [Boolean] check whether to perform any of the checks
784
- # @return [NilClass]
785
- def add_path(f, check=false)
786
- old_state = @files[f] || DirStateEntry.new # it's an array of info, remember
787
-
788
- if check || old_state.removed?
789
- raise "Bad Filename" if f.match(/\r|\n/)
790
- raise "Directory #{f} already exists" if @dirs[f]
791
-
792
- # make sure we don't have any files with the same name as a directory
793
- directories_to(f).each do |d|
794
- break if @dirs[d]
795
-
796
- if @files[d] && !@files[d].removed?
797
- raise "File #{d} clashes with #{f}! Fix their names"
798
- end
799
- end
800
- end
801
-
802
- # only inc the dirs if the file is untracked or being removed.
803
- if [:untracked, :removed].include? old_state.status
804
- # inc the number of dirs in each dir
805
- inc_directories_to f
806
- end
807
-
808
- nil
809
- end
810
-
811
- ##
812
- # Conditional wrapper around +dec_directories_to+. It will dec the
813
- # directories if the file in question (+f+) is either untracked or
814
- # being removed.
815
- #
816
- # @param [String] f Should be formatted like ["action", mode, size, mtime]
817
- # @return [NilClass]
818
- def drop_path(f)
819
- unless [:untracked, :removed].include? f[0]
820
- dec_directories_to(f)
821
- end
822
-
823
- nil
824
- end
825
-
826
- ##
827
- # All directories leading up to this path
828
- #
829
- # @example directories_to "/Users/ari/src/monkey.txt" # =>
830
- # ["/Users/ari/src", "/Users/ari", "/Users"]
831
- # @param [String] path the path to the file we're examining
832
- # @return [Array] the directories leading up to this path
833
- def directories_to(path)
834
- File.amp_directories_to path
835
- end
836
-
837
- ##
838
- # Increment all directories' dir-count leading up to this path.
839
- # The dir-count is the path's value in @dirs.
840
- # This is used when adding a file.
841
- #
842
- # @param [String] path the path we're disecting
843
- # @return [NilClass]
844
- def inc_directories_to(path)
845
- p = directories_to(path).first
846
- @dirs[p] ||= 0
847
- @dirs[p] += 1
848
- nil
849
- end
850
-
851
- ##
852
- # Decrement all directories' dir-count leading up to this path.
853
- # The dir-count is the path's value in @dirs.
854
- # This is used when removing a file.
855
- #
856
- # @param [String] path the path we're disecting
857
- # @return [NilClass]
858
- def dec_directories_to(path)
859
- p = directories_to(path).first
860
- # if the dir has 0, kill the dir. we don't need it anymore
861
- if @dirs[p] && @dirs[p].zero?
862
- @dirs.delete p
863
- elsif @dirs[p]
864
- @dirs[p] -= 1 # we only need to inc the latest dir
865
- end
866
-
867
- nil
868
- end
869
-
870
- ##
871
- # I wish I knew what this did or when it was called.
872
- #
873
- # @todo figure out what this does
874
- # @param [String] path the path to a file
875
- # @return [String] All I know is that this returns a string
876
- def normalize(path)
877
- fold_path = @folds[path]
878
- fold_path = path unless fold_path # if fold_path is true, then this line returns nil
879
- fold_path # so we need an extra line here to make sure it returns a good value
880
- end
881
-
882
- ##
883
- # Are we ignoring the directory?
884
- #
885
- # @param [String] dir the directory we're checking, either aboslute or relative
886
- # @return [Boolean] are we ignoring the dir?
887
- def ignoring_directory?(dir)
888
- return true if @ignore_all
889
- return false if @ignore_all == false
890
- return false if dir == '.' # base cases
891
- return true if ignore dir # base cases
892
-
893
- !!directories_to(dir).any? {|d| ignore d }
894
- end
895
- alias_method :ignoring_dir?, :ignoring_directory?
896
-
897
- ##
898
- # Parses the dirstate file in .hg
899
- #
900
- # @param [String] file path to the file to parse
901
- # @return [((String, String), Hash<String => (Integer, Integer, Integer)>, Hash<String => String>)]
902
- # a tuple of (parents, files, copies). Parents is a tuple of the parents,
903
- # files is a hash of filename => [mode, size, mtime], and copies is a hash of src => dest
904
- def parse(file)
905
- # the main data we need to return
906
- files = {}
907
- copies = {}
908
- parents = []
909
- @opener.open file, "r" do |s|
910
-
911
- # the parents are the first 40 bytes
912
- parent = s.read(20) || NULL_ID
913
- parent_ = s.read(20) || NULL_ID
914
- parents = [parent, parent_]
915
-
916
- # 1 character + 4 32-bit ints = 17 bytes
917
- e_size = 17
918
-
919
- # this loop is just cycling through and reading every entry
920
- while !s.eof?
921
- # read 1 entry
922
- info = s.read(e_size).unpack FORMAT
923
-
924
- # byte swap and shizzle
925
- info = [info[0].to_dirstate_symbol, info[1], info[2].to_signed(32), info[3].to_signed(32), info[4]]
926
- # ^^^^ we have to sign them because otherwise they'll be hugely wrong
927
-
928
- # read in the filename
929
- f = s.read(info[4])
930
-
931
- # if it has an \0, then we've moved/copied it
932
- if f.match(/\0/)
933
- source, dest = f.split "\0"
934
- copies[source] = dest
935
- f = source
936
- end
937
-
938
- # and put in the info for the file itself
939
- files[f] = DirStateEntry.new(*info[0..3])
940
- end
941
- end
942
-
943
- [parents, files, copies]
944
- rescue Errno::ENOENT
945
- # no file? easy peasy
946
- [[NULL_ID, NULL_ID], {}, {}]
947
- end
948
- end
949
- end
950
- end