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,129 @@
|
|
|
1
|
+
require 'zlib'
|
|
2
|
+
|
|
3
|
+
module Amp
|
|
4
|
+
module Mercurial
|
|
5
|
+
module RevlogSupport
|
|
6
|
+
|
|
7
|
+
class RevlogError < StandardError; end
|
|
8
|
+
class LookupError < StandardError; end
|
|
9
|
+
|
|
10
|
+
module Support
|
|
11
|
+
extend self
|
|
12
|
+
|
|
13
|
+
# Old version of the revlog file format
|
|
14
|
+
REVLOG_VERSION_0 = 0
|
|
15
|
+
# Current version of the revlog file format
|
|
16
|
+
REVLOG_VERSION_NG = 1
|
|
17
|
+
# A flag marking that the data is stored with the index
|
|
18
|
+
REVLOG_NG_INLINE_DATA = (1 << 16)
|
|
19
|
+
# Default flags - always start inline (turn off inline if file is huge)
|
|
20
|
+
REVLOG_DEFAULT_FLAGS = REVLOG_NG_INLINE_DATA
|
|
21
|
+
# Default format - the most recent
|
|
22
|
+
REVLOG_DEFAULT_FORMAT = REVLOG_VERSION_NG
|
|
23
|
+
# Default version in general
|
|
24
|
+
REVLOG_DEFAULT_VERSION = REVLOG_DEFAULT_FORMAT | REVLOG_DEFAULT_FLAGS
|
|
25
|
+
|
|
26
|
+
##
|
|
27
|
+
# This bears some explanation.
|
|
28
|
+
#
|
|
29
|
+
# Rather than simply having a 4-byte header for the index file format, the
|
|
30
|
+
# Mercurial format takes the first entry in the index, and stores the header
|
|
31
|
+
# in its offset field. (The offset field is a 64-bit unsigned integer which
|
|
32
|
+
# stores the offset into the data or index of the associated record's data)
|
|
33
|
+
# They take advantage of the fact that the first entry's offset will always
|
|
34
|
+
# be 0. As such, its offset field is always going to be zero, so it's safe
|
|
35
|
+
# to store data there.
|
|
36
|
+
#
|
|
37
|
+
# The format is ((flags << 16) | (version)), where +flags+ is a bitmask (up to 48
|
|
38
|
+
# bits) and +version+ is a 16-bit unsigned short.
|
|
39
|
+
#
|
|
40
|
+
# The worst part is, EVERY SINGLE ENTRY has its offset shifted 16 bits to the left,
|
|
41
|
+
# apparently all because of this. It fucking baffles my mind.
|
|
42
|
+
#
|
|
43
|
+
# So yeah. offset = value >> 16.
|
|
44
|
+
def get_offset(o); o >> 16; end
|
|
45
|
+
# And yeah. version = value && 0xFFFF (last 16 bits)
|
|
46
|
+
def get_version(t); t & 0xFFFF; end
|
|
47
|
+
|
|
48
|
+
# Combine an offset and a version to spit this baby out
|
|
49
|
+
def offset_version(offset,type)
|
|
50
|
+
(offset << 16) | type
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
##
|
|
54
|
+
# generate a hash from the given text and its parent hashes
|
|
55
|
+
#
|
|
56
|
+
# This hash combines both the current file contents and its history
|
|
57
|
+
# in a manner that makes it easy to distinguish nodes with the same
|
|
58
|
+
# content in the revision graph.
|
|
59
|
+
#
|
|
60
|
+
# since an entry in a revlog is pretty
|
|
61
|
+
# much [parent1, parent2, text], we use a hash of the previous entry
|
|
62
|
+
# as a reference to that previous entry. To create a reference to this
|
|
63
|
+
# entry, we make a hash of the first parent (which is just its ID), the
|
|
64
|
+
# second parent, and the text.
|
|
65
|
+
#
|
|
66
|
+
# @return [String] the digest of the two parents and the extra text
|
|
67
|
+
def history_hash(text, p1, p2)
|
|
68
|
+
list = [p1, p2].sort
|
|
69
|
+
s = list[0].sha1
|
|
70
|
+
s.update list[1]
|
|
71
|
+
s.update text
|
|
72
|
+
s.digest
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
##
|
|
76
|
+
# returns the possibly-compressed version of the text, in a hash:
|
|
77
|
+
#
|
|
78
|
+
# @return [Hash] :compression => 'u' or ''
|
|
79
|
+
def compress(text)
|
|
80
|
+
return {:compression => "", :text => text} if text.empty?
|
|
81
|
+
size = text.size
|
|
82
|
+
binary = nil
|
|
83
|
+
if size < 44
|
|
84
|
+
elsif size > 1000000 #big ole file
|
|
85
|
+
deflater = Zlib::Deflate.new
|
|
86
|
+
parts = []
|
|
87
|
+
position = 0
|
|
88
|
+
while position < size
|
|
89
|
+
newposition = position + 2**20
|
|
90
|
+
parts << deflater.deflate(text[position..(newposition-1)], Zlib::NO_FLUSH)
|
|
91
|
+
position = newposition
|
|
92
|
+
end
|
|
93
|
+
parts << deflater.flush
|
|
94
|
+
binary = parts.join if parts.map {|e| e.size}.sum < size # only add it if
|
|
95
|
+
# compression made it smaller
|
|
96
|
+
else #tiny, just compress it
|
|
97
|
+
binary = Zlib::Deflate.deflate text
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
if binary.nil? || binary.size > size
|
|
101
|
+
return {:compression => "", :text => text} if text[0,1] == "\0"
|
|
102
|
+
return {:compression => 'u', :text => text}
|
|
103
|
+
end
|
|
104
|
+
{:compression => "", :text => binary}
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
##
|
|
108
|
+
# Decompresses the given binary text. The binary text could be
|
|
109
|
+
# uncompressed, in which case, we'll figure that out. Don't worry.
|
|
110
|
+
#
|
|
111
|
+
# @param [String] binary the text to (possibly) decompress
|
|
112
|
+
# @return [String] the text decompressed
|
|
113
|
+
def decompress(binary)
|
|
114
|
+
return binary if binary.empty?
|
|
115
|
+
case binary[0,1]
|
|
116
|
+
when "\0"
|
|
117
|
+
binary #we're just stored as binary
|
|
118
|
+
when "x"
|
|
119
|
+
Zlib::Inflate.inflate(binary) #we're zlibbed
|
|
120
|
+
when "u"
|
|
121
|
+
binary[1..-1] #we're uncompressed text
|
|
122
|
+
else
|
|
123
|
+
raise LookupError.new("Unknown compression type #{binary[0,1]}")
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
@@ -0,0 +1,597 @@
|
|
|
1
|
+
module Amp
|
|
2
|
+
module Mercurial
|
|
3
|
+
|
|
4
|
+
##
|
|
5
|
+
# This class allows you to access a file at a given revision in the repo's
|
|
6
|
+
# history. You can compare them, sort them, access the changeset, and
|
|
7
|
+
# all sorts of stuff.
|
|
8
|
+
class VersionedFile < Amp::Repositories::AbstractVersionedFile
|
|
9
|
+
include Mercurial::RevlogSupport::Node
|
|
10
|
+
|
|
11
|
+
attr_accessor :file_id
|
|
12
|
+
attr_accessor :change_id
|
|
13
|
+
attr_accessor :path
|
|
14
|
+
attr_accessor :repo
|
|
15
|
+
|
|
16
|
+
##
|
|
17
|
+
# Creates a new {VersionedFile}. You need to pass in the repo and the path
|
|
18
|
+
# to the file, as well as one of the following: a revision index/ID, the
|
|
19
|
+
# node_id of the file's revision in the filelog, or a changeset at a given
|
|
20
|
+
# index.
|
|
21
|
+
#
|
|
22
|
+
# Oh, and just FYI because it might interest you, there are three ways to
|
|
23
|
+
# specify the revision of a VFile: change_id (revision in changelog),
|
|
24
|
+
# file_id (revision in file_log), and an actual changeset. Instead of
|
|
25
|
+
# sticking with one way, favoring one way, or converting everything
|
|
26
|
+
# to a single form, they do everything
|
|
27
|
+
# three different ways. It's not particularly difficult to deal with, per se,
|
|
28
|
+
# but it's just fucking stupid. like seriously wtf. with that said, mad props
|
|
29
|
+
# to the mercurial team because they put out a rock solid piece of code that
|
|
30
|
+
# has inspired me.
|
|
31
|
+
#
|
|
32
|
+
# @param [Repository] repo The repo we're working with
|
|
33
|
+
# @param [String] path the path to the file
|
|
34
|
+
# @param [Hash] opts the options to customize how we load this file
|
|
35
|
+
# @option [FileLog] opts :file_log (nil) The FileLog to use for loading data
|
|
36
|
+
# @option [String] opts :change_id (nil) The revision ID/index to use to
|
|
37
|
+
# figure out which revision we're working with
|
|
38
|
+
# @option [Changeset] opts :changeset (nil) the changeset to use to figure
|
|
39
|
+
# which revision we're working with
|
|
40
|
+
# @option [String] opts :file_id (nil) perhaps the ID of the revision in
|
|
41
|
+
# the file_log to use?
|
|
42
|
+
def initialize(repo, path, opts={})
|
|
43
|
+
@repo, @path = repo, path
|
|
44
|
+
raise StandardError.new("specify a revision!") unless opts[:change_id] ||
|
|
45
|
+
opts[:file_id] ||
|
|
46
|
+
opts[:changeset]
|
|
47
|
+
@file_log = opts[:file_log] || nil
|
|
48
|
+
@change_id = opts[:change_id] || nil
|
|
49
|
+
@changeset = opts[:changeset] || nil
|
|
50
|
+
@file_id = opts[:file_id] || nil
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
##
|
|
54
|
+
# Commit this {VersionedFile} as part of a larger transaction. This will
|
|
55
|
+
# not commit anything if the file hasn't actually been changed.
|
|
56
|
+
#
|
|
57
|
+
# commit_file:
|
|
58
|
+
# if file_has_been_copied:
|
|
59
|
+
# get_new_file_pointers # for the old file_log, since it needs to be transfered
|
|
60
|
+
# elsif file_has_been_merged:
|
|
61
|
+
# deal_with_merges
|
|
62
|
+
# end
|
|
63
|
+
#
|
|
64
|
+
# add_file_log_entry
|
|
65
|
+
#
|
|
66
|
+
# @param [Hash] opts
|
|
67
|
+
# @option opts [Array<Amp::Mercurial::ManifestEntry>] manifests the manifests of
|
|
68
|
+
# the parents
|
|
69
|
+
# @option opts [String] link_revision the revision index we'll be adding
|
|
70
|
+
# this under
|
|
71
|
+
# @option opts [Amp::Mercurial::Journal] journal the journal for aborting
|
|
72
|
+
# failed commits
|
|
73
|
+
# @option opts [Array<String>] changed the running tally of changed files
|
|
74
|
+
# @return [String] the file_id (where this revision is in its file log)
|
|
75
|
+
def commit(opts={})
|
|
76
|
+
f_log = repo.file_log @path # :: FileLog
|
|
77
|
+
copied = renamed? # [ path, ]
|
|
78
|
+
meta_data = {}
|
|
79
|
+
|
|
80
|
+
fp1 = opts[:manifests][0][path] || NULL_ID # :: String (file_id)
|
|
81
|
+
fp2 = opts[:manifests][1][path] || NULL_ID # :: String (file_id)
|
|
82
|
+
|
|
83
|
+
# DEALIN' WITH MERGES AN' COPIES!
|
|
84
|
+
if copied && copied[0] != @path
|
|
85
|
+
# COPIES!!!
|
|
86
|
+
#
|
|
87
|
+
# we need new pointers because when we deal with merges, we need
|
|
88
|
+
# to know if we have more work to do. If fp1 is NULL_ID, then it means
|
|
89
|
+
# we need to look up the copy data so we can get the old file log data.
|
|
90
|
+
fp1, fp2, meta_data = *determine_new_file_pointers(fp1, fp2, copied[0], opts)
|
|
91
|
+
elsif fp2 != NULL_ID
|
|
92
|
+
# MERGES
|
|
93
|
+
fpa = f_log.ancestor fp1, fp2
|
|
94
|
+
|
|
95
|
+
fp1, fp2 = fp2, NULL_ID if fpa == fp1
|
|
96
|
+
fp2 = NULL_ID if fpa != fp2 && fpa == fp2
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Else, if there is no second parent and the file hasn't been copied
|
|
100
|
+
# and nothing has changed in the file (that's the last little
|
|
101
|
+
# comparison), then we just return fp1 because there's nothing to write.
|
|
102
|
+
if fp2 == NULL_ID && meta_data.empty? && !(f_log.cmp(fp1, data))
|
|
103
|
+
return fp1
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Add it to the list of CHANGED files. If we've made it this far,
|
|
107
|
+
# we have a file that has changed.
|
|
108
|
+
opts[:changed] << @path
|
|
109
|
+
|
|
110
|
+
# Add the motherfucking file log entry. Have a nice day.
|
|
111
|
+
f_log.add data, meta_data, opts[:journal], opts[:link_revision], fp1, fp2
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
##
|
|
115
|
+
# Determine the new file indices of the {FileLog} for this
|
|
116
|
+
# {VersionedFile}. This deals with merges and copies and anything else
|
|
117
|
+
# that might stymie the standard process.
|
|
118
|
+
#
|
|
119
|
+
# @param [String] fp1 the file index of the first parent
|
|
120
|
+
# @param [String] fp2 the file index of the second parent
|
|
121
|
+
# @param [Hash<Symbol => Object>] opts
|
|
122
|
+
# @option opts [Array<ManifestEntry>] :manifests the manifests of the
|
|
123
|
+
# first and second parents, respectively.
|
|
124
|
+
# @return [(String, String, Hash<String => String>)] the first file index,
|
|
125
|
+
# the second file index, and any meta data
|
|
126
|
+
def determine_new_file_pointers(fp1, fp2, copied_file, opts={})
|
|
127
|
+
# Mark the new revision of this file as a copy of another
|
|
128
|
+
# file. This copy data will effectively act as a parent
|
|
129
|
+
# of this new revision. If this is a merge, the first
|
|
130
|
+
# parent will be the nullid (meaning "look up the copy data")
|
|
131
|
+
# and the second one will be the other parent. For example:
|
|
132
|
+
#
|
|
133
|
+
# 0 --- 1 --- 3 rev1 changes file foo
|
|
134
|
+
# \ / rev2 renames foo to bar and changes it
|
|
135
|
+
# \- 2 -/ rev3 should have bar with all changes and
|
|
136
|
+
# should record that bar descends from
|
|
137
|
+
# bar in rev2 and foo in rev1
|
|
138
|
+
#
|
|
139
|
+
# this allows this merge to succeed:
|
|
140
|
+
#
|
|
141
|
+
# 0 --- 1 --- 3 rev4 reverts the content change from rev2
|
|
142
|
+
# \ / merging rev3 and rev4 should use bar@rev2
|
|
143
|
+
# \- 2 --- 4 as the merge base
|
|
144
|
+
|
|
145
|
+
copied_revision = opts[:manifests][0][copied_file]
|
|
146
|
+
new_fp = fp2
|
|
147
|
+
|
|
148
|
+
if opts[:manifests][1] # branch merge
|
|
149
|
+
# copied on remote side
|
|
150
|
+
if fp2 == NULL_ID || copied_revision == nil
|
|
151
|
+
if opts[:manifests][1][copied_file]
|
|
152
|
+
copied_revision = opts[:manifests][1][copied_file]
|
|
153
|
+
new_fp = fp1
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
if copied_revision.nil? || copied_revision.empty?
|
|
159
|
+
ancestor = repo["."].ancestors.detect {|ancestor| ancestor[copied_file] }
|
|
160
|
+
copied_revision = ancestor[copied_file].file_node
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
UI::say "#{@path}: copy #{copied_file}:#{copied_revision.hexlify}"
|
|
164
|
+
|
|
165
|
+
meta_data = {}
|
|
166
|
+
meta_data["copy"] = copied_file
|
|
167
|
+
meta_data["copyrev"] = copied_revision.hexlify
|
|
168
|
+
fp1, fp2 = NULL_ID, new_fp
|
|
169
|
+
|
|
170
|
+
[fp1, fp2, meta_data]
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
##
|
|
174
|
+
# Is it more recent than +other+?
|
|
175
|
+
def <=>(other)
|
|
176
|
+
change_id <=> other.change_id
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
##
|
|
180
|
+
# Returns the changeset that this file belongs to
|
|
181
|
+
#
|
|
182
|
+
# @return [Changeset] the changeset this file belongs to
|
|
183
|
+
def changeset
|
|
184
|
+
@changeset ||= Changeset.new @repo, change_id
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
##
|
|
188
|
+
# Dunno why this is here
|
|
189
|
+
#
|
|
190
|
+
# @return [String]
|
|
191
|
+
def repo_path
|
|
192
|
+
@path
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
##
|
|
196
|
+
# The file log that tracks this file
|
|
197
|
+
#
|
|
198
|
+
# @return [FileLog] The revision log tracking this file
|
|
199
|
+
def file_log
|
|
200
|
+
@file_log ||= @repo.file_log @path
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
##
|
|
204
|
+
# The revision of the repository that this {VersionedFile} belongs to.
|
|
205
|
+
def change_id
|
|
206
|
+
@change_id ||= @changeset.revision if @changeset
|
|
207
|
+
@change_id ||= file_log[file_rev].link_rev unless @changeset
|
|
208
|
+
@change_id
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
##
|
|
212
|
+
# The version of the file in its own history.
|
|
213
|
+
def file_node
|
|
214
|
+
@file_node ||= file_log.lookup_id(@file_id) if @file_id
|
|
215
|
+
@file_node ||= changeset.file_node(@path) unless @file_id
|
|
216
|
+
@file_node ||= NULL_ID
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
##
|
|
220
|
+
# Returns the index into the file log's history for this file
|
|
221
|
+
def file_rev
|
|
222
|
+
@file_rev ||= file_log.rev(file_node)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
##
|
|
226
|
+
# Is this a null version?
|
|
227
|
+
def nil?
|
|
228
|
+
file_node.nil?
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
##
|
|
232
|
+
# String representation.
|
|
233
|
+
def to_s
|
|
234
|
+
"#{path}@#{node.hexlify[0..11]}"
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
##
|
|
238
|
+
# IRB Inspector string representation
|
|
239
|
+
def inspect
|
|
240
|
+
"#<HG Versioned File: #{to_s}>"
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
##
|
|
244
|
+
# Hash value for sticking this fucker in a hash
|
|
245
|
+
def hash
|
|
246
|
+
return (path + file_id.to_s).hash
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
##
|
|
250
|
+
# Equality! Compares paths and revision indexes
|
|
251
|
+
def ==(other)
|
|
252
|
+
return false unless @path && @file_id && other.path && other.file_id
|
|
253
|
+
@path == other.path && @file_id == other.file_id
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
##
|
|
257
|
+
# Retrieves the file with a different ID
|
|
258
|
+
#
|
|
259
|
+
# @param [String] file_id a new file ID revision in file_log
|
|
260
|
+
def file(file_id)
|
|
261
|
+
self.class.new @repo, @path, :file_id => file_id, :file_log => file_log
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# Gets the flags for this file (x, l, or empty string)
|
|
265
|
+
def flags; changeset.flags(@path); end
|
|
266
|
+
# The manifest that this file revision is from
|
|
267
|
+
def manifest_entry; changeset.manifest_entry; end
|
|
268
|
+
# Node ID for this file's revision
|
|
269
|
+
def node; changeset.node; end
|
|
270
|
+
# All files in this changeset that this revision of this file was committed
|
|
271
|
+
def files; changeset.all_files; end
|
|
272
|
+
# The data in this file
|
|
273
|
+
def data; file_log.read(file_node); end
|
|
274
|
+
# The path to this file
|
|
275
|
+
def path; @path; end
|
|
276
|
+
# The size of this file
|
|
277
|
+
def size; file_log.size(file_rev); end
|
|
278
|
+
|
|
279
|
+
# Returns the revision index
|
|
280
|
+
def revision
|
|
281
|
+
return changeset.rev if @changeset || @change_id
|
|
282
|
+
link_rev
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
# Link-revision index
|
|
286
|
+
def link_rev; file_log[file_rev].link_rev; end
|
|
287
|
+
|
|
288
|
+
##
|
|
289
|
+
# Compares to a bit of text.
|
|
290
|
+
# Returns true if different, false for the same.
|
|
291
|
+
# (much like <=> == 0 for the same)
|
|
292
|
+
def cmp(text)
|
|
293
|
+
file_log.cmp(file_node, text)
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
##
|
|
297
|
+
# Has this file been renamed? If so give some good info. Returns
|
|
298
|
+
# the new location and the flags ('x', 'l', '')
|
|
299
|
+
#
|
|
300
|
+
# @return [Array<String, String>] [new_path, flags]
|
|
301
|
+
def renamed?
|
|
302
|
+
renamed = file_log.renamed?(file_node)
|
|
303
|
+
return renamed unless renamed
|
|
304
|
+
|
|
305
|
+
return renamed if rev == link_rev
|
|
306
|
+
|
|
307
|
+
name = path
|
|
308
|
+
fnode = file_node
|
|
309
|
+
changeset.parents.each do |p|
|
|
310
|
+
pnode = p.filenode(name)
|
|
311
|
+
next if pnode.nil?
|
|
312
|
+
|
|
313
|
+
# Why the fuck does this method return nil. This could fuck things
|
|
314
|
+
# up down the line. There better be a good fucking reason for this.
|
|
315
|
+
# Sorry I'm so irritated. I just need some food.
|
|
316
|
+
return nil if fnode == pnode
|
|
317
|
+
end
|
|
318
|
+
renamed
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
##
|
|
322
|
+
# What are this revised file's parents? Return them as {VersionedFile}s.
|
|
323
|
+
def parents
|
|
324
|
+
p = @path
|
|
325
|
+
fl = file_log
|
|
326
|
+
pl = file_log.parents(file_node).map {|n| [p, n, fl] }
|
|
327
|
+
|
|
328
|
+
r = file_log.renamed?(file_node)
|
|
329
|
+
pl[0] = [r[0], r[1], nil] if r
|
|
330
|
+
|
|
331
|
+
pl.select {|parent,n,l| n != NULL_ID}.map do |parent, n, l|
|
|
332
|
+
VersionedFile.new(@repo, parent, :file_id => n, :file_log => l)
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
##
|
|
337
|
+
# What are this file's children?
|
|
338
|
+
def children
|
|
339
|
+
c = file_log.children(file_node)
|
|
340
|
+
c.map do |x|
|
|
341
|
+
VersionedFile.new(@repo, @path, :file_id => x, :file_log => file_log)
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
def annotate_decorate(text, revision, line_number = false)
|
|
346
|
+
if line_number
|
|
347
|
+
size = text.split("\n").size
|
|
348
|
+
retarr = [nil,text]
|
|
349
|
+
retarr[0] = (1..size).map {|i| [revision, i]}
|
|
350
|
+
else
|
|
351
|
+
retarr = [nil, text]
|
|
352
|
+
retarr[0] = [[revision, false]] * text.split("\n").size
|
|
353
|
+
end
|
|
354
|
+
retarr
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def annotate_diff_pair(parent, child)
|
|
358
|
+
Diffs::BinaryDiff.blocks_as_array(parent[1], child[1]).each do |a1,a2,b1,b2|
|
|
359
|
+
child[0][b1..(b2-1)] = parent[0][a1..(a2-1)]
|
|
360
|
+
end
|
|
361
|
+
child
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
def annotate_get_file(path, file_id)
|
|
365
|
+
log = path == @path ? file_log : @repo.get_file(path)
|
|
366
|
+
return VersionedFile.new(@repo, path, :file_id => file_id, :file_log => log)
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def annotate_parents_helper(file, follow_copies = false)
|
|
370
|
+
path = file.path
|
|
371
|
+
if file.file_rev.nil?
|
|
372
|
+
parent_list = file.parents.map {|n| [n.path, n.file_rev]}
|
|
373
|
+
else
|
|
374
|
+
parent_list = file.file_log.parent_indices_for_index(file.file_rev)
|
|
375
|
+
parent_list.map! {|n| [path, n]}
|
|
376
|
+
end
|
|
377
|
+
if follow_copies
|
|
378
|
+
r = file.renamed?
|
|
379
|
+
pl[0] = [r[0], @repo.get_file(r[0]).revision(r[1])] if r
|
|
380
|
+
end
|
|
381
|
+
return parent_list.select {|p, n| n != NULL_REV}.
|
|
382
|
+
map {|p, n| annotate_get_file(p, n)}
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
def annotate(follow_copies = false, line_number = false)
|
|
386
|
+
base = (revision != link_rev) ? file(file_rev) : self
|
|
387
|
+
|
|
388
|
+
needed = {base => 1}
|
|
389
|
+
counters = {(base.path + base.file_id.to_s) => 1}
|
|
390
|
+
visit = [base]
|
|
391
|
+
files = [base.path]
|
|
392
|
+
|
|
393
|
+
while visit.any?
|
|
394
|
+
file = visit.shift
|
|
395
|
+
annotate_parents_helper(file).each do |p|
|
|
396
|
+
unless needed.include? p
|
|
397
|
+
needed[p] = 1
|
|
398
|
+
counters[p.path + p.file_id.to_s] = 1
|
|
399
|
+
visit << p
|
|
400
|
+
files << p.path unless files.include? p.path
|
|
401
|
+
end
|
|
402
|
+
end
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
visit = []
|
|
406
|
+
files.each do |f|
|
|
407
|
+
filenames = needed.keys.select {|k| k.path == f}.map {|n| [n.revision, n]}
|
|
408
|
+
visit += filenames
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
hist = {}
|
|
412
|
+
lastfile = ""
|
|
413
|
+
visit.sort.each do |rev, file_ann|
|
|
414
|
+
curr = annotate_decorate(file_ann.data, file_ann, line_number)
|
|
415
|
+
annotate_parents_helper(file_ann).each do |p|
|
|
416
|
+
next if p.file_id == NULL_ID
|
|
417
|
+
curr = annotate_diff_pair(hist[p.path + p.file_id.to_s], curr)
|
|
418
|
+
counters[p.path + p.file_id.to_s] -= 1
|
|
419
|
+
hist.delete(p.path + p.file_id.to_s) if counters[p.path + p.file_id.to_s] == 0
|
|
420
|
+
end
|
|
421
|
+
hist[file_ann.path+file_ann.file_id.to_s] = curr
|
|
422
|
+
lastfile = file_ann
|
|
423
|
+
end
|
|
424
|
+
returnarr = []
|
|
425
|
+
hist[lastfile.path+lastfile.file_id.to_s].inspect # force all lazy-loading to stoppeth
|
|
426
|
+
ret = hist[lastfile.path+lastfile.file_id.to_s][0].each_with_index do |obj, i|
|
|
427
|
+
returnarr << obj + [hist[lastfile.path+lastfile.file_id.to_s][1].split_newlines[i]]
|
|
428
|
+
end
|
|
429
|
+
# hist[lastfile.path+lastfile.file_id.to_s][0][i] + hist[lastfile.path+lastfile.file_id.to_s][1].split_newlines[i]
|
|
430
|
+
# end
|
|
431
|
+
ret = hist[lastfile.path+lastfile.file_id.to_s][0].zip(hist[lastfile.path+lastfile.file_id.to_s][1].split_newlines)
|
|
432
|
+
returnarr
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
def get_parents_helper(vertex, ancestor_cache, filelog_cache)
|
|
436
|
+
return ancestor_cache[vertex] if ancestor_cache[vertex]
|
|
437
|
+
file, node = vertex
|
|
438
|
+
filelog_cache[file] = @repo.get_file(file) unless filelog_cache[file]
|
|
439
|
+
|
|
440
|
+
filelog = filelog_cache[file]
|
|
441
|
+
parent_list = filelog.parents(node).select {|p| p != NULL_ID}.map {|p| [file, p]}
|
|
442
|
+
|
|
443
|
+
has_renamed = filelog.renamed?(node)
|
|
444
|
+
|
|
445
|
+
parent_list << has_renamed if has_renamed
|
|
446
|
+
ancestor_cache[vertex] = parent_list
|
|
447
|
+
parent_list
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
def ancestor(file_2)
|
|
451
|
+
ancestor_cache = {}
|
|
452
|
+
[self, file_2].each do |c|
|
|
453
|
+
if c.file_rev == NULL_REV || c.file_rev.nil?
|
|
454
|
+
parent_list = c.parents.map {|n| [n.path, n.file_node]}
|
|
455
|
+
ancestor_cache[[c.path, c.file_node]] = parent_list
|
|
456
|
+
end
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
filelog_cache = {repo_path => file_log, file_2.repo_path => file_2.file_log}
|
|
460
|
+
a, b = [path, file_node], [file_2.path, file_2.file_node]
|
|
461
|
+
parents_proc = proc {|vertex| get_parents_helper(vertex, ancestor_cache, filelog_cache)}
|
|
462
|
+
|
|
463
|
+
v = Graphs::AncestorCalculator.ancestors(a, b, parents_proc)
|
|
464
|
+
if v
|
|
465
|
+
file, node = v
|
|
466
|
+
return VersionedFile.new(@repo, file, :file_id => node, :file_log => filelog_cache[file])
|
|
467
|
+
end
|
|
468
|
+
return nil
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
##
|
|
474
|
+
# This is a VersionedFile, except it's in the working directory, so its data
|
|
475
|
+
# is stored on disk in the actual file. Other than that, it's basically the
|
|
476
|
+
# same in its interface!
|
|
477
|
+
class VersionedWorkingFile < VersionedFile
|
|
478
|
+
|
|
479
|
+
##
|
|
480
|
+
# Initializes a new working dir file - slightly different semantics here
|
|
481
|
+
def initialize(repo, path, opts={})
|
|
482
|
+
@repo, @path = repo, path
|
|
483
|
+
@change_id = nil
|
|
484
|
+
@file_rev, @file_node = nil, nil
|
|
485
|
+
|
|
486
|
+
@file_log = opts[:file_log] if opts[:file_log]
|
|
487
|
+
@changeset = opts[:working_changeset]
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
##
|
|
491
|
+
# Gets the working directory changeset
|
|
492
|
+
def changeset
|
|
493
|
+
@changeset ||= WorkingDirectoryChangeset.new(@repo)
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
##
|
|
497
|
+
# Dunno why this is here
|
|
498
|
+
def repo_path
|
|
499
|
+
@repo.dirstate.copy_map[@path] || @path
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
##
|
|
503
|
+
# Gets the file log?
|
|
504
|
+
#
|
|
505
|
+
# @todo add a @file_log ||= in the front?
|
|
506
|
+
def file_log
|
|
507
|
+
@repo.file_log repo_path
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
##
|
|
511
|
+
# String representation
|
|
512
|
+
def to_s
|
|
513
|
+
"#{path}@#{@changeset}"
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
##
|
|
517
|
+
# Returns the file at a different revision
|
|
518
|
+
def file(file_id)
|
|
519
|
+
VersionedFile.new(@repo, repo_path, :file_id => file_id, :file_log => file_log)
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
##
|
|
523
|
+
# Get what revision this is
|
|
524
|
+
def revision
|
|
525
|
+
return @changeset.revision if @changeset
|
|
526
|
+
file_log[@file_rev].link_rev
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
##
|
|
530
|
+
# Get the contents of this file
|
|
531
|
+
def data
|
|
532
|
+
data = @repo.working_read(@path)
|
|
533
|
+
data
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
##
|
|
537
|
+
# Has this file been renamed? If so give some good info. Returns
|
|
538
|
+
# the new location and the flags ('x', 'l', '')
|
|
539
|
+
#
|
|
540
|
+
# @return [Array<String, String>] [new_path, flags]
|
|
541
|
+
def renamed?
|
|
542
|
+
rp = repo_path
|
|
543
|
+
return nil if rp == @path
|
|
544
|
+
[rp, (self.changeset.parents[0].manifest_entry[rp] || NULL_ID)]
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
##
|
|
548
|
+
# The working directory's parents are the heads, so get this file in
|
|
549
|
+
# the previous revision.
|
|
550
|
+
def parents
|
|
551
|
+
p = @path
|
|
552
|
+
rp = repo_path
|
|
553
|
+
pcl = @changeset.parents
|
|
554
|
+
fl = file_log
|
|
555
|
+
pl = [[rp, pcl[0].manifest_entry[rp] || NULL_ID, fl]]
|
|
556
|
+
if pcl.size > 1
|
|
557
|
+
if rp != p
|
|
558
|
+
fl = nil
|
|
559
|
+
end
|
|
560
|
+
pl << [p, pcl[1].manifest_entry[p] || NULL_ID, fl]
|
|
561
|
+
end
|
|
562
|
+
pl.select {|_, n, __| n != NULL_ID}.map do |parent, n, l|
|
|
563
|
+
VersionedFile.new(@repo, parent, :file_id => n, :file_log => l)
|
|
564
|
+
end
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
##
|
|
568
|
+
# Returns the current size of the file
|
|
569
|
+
#
|
|
570
|
+
def size
|
|
571
|
+
File.stat(@repo.join(@path)).size
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
##
|
|
575
|
+
# Returns the date that this file was last modified.
|
|
576
|
+
def date
|
|
577
|
+
t, tz = changeset.date
|
|
578
|
+
begin
|
|
579
|
+
return [FileUtils.lstat(@repo.join(@path)).mtime, tz]
|
|
580
|
+
rescue Errno::ENOENT
|
|
581
|
+
return [t, tz]
|
|
582
|
+
end
|
|
583
|
+
end
|
|
584
|
+
|
|
585
|
+
##
|
|
586
|
+
# Compares to the given text. Overridden because this file is
|
|
587
|
+
# stored on disk in the actual working directory.
|
|
588
|
+
#
|
|
589
|
+
# @param [String] text the text to compare to
|
|
590
|
+
# @return [Boolean] true if the two are different
|
|
591
|
+
def cmp(text)
|
|
592
|
+
@repo.working_read(@path) != text
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
end
|
|
596
|
+
end
|
|
597
|
+
end
|