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,433 @@
|
|
|
1
|
+
module Amp
|
|
2
|
+
module Repositories
|
|
3
|
+
module Mercurial
|
|
4
|
+
|
|
5
|
+
##
|
|
6
|
+
# This module adds verification to Mercurial repositories.
|
|
7
|
+
#
|
|
8
|
+
# The main public method provided is #verify. The rest are support methods that
|
|
9
|
+
# will keep to themselves.
|
|
10
|
+
#
|
|
11
|
+
# This is directly ported from verify.py from the Mercurial source. This is for
|
|
12
|
+
# the simple reason that, because we are re-implementing Mercurial, we should
|
|
13
|
+
# rely on their verification over our own. If we discover bugs in their
|
|
14
|
+
# verification, we'll patch them and send in the patches to selenic, but for now, we'll
|
|
15
|
+
# trust that theirs is on the money.
|
|
16
|
+
module Verification
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
# Runs a verification sweep on the repository.
|
|
20
|
+
#
|
|
21
|
+
# @return [VerificationResult] the results of the verification, which
|
|
22
|
+
# includes error messages, warning counts, and so on.
|
|
23
|
+
def verify
|
|
24
|
+
result = Verifier.new(self).verify
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
##
|
|
28
|
+
# Handles all logic for verifying a single repository and collecting the results.
|
|
29
|
+
#
|
|
30
|
+
# Public interface: initialize with a repository and run #verify.
|
|
31
|
+
class Verifier
|
|
32
|
+
attr_accessor :repository
|
|
33
|
+
alias_method :repo, :repository
|
|
34
|
+
|
|
35
|
+
attr_reader :changelog, :manifest
|
|
36
|
+
|
|
37
|
+
##
|
|
38
|
+
# Creates a new Verifier. The Verifier can verify a Mercurial repository.
|
|
39
|
+
#
|
|
40
|
+
# @param [Repository] repo the repository this verifier will examine
|
|
41
|
+
def initialize(repo)
|
|
42
|
+
@repository = repo
|
|
43
|
+
@result = VerificationResult.new(0, 0, 0, 0, 0)
|
|
44
|
+
|
|
45
|
+
@bad_revisions = {}
|
|
46
|
+
@changelog = repo.changelog
|
|
47
|
+
@manifest = repo.manifest
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
##
|
|
51
|
+
# Runs a verification sweep on the repository this verifier is handling.
|
|
52
|
+
#
|
|
53
|
+
# @return [VerificationResult] the results of the verification, which
|
|
54
|
+
# includes error messages, warning counts, and so on.
|
|
55
|
+
def verify
|
|
56
|
+
# Maps manifest node IDs to the link revision to which they belong
|
|
57
|
+
manifest_linkrevs = Hash.new {|h,k| h[k] = []}
|
|
58
|
+
|
|
59
|
+
# Maps filenames to a list of link revisions (global revision #s) in which
|
|
60
|
+
# that file was changed
|
|
61
|
+
file_linkrevs = Hash.new {|h, k| h[k] = []}
|
|
62
|
+
|
|
63
|
+
# file_node_ids stores a hash for each file. The hash stored maps that file's node IDs
|
|
64
|
+
# (the node stored in the file log itself) to the global "link revision index" - the
|
|
65
|
+
# revision index in the changelog (and the one the user always sees)
|
|
66
|
+
file_node_ids = Hash.new {|h, k| h[k] = {}}
|
|
67
|
+
|
|
68
|
+
verify_changelog(manifest_linkrevs, file_linkrevs)
|
|
69
|
+
verify_manifest(manifest_linkrevs, file_node_ids)
|
|
70
|
+
verify_crosscheck(manifest_linkrevs, file_linkrevs, file_node_ids)
|
|
71
|
+
UI.status("checking files")
|
|
72
|
+
store_files = verify_store
|
|
73
|
+
verify_files(file_linkrevs, file_node_ids, store_files)
|
|
74
|
+
@result
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
##
|
|
78
|
+
# Verifies the changelog. Updates acceptable file_linkrevs and manifest_linkrevs
|
|
79
|
+
# along the way, since the changelog knows which files have been changed when,
|
|
80
|
+
# and which manifest entries go with which changelog entries.
|
|
81
|
+
#
|
|
82
|
+
# @param [Hash] manifest_linkrevs the mapping between manifest node IDs and changelog
|
|
83
|
+
# revision numbers
|
|
84
|
+
# @param [Hash] file_linkrevs a mapping between filenames and a list of changelog
|
|
85
|
+
# revision numbers where the file was modified, added, or deleted.
|
|
86
|
+
def verify_changelog(manifest_linkrevs, file_linkrevs)
|
|
87
|
+
Amp::UI.status("checking changelog...")
|
|
88
|
+
check_revlog(@changelog, "changelog")
|
|
89
|
+
seen = {}
|
|
90
|
+
# can't use the nice #each because it assumes functioning changelog and whatnot
|
|
91
|
+
@changelog.size.times do |idx|
|
|
92
|
+
node = @changelog.node_id_for_index idx
|
|
93
|
+
check_entry(@changelog, idx, node, seen, [idx], "changelog")
|
|
94
|
+
begin
|
|
95
|
+
changelog_entry = @changelog.read(node)
|
|
96
|
+
manifest_linkrevs[changelog_entry.first] << idx
|
|
97
|
+
changelog_entry[3].each {|f| file_linkrevs[f] << idx}
|
|
98
|
+
rescue Exception => err
|
|
99
|
+
exception(idx, "unpacking changeset #{node.short_hex}:", err, "changelog")
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
@result.changesets = @changelog.size
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
##
|
|
106
|
+
# Verifies the manifest and its nodes. Also updates file_node_ids to store the
|
|
107
|
+
# node ID of files at given points in the manifest's history.
|
|
108
|
+
#
|
|
109
|
+
# @param [Hash] manifest_linkrevs the mapping between manifest node IDs and changelog
|
|
110
|
+
# revision numbers
|
|
111
|
+
# @param [Hash] file_node_ids maps filenames to a mapping from file node IDs to global
|
|
112
|
+
# link revisions.
|
|
113
|
+
def verify_manifest(manifest_linkrevs, file_node_ids)
|
|
114
|
+
Amp::UI.status("checking manifests...")
|
|
115
|
+
check_revlog(@manifest, "manifest")
|
|
116
|
+
seen = {}
|
|
117
|
+
|
|
118
|
+
@manifest.size.times do |idx|
|
|
119
|
+
node = @manifest.node_id_for_index idx
|
|
120
|
+
link_rev = check_entry(@manifest, idx, node, seen, manifest_linkrevs[node], "manifest")
|
|
121
|
+
manifest_linkrevs.delete node
|
|
122
|
+
|
|
123
|
+
begin
|
|
124
|
+
@manifest.read_delta(node).each do |filename, file_node|
|
|
125
|
+
if filename.empty?
|
|
126
|
+
error(link_rev, "file without name in manifest")
|
|
127
|
+
elsif filename != "/dev/null"
|
|
128
|
+
file_node_map = file_node_ids[filename]
|
|
129
|
+
file_node_map[file_node] ||= idx
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
rescue Exception => err
|
|
133
|
+
exception(idx, "reading manfiest delta #{node.short_hex}", err, "manifest")
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
##
|
|
139
|
+
# Crosschecks the changelog agains the manifest and vice-versa. There should be no
|
|
140
|
+
# remaining unmatched manifest node IDs, nor any files not in file_node_map.
|
|
141
|
+
# A few other checks, too.
|
|
142
|
+
#
|
|
143
|
+
# @param [Hash] manifest_linkrevs the mapping between manifest node IDs and changelog
|
|
144
|
+
# revision numbers
|
|
145
|
+
# @param [Hash] file_linkrevs a mapping between filenames and a list of changelog
|
|
146
|
+
# revision numbers where the file was modified, added, or deleted.
|
|
147
|
+
# @param [Hash] file_node_ids maps filenames to a mapping from file node IDs to global
|
|
148
|
+
# link revisions.
|
|
149
|
+
def verify_crosscheck(manifest_linkrevs, file_linkrevs, file_node_ids)
|
|
150
|
+
Amp::UI.status("crosschecking files in changesets and manifests")
|
|
151
|
+
|
|
152
|
+
# Check for node IDs found in the changelog, but not the manifest
|
|
153
|
+
if @manifest.any?
|
|
154
|
+
# check for any manifest node IDs we found in changesets, but not in the manifest
|
|
155
|
+
manifest_linkrevs.map {|node, idx| [idx, node]}.sort.each do |idx, node|
|
|
156
|
+
error(idx, "changeset refers to unknown manifest #{node.short_hex}")
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# check for any file node IDs we found in the changeset, but not in the manifest
|
|
160
|
+
file_linkrevs.sort.each do |file, _|
|
|
161
|
+
if file_node_ids[file].empty?
|
|
162
|
+
error(file_linkrevs[file].first, "in changeset but not in manifest", file)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Check for node IDs found in the manifest, but not the changelog.
|
|
168
|
+
if @changelog.any?
|
|
169
|
+
file_node_ids.map {|file,_| file}.sort.each do |file|
|
|
170
|
+
unless file_linkrevs[file]
|
|
171
|
+
begin
|
|
172
|
+
filelog = @repository.file_log file
|
|
173
|
+
link_rev = file_node_ids[file].map {|node| filelog.link_revision_for_index(filelog.revision_index_for_node(node))}.min
|
|
174
|
+
rescue
|
|
175
|
+
link_rev = nil
|
|
176
|
+
end
|
|
177
|
+
error(link_rev, "in manifest but not in changeset", file)
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
##
|
|
184
|
+
# Verifies the store, and returns a hash with names of files that are OK
|
|
185
|
+
#
|
|
186
|
+
# @return [Hash<String => Boolean>] a hash with filenames as keys and "true" or "false"
|
|
187
|
+
# as values, indicating whether the file exists and is accessible
|
|
188
|
+
def verify_store
|
|
189
|
+
store_files = {}
|
|
190
|
+
@repository.store.datafiles.each do |file, encoded_filename, size|
|
|
191
|
+
if file.nil? || file.empty?
|
|
192
|
+
error(nil, "can't decode filename from store: #{encoded_filename}")
|
|
193
|
+
elsif size > 0
|
|
194
|
+
store_files[file] = true
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
store_files
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
##
|
|
201
|
+
# Verifies the individual file logs one by one.
|
|
202
|
+
#
|
|
203
|
+
# @param [Hash] file_linkrevs a mapping between filenames and a list of changelog
|
|
204
|
+
# revision numbers where the file was modified, added, or deleted.
|
|
205
|
+
# @param [Hash] file_node_ids maps filenames to a mapping from file node IDs to global
|
|
206
|
+
# link revisions.
|
|
207
|
+
# @param [Hash] store_files a mapping keeping track of which file logs are in the store
|
|
208
|
+
def verify_files(file_linkrevs, file_node_ids, store_files)
|
|
209
|
+
files = (file_node_ids.keys + file_linkrevs.keys).uniq.sort
|
|
210
|
+
@result.files = files.size
|
|
211
|
+
files.each do |file|
|
|
212
|
+
link_rev = file_linkrevs[file].first
|
|
213
|
+
|
|
214
|
+
begin
|
|
215
|
+
file_log = @repository.file_log file
|
|
216
|
+
rescue Exception => err
|
|
217
|
+
error(link_rev, "broken revlog! (#{err})", file)
|
|
218
|
+
next
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
file_log.files.each do |ff|
|
|
222
|
+
unless store_files.delete(ff)
|
|
223
|
+
error(link_rev, "missing revlog!", ff)
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
verify_filelog(file, file_log, file_linkrevs, file_node_ids)
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
##
|
|
232
|
+
# Verifies a single file log. This is a complicated process - we need to cross-
|
|
233
|
+
# check a lot of data, which is why this has been extracted into its own method.
|
|
234
|
+
#
|
|
235
|
+
# @param [String] filename the name of the file we're verifying
|
|
236
|
+
# @param [FileLog] file_log the file log we're verifying
|
|
237
|
+
# @param [Hash] file_linkrevs a mapping between filenames and a list of changelog
|
|
238
|
+
# revision numbers where the file was modified, added, or deleted.
|
|
239
|
+
# @param [Hash] file_node_ids maps filenames to a mapping from file node IDs to global
|
|
240
|
+
# link revisions.
|
|
241
|
+
def verify_filelog(file, file_log, file_linkrevs, file_node_ids)
|
|
242
|
+
check_revlog(file_log, file)
|
|
243
|
+
seen = {}
|
|
244
|
+
file_log.index_size.times do |idx|
|
|
245
|
+
@result.revisions += 1
|
|
246
|
+
node = file_log.node_id_for_index(idx)
|
|
247
|
+
link_rev = check_entry(file_log, idx, node, seen, file_linkrevs[file], file)
|
|
248
|
+
|
|
249
|
+
# Make sure that one of the manifests referenced the node ID. If not, one of our
|
|
250
|
+
# manifests is wrong!
|
|
251
|
+
if file_node_ids[file]
|
|
252
|
+
if @manifest.any? && !file_node_ids[file][node]
|
|
253
|
+
error(link_rev, "#{node.short_hex} not found in manifests", file)
|
|
254
|
+
else
|
|
255
|
+
file_node_ids[file].delete node
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# Make sure the size of the uncompressed file is correct.
|
|
260
|
+
begin
|
|
261
|
+
text = file_log.read node
|
|
262
|
+
rename_info = file_log.renamed? node
|
|
263
|
+
if text.size != file_log.uncompressed_size_for_index(idx)
|
|
264
|
+
if file_log.decompress_revision(node).size != file_log.uncompressed_size_for_index(idx)
|
|
265
|
+
error(link_rev, "unpacked size is #{text.size}, #{file_log.size(idx)} expected", file)
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
rescue Exception => err
|
|
269
|
+
exception(link_rev, "unpacking #{node.short_hex}", err, file)
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
# Check if we screwed up renaming a file (like lost the source revlog or something)
|
|
273
|
+
begin
|
|
274
|
+
if rename_info && rename_info.any?
|
|
275
|
+
filelog_src = @repository.file_log(rename_info.first)
|
|
276
|
+
if filelog_src.index_size == 0
|
|
277
|
+
error(link_rev, "empty or missing copy source revlog "+
|
|
278
|
+
"#{rename_info[0]}, #{rename_info[1].short_hex}", file)
|
|
279
|
+
elsif rename_info[1] == Amp::Mercurial::RevlogSupport::Node::NULL_ID
|
|
280
|
+
warn("#{file}@#{link_rev}: copy source revision is NULL_ID "+
|
|
281
|
+
"#{rename_info[0]}:#{rename_info[1].short_hex}", file)
|
|
282
|
+
else
|
|
283
|
+
rev = filelog_src.revision_index_for_node(rename_info[1])
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
rescue Exception => err
|
|
287
|
+
exception(link_rev, "checking rename of #{node.short_hex}", err, file)
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
# Final cross-check
|
|
292
|
+
if file_node_ids[file] && file_node_ids[file].any?
|
|
293
|
+
file_node_ids[file].map { |node, link_rev|
|
|
294
|
+
[@manifest.link_revision_for_index(link_rev), node]
|
|
295
|
+
}.sort.each do |link_rev, node|
|
|
296
|
+
error(link_rev, "#{node.short_hex} in manifests not found", file)
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
private
|
|
302
|
+
|
|
303
|
+
##
|
|
304
|
+
# Checks a revlog for inconsistencies with the main format, such as
|
|
305
|
+
# having trailing bytes or incorrect formats
|
|
306
|
+
#
|
|
307
|
+
# @param [Revlog] log the log we will be verifying
|
|
308
|
+
# @param [String] name the name of the file this log is stored in
|
|
309
|
+
def check_revlog(log, name)
|
|
310
|
+
if log.empty? && (@changelog.any? || @manifest.any?)
|
|
311
|
+
return error(0, "#{name} is empty or missing")
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
size_diffs = log.checksize
|
|
315
|
+
# checksize returns a hash with these keys: index_diff, data_diff
|
|
316
|
+
if size_diffs[:data_diff] != 0
|
|
317
|
+
error(nil, "data size off by #{size_diffs[:data_diff]} bytes", name)
|
|
318
|
+
end
|
|
319
|
+
if size_diffs[:index_diff] != 0
|
|
320
|
+
error(nil, "index off by #{size_diffs[:index_diff]} bytes", name)
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
v0 = Amp::Mercurial::RevlogSupport::Support::REVLOG_VERSION_0
|
|
324
|
+
if log.index.version != v0
|
|
325
|
+
warn("#{name} uses revlog format 1. changelog uses format 0.") if @changelog.index.version == v0
|
|
326
|
+
elsif log.index.version == v0
|
|
327
|
+
warn("#{name} uses revlog format 0. that's really old.")
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
##
|
|
332
|
+
# Checks a single entry in a revision log for inconsistencies.
|
|
333
|
+
#
|
|
334
|
+
# @param [Revlog] log the revision log we're examining
|
|
335
|
+
# @param [Fixnum] revision the index # of the revision being examined
|
|
336
|
+
# @param [String] node the node ID of the revision being examined
|
|
337
|
+
# @param [Hash] seen the list of node IDs we've already seen
|
|
338
|
+
# @param [Array] ok_link_revisions the acceptable link revisions for the given entry
|
|
339
|
+
# @param [String] filename the name of the file containing the revlog
|
|
340
|
+
def check_entry(log, revision, node, seen, ok_link_revisions, filename)
|
|
341
|
+
link_rev = log.link_revision_for_index log.revision_index_for_node(node)
|
|
342
|
+
# is the link_revision invalid?
|
|
343
|
+
if link_rev < 0 || (changelog.any? && ! ok_link_revisions.include?(link_rev))
|
|
344
|
+
problem = (link_rev < 0 || link_rev >= changelog.size) ? "nonexistent" : "unexpected"
|
|
345
|
+
error(nil, "revision #{revision} points to #{problem} changeset #{link_rev}", filename)
|
|
346
|
+
|
|
347
|
+
if ok_link_revisions.any?
|
|
348
|
+
warn("(expected #{ok_link_revisions.join(" ")})")
|
|
349
|
+
end
|
|
350
|
+
link_rev = nil # don't use this link_revision, because it's clearly wrong.
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
begin
|
|
354
|
+
log.parents_for_node(node).each do |parent|
|
|
355
|
+
if !seen[parent] && parent != Amp::Mercurial::RevlogSupport::Node::NULL_ID
|
|
356
|
+
error(link_rev, "unknown parent #{parent.short_hex} of #{node.short_hex}", filename)
|
|
357
|
+
end
|
|
358
|
+
end
|
|
359
|
+
rescue StandardError => e
|
|
360
|
+
# TODO: do real exception handling
|
|
361
|
+
exception(link_rev, "error checking parents of #{node.short_hex}: ", e, filename)
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
if seen[node]
|
|
365
|
+
error(link_rev, "duplicate revision #{revision} (#{seen[node]})", filename)
|
|
366
|
+
end
|
|
367
|
+
seen[node] = revision
|
|
368
|
+
return link_rev
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
##
|
|
372
|
+
# Produce an error based on an exception. Matches mercurial's.
|
|
373
|
+
#
|
|
374
|
+
# @param [Fixnum] revision the link-revision the error is associated with
|
|
375
|
+
# @param [String, #to_s] message the message to print with the error
|
|
376
|
+
# @param [Exception] exception the exception that raised this error
|
|
377
|
+
# @param [String, #to_s] filename (nil) the name of the file with an error.
|
|
378
|
+
# nil for changelog/manifest
|
|
379
|
+
def exception(revision, message, exception, filename)
|
|
380
|
+
if exception.kind_of?(Interrupt)
|
|
381
|
+
UI.warn("interrupted")
|
|
382
|
+
raise
|
|
383
|
+
end
|
|
384
|
+
error(revision, "#{message} #{exception}\n", filename)
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
##
|
|
388
|
+
# Produce an error that looks like Mercurial's
|
|
389
|
+
# meh compatibility makes me sad
|
|
390
|
+
#
|
|
391
|
+
# @param [Fixnum] revision the link-revision the error is associated with
|
|
392
|
+
# @param [String, #to_s] message the message to print with the error
|
|
393
|
+
# @param [String, #to_s] filename (nil) the name of the file with an error.
|
|
394
|
+
# nil for changelog/manifest
|
|
395
|
+
def error(revision, message, filename = nil)
|
|
396
|
+
if revision
|
|
397
|
+
@bad_revisions[revision] = true
|
|
398
|
+
else
|
|
399
|
+
revision = "?"
|
|
400
|
+
end
|
|
401
|
+
new_message = "#{revision}: #{message}"
|
|
402
|
+
new_message = "#{filename}@#{new_message}" if filename
|
|
403
|
+
UI.say new_message
|
|
404
|
+
@result.errors += 1
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
##
|
|
408
|
+
# Adds a warning to the results
|
|
409
|
+
#
|
|
410
|
+
# @param [String, #to_s] message the user's warning
|
|
411
|
+
def warn(message)
|
|
412
|
+
UI.say "warning: #{message}"
|
|
413
|
+
@result.warnings += 1
|
|
414
|
+
end
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
##
|
|
418
|
+
# Simple struct that handles the results of a verification.
|
|
419
|
+
class VerificationResult < Struct.new(:warnings, :errors, :revisions, :files, :changesets)
|
|
420
|
+
def initialize(*args)
|
|
421
|
+
super(*args)
|
|
422
|
+
@warnings = 0
|
|
423
|
+
@errors = 0
|
|
424
|
+
@revisions = 0
|
|
425
|
+
@files = 0
|
|
426
|
+
@changesets = 0
|
|
427
|
+
end
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
end
|
|
431
|
+
end
|
|
432
|
+
end
|
|
433
|
+
end
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
module Amp
|
|
2
|
+
module Repositories
|
|
3
|
+
module Mercurial
|
|
4
|
+
|
|
5
|
+
##
|
|
6
|
+
# = BundleRepository
|
|
7
|
+
# This class represents a read-only repository that combines both local
|
|
8
|
+
# repository data with a bundle file. The bundle file contains un-merged-in
|
|
9
|
+
# changesets - this is useful for, say, previewing the results of a pull
|
|
10
|
+
# action.
|
|
11
|
+
#
|
|
12
|
+
# A bundle is stored in the following manner:
|
|
13
|
+
# - Changelog entries
|
|
14
|
+
# - Manifest entries
|
|
15
|
+
# - Modified File entry #1
|
|
16
|
+
# - Modified File entry #2
|
|
17
|
+
# - ...
|
|
18
|
+
# - Modified file entry #N
|
|
19
|
+
class BundleRepository < LocalRepository
|
|
20
|
+
def initialize(path="", config=nil, bundle_name="")
|
|
21
|
+
@temp_parent = nil
|
|
22
|
+
# Figure out what to do here - if there's no current local repository, that
|
|
23
|
+
# takes some special work.
|
|
24
|
+
begin
|
|
25
|
+
super(path, false, config) # don't create, just look for a repository
|
|
26
|
+
rescue
|
|
27
|
+
# Ok, no local repository. Let's make one really quickly.
|
|
28
|
+
@temp_parent = File.join(Dir.tmpdir, File.amp_make_tmpname("bundlerepo"))
|
|
29
|
+
File.mkdir(@temp_parent)
|
|
30
|
+
tmprepo = LocalRepository.new(@temp_parent, true, config) # true -> create
|
|
31
|
+
super(@temp_parent, false, config) # and proceed as scheduled!
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Set up our URL variable, if anyone asks us what it is
|
|
35
|
+
if path
|
|
36
|
+
@url = "bundle:#{path}+#{bundle_name}"
|
|
37
|
+
else
|
|
38
|
+
@url = "bundle:#{bundle_name}"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
@temp_file = nil
|
|
42
|
+
@bundle_file = File.open(bundle_name, "r")
|
|
43
|
+
|
|
44
|
+
@bundle_file.seek(0, IO::SEEK_END)
|
|
45
|
+
Amp::UI.debug "Bundle File Size: #{@bundle_file.tell}"
|
|
46
|
+
@bundle_file.seek(0, IO::SEEK_SET)
|
|
47
|
+
|
|
48
|
+
# OK, now for the fun part - check the header to see if we're compressed.
|
|
49
|
+
header = @bundle_file.read(6)
|
|
50
|
+
# And switch based on that header
|
|
51
|
+
if !header.start_with?("HG")
|
|
52
|
+
# Not even an HG file. FML. Bail
|
|
53
|
+
raise abort("#{bundle_name}: not a Mercurial bundle file")
|
|
54
|
+
elsif not header.start_with?("HG10")
|
|
55
|
+
# Not a version we understand, bail
|
|
56
|
+
raise abort("#{bundle_name}: unknown bundle version")
|
|
57
|
+
elsif header == "HG10BZ" || header == "HG10GZ"
|
|
58
|
+
# Compressed! We'll have to save to a new file, because this could get messy.
|
|
59
|
+
temp_file = Tempfile.new("hg-bundle-hg10un", @root)
|
|
60
|
+
@temp_file_path = temp_file.path
|
|
61
|
+
# Are we BZip, or GZip?
|
|
62
|
+
case header
|
|
63
|
+
when "HG10BZ"
|
|
64
|
+
# fuck BZip. Seriously.
|
|
65
|
+
headerio = StringIO.new "BZ", (ruby_19? ? "w+:ASCII-8BIT" : "w+")
|
|
66
|
+
input = Amp::Support::MultiIO.new(headerio, @bundle_file)
|
|
67
|
+
decomp = BZ2::Reader.new(input)
|
|
68
|
+
when "HG10GZ"
|
|
69
|
+
# Gzip is much nicer.
|
|
70
|
+
decomp = Zlib::GzipReader.new(@bundle_file)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# We're writing this in an uncompressed fashion, of course.
|
|
74
|
+
@temp_file.write("HG10UN")
|
|
75
|
+
# While we can uncompressed....
|
|
76
|
+
while !r.eof? do
|
|
77
|
+
# Write the uncompressed data to our new file!
|
|
78
|
+
@temp_file.write decomp.read(4096)
|
|
79
|
+
end
|
|
80
|
+
# and close 'er up
|
|
81
|
+
@temp_file.close
|
|
82
|
+
|
|
83
|
+
# Close the compressed bundle file
|
|
84
|
+
@bundle_file.close
|
|
85
|
+
# And re-open the uncompressed bundle file!
|
|
86
|
+
@bundle_file = File.open(@temp_file_path, "r")
|
|
87
|
+
# Skip the header.
|
|
88
|
+
@bundle_file.seek(6)
|
|
89
|
+
elsif header == "HG10UN"
|
|
90
|
+
# uncompressed, do nothing
|
|
91
|
+
else
|
|
92
|
+
# We have no idae what's going on
|
|
93
|
+
raise abort("#{bundle_name}: unknown bundle compression type")
|
|
94
|
+
end
|
|
95
|
+
# This hash stores pairs of {filename => position_in_bundle_file_of_this_file}
|
|
96
|
+
@bundle_files_positions = {}
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
##
|
|
100
|
+
# Gets the changelog of the repository. This is different from {LocalRepository#changelog}
|
|
101
|
+
# in that it uses a {BundleChangeLog}. Also, since the manifest is stored in the bundle
|
|
102
|
+
# directly after the changelog, by checking our position in the bundle file, we can save
|
|
103
|
+
# where the bundle_file is stored.
|
|
104
|
+
#
|
|
105
|
+
# @return [BundleChangeLog] the changelog for this repository.
|
|
106
|
+
def changelog
|
|
107
|
+
@changelog ||= Bundles::Mercurial::BundleChangeLog.new(@store.opener, @bundle_file)
|
|
108
|
+
@manifest_start ||= @bundle_file.tell
|
|
109
|
+
@changelog
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
##
|
|
113
|
+
# Gets the manifest of the repository. This is different from {LocalRepository#manifest}
|
|
114
|
+
# in that it uses a {BundleManifest}. The file logs are stored in the bundle directly
|
|
115
|
+
# after the manifest, so once we load the manifest, we save where the file logs start
|
|
116
|
+
# when we are done loading the manifest.
|
|
117
|
+
#
|
|
118
|
+
# This has the side-effect of loading the changelog, if it hasn't been loaded already -#
|
|
119
|
+
# this is necessary because the manifest changesets are stored after the changelog changesets,
|
|
120
|
+
# and we must fully load the changelog changesets to know where to look for the manifest changesets.
|
|
121
|
+
#
|
|
122
|
+
# Don't look at me, I didn't design the file format.
|
|
123
|
+
#
|
|
124
|
+
# @return [BundleChangeLog] the changelog for this repository.
|
|
125
|
+
def manifest
|
|
126
|
+
return @manifest if @manifest
|
|
127
|
+
@bundle_file.seek manifest_start
|
|
128
|
+
@manifest ||= Bundles::BundleManifest.new @store.opener, @bundle_file, proc {|n| changelog.rev(n) }
|
|
129
|
+
@file_start ||= @bundle_file.tell
|
|
130
|
+
@manifest
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
##
|
|
134
|
+
# Returns the position in the bundle file where the manifest changesets are located.
|
|
135
|
+
# This involves loading the changelog first - see {#manifest}
|
|
136
|
+
#
|
|
137
|
+
# @return [Integer] the position in the bundle file where we can find the manifest
|
|
138
|
+
# changesets.
|
|
139
|
+
def manifest_start
|
|
140
|
+
changelog && @manifest_start
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
##
|
|
144
|
+
# Returns the position in the bundle file where the file log changesets are located.
|
|
145
|
+
# This involves loading the changelog and the manifest first - see {#manifest}.
|
|
146
|
+
#
|
|
147
|
+
# @return [Integer] the position in the bundle file where we can find the file-log
|
|
148
|
+
# changesets.
|
|
149
|
+
def file_start
|
|
150
|
+
manifest && @file_start
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
##
|
|
154
|
+
# Gets the file-log for the given path, so we can look at an individual
|
|
155
|
+
# file's history, for example. However, we need to be cognizant of files that
|
|
156
|
+
# traverse the local repository's history as well as the bundle file.
|
|
157
|
+
#
|
|
158
|
+
# @param [String] f the path to the file
|
|
159
|
+
# @return [FileLog] a filelog (a type of revision log) for the given file
|
|
160
|
+
def file(filename)
|
|
161
|
+
|
|
162
|
+
# Load the file-log positions now - we didn't do this in the constructor for a reason
|
|
163
|
+
# (if they don't ask for them, don't load them!)
|
|
164
|
+
if @bundle_files_positions.empty?
|
|
165
|
+
# Jump to the file position
|
|
166
|
+
@bundle_file.seek file_start
|
|
167
|
+
while true
|
|
168
|
+
# get a changegroup chunk - it'll be the filename
|
|
169
|
+
chunk = RevlogSupport::ChangeGroup.get_chunk @bundle_file
|
|
170
|
+
# no filename? bail
|
|
171
|
+
break if chunk.nil? || chunk.empty?
|
|
172
|
+
|
|
173
|
+
# Now that we've read the filename, we're at the start of the changelogs for that
|
|
174
|
+
# file. So let's save this position for later.
|
|
175
|
+
@bundle_files_positions[chunk] = @bundle_file.tell
|
|
176
|
+
# Then read chunks until we get to the next file!
|
|
177
|
+
RevlogSupport::ChangeGroup.each_chunk(@bundle_file) {|c|}
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Remove leading slash
|
|
182
|
+
filename = filename.shift("/")
|
|
183
|
+
|
|
184
|
+
# Does this file cross local history as well as the bundle?
|
|
185
|
+
if @bundle_files_positions[filename]
|
|
186
|
+
# If so, we'll need to make a BundleFileLog. Meh.
|
|
187
|
+
@bundle_file.seek @bundle_files_positions[filename]
|
|
188
|
+
Bundles::BundleFileLog.new @store.opener, filename, @bundle_file, proc {|n| changelog.rev(n) }
|
|
189
|
+
else
|
|
190
|
+
# Nope? Make a normal FileLog!
|
|
191
|
+
FileLog.new(@store.opener, filename)
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
##
|
|
196
|
+
# Gets the URL for this repository - unused, I believe.
|
|
197
|
+
#
|
|
198
|
+
# @return [String] the URL for the repository
|
|
199
|
+
def url; @url; end
|
|
200
|
+
|
|
201
|
+
##
|
|
202
|
+
# Closes the repository - in this case, it closes the bundle_file. Analogous to closing
|
|
203
|
+
# an SSHRepository's socket.
|
|
204
|
+
def close
|
|
205
|
+
@bundle_file.close
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# We can't copy files. Read-only.
|
|
209
|
+
def can_copy?; false; end
|
|
210
|
+
# Gets the current working directory. Not sure why we need this.
|
|
211
|
+
def get_cwd; Dir.pwd; end
|
|
212
|
+
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
end
|