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
|
@@ -0,0 +1,716 @@
|
|
|
1
|
+
module Amp
|
|
2
|
+
module Repositories
|
|
3
|
+
module Mercurial
|
|
4
|
+
|
|
5
|
+
##
|
|
6
|
+
# An entry in the dirstate. Similar to IndexEntry for revlogs. Simple struct, that's
|
|
7
|
+
# all.
|
|
8
|
+
class DirStateEntry < Struct.new(:status, :mode, :size, :mtime)
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
# shortcuts!
|
|
12
|
+
def removed?; self.status == :removed; end
|
|
13
|
+
def added?; self.status == :added; end
|
|
14
|
+
def untracked?; self.status == :untracked; end
|
|
15
|
+
def modified?; self.status == :modified; end
|
|
16
|
+
def merged?; self.status == :merged; end
|
|
17
|
+
def normal?; self.status == :normal; end
|
|
18
|
+
def forgotten?; self.status == :forgotten; end
|
|
19
|
+
|
|
20
|
+
##
|
|
21
|
+
# Do I represent a dirty object?
|
|
22
|
+
#
|
|
23
|
+
# @return [Boolean] does this array represent a dirty object in a DirState?
|
|
24
|
+
def dirty?
|
|
25
|
+
self[-2] == -2 && self[-1] == -1 && self.normal?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
##
|
|
29
|
+
# Do I possibly represent a dirty object?
|
|
30
|
+
#
|
|
31
|
+
# @return [Boolean] does this array possibly represent a dirty object in a DirState?
|
|
32
|
+
def maybe_dirty?
|
|
33
|
+
self[-2] == -1 && self[-1] == -1 && self.normal?
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
##
|
|
38
|
+
# = DirState
|
|
39
|
+
# This class handles parsing and manipulating the "dirstate" file, which is stored
|
|
40
|
+
# in the .hg folder. This file handles which files are marked for addition, removal,
|
|
41
|
+
# copies, and so on. The structure of each entry is below.
|
|
42
|
+
#
|
|
43
|
+
#
|
|
44
|
+
# class DirStateEntry < BitStruct
|
|
45
|
+
# default_options :endian => :network
|
|
46
|
+
#
|
|
47
|
+
# char :status , 8, "the state of the file"
|
|
48
|
+
# signed :mode , 32, "mode"
|
|
49
|
+
# signed :size , 32, "size"
|
|
50
|
+
# signed :mtime , 32, "mtime"
|
|
51
|
+
# signed :fname_size , 32, "filename size"
|
|
52
|
+
#
|
|
53
|
+
# end
|
|
54
|
+
class DirState
|
|
55
|
+
include Amp::Mercurial::Ignore
|
|
56
|
+
include Amp::Mercurial::RevlogSupport::Node
|
|
57
|
+
|
|
58
|
+
UNKNOWN = DirStateEntry.new(:untracked, 0, 0, 0)
|
|
59
|
+
FORMAT = "cNNNN"
|
|
60
|
+
|
|
61
|
+
class FileNotInRootError < StandardError; end
|
|
62
|
+
class AbsolutePathNeededError < StandardError; end
|
|
63
|
+
|
|
64
|
+
# The parents of the current state. If there's been an uncommitted merge,
|
|
65
|
+
# it will be two. Otherwise it will just be one parent and +NULL_ID+
|
|
66
|
+
attr_reader :parents
|
|
67
|
+
|
|
68
|
+
# The number of directories in each base ["dir" => #_of_dirs]
|
|
69
|
+
attr_reader :dirs
|
|
70
|
+
|
|
71
|
+
# The files mapped to their stats (state, mode, size, mtime)
|
|
72
|
+
# [state, mode, size, mtime]
|
|
73
|
+
attr_reader :files
|
|
74
|
+
|
|
75
|
+
# A map of files to be copied, because we want to preserve their history
|
|
76
|
+
# "dest" => "source"
|
|
77
|
+
attr_reader :copy_map
|
|
78
|
+
|
|
79
|
+
# I still don't know what this does
|
|
80
|
+
attr_reader :folds
|
|
81
|
+
|
|
82
|
+
# The conglomerate config object of global configs and the repo
|
|
83
|
+
# specific config.
|
|
84
|
+
attr_reader :config
|
|
85
|
+
|
|
86
|
+
# The root of the repository
|
|
87
|
+
attr_reader :root
|
|
88
|
+
|
|
89
|
+
# The opener to access files. The only files that will be touched lie
|
|
90
|
+
# in the .hg/ directory, so the default MUST be +:open_hg+.
|
|
91
|
+
attr_reader :opener
|
|
92
|
+
|
|
93
|
+
##
|
|
94
|
+
# Creates a DirState object. This is used to represent, in memory (and
|
|
95
|
+
# occasionally on file) how the repository is being changed.
|
|
96
|
+
# It's really simple, and it is really the basis for _using_ the repo
|
|
97
|
+
# (contrary to how Revlog is the basis for _saving_ the repo).
|
|
98
|
+
#
|
|
99
|
+
# @param [String] root the absolute path to the root of the repository
|
|
100
|
+
# @param [Amp::AmpConfig] config the config file of hgrc
|
|
101
|
+
# @param [Amp::Opener] opener the opener to open files with
|
|
102
|
+
def initialize(root, config, opener)
|
|
103
|
+
unless root[0, 1] == "/"
|
|
104
|
+
raise AbsolutePathNeededError, "#{root} is not an absolute path!"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# root must be an aboslute path with no ending slash
|
|
108
|
+
@root = root[-1, 1] == "/" ? root[0..-2] : root # the root of the repo
|
|
109
|
+
@config = config # the config file where we get defaults
|
|
110
|
+
@opener = opener # opener to retrieve files (default: open_hg)
|
|
111
|
+
@dirty = false # has something changed, and do we need to write?
|
|
112
|
+
@dirty_parents = false
|
|
113
|
+
@parents = [NULL_ID, NULL_ID] # the parent revisions
|
|
114
|
+
@dirs = {} # number of directories in each base ["dir" => #_of_dirs]
|
|
115
|
+
@files = {} # the files mapped to their statistics
|
|
116
|
+
@copy_map = {} # src => dest
|
|
117
|
+
@ignore = [] # dirs and files to ignore
|
|
118
|
+
@folds = []
|
|
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.
|
|
159
|
+
#
|
|
160
|
+
# @param [String] file the path to the file that will be checked by
|
|
161
|
+
# the .hgignore file
|
|
162
|
+
# @return [Boolean] whether we're ignoring the path or not
|
|
163
|
+
def ignore(file)
|
|
164
|
+
@ignore_matches ||= parse_ignore @root, @ignore
|
|
165
|
+
@ignore_matches.call file
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
##
|
|
169
|
+
# Gets the current branch.
|
|
170
|
+
#
|
|
171
|
+
# @return [String] the current branch in the working directory
|
|
172
|
+
def branch
|
|
173
|
+
text = @opener.read('branch').strip
|
|
174
|
+
@branch ||= text.empty? ? "default" : text
|
|
175
|
+
rescue
|
|
176
|
+
@branch = "default"
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
##
|
|
180
|
+
# Set the branch to +branch+.
|
|
181
|
+
#
|
|
182
|
+
# @param [#to_s] brnch the branch to switch to
|
|
183
|
+
# @return [String] +brnch+.to_s
|
|
184
|
+
def branch=(brnch)
|
|
185
|
+
@branch = brnch.to_s
|
|
186
|
+
|
|
187
|
+
@opener.open 'branch', 'w' do |f|
|
|
188
|
+
f.puts brnch.to_s
|
|
189
|
+
end
|
|
190
|
+
@branch
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
##
|
|
194
|
+
# Set the parents to +p+
|
|
195
|
+
#
|
|
196
|
+
# @param [Array<String>] p the parents as binary strings
|
|
197
|
+
# @return [Array<String>] the parents, as will be used by the dirstate
|
|
198
|
+
def parents=(p)
|
|
199
|
+
@parents = if p.is_a? Array
|
|
200
|
+
p.size == 1 ? p + [NULL_ID] : p[0..1]
|
|
201
|
+
else
|
|
202
|
+
[p, NULL_ID]
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
@dirty_parents = true
|
|
206
|
+
@dirty = true
|
|
207
|
+
@parents # return this
|
|
208
|
+
end
|
|
209
|
+
alias_method :parent, :parents
|
|
210
|
+
|
|
211
|
+
##
|
|
212
|
+
# Set the file as "to be added".
|
|
213
|
+
#
|
|
214
|
+
# @param [String] file the path of the file to add
|
|
215
|
+
# @return [Boolean] a success marker
|
|
216
|
+
def add(*files)
|
|
217
|
+
files.each do |file|
|
|
218
|
+
state = self[file]
|
|
219
|
+
if state.added? || state.modified? || state.normal?
|
|
220
|
+
# fail if it's being tracked
|
|
221
|
+
UI.warn "#{file} already tracked!"
|
|
222
|
+
elsif state.removed?
|
|
223
|
+
# check back on it if it's being removed
|
|
224
|
+
normal_lookup file
|
|
225
|
+
else
|
|
226
|
+
# else add it
|
|
227
|
+
add_path file, true
|
|
228
|
+
|
|
229
|
+
@dirty = true
|
|
230
|
+
@files[file] = DirStateEntry.new(:added, 0, -1, -1)
|
|
231
|
+
@copy_map.delete file
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
true # success
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
##
|
|
238
|
+
# Set the file as "normal", meaning no changes. This is the same
|
|
239
|
+
# as dirstate.normal in dirstate.py, for those referencing both.
|
|
240
|
+
#
|
|
241
|
+
# @param [String] file the path of the file to clean
|
|
242
|
+
# @return [Boolean] a success marker
|
|
243
|
+
def normal(file)
|
|
244
|
+
@dirty = true
|
|
245
|
+
add_path file, true
|
|
246
|
+
|
|
247
|
+
f = File.lstat "#{@root}/#{file}"
|
|
248
|
+
@files[file] = DirStateEntry.new(:normal, f.mode, f.size, f.mtime.to_i)
|
|
249
|
+
@copy_map.delete file
|
|
250
|
+
true # success
|
|
251
|
+
end
|
|
252
|
+
alias_method :clean, :normal
|
|
253
|
+
|
|
254
|
+
##
|
|
255
|
+
# Set the file as normal, but possibly dirty. It's like when you
|
|
256
|
+
# meet a cool girl, and she seems really innocent and it's a chance
|
|
257
|
+
# for you to maybe change yourself and make a new friend, but then
|
|
258
|
+
# she *might* actually be a total slut. Better milk that grapevine
|
|
259
|
+
# to find out the truth. Oddly specific, huh.
|
|
260
|
+
#
|
|
261
|
+
# THUS IS THE HISTORY OF THIS METHOD!
|
|
262
|
+
#
|
|
263
|
+
# And then one day you go to the movies with some other girl, and the
|
|
264
|
+
# original crazy slutty girl is the cashier next to you. Unsure of
|
|
265
|
+
# what to do, you don't do anything. Next thing you know, she's trying
|
|
266
|
+
# to get your attention to say hey. WTF? Anyone know what's up with this
|
|
267
|
+
# girl?
|
|
268
|
+
#
|
|
269
|
+
# After milking that grapevine, you find out that she's not a great person.
|
|
270
|
+
# There's nothing interesting there and you should just move on.
|
|
271
|
+
#
|
|
272
|
+
# *sigh* girls.
|
|
273
|
+
#
|
|
274
|
+
# @param [String] file the path of the file to mark
|
|
275
|
+
# @return [Boolean] a success marker
|
|
276
|
+
def maybe_dirty(file)
|
|
277
|
+
if @files[file] && @parents.last != NULL_ID
|
|
278
|
+
# if there's a merge happening and the file was either modified
|
|
279
|
+
# or dirty before being removed, restore that state.
|
|
280
|
+
# I'm quoting the python with that one.
|
|
281
|
+
# I guess what it's saying is that if a file is being removed
|
|
282
|
+
# by a merge, but it was altered somehow beforehand on the local
|
|
283
|
+
# repo, then play it safe and bring back the dead. Divine intervention
|
|
284
|
+
# on the side of the local repo.
|
|
285
|
+
|
|
286
|
+
# info here is a standard array of info
|
|
287
|
+
# [action, mode, size, mtime]
|
|
288
|
+
info = @files[file]
|
|
289
|
+
|
|
290
|
+
if info.removed? and [-1, -2].member? info.size
|
|
291
|
+
source = @copy_map[file]
|
|
292
|
+
|
|
293
|
+
# do the appropriate action
|
|
294
|
+
case info.size
|
|
295
|
+
when -1 # either merge it
|
|
296
|
+
merge file
|
|
297
|
+
when -2 # or mark it as dirty
|
|
298
|
+
dirty file
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
copy source => file if source
|
|
302
|
+
return
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
# next step... the base case!
|
|
306
|
+
return true if info.modified? || info.maybe_dirty? and info.size == -2
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
@dirty = true # make the repo dirty
|
|
310
|
+
add_path file # add the file
|
|
311
|
+
|
|
312
|
+
@files[file] = DirStateEntry.new(:normal, 0, -1, -1) # give it info
|
|
313
|
+
@copy_map.delete file # we're not copying it since we're adding it
|
|
314
|
+
true # success
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
##
|
|
318
|
+
# Checks whether the dirstate is tracking the given file.
|
|
319
|
+
#
|
|
320
|
+
# @param f the file to check for
|
|
321
|
+
# @return [Boolean] whether or not the file is being tracked.
|
|
322
|
+
def include?(path)
|
|
323
|
+
not @files[path].nil?
|
|
324
|
+
end
|
|
325
|
+
alias_method :tracking?, :include?
|
|
326
|
+
|
|
327
|
+
##
|
|
328
|
+
# Mark the file as "dirty"
|
|
329
|
+
#
|
|
330
|
+
# @param [String] file the path of the file to mark
|
|
331
|
+
# @return [Boolean] a success marker
|
|
332
|
+
def dirty(file)
|
|
333
|
+
@dirty = true
|
|
334
|
+
add_path file
|
|
335
|
+
|
|
336
|
+
@files[file] = DirStateEntry.new(:normal, 0, -2, -1)
|
|
337
|
+
@copy_map.delete file
|
|
338
|
+
true # success
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
##
|
|
342
|
+
# Set the file as "to be removed"
|
|
343
|
+
#
|
|
344
|
+
# @param [String] file the path of the file to remove
|
|
345
|
+
# @return [Boolean] a success marker
|
|
346
|
+
def remove(file)
|
|
347
|
+
if self[file].added?
|
|
348
|
+
# Is it already added? if so, forgettaboutit
|
|
349
|
+
forget file
|
|
350
|
+
true # success!
|
|
351
|
+
elsif !tracking?(file)
|
|
352
|
+
# Are we not even tracking this file? dumbass
|
|
353
|
+
UI.warn("#{file} not being tracked!")
|
|
354
|
+
false # no success
|
|
355
|
+
else
|
|
356
|
+
# Woooo we can delete it
|
|
357
|
+
@dirty = true
|
|
358
|
+
drop_path file
|
|
359
|
+
|
|
360
|
+
size = 0
|
|
361
|
+
if @parents.last.null? && (info = @files[file])
|
|
362
|
+
if info.merged?
|
|
363
|
+
size = -1
|
|
364
|
+
elsif info.normal? && info.size == -2
|
|
365
|
+
size = -2
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
@files[file] = DirStateEntry.new(:removed, 0, size, 0)
|
|
369
|
+
@copy_map.delete file if size.zero?
|
|
370
|
+
true
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
##
|
|
375
|
+
# Prepare the file to be merged
|
|
376
|
+
#
|
|
377
|
+
# @param [String] file the path of the file to merge
|
|
378
|
+
# @return [Boolean] a success marker
|
|
379
|
+
def merge(file)
|
|
380
|
+
@dirty = true
|
|
381
|
+
add_path file
|
|
382
|
+
|
|
383
|
+
stats = File.lstat "#{@root}/#{file}"
|
|
384
|
+
add_path file
|
|
385
|
+
@files[file] = DirStateEntry.new(:merged, stats.mode, stats.size, stats.mtime.to_i)
|
|
386
|
+
@copy_map.delete file
|
|
387
|
+
true # success
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
##
|
|
391
|
+
# Forget the file
|
|
392
|
+
#
|
|
393
|
+
# @param [String] file the path of the file to forget
|
|
394
|
+
# @return [Boolean] a success marker
|
|
395
|
+
def forget(file)
|
|
396
|
+
@dirty = true
|
|
397
|
+
drop_path file
|
|
398
|
+
@files.delete file
|
|
399
|
+
true # success
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
##
|
|
403
|
+
# Returns a list of all the files tracked by the dirstate.
|
|
404
|
+
#
|
|
405
|
+
# @return [Array<String>] all the files being tracked, unsorted
|
|
406
|
+
def all_files
|
|
407
|
+
@files.keys
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
##
|
|
411
|
+
# Invalidates the dirstate, making it completely unusable until it is
|
|
412
|
+
# re-read. Should only be used in error situations.
|
|
413
|
+
def invalidate!
|
|
414
|
+
%w(@files @copy_map @folds @branch @parents @dirs @ignore).each do |ivar|
|
|
415
|
+
instance_variable_set(ivar, nil)
|
|
416
|
+
end
|
|
417
|
+
@dirty = false
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
##
|
|
421
|
+
# Refresh the directory's state, making everything empty.
|
|
422
|
+
# Called by #rebuild.
|
|
423
|
+
#
|
|
424
|
+
# This is not the same as #initialize, so we can't just run
|
|
425
|
+
# `send :initialize` and call it a day :-(
|
|
426
|
+
#
|
|
427
|
+
# @return [Boolean] a success marker
|
|
428
|
+
def clear
|
|
429
|
+
@files = {}
|
|
430
|
+
@dirs = {}
|
|
431
|
+
@copy_map = {}
|
|
432
|
+
@parents = [NULL_ID, NULL_ID]
|
|
433
|
+
@dirty = true
|
|
434
|
+
|
|
435
|
+
true # success
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
##
|
|
439
|
+
# Rebuild the directory's state.
|
|
440
|
+
#
|
|
441
|
+
# @param [String] parent the binary format of the parent
|
|
442
|
+
# @param [ManifestEntry] files the files in a specific revision
|
|
443
|
+
# @return [Boolean] a success marker
|
|
444
|
+
def rebuild(parent, files)
|
|
445
|
+
clear
|
|
446
|
+
|
|
447
|
+
# alter each file according to its flags
|
|
448
|
+
files.each do |f|
|
|
449
|
+
mode = files.flags(f).include?('x') ? 0777 : 0666
|
|
450
|
+
@files[f] = DirStateEntry.new(:normal, mode, -1, 0)
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
@parents = [parent, NULL_ID]
|
|
454
|
+
@dirty_parents = true
|
|
455
|
+
true # success
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
##
|
|
459
|
+
# Save the data to .hg/dirstate.
|
|
460
|
+
# Uses mode: "w", so it overwrites everything
|
|
461
|
+
#
|
|
462
|
+
# @todo watch memory usage - +si+ could grow unrestrictedly which would
|
|
463
|
+
# bog down the entire program
|
|
464
|
+
# @return [Boolean] a success marker
|
|
465
|
+
def write
|
|
466
|
+
return true unless @dirty
|
|
467
|
+
begin
|
|
468
|
+
@opener.open "dirstate", 'w' do |state|
|
|
469
|
+
gran = @config['dirstate']['granularity'] || 1 # self._ui.config('dirstate', 'granularity', 1)
|
|
470
|
+
|
|
471
|
+
limit = 2147483647 # sorry for the literal use...
|
|
472
|
+
limit = state.mtime - gran if gran > 0
|
|
473
|
+
|
|
474
|
+
si = StringIO.new "", (ruby_19? ? "w+:ASCII-8BIT" : "w+")
|
|
475
|
+
si.write @parents.join
|
|
476
|
+
|
|
477
|
+
@files.each do |file, info|
|
|
478
|
+
file = file.dup # so we don't corrupt vars
|
|
479
|
+
info = info.dup.to_a # UNLIKE PYTHON
|
|
480
|
+
info[0] = info[0].to_hg_int
|
|
481
|
+
|
|
482
|
+
# I should probably do mah physics hw. nah, i'll do it
|
|
483
|
+
# tomorrow during my break
|
|
484
|
+
# good news - i did pretty well on my physics test by using
|
|
485
|
+
# brian ford's name instead of my own.
|
|
486
|
+
file = "#{file}\0#{@copy_map[file]}" if @copy_map[file]
|
|
487
|
+
info = [info[0], 0, (-1).to_signed_32, (-1).to_signed_32] if info[3].to_i > limit.to_i and info[0] == :normal
|
|
488
|
+
info << file.size # the final element to make it pass, which is the length of the filename
|
|
489
|
+
info = info.pack FORMAT # pack them their lunch
|
|
490
|
+
si.write info # and send them off
|
|
491
|
+
si.write file # to school
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
state.write si.string
|
|
495
|
+
@dirty = false
|
|
496
|
+
@dirty_parents = false
|
|
497
|
+
|
|
498
|
+
true # success
|
|
499
|
+
end
|
|
500
|
+
rescue IOError
|
|
501
|
+
false
|
|
502
|
+
end
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
##
|
|
506
|
+
# Copies the files in h (represented as "dest" => "source").
|
|
507
|
+
#
|
|
508
|
+
# @param [Hash<String => String>] h the keys are sources and the values
|
|
509
|
+
# are dests
|
|
510
|
+
# @return [Boolean] a success marker
|
|
511
|
+
def copy(h={})
|
|
512
|
+
h.each do |source, dest|
|
|
513
|
+
next if source == dest
|
|
514
|
+
return true unless source
|
|
515
|
+
|
|
516
|
+
@dirty = true
|
|
517
|
+
|
|
518
|
+
if @copy_map[dest]
|
|
519
|
+
then @copy_map.delete dest
|
|
520
|
+
else @copy_map[dest] = source
|
|
521
|
+
end
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
true # success
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
##
|
|
528
|
+
# Reads the data in the .hg folder and fills in the vars
|
|
529
|
+
#
|
|
530
|
+
# @return [Amp::DirState] self -- chainable!
|
|
531
|
+
def read!
|
|
532
|
+
@parents, @files, @copy_map = parse('dirstate')
|
|
533
|
+
self # chainable
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
##
|
|
537
|
+
# Are we ignoring the directory?
|
|
538
|
+
#
|
|
539
|
+
# @param [String] dir the directory we're checking, either aboslute or relative
|
|
540
|
+
# @return [Boolean] are we ignoring the dir?
|
|
541
|
+
def ignoring_directory?(dir)
|
|
542
|
+
return false if dir == '.' # base cases
|
|
543
|
+
return true if ignore dir # base cases
|
|
544
|
+
!!directories_to(dir).any? {|d| ignore d }
|
|
545
|
+
end
|
|
546
|
+
alias_method :ignoring_dir?, :ignoring_directory?
|
|
547
|
+
|
|
548
|
+
private
|
|
549
|
+
##
|
|
550
|
+
# Generates the @ignore array
|
|
551
|
+
# The array is full of paths relative to the root, which
|
|
552
|
+
# makes things easier for the proc-generation phase.
|
|
553
|
+
#
|
|
554
|
+
# @return [NilClass]
|
|
555
|
+
def generate_ignore
|
|
556
|
+
@ignore = @config['ui'].select {|k, v| k == "ignore" }
|
|
557
|
+
|
|
558
|
+
@ignore << ".hgignore"
|
|
559
|
+
@ignore.compact
|
|
560
|
+
|
|
561
|
+
nil
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
##
|
|
565
|
+
# Perform various checks on the file before upping the content count
|
|
566
|
+
# for all of its parent directories. It checks for:
|
|
567
|
+
# * filenames containing "\n" or "\r" (newlines and carriage returns)
|
|
568
|
+
# * filenames with the same names as directories
|
|
569
|
+
# * clashing filenames
|
|
570
|
+
#
|
|
571
|
+
# It only increments the dirs' file count if the file is untracked or
|
|
572
|
+
# being removed.
|
|
573
|
+
#
|
|
574
|
+
# @param [String] f Should be formatted like ["action", mode, size, mtime]
|
|
575
|
+
# @param [Boolean] check whether to perform any of the checks
|
|
576
|
+
# @return [NilClass]
|
|
577
|
+
def add_path(f, check=false)
|
|
578
|
+
old_state = @files[f] || DirStateEntry.new # it's an array of info, remember
|
|
579
|
+
|
|
580
|
+
if check || old_state.removed?
|
|
581
|
+
raise "Bad Filename" if f.match(/\r|\n/)
|
|
582
|
+
raise "Directory #{f} already exists" if @dirs[f]
|
|
583
|
+
|
|
584
|
+
# make sure we don't have any files with the same name as a directory
|
|
585
|
+
directories_to(f).each do |d|
|
|
586
|
+
break if @dirs[d]
|
|
587
|
+
|
|
588
|
+
if @files[d] && !@files[d].removed?
|
|
589
|
+
raise "File #{d} clashes with #{f}! Fix their names"
|
|
590
|
+
end
|
|
591
|
+
end
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
# only inc the dirs if the file is untracked or being removed.
|
|
595
|
+
if [:untracked, :removed].include? old_state.status
|
|
596
|
+
# inc the number of dirs in each dir
|
|
597
|
+
inc_directories_to f
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
nil
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
##
|
|
604
|
+
# Conditional wrapper around +dec_directories_to+. It will dec the
|
|
605
|
+
# directories if the file in question (+f+) is either untracked or
|
|
606
|
+
# being removed.
|
|
607
|
+
#
|
|
608
|
+
# @param [String] f Should be formatted like ["action", mode, size, mtime]
|
|
609
|
+
# @return [NilClass]
|
|
610
|
+
def drop_path(f)
|
|
611
|
+
unless [:untracked, :removed].include? f[0]
|
|
612
|
+
dec_directories_to(f)
|
|
613
|
+
end
|
|
614
|
+
|
|
615
|
+
nil
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
##
|
|
619
|
+
# All directories leading up to this path
|
|
620
|
+
#
|
|
621
|
+
# @example directories_to "/Users/ari/src/monkey.txt" # =>
|
|
622
|
+
# ["/Users/ari/src", "/Users/ari", "/Users"]
|
|
623
|
+
# @param [String] path the path to the file we're examining
|
|
624
|
+
# @return [Array] the directories leading up to this path
|
|
625
|
+
def directories_to(path)
|
|
626
|
+
File.amp_directories_to path
|
|
627
|
+
end
|
|
628
|
+
|
|
629
|
+
##
|
|
630
|
+
# Increment all directories' dir-count leading up to this path.
|
|
631
|
+
# The dir-count is the path's value in @dirs.
|
|
632
|
+
# This is used when adding a file.
|
|
633
|
+
#
|
|
634
|
+
# @param [String] path the path we're disecting
|
|
635
|
+
# @return [NilClass]
|
|
636
|
+
def inc_directories_to(path)
|
|
637
|
+
p = directories_to(path).first
|
|
638
|
+
@dirs[p] ||= 0
|
|
639
|
+
@dirs[p] += 1
|
|
640
|
+
nil
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
##
|
|
644
|
+
# Decrement all directories' dir-count leading up to this path.
|
|
645
|
+
# The dir-count is the path's value in @dirs.
|
|
646
|
+
# This is used when removing a file.
|
|
647
|
+
#
|
|
648
|
+
# @param [String] path the path we're disecting
|
|
649
|
+
# @return [NilClass]
|
|
650
|
+
def dec_directories_to(path)
|
|
651
|
+
p = directories_to(path).first
|
|
652
|
+
# if the dir has 0, kill the dir. we don't need it anymore
|
|
653
|
+
if @dirs[p] && @dirs[p].zero?
|
|
654
|
+
@dirs.delete p
|
|
655
|
+
elsif @dirs[p]
|
|
656
|
+
@dirs[p] -= 1 # we only need to inc the latest dir
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
nil
|
|
660
|
+
end
|
|
661
|
+
|
|
662
|
+
##
|
|
663
|
+
# Parses the dirstate file in .hg
|
|
664
|
+
#
|
|
665
|
+
# @param [String] file path to the file to parse
|
|
666
|
+
# @return [((String, String), Hash<String => (Integer, Integer, Integer)>, Hash<String => String>)]
|
|
667
|
+
# a tuple of (parents, files, copies). Parents is a tuple of the parents,
|
|
668
|
+
# files is a hash of filename => [mode, size, mtime], and copies is a hash of src => dest
|
|
669
|
+
def parse(file)
|
|
670
|
+
# the main data we need to return
|
|
671
|
+
files = {}
|
|
672
|
+
copies = {}
|
|
673
|
+
parents = []
|
|
674
|
+
@opener.open file, "r" do |s|
|
|
675
|
+
|
|
676
|
+
# the parents are the first 40 bytes
|
|
677
|
+
parent = s.read(20) || NULL_ID
|
|
678
|
+
parent_ = s.read(20) || NULL_ID
|
|
679
|
+
parents = [parent, parent_]
|
|
680
|
+
|
|
681
|
+
# 1 character + 4 32-bit ints = 17 bytes
|
|
682
|
+
e_size = 17
|
|
683
|
+
|
|
684
|
+
# this loop is just cycling through and reading every entry
|
|
685
|
+
while !s.eof?
|
|
686
|
+
# read 1 entry
|
|
687
|
+
info = s.read(e_size).unpack FORMAT
|
|
688
|
+
|
|
689
|
+
# byte swap and shizzle
|
|
690
|
+
info = [info[0].to_dirstate_symbol, info[1], info[2].to_signed_32, info[3].to_signed_32, info[4]]
|
|
691
|
+
# ^^^^ we have to sign them because otherwise they'll be hugely wrong
|
|
692
|
+
|
|
693
|
+
# read in the filename
|
|
694
|
+
f = s.read(info[4])
|
|
695
|
+
|
|
696
|
+
# if it has an \0, then we've moved/copied it
|
|
697
|
+
if f.match(/\0/)
|
|
698
|
+
source, dest = f.split "\0"
|
|
699
|
+
copies[source] = dest
|
|
700
|
+
f = source
|
|
701
|
+
end
|
|
702
|
+
|
|
703
|
+
# and put in the info for the file itself
|
|
704
|
+
files[f] = DirStateEntry.new(*info[0..3])
|
|
705
|
+
end
|
|
706
|
+
end
|
|
707
|
+
|
|
708
|
+
[parents, files, copies]
|
|
709
|
+
rescue Errno::ENOENT
|
|
710
|
+
# no file? easy peasy
|
|
711
|
+
[[NULL_ID, NULL_ID], {}, {}]
|
|
712
|
+
end
|
|
713
|
+
end
|
|
714
|
+
end
|
|
715
|
+
end
|
|
716
|
+
end
|