amp 0.5.2 → 0.5.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|