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.
- data/.gitignore +12 -0
- data/.hgignore +3 -0
- data/AUTHORS +1 -1
- data/Manifest.txt +99 -38
- data/README.md +3 -3
- data/Rakefile +53 -18
- data/SCHEDULE.markdown +5 -1
- data/TODO.markdown +120 -149
- data/ampfile.rb +3 -1
- data/bin/amp +4 -1
- data/ext/amp/bz2/extconf.rb +1 -1
- data/ext/amp/mercurial_patch/extconf.rb +1 -1
- data/ext/amp/mercurial_patch/mpatch.c +4 -3
- data/ext/amp/priority_queue/extconf.rb +1 -1
- data/ext/amp/support/extconf.rb +1 -1
- data/ext/amp/support/support.c +1 -1
- data/lib/amp.rb +125 -67
- data/lib/amp/commands/command.rb +12 -10
- data/lib/amp/commands/command_support.rb +8 -1
- data/lib/amp/commands/commands/help.rb +2 -20
- data/lib/amp/commands/commands/init.rb +14 -2
- data/lib/amp/commands/commands/templates.rb +6 -4
- data/lib/amp/commands/commands/version.rb +15 -1
- data/lib/amp/commands/commands/workflow.rb +3 -3
- data/lib/amp/commands/commands/workflows/git/add.rb +3 -3
- data/lib/amp/commands/commands/workflows/git/copy.rb +1 -1
- data/lib/amp/commands/commands/workflows/git/rm.rb +4 -2
- data/lib/amp/commands/commands/workflows/hg/add.rb +1 -1
- data/lib/amp/commands/commands/workflows/hg/addremove.rb +2 -2
- data/lib/amp/commands/commands/workflows/hg/annotate.rb +8 -2
- data/lib/amp/commands/commands/workflows/hg/bisect.rb +253 -0
- data/lib/amp/commands/commands/workflows/hg/branch.rb +1 -1
- data/lib/amp/commands/commands/workflows/hg/branches.rb +3 -3
- data/lib/amp/commands/commands/workflows/hg/bundle.rb +3 -3
- data/lib/amp/commands/commands/workflows/hg/clone.rb +4 -5
- data/lib/amp/commands/commands/workflows/hg/commit.rb +37 -1
- data/lib/amp/commands/commands/workflows/hg/copy.rb +2 -1
- data/lib/amp/commands/commands/workflows/hg/debug/index.rb +1 -1
- data/lib/amp/commands/commands/workflows/hg/diff.rb +3 -8
- data/lib/amp/commands/commands/workflows/hg/forget.rb +5 -4
- data/lib/amp/commands/commands/workflows/hg/identify.rb +6 -6
- data/lib/amp/commands/commands/workflows/hg/import.rb +1 -1
- data/lib/amp/commands/commands/workflows/hg/incoming.rb +2 -2
- data/lib/amp/commands/commands/workflows/hg/log.rb +5 -4
- data/lib/amp/commands/commands/workflows/hg/merge.rb +1 -1
- data/lib/amp/commands/commands/workflows/hg/move.rb +5 -3
- data/lib/amp/commands/commands/workflows/hg/outgoing.rb +1 -1
- data/lib/amp/commands/commands/workflows/hg/push.rb +6 -7
- data/lib/amp/commands/commands/workflows/hg/remove.rb +2 -2
- data/lib/amp/commands/commands/workflows/hg/resolve.rb +6 -23
- data/lib/amp/commands/commands/workflows/hg/root.rb +1 -2
- data/lib/amp/commands/commands/workflows/hg/status.rb +21 -12
- data/lib/amp/commands/commands/workflows/hg/tag.rb +2 -2
- data/lib/amp/commands/commands/workflows/hg/untrack.rb +12 -0
- data/lib/amp/commands/commands/workflows/hg/verify.rb +13 -3
- data/lib/amp/commands/commands/workflows/hg/what_changed.rb +18 -0
- data/lib/amp/commands/dispatch.rb +12 -13
- data/lib/amp/dependencies/amp_support.rb +1 -1
- data/lib/amp/dependencies/amp_support/ruby_amp_support.rb +1 -0
- data/lib/amp/dependencies/maruku.rb +136 -0
- data/lib/amp/dependencies/maruku/attributes.rb +227 -0
- data/lib/amp/dependencies/maruku/defaults.rb +71 -0
- data/lib/amp/dependencies/maruku/errors_management.rb +92 -0
- data/lib/amp/dependencies/maruku/helpers.rb +260 -0
- data/lib/amp/dependencies/maruku/input/charsource.rb +326 -0
- data/lib/amp/dependencies/maruku/input/extensions.rb +69 -0
- data/lib/amp/dependencies/maruku/input/html_helper.rb +189 -0
- data/lib/amp/dependencies/maruku/input/linesource.rb +111 -0
- data/lib/amp/dependencies/maruku/input/parse_block.rb +615 -0
- data/lib/amp/dependencies/maruku/input/parse_doc.rb +234 -0
- data/lib/amp/dependencies/maruku/input/parse_span_better.rb +746 -0
- data/lib/amp/dependencies/maruku/input/rubypants.rb +225 -0
- data/lib/amp/dependencies/maruku/input/type_detection.rb +147 -0
- data/lib/amp/dependencies/maruku/input_textile2/t2_parser.rb +163 -0
- data/lib/amp/dependencies/maruku/maruku.rb +33 -0
- data/lib/amp/dependencies/maruku/output/to_ansi.rb +223 -0
- data/lib/amp/dependencies/maruku/output/to_html.rb +991 -0
- data/lib/amp/dependencies/maruku/output/to_markdown.rb +164 -0
- data/lib/amp/dependencies/maruku/output/to_s.rb +56 -0
- data/lib/amp/dependencies/maruku/string_utils.rb +191 -0
- data/lib/amp/dependencies/maruku/structures.rb +167 -0
- data/lib/amp/dependencies/maruku/structures_inspect.rb +87 -0
- data/lib/amp/dependencies/maruku/structures_iterators.rb +61 -0
- data/lib/amp/dependencies/maruku/textile2.rb +1 -0
- data/lib/amp/dependencies/maruku/toc.rb +199 -0
- data/lib/amp/dependencies/maruku/usage/example1.rb +33 -0
- data/lib/amp/dependencies/maruku/version.rb +40 -0
- data/lib/amp/dependencies/priority_queue.rb +2 -1
- data/lib/amp/dependencies/python_config.rb +2 -1
- data/lib/amp/graphs/ancestor.rb +2 -1
- data/lib/amp/graphs/copies.rb +236 -233
- data/lib/amp/help/entries/__default__.erb +31 -0
- data/lib/amp/help/entries/commands.erb +6 -0
- data/lib/amp/help/entries/mdtest.md +35 -0
- data/lib/amp/help/entries/silly +3 -0
- data/lib/amp/help/help.rb +288 -0
- data/lib/amp/profiling_hacks.rb +5 -3
- data/lib/amp/repository/abstract/abstract_changeset.rb +97 -0
- data/lib/amp/repository/abstract/abstract_local_repo.rb +181 -0
- data/lib/amp/repository/abstract/abstract_staging_area.rb +180 -0
- data/lib/amp/repository/abstract/abstract_versioned_file.rb +100 -0
- data/lib/amp/repository/abstract/common_methods/changeset.rb +75 -0
- data/lib/amp/repository/abstract/common_methods/local_repo.rb +277 -0
- data/lib/amp/repository/abstract/common_methods/staging_area.rb +233 -0
- data/lib/amp/repository/abstract/common_methods/versioned_file.rb +71 -0
- data/lib/amp/repository/generic_repo_picker.rb +78 -0
- data/lib/amp/repository/git/repo_format/changeset.rb +336 -0
- data/lib/amp/repository/git/repo_format/staging_area.rb +192 -0
- data/lib/amp/repository/git/repo_format/versioned_file.rb +119 -0
- data/lib/amp/repository/git/repositories/local_repository.rb +164 -0
- data/lib/amp/repository/git/repository.rb +41 -0
- data/lib/amp/repository/mercurial/encoding/mercurial_diff.rb +382 -0
- data/lib/amp/repository/mercurial/encoding/mercurial_patch.rb +1 -0
- data/lib/amp/repository/mercurial/encoding/patch.rb +294 -0
- data/lib/amp/repository/mercurial/encoding/pure_ruby/ruby_mercurial_patch.rb +124 -0
- data/lib/amp/repository/mercurial/merging/merge_ui.rb +327 -0
- data/lib/amp/repository/mercurial/merging/simple_merge.rb +452 -0
- data/lib/amp/repository/mercurial/repo_format/branch_manager.rb +266 -0
- data/lib/amp/repository/mercurial/repo_format/changeset.rb +768 -0
- data/lib/amp/repository/mercurial/repo_format/dir_state.rb +716 -0
- data/lib/amp/repository/mercurial/repo_format/journal.rb +218 -0
- data/lib/amp/repository/mercurial/repo_format/lock.rb +210 -0
- data/lib/amp/repository/mercurial/repo_format/merge_state.rb +228 -0
- data/lib/amp/repository/mercurial/repo_format/staging_area.rb +367 -0
- data/lib/amp/repository/mercurial/repo_format/store.rb +487 -0
- data/lib/amp/repository/mercurial/repo_format/tag_manager.rb +322 -0
- data/lib/amp/repository/mercurial/repo_format/updatable.rb +543 -0
- data/lib/amp/repository/mercurial/repo_format/updater.rb +848 -0
- data/lib/amp/repository/mercurial/repo_format/verification.rb +433 -0
- data/lib/amp/repository/mercurial/repositories/bundle_repository.rb +216 -0
- data/lib/amp/repository/mercurial/repositories/http_repository.rb +386 -0
- data/lib/amp/repository/mercurial/repositories/local_repository.rb +2034 -0
- data/lib/amp/repository/mercurial/repository.rb +119 -0
- data/lib/amp/repository/mercurial/revlogs/bundle_revlogs.rb +249 -0
- data/lib/amp/repository/mercurial/revlogs/changegroup.rb +217 -0
- data/lib/amp/repository/mercurial/revlogs/changelog.rb +339 -0
- data/lib/amp/repository/mercurial/revlogs/file_log.rb +152 -0
- data/lib/amp/repository/mercurial/revlogs/index.rb +500 -0
- data/lib/amp/repository/mercurial/revlogs/manifest.rb +201 -0
- data/lib/amp/repository/mercurial/revlogs/node.rb +20 -0
- data/lib/amp/repository/mercurial/revlogs/revlog.rb +1026 -0
- data/lib/amp/repository/mercurial/revlogs/revlog_support.rb +129 -0
- data/lib/amp/repository/mercurial/revlogs/versioned_file.rb +597 -0
- data/lib/amp/repository/repository.rb +11 -88
- data/lib/amp/server/extension/amp_extension.rb +3 -3
- data/lib/amp/server/fancy_http_server.rb +1 -1
- data/lib/amp/server/fancy_views/_browser.haml +1 -1
- data/lib/amp/server/fancy_views/_diff_file.haml +1 -8
- data/lib/amp/server/fancy_views/changeset.haml +2 -2
- data/lib/amp/server/fancy_views/file.haml +1 -1
- data/lib/amp/server/fancy_views/file_diff.haml +1 -1
- data/lib/amp/support/amp_ui.rb +13 -29
- data/lib/amp/support/generator.rb +1 -1
- data/lib/amp/support/loaders.rb +1 -2
- data/lib/amp/support/logger.rb +10 -16
- data/lib/amp/support/match.rb +18 -4
- data/lib/amp/support/mercurial/ignore.rb +151 -0
- data/lib/amp/support/openers.rb +8 -3
- data/lib/amp/support/support.rb +91 -46
- data/lib/amp/templates/{blank.commit.erb → mercurial/blank.commit.erb} +0 -0
- data/lib/amp/templates/{blank.log.erb → mercurial/blank.log.erb} +0 -0
- data/lib/amp/templates/{default.commit.erb → mercurial/default.commit.erb} +0 -0
- data/lib/amp/templates/{default.log.erb → mercurial/default.log.erb} +0 -0
- data/lib/amp/templates/template.rb +18 -18
- data/man/amp.1 +51 -0
- data/site/src/about/commands.haml +1 -1
- data/site/src/css/amp.css +1 -1
- data/site/src/index.haml +3 -3
- data/tasks/man.rake +39 -0
- data/tasks/stats.rake +1 -10
- data/tasks/yard.rake +1 -50
- data/test/dirstate_tests/test_dir_state.rb +10 -8
- data/test/functional_tests/annotate.out +31 -0
- data/test/functional_tests/test_functional.rb +155 -63
- data/test/localrepo_tests/ampfile.rb +12 -0
- data/test/localrepo_tests/test_local_repo.rb +56 -57
- data/test/manifest_tests/test_manifest.rb +3 -5
- data/test/merge_tests/test_merge.rb +3 -3
- data/test/revlog_tests/test_revlog.rb +14 -6
- data/test/store_tests/test_fncache_store.rb +19 -19
- data/test/test_19_compatibility.rb +46 -0
- data/test/test_base85.rb +2 -2
- data/test/test_bdiff.rb +2 -2
- data/test/test_changegroup.rb +59 -0
- data/test/test_commands.rb +2 -2
- data/test/test_difflib.rb +2 -2
- data/test/test_generator.rb +34 -0
- data/test/test_ignore.rb +203 -0
- data/test/test_journal.rb +18 -13
- data/test/test_match.rb +2 -2
- data/test/test_mdiff.rb +3 -3
- data/test/test_mpatch.rb +3 -3
- data/test/test_multi_io.rb +40 -0
- data/test/test_support.rb +18 -2
- data/test/test_templates.rb +38 -0
- data/test/test_ui.rb +79 -0
- data/test/testutilities.rb +56 -0
- metadata +168 -49
- data/ext/amp/bz2/mkmf.log +0 -38
- data/lib/amp/encoding/mercurial_diff.rb +0 -378
- data/lib/amp/encoding/mercurial_patch.rb +0 -1
- data/lib/amp/encoding/patch.rb +0 -292
- data/lib/amp/encoding/pure_ruby/ruby_mercurial_patch.rb +0 -123
- data/lib/amp/merges/merge_state.rb +0 -164
- data/lib/amp/merges/merge_ui.rb +0 -322
- data/lib/amp/merges/simple_merge.rb +0 -450
- data/lib/amp/repository/branch_manager.rb +0 -234
- data/lib/amp/repository/dir_state.rb +0 -950
- data/lib/amp/repository/journal.rb +0 -203
- data/lib/amp/repository/lock.rb +0 -207
- data/lib/amp/repository/repositories/bundle_repository.rb +0 -214
- data/lib/amp/repository/repositories/http_repository.rb +0 -377
- data/lib/amp/repository/repositories/local_repository.rb +0 -2661
- data/lib/amp/repository/store.rb +0 -485
- data/lib/amp/repository/tag_manager.rb +0 -319
- data/lib/amp/repository/updatable.rb +0 -532
- data/lib/amp/repository/verification.rb +0 -431
- data/lib/amp/repository/versioned_file.rb +0 -475
- data/lib/amp/revlogs/bundle_revlogs.rb +0 -246
- data/lib/amp/revlogs/changegroup.rb +0 -217
- data/lib/amp/revlogs/changelog.rb +0 -338
- data/lib/amp/revlogs/changeset.rb +0 -521
- data/lib/amp/revlogs/file_log.rb +0 -165
- data/lib/amp/revlogs/index.rb +0 -493
- data/lib/amp/revlogs/manifest.rb +0 -195
- data/lib/amp/revlogs/node.rb +0 -18
- data/lib/amp/revlogs/revlog.rb +0 -1045
- data/lib/amp/revlogs/revlog_support.rb +0 -126
- data/lib/amp/support/ignore.rb +0 -144
- data/site/Rakefile +0 -38
- data/test/test_amp.rb +0 -9
- 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
|