amp 0.5.0
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 +1 -0
- data/.hgignore +26 -0
- data/AUTHORS +2 -0
- data/History.txt +6 -0
- data/LICENSE +37 -0
- data/MANIFESTO +7 -0
- data/Manifest.txt +294 -0
- data/README.md +129 -0
- data/Rakefile +102 -0
- data/SCHEDULE.markdown +12 -0
- data/STYLE +27 -0
- data/TODO.markdown +149 -0
- data/ampfile.rb +47 -0
- data/bin/amp +30 -0
- data/bin/amp1.9 +30 -0
- data/ext/amp/bz2/README.txt +39 -0
- data/ext/amp/bz2/bz2.c +1582 -0
- data/ext/amp/bz2/extconf.rb +77 -0
- data/ext/amp/bz2/mkmf.log +29 -0
- data/ext/amp/mercurial_patch/extconf.rb +5 -0
- data/ext/amp/mercurial_patch/mpatch.c +405 -0
- data/ext/amp/priority_queue/extconf.rb +5 -0
- data/ext/amp/priority_queue/priority_queue.c +947 -0
- data/ext/amp/support/extconf.rb +5 -0
- data/ext/amp/support/support.c +250 -0
- data/lib/amp.rb +200 -0
- data/lib/amp/commands/command.rb +507 -0
- data/lib/amp/commands/command_support.rb +137 -0
- data/lib/amp/commands/commands/config.rb +143 -0
- data/lib/amp/commands/commands/help.rb +29 -0
- data/lib/amp/commands/commands/init.rb +10 -0
- data/lib/amp/commands/commands/templates.rb +137 -0
- data/lib/amp/commands/commands/version.rb +7 -0
- data/lib/amp/commands/commands/workflow.rb +28 -0
- data/lib/amp/commands/commands/workflows/git/add.rb +65 -0
- data/lib/amp/commands/commands/workflows/git/copy.rb +27 -0
- data/lib/amp/commands/commands/workflows/git/mv.rb +23 -0
- data/lib/amp/commands/commands/workflows/git/rm.rb +60 -0
- data/lib/amp/commands/commands/workflows/hg/add.rb +53 -0
- data/lib/amp/commands/commands/workflows/hg/addremove.rb +86 -0
- data/lib/amp/commands/commands/workflows/hg/annotate.rb +46 -0
- data/lib/amp/commands/commands/workflows/hg/archive.rb +126 -0
- data/lib/amp/commands/commands/workflows/hg/branch.rb +28 -0
- data/lib/amp/commands/commands/workflows/hg/branches.rb +30 -0
- data/lib/amp/commands/commands/workflows/hg/bundle.rb +115 -0
- data/lib/amp/commands/commands/workflows/hg/clone.rb +95 -0
- data/lib/amp/commands/commands/workflows/hg/commit.rb +42 -0
- data/lib/amp/commands/commands/workflows/hg/copy.rb +31 -0
- data/lib/amp/commands/commands/workflows/hg/debug/dirstate.rb +32 -0
- data/lib/amp/commands/commands/workflows/hg/debug/index.rb +36 -0
- data/lib/amp/commands/commands/workflows/hg/default.rb +9 -0
- data/lib/amp/commands/commands/workflows/hg/diff.rb +30 -0
- data/lib/amp/commands/commands/workflows/hg/forget.rb +11 -0
- data/lib/amp/commands/commands/workflows/hg/heads.rb +25 -0
- data/lib/amp/commands/commands/workflows/hg/identify.rb +23 -0
- data/lib/amp/commands/commands/workflows/hg/import.rb +135 -0
- data/lib/amp/commands/commands/workflows/hg/incoming.rb +85 -0
- data/lib/amp/commands/commands/workflows/hg/info.rb +18 -0
- data/lib/amp/commands/commands/workflows/hg/log.rb +21 -0
- data/lib/amp/commands/commands/workflows/hg/manifest.rb +13 -0
- data/lib/amp/commands/commands/workflows/hg/merge.rb +53 -0
- data/lib/amp/commands/commands/workflows/hg/move.rb +28 -0
- data/lib/amp/commands/commands/workflows/hg/outgoing.rb +61 -0
- data/lib/amp/commands/commands/workflows/hg/pull.rb +74 -0
- data/lib/amp/commands/commands/workflows/hg/push.rb +20 -0
- data/lib/amp/commands/commands/workflows/hg/remove.rb +45 -0
- data/lib/amp/commands/commands/workflows/hg/resolve.rb +83 -0
- data/lib/amp/commands/commands/workflows/hg/revert.rb +53 -0
- data/lib/amp/commands/commands/workflows/hg/root.rb +13 -0
- data/lib/amp/commands/commands/workflows/hg/serve.rb +38 -0
- data/lib/amp/commands/commands/workflows/hg/status.rb +116 -0
- data/lib/amp/commands/commands/workflows/hg/tag.rb +69 -0
- data/lib/amp/commands/commands/workflows/hg/tags.rb +27 -0
- data/lib/amp/commands/commands/workflows/hg/tip.rb +13 -0
- data/lib/amp/commands/commands/workflows/hg/update.rb +27 -0
- data/lib/amp/commands/commands/workflows/hg/verify.rb +9 -0
- data/lib/amp/commands/commands/workflows/hg/view.rb +36 -0
- data/lib/amp/commands/dispatch.rb +181 -0
- data/lib/amp/commands/hooks.rb +81 -0
- data/lib/amp/dependencies/amp_support.rb +1 -0
- data/lib/amp/dependencies/amp_support/ruby_amp_support.rb +103 -0
- data/lib/amp/dependencies/minitar.rb +979 -0
- data/lib/amp/dependencies/priority_queue.rb +18 -0
- data/lib/amp/dependencies/priority_queue/c_priority_queue.rb +1 -0
- data/lib/amp/dependencies/priority_queue/poor_priority_queue.rb +46 -0
- data/lib/amp/dependencies/priority_queue/ruby_priority_queue.rb +525 -0
- data/lib/amp/dependencies/python_config.rb +211 -0
- data/lib/amp/dependencies/trollop.rb +713 -0
- data/lib/amp/dependencies/zip/ioextras.rb +155 -0
- data/lib/amp/dependencies/zip/stdrubyext.rb +111 -0
- data/lib/amp/dependencies/zip/tempfile_bugfixed.rb +186 -0
- data/lib/amp/dependencies/zip/zip.rb +1850 -0
- data/lib/amp/dependencies/zip/zipfilesystem.rb +609 -0
- data/lib/amp/dependencies/zip/ziprequire.rb +90 -0
- data/lib/amp/encoding/base85.rb +97 -0
- data/lib/amp/encoding/binary_diff.rb +82 -0
- data/lib/amp/encoding/difflib.rb +166 -0
- data/lib/amp/encoding/mercurial_diff.rb +378 -0
- data/lib/amp/encoding/mercurial_patch.rb +1 -0
- data/lib/amp/encoding/patch.rb +292 -0
- data/lib/amp/encoding/pure_ruby/ruby_mercurial_patch.rb +123 -0
- data/lib/amp/extensions/ditz.rb +41 -0
- data/lib/amp/extensions/lighthouse.rb +167 -0
- data/lib/amp/graphs/ancestor.rb +147 -0
- data/lib/amp/graphs/copies.rb +261 -0
- data/lib/amp/merges/merge_state.rb +164 -0
- data/lib/amp/merges/merge_ui.rb +322 -0
- data/lib/amp/merges/simple_merge.rb +450 -0
- data/lib/amp/profiling_hacks.rb +36 -0
- data/lib/amp/repository/branch_manager.rb +234 -0
- data/lib/amp/repository/dir_state.rb +950 -0
- data/lib/amp/repository/journal.rb +203 -0
- data/lib/amp/repository/lock.rb +207 -0
- data/lib/amp/repository/repositories/bundle_repository.rb +214 -0
- data/lib/amp/repository/repositories/http_repository.rb +377 -0
- data/lib/amp/repository/repositories/local_repository.rb +2661 -0
- data/lib/amp/repository/repository.rb +94 -0
- data/lib/amp/repository/store.rb +485 -0
- data/lib/amp/repository/tag_manager.rb +319 -0
- data/lib/amp/repository/updatable.rb +532 -0
- data/lib/amp/repository/verification.rb +431 -0
- data/lib/amp/repository/versioned_file.rb +475 -0
- data/lib/amp/revlogs/bundle_revlogs.rb +246 -0
- data/lib/amp/revlogs/changegroup.rb +217 -0
- data/lib/amp/revlogs/changelog.rb +338 -0
- data/lib/amp/revlogs/changeset.rb +521 -0
- data/lib/amp/revlogs/file_log.rb +165 -0
- data/lib/amp/revlogs/index.rb +493 -0
- data/lib/amp/revlogs/manifest.rb +195 -0
- data/lib/amp/revlogs/node.rb +18 -0
- data/lib/amp/revlogs/revlog.rb +1032 -0
- data/lib/amp/revlogs/revlog_support.rb +126 -0
- data/lib/amp/server/amp_user.rb +44 -0
- data/lib/amp/server/extension/amp_extension.rb +396 -0
- data/lib/amp/server/extension/authorization.rb +201 -0
- data/lib/amp/server/fancy_http_server.rb +252 -0
- data/lib/amp/server/fancy_views/_browser.haml +28 -0
- data/lib/amp/server/fancy_views/_diff_file.haml +13 -0
- data/lib/amp/server/fancy_views/_navbar.haml +17 -0
- data/lib/amp/server/fancy_views/changeset.haml +31 -0
- data/lib/amp/server/fancy_views/commits.haml +32 -0
- data/lib/amp/server/fancy_views/file.haml +35 -0
- data/lib/amp/server/fancy_views/file_diff.haml +23 -0
- data/lib/amp/server/fancy_views/harshcss/all_hallows_eve.css +72 -0
- data/lib/amp/server/fancy_views/harshcss/amy.css +147 -0
- data/lib/amp/server/fancy_views/harshcss/twilight.css +138 -0
- data/lib/amp/server/fancy_views/stylesheet.sass +175 -0
- data/lib/amp/server/http_server.rb +140 -0
- data/lib/amp/server/repo_user_management.rb +287 -0
- data/lib/amp/support/amp_config.rb +164 -0
- data/lib/amp/support/amp_ui.rb +287 -0
- data/lib/amp/support/docs.rb +54 -0
- data/lib/amp/support/generator.rb +78 -0
- data/lib/amp/support/ignore.rb +144 -0
- data/lib/amp/support/loaders.rb +93 -0
- data/lib/amp/support/logger.rb +103 -0
- data/lib/amp/support/match.rb +151 -0
- data/lib/amp/support/multi_io.rb +87 -0
- data/lib/amp/support/openers.rb +121 -0
- data/lib/amp/support/ruby_19_compatibility.rb +66 -0
- data/lib/amp/support/support.rb +1095 -0
- data/lib/amp/templates/blank.commit.erb +23 -0
- data/lib/amp/templates/blank.log.erb +18 -0
- data/lib/amp/templates/default.commit.erb +23 -0
- data/lib/amp/templates/default.log.erb +26 -0
- data/lib/amp/templates/template.rb +165 -0
- data/site/Rakefile +24 -0
- data/site/src/about/ampfile.haml +57 -0
- data/site/src/about/commands.haml +106 -0
- data/site/src/about/index.haml +33 -0
- data/site/src/about/performance.haml +31 -0
- data/site/src/about/workflows.haml +34 -0
- data/site/src/contribute/index.haml +65 -0
- data/site/src/contribute/style.haml +297 -0
- data/site/src/css/active4d.css +114 -0
- data/site/src/css/all_hallows_eve.css +72 -0
- data/site/src/css/all_themes.css +3299 -0
- data/site/src/css/amp.css +260 -0
- data/site/src/css/amy.css +147 -0
- data/site/src/css/blackboard.css +88 -0
- data/site/src/css/brilliance_black.css +605 -0
- data/site/src/css/brilliance_dull.css +599 -0
- data/site/src/css/cobalt.css +149 -0
- data/site/src/css/cur_amp.css +185 -0
- data/site/src/css/dawn.css +121 -0
- data/site/src/css/eiffel.css +121 -0
- data/site/src/css/espresso_libre.css +109 -0
- data/site/src/css/idle.css +62 -0
- data/site/src/css/iplastic.css +80 -0
- data/site/src/css/lazy.css +73 -0
- data/site/src/css/mac_classic.css +123 -0
- data/site/src/css/magicwb_amiga.css +104 -0
- data/site/src/css/pastels_on_dark.css +188 -0
- data/site/src/css/reset.css +55 -0
- data/site/src/css/slush_poppies.css +85 -0
- data/site/src/css/spacecadet.css +51 -0
- data/site/src/css/sunburst.css +180 -0
- data/site/src/css/twilight.css +137 -0
- data/site/src/css/zenburnesque.css +91 -0
- data/site/src/get/index.haml +32 -0
- data/site/src/helpers.rb +121 -0
- data/site/src/images/amp_logo.png +0 -0
- data/site/src/images/carbonica.png +0 -0
- data/site/src/images/revolution.png +0 -0
- data/site/src/images/tab-bg.png +0 -0
- data/site/src/images/tab-sliding-left.png +0 -0
- data/site/src/images/tab-sliding-right.png +0 -0
- data/site/src/include/_footer.haml +22 -0
- data/site/src/include/_header.haml +17 -0
- data/site/src/index.haml +104 -0
- data/site/src/learn/index.haml +46 -0
- data/site/src/scripts/jquery-1.3.2.min.js +19 -0
- data/site/src/scripts/jquery.cookie.js +96 -0
- data/tasks/stats.rake +155 -0
- data/tasks/yard.rake +171 -0
- data/test/dirstate_tests/dirstate +0 -0
- data/test/dirstate_tests/hgrc +5 -0
- data/test/dirstate_tests/test_dir_state.rb +192 -0
- data/test/functional_tests/resources/.hgignore +2 -0
- data/test/functional_tests/resources/STYLE.txt +25 -0
- data/test/functional_tests/resources/command.rb +372 -0
- data/test/functional_tests/resources/commands/annotate.rb +57 -0
- data/test/functional_tests/resources/commands/experimental/lolcats.rb +17 -0
- data/test/functional_tests/resources/commands/heads.rb +22 -0
- data/test/functional_tests/resources/commands/manifest.rb +12 -0
- data/test/functional_tests/resources/commands/status.rb +90 -0
- data/test/functional_tests/resources/version2/.hgignore +5 -0
- data/test/functional_tests/resources/version2/STYLE.txt +25 -0
- data/test/functional_tests/resources/version2/command.rb +372 -0
- data/test/functional_tests/resources/version2/commands/annotate.rb +45 -0
- data/test/functional_tests/resources/version2/commands/experimental/lolcats.rb +17 -0
- data/test/functional_tests/resources/version2/commands/heads.rb +22 -0
- data/test/functional_tests/resources/version2/commands/manifest.rb +12 -0
- data/test/functional_tests/resources/version2/commands/status.rb +90 -0
- data/test/functional_tests/resources/version3/.hgignore +5 -0
- data/test/functional_tests/resources/version3/STYLE.txt +31 -0
- data/test/functional_tests/resources/version3/command.rb +376 -0
- data/test/functional_tests/resources/version3/commands/annotate.rb +45 -0
- data/test/functional_tests/resources/version3/commands/experimental/lolcats.rb +17 -0
- data/test/functional_tests/resources/version3/commands/heads.rb +22 -0
- data/test/functional_tests/resources/version3/commands/manifest.rb +12 -0
- data/test/functional_tests/resources/version3/commands/status.rb +90 -0
- data/test/functional_tests/resources/version4/.hgignore +5 -0
- data/test/functional_tests/resources/version4/STYLE.txt +31 -0
- data/test/functional_tests/resources/version4/command.rb +376 -0
- data/test/functional_tests/resources/version4/commands/experimental/lolcats.rb +17 -0
- data/test/functional_tests/resources/version4/commands/heads.rb +22 -0
- data/test/functional_tests/resources/version4/commands/manifest.rb +12 -0
- data/test/functional_tests/resources/version4/commands/stats.rb +25 -0
- data/test/functional_tests/resources/version4/commands/status.rb +90 -0
- data/test/functional_tests/resources/version5_1/.hgignore +5 -0
- data/test/functional_tests/resources/version5_1/STYLE.txt +2 -0
- data/test/functional_tests/resources/version5_1/command.rb +374 -0
- data/test/functional_tests/resources/version5_1/commands/experimental/lolcats.rb +17 -0
- data/test/functional_tests/resources/version5_1/commands/heads.rb +22 -0
- data/test/functional_tests/resources/version5_1/commands/manifest.rb +12 -0
- data/test/functional_tests/resources/version5_1/commands/stats.rb +25 -0
- data/test/functional_tests/resources/version5_1/commands/status.rb +90 -0
- data/test/functional_tests/resources/version5_2/.hgignore +5 -0
- data/test/functional_tests/resources/version5_2/STYLE.txt +14 -0
- data/test/functional_tests/resources/version5_2/command.rb +376 -0
- data/test/functional_tests/resources/version5_2/commands/experimental/lolcats.rb +17 -0
- data/test/functional_tests/resources/version5_2/commands/manifest.rb +12 -0
- data/test/functional_tests/resources/version5_2/commands/newz.rb +12 -0
- data/test/functional_tests/resources/version5_2/commands/stats.rb +25 -0
- data/test/functional_tests/resources/version5_2/commands/status.rb +90 -0
- data/test/functional_tests/test_functional.rb +604 -0
- data/test/localrepo_tests/test_local_repo.rb +121 -0
- data/test/localrepo_tests/testrepo.tar.gz +0 -0
- data/test/manifest_tests/00manifest.i +0 -0
- data/test/manifest_tests/test_manifest.rb +72 -0
- data/test/merge_tests/base.txt +10 -0
- data/test/merge_tests/expected.local.txt +16 -0
- data/test/merge_tests/local.txt +11 -0
- data/test/merge_tests/remote.txt +11 -0
- data/test/merge_tests/test_merge.rb +26 -0
- data/test/revlog_tests/00changelog.i +0 -0
- data/test/revlog_tests/revision_added_changelog.i +0 -0
- data/test/revlog_tests/test_adding_index.i +0 -0
- data/test/revlog_tests/test_revlog.rb +333 -0
- data/test/revlog_tests/testindex.i +0 -0
- data/test/store_tests/store.tar.gz +0 -0
- data/test/store_tests/test_fncache_store.rb +122 -0
- data/test/test_amp.rb +9 -0
- data/test/test_base85.rb +14 -0
- data/test/test_bdiff.rb +42 -0
- data/test/test_commands.rb +122 -0
- data/test/test_difflib.rb +50 -0
- data/test/test_helper.rb +15 -0
- data/test/test_journal.rb +29 -0
- data/test/test_match.rb +134 -0
- data/test/test_mdiff.rb +74 -0
- data/test/test_mpatch.rb +14 -0
- data/test/test_support.rb +24 -0
- metadata +385 -0
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
module Amp
|
|
2
|
+
|
|
3
|
+
##
|
|
4
|
+
# This is used for faking data writing, until we're totally done
|
|
5
|
+
# accumulating data.
|
|
6
|
+
class FakeFileAppender
|
|
7
|
+
|
|
8
|
+
##
|
|
9
|
+
# Initializes the fake file with the pointer to the real deal,
|
|
10
|
+
# and also the current data
|
|
11
|
+
def initialize(fp, buffer, size)
|
|
12
|
+
@data = buffer
|
|
13
|
+
@fp = fp
|
|
14
|
+
@offset = fp.tell
|
|
15
|
+
@size = size
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
# Returns the endpoint of the data in the (fake) file
|
|
20
|
+
def endpt
|
|
21
|
+
@size + @data.join.size
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
##
|
|
25
|
+
# Returns the current offset of the (fake) file
|
|
26
|
+
def tell
|
|
27
|
+
@offset
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
##
|
|
31
|
+
# Nothing, since we don't flush, we just sit here
|
|
32
|
+
def flush; end
|
|
33
|
+
|
|
34
|
+
##
|
|
35
|
+
# Closes the real file pointer for this fake file
|
|
36
|
+
def close
|
|
37
|
+
@fp.close
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
##
|
|
41
|
+
# Seeks to the position requested. We're faking this, but it's a little
|
|
42
|
+
# tougher, because if we break the fake file down like this:
|
|
43
|
+
#
|
|
44
|
+
# [ actual file size = 10 | pending data size = 10 ]
|
|
45
|
+
#
|
|
46
|
+
# If the user seeks to 15, we need to make sure we don't try to seek to
|
|
47
|
+
# 15 in the file, as that would cause errors.
|
|
48
|
+
def seek(offset, whence)
|
|
49
|
+
if whence == IO::SEEK_SET
|
|
50
|
+
@offset = offset
|
|
51
|
+
elsif whence == IO::SEEK_CUR
|
|
52
|
+
@offset += offset
|
|
53
|
+
elsif whence == IO::SEEK_END
|
|
54
|
+
@offset = self.endpt + offset
|
|
55
|
+
end
|
|
56
|
+
if @offset < @size
|
|
57
|
+
@fp.seek @offset
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
##
|
|
62
|
+
# Reads from the fake file, for _count_ bytes. This is a little sketchy
|
|
63
|
+
# because we might be reading some real data and some fake data, or all
|
|
64
|
+
# real, or all fake.
|
|
65
|
+
#
|
|
66
|
+
# @param [Integer] count how much to read. -1 will read it all.
|
|
67
|
+
# @return [String] the contents
|
|
68
|
+
def read(count=-1)
|
|
69
|
+
ret = ""
|
|
70
|
+
|
|
71
|
+
if @offset < @size
|
|
72
|
+
s = @fp.read(count)
|
|
73
|
+
ret = s
|
|
74
|
+
@offset += s.size
|
|
75
|
+
if count > 0
|
|
76
|
+
count -= s.size
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
unless count.zero?
|
|
81
|
+
doff = @offset - @size
|
|
82
|
+
@data.unshift @data.join
|
|
83
|
+
@data.delete_range(1..-1)
|
|
84
|
+
s = @data[0][doff..(doff+count-1)]
|
|
85
|
+
@offset += s.size
|
|
86
|
+
ret += s
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
ret
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
##
|
|
93
|
+
# Writes to the fake file. Notice - this will only work because we only
|
|
94
|
+
# append to the file - we don't write in the middle anywhere, or this
|
|
95
|
+
# scheme would fail.
|
|
96
|
+
#
|
|
97
|
+
# @param [#to_s] s data to write
|
|
98
|
+
def write(s)
|
|
99
|
+
@data << s.to_s
|
|
100
|
+
@offset += s.size
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
class DelayedOpener
|
|
105
|
+
attr_accessor :default
|
|
106
|
+
|
|
107
|
+
##
|
|
108
|
+
# Initializes the delayed opener, needing the real deal, and the owner
|
|
109
|
+
# of the delayed opener.
|
|
110
|
+
#
|
|
111
|
+
# @param [Opener] realopener the actual opener. Since the DelayedOpener
|
|
112
|
+
# doesn't know how to open files, we'll need this!
|
|
113
|
+
# @param [ChangeLog] owner the owner of the delayed opener. This reference
|
|
114
|
+
# is needed because we need to check on the state of the delayed_buffer
|
|
115
|
+
# and what not.
|
|
116
|
+
def initialize(realopener, owner)
|
|
117
|
+
@real_opener, @owner = realopener, owner
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
##
|
|
121
|
+
# Specialized opener that will fake writing when asked to. We need to
|
|
122
|
+
# know who our owner is and we need a real opener so we can actually
|
|
123
|
+
# open files. The DelayedOpener doesn't know how to do real IO.
|
|
124
|
+
def open(name, mode="r")
|
|
125
|
+
fp = @real_opener.open(name, mode)
|
|
126
|
+
return fp if name != @owner.index_file
|
|
127
|
+
|
|
128
|
+
# Are we holding off on writing? if so, set up the file where we
|
|
129
|
+
# writing pending data.
|
|
130
|
+
|
|
131
|
+
if @owner.delay_count == 0
|
|
132
|
+
@owner.delay_name = File.amp_name(fp)
|
|
133
|
+
mode.gsub!(/a/, 'w') if @owner.empty?
|
|
134
|
+
return @real_opener.open(name+".a", mode) # See what I did there?
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# FakeFileAppender = fake file so we don't do any real writing yet
|
|
138
|
+
size = File.stat(@real_opener.join(name)).size
|
|
139
|
+
ffa = FakeFileAppender.new(fp, @owner.delay_buffer, size)
|
|
140
|
+
|
|
141
|
+
return ffa
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
##
|
|
146
|
+
# A ChangeLog is a special revision log that stores the actual commit data,
|
|
147
|
+
# including usernames, dates, messages, all kinds of stuff.
|
|
148
|
+
#
|
|
149
|
+
# This version of the revision log is special though, because sometimes
|
|
150
|
+
# we have to hold off on writing until all other updates are done, for
|
|
151
|
+
# example during merges that might fail. So we have to actually have
|
|
152
|
+
# a real Opener and a fake one, which will save the data in memory.
|
|
153
|
+
# When you call #finalize, the fake file will replace the real deal.
|
|
154
|
+
class ChangeLog < Amp::Revlog
|
|
155
|
+
attr_accessor :delay_count, :delay_name, :index_file, :delay_buffer, :node_map
|
|
156
|
+
|
|
157
|
+
##
|
|
158
|
+
# Initializes the revision log. Just pass in an Opener. Amp::Opener.new(path)
|
|
159
|
+
# will do just fine.
|
|
160
|
+
#
|
|
161
|
+
# @param [Amp::Opener] opener an object that knows how to open
|
|
162
|
+
# and return files based on a root directory.
|
|
163
|
+
def initialize(opener)
|
|
164
|
+
super(opener, "00changelog.i")
|
|
165
|
+
@node_map = @index.node_map
|
|
166
|
+
end
|
|
167
|
+
alias_method :changelog_initialize, :initialize
|
|
168
|
+
|
|
169
|
+
##
|
|
170
|
+
# Tells the changelog to stop writing updates directly to the file,
|
|
171
|
+
# and start saving any new info to memory/other files. Used when the
|
|
172
|
+
# changelog has to be the last file saved.
|
|
173
|
+
def delay_update
|
|
174
|
+
@_real_opener = @opener
|
|
175
|
+
@opener = DelayedOpener.new(@_real_opener, self) # Our fake Opener
|
|
176
|
+
@delay_count = self.size
|
|
177
|
+
@delay_buffer = []
|
|
178
|
+
@delay_name = nil
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
##
|
|
182
|
+
# Finalizes the changelog by swapping out the fake file if it has to.
|
|
183
|
+
# If there's any other data left in the buffer, it will be written
|
|
184
|
+
# as well.
|
|
185
|
+
def finalize(journal)
|
|
186
|
+
if @delay_name
|
|
187
|
+
src = @_real_opener.join(@index_file+".a")
|
|
188
|
+
dest = @_real_opener.join(@index_file)
|
|
189
|
+
@opener = @_real_opener # switch back to normal mode....
|
|
190
|
+
return File.amp_force_rename(src, dest)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
if @delay_buffer && @delay_buffer.any?
|
|
194
|
+
@fp = open(@index_file, "a")
|
|
195
|
+
@fp.write @delay_buffer.join
|
|
196
|
+
@fp.close
|
|
197
|
+
@delay_buffer = []
|
|
198
|
+
end
|
|
199
|
+
# check_inline_size journal
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
##
|
|
203
|
+
# Reads while we're blocking this changelog's output.
|
|
204
|
+
# @param file the file to read in as a revision log
|
|
205
|
+
def read_pending(file)
|
|
206
|
+
r = Revlog.new(@opener, file)
|
|
207
|
+
@index = r.index
|
|
208
|
+
@node_map = r.index.node_map
|
|
209
|
+
@chunk_cache = r.chunk_cache
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
##
|
|
213
|
+
# Writes our data, while being aware of the delay buffer when we're holding
|
|
214
|
+
# off on finalizing the changelog.
|
|
215
|
+
def write_pending
|
|
216
|
+
if @delay_buffer && @delay_buffer.size > 0
|
|
217
|
+
fp1 = @_real_opener.open(@index_file)
|
|
218
|
+
fp2 = @_real_opener.open(@index_file + ".a", "w+")
|
|
219
|
+
UI.debug "trying to open #{@index_file + ".a"}..."
|
|
220
|
+
fp2.write fp1.read
|
|
221
|
+
fp2.write @delay_buffer.join
|
|
222
|
+
fp2.close
|
|
223
|
+
fp1.close
|
|
224
|
+
@delay_buffer = []
|
|
225
|
+
@delay_name = @index_file
|
|
226
|
+
end
|
|
227
|
+
return true if @delay_name && @delay_name.any?
|
|
228
|
+
false
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
##
|
|
232
|
+
# Does a check on our size, but knows enough to quit if we're still in
|
|
233
|
+
# delayed-writing mode.
|
|
234
|
+
# @param [Amp::Journal] journal the journal to use to keep track of our transaction
|
|
235
|
+
# @param [File] fp the file pointer to use to check our size
|
|
236
|
+
def check_inline_size(journal, fp=nil)
|
|
237
|
+
return if @opener.is_a? DelayedOpener
|
|
238
|
+
super(journal, fp)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
##
|
|
242
|
+
# Decodes the extra data stored with the commit, such as requirements
|
|
243
|
+
# or just about anything else we need to save
|
|
244
|
+
# @param [String] text the data in the revision, decompressed
|
|
245
|
+
# @return [Hash] key-value pairs, joining each file with its extra data
|
|
246
|
+
def decode_extra(text)
|
|
247
|
+
extra = {}
|
|
248
|
+
text.split("\0").select {|l| l.any? }.
|
|
249
|
+
map {|l| l.remove_slashes.split(":",2) }.
|
|
250
|
+
each {|k,v| extra[k]=v }
|
|
251
|
+
extra
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
##
|
|
255
|
+
# Encodes the extra data in a format we can use for writing.
|
|
256
|
+
# @param [Hash] data the extra data to format
|
|
257
|
+
# @return [String] the encoded data
|
|
258
|
+
def encode_extra(data)
|
|
259
|
+
" " + data.sort.map {|k| "#{k}:#{data[k]}".add_slashes }.join("\0")
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
##
|
|
263
|
+
# Reads the revision at the given node_id. It returns it in a format
|
|
264
|
+
# that tells us everything about the revision - the manifest, the user
|
|
265
|
+
# who committed it, timestamps, the relevant filenames, the description
|
|
266
|
+
# message, and any extra data.
|
|
267
|
+
#
|
|
268
|
+
# @todo Text encodings, I hate you. but i must do them
|
|
269
|
+
# @param [Fixnum] node the node ID to lookup into the revision log
|
|
270
|
+
# @return [[String, String, [Float, Integer], [String], String, Hash]]
|
|
271
|
+
# The format is [Manifest, Username, [Time, Timezone], [Filenames],
|
|
272
|
+
# Message, ExtraData].
|
|
273
|
+
def read(node)
|
|
274
|
+
text = decompress_revision node
|
|
275
|
+
if text.nil? || text.empty?
|
|
276
|
+
return [NULL_ID, "", [0,0], [], "", {"branch" => "default"}]
|
|
277
|
+
end
|
|
278
|
+
#p text
|
|
279
|
+
last = text.index("\n\n")
|
|
280
|
+
desc = text[last+2..-1] #TODO: encoding
|
|
281
|
+
l = text[0..last].split("\n")
|
|
282
|
+
manifest = l[0].unhexlify
|
|
283
|
+
user = l[1] #TODO: encoding
|
|
284
|
+
extra_data = l[2].split(' ', 3)
|
|
285
|
+
if extra_data.size != 3
|
|
286
|
+
time = extra_data.shift.to_f
|
|
287
|
+
timezone = extra_data.shift.to_i
|
|
288
|
+
extra = {}
|
|
289
|
+
else
|
|
290
|
+
time, timezone, extra = extra_data
|
|
291
|
+
time, timezone = time.to_f, timezone.to_i
|
|
292
|
+
extra = decode_extra text
|
|
293
|
+
end
|
|
294
|
+
extra["branch"] = "default" unless extra["branch"]
|
|
295
|
+
|
|
296
|
+
files = l[3..-1]
|
|
297
|
+
|
|
298
|
+
#puts(">> Ari's tipmost changeset: "+[manifest, user, [time, timezone], files, desc, extra].inspect) #killme
|
|
299
|
+
|
|
300
|
+
[manifest, user, [time, timezone], files, desc, extra]
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
##
|
|
304
|
+
# Adds the given commit to the changelog.
|
|
305
|
+
#
|
|
306
|
+
# @todo Handle text encodings
|
|
307
|
+
# @param [Amp::Manifest] Manifest a hex-version of a node_id or something?
|
|
308
|
+
# @param [String] files the files relevant to the commit, to be included
|
|
309
|
+
# @param [String] desc the commit message from the user
|
|
310
|
+
# @param [Amp::Journal] journal the transaction journal to write to for rollbacks if
|
|
311
|
+
# something goes horribly wrong
|
|
312
|
+
# @param [String] p1 the first parent of this node
|
|
313
|
+
# @param [String] p2 the second parent of this node
|
|
314
|
+
# @param [Strng] user the username of the committer
|
|
315
|
+
# @param [Time] date the date of the commit
|
|
316
|
+
# @param [Hash] extra any extra data
|
|
317
|
+
def add(manifest, files, desc, journal, p1=nil, p2=nil, user=nil, date=nil, extra={})
|
|
318
|
+
user = user.strip
|
|
319
|
+
raise RevlogSupport::RevlogError.new("no \\n in username") if user=~ /\n/
|
|
320
|
+
user, desc = user, desc #TODO: encoding!
|
|
321
|
+
|
|
322
|
+
date = Time.now unless date
|
|
323
|
+
parsed_date = "#{date.to_i} #{-1 * date.utc_offset}"
|
|
324
|
+
|
|
325
|
+
if extra && ["default", "", nil].include?(extra["branch"])
|
|
326
|
+
extra.delete "branch"
|
|
327
|
+
end
|
|
328
|
+
if extra
|
|
329
|
+
extra = (extra.any?) ? encode_extra(extra) : ""
|
|
330
|
+
parsed_date = "#{parsed_date}#{extra}"
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
l = [manifest.hexlify, user, parsed_date] + files.sort + ["", desc]
|
|
334
|
+
text = l.join "\n"
|
|
335
|
+
add_revision text, journal, self.size, p1, p2
|
|
336
|
+
end
|
|
337
|
+
end
|
|
338
|
+
end
|
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
module Amp
|
|
2
|
+
|
|
3
|
+
##
|
|
4
|
+
# A Changeset is a simple way of accessing the repository within a certain
|
|
5
|
+
# revision. For example, if the user specifies revision # 36, or revision
|
|
6
|
+
# 3adf21, then we can look those up, and work within the repository at the
|
|
7
|
+
# moment of that revision.
|
|
8
|
+
class Changeset
|
|
9
|
+
include RevlogSupport::Node
|
|
10
|
+
include Comparable
|
|
11
|
+
include Enumerable
|
|
12
|
+
|
|
13
|
+
attr_reader :repo
|
|
14
|
+
alias_method :repository, :repo
|
|
15
|
+
|
|
16
|
+
##
|
|
17
|
+
# Initializes a new changeset. We need a repository to work with, and also
|
|
18
|
+
# a change_id. this change_id could be a revision index or a node_id for
|
|
19
|
+
# the revision.
|
|
20
|
+
#
|
|
21
|
+
# @param [Repository] repo a repository to work with.
|
|
22
|
+
# @param [Integer, String] change_id an ID or index to lookup to find this
|
|
23
|
+
# changeset.
|
|
24
|
+
#
|
|
25
|
+
def initialize(repo, change_id='')
|
|
26
|
+
change_id = '.' if change_id == ''
|
|
27
|
+
@repo = repo
|
|
28
|
+
if change_id.kind_of? Integer
|
|
29
|
+
@revision = change_id
|
|
30
|
+
@node_id = @repo.changelog.node_id_for_index change_id
|
|
31
|
+
else
|
|
32
|
+
@node_id = @repo.lookup change_id
|
|
33
|
+
@revision = @repo.changelog.rev @node_id
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
##
|
|
38
|
+
# Converts the revision to a number
|
|
39
|
+
def to_i; @revision; end
|
|
40
|
+
|
|
41
|
+
##
|
|
42
|
+
# Converts the revision to an easy-to-digest string
|
|
43
|
+
def to_s(opts = {})
|
|
44
|
+
if opts[:template]
|
|
45
|
+
to_templated_s(opts)
|
|
46
|
+
else
|
|
47
|
+
@node_id[0..5].hexlify
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
##
|
|
52
|
+
#
|
|
53
|
+
def to_templated_s(opts={})
|
|
54
|
+
|
|
55
|
+
change_node = node
|
|
56
|
+
revision = self.revision
|
|
57
|
+
log = @repo.changelog
|
|
58
|
+
changes = log.read change_node
|
|
59
|
+
username = changes[1]
|
|
60
|
+
date = Time.at changes[2].first
|
|
61
|
+
files = changes[3]
|
|
62
|
+
description = changes[4]
|
|
63
|
+
extra = changes[5]
|
|
64
|
+
branch = extra["branch"]
|
|
65
|
+
cs_tags = tags
|
|
66
|
+
type = opts[:template_type].to_s || 'log'
|
|
67
|
+
|
|
68
|
+
added = opts[:added]
|
|
69
|
+
removed = opts[:removed]
|
|
70
|
+
updated = opts[:updated]
|
|
71
|
+
config = opts
|
|
72
|
+
|
|
73
|
+
parents = useful_parents log, revision
|
|
74
|
+
parents.map! {|p| [p, log.node(p)[0..5].hexlify] }
|
|
75
|
+
|
|
76
|
+
p1 = useful_parents(log, revision)[0]
|
|
77
|
+
p2 = useful_parents(log, revision)[1]
|
|
78
|
+
|
|
79
|
+
return "" if opts[:no_output]
|
|
80
|
+
|
|
81
|
+
config = opts
|
|
82
|
+
|
|
83
|
+
template = opts[:template]
|
|
84
|
+
template = "default-#{type}" if (template.nil? || template.to_s == "default")
|
|
85
|
+
|
|
86
|
+
template = Support::Template[template]
|
|
87
|
+
template.render({}, binding)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def useful_parents(log, revision)
|
|
91
|
+
parents = log.parent_indices_for_index revision
|
|
92
|
+
if parents[1] == -1
|
|
93
|
+
if parents[0] >= revision - 1
|
|
94
|
+
parents = []
|
|
95
|
+
else
|
|
96
|
+
parents = [parents[0]]
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
parents
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
##
|
|
103
|
+
# Gives an easier way to digest this changeset while reminding us it's a
|
|
104
|
+
# changeset
|
|
105
|
+
def inspect
|
|
106
|
+
"#<Changeset #{to_s}>"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
##
|
|
110
|
+
# Hash function for putting these bad boys in hashes
|
|
111
|
+
#
|
|
112
|
+
# @return [Integer] a hash value.
|
|
113
|
+
def hash
|
|
114
|
+
return @revision.hash if @revision
|
|
115
|
+
return object_id
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
##
|
|
119
|
+
# Compares 2 changesets so we can sort them and whatnot
|
|
120
|
+
#
|
|
121
|
+
# @param [Changeset] other a changeset we will compare against
|
|
122
|
+
# @return [Integer] -1, 0, or 1. Typical comparison.
|
|
123
|
+
def <=>(other)
|
|
124
|
+
return 0 if @revision.nil? || other.revision.nil?
|
|
125
|
+
@revision <=> other.revision
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
##
|
|
129
|
+
# Are we a null revision?
|
|
130
|
+
# @return [Boolean] null?
|
|
131
|
+
def nil?
|
|
132
|
+
@revision != NULL_REV
|
|
133
|
+
end
|
|
134
|
+
alias_method :null?, :nil?
|
|
135
|
+
|
|
136
|
+
# Gets the raw changeset data for this revision. This includes
|
|
137
|
+
# the user who committed it, the description of the commit, and so on.
|
|
138
|
+
# Returns this: [manifest, user, [time, timezone], files, desc, extra]
|
|
139
|
+
def raw_changeset
|
|
140
|
+
@repo.changelog.read(@node_id)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
##
|
|
144
|
+
# Returns the {ManifestEntry} for this revision. This will give
|
|
145
|
+
# us info on any file we want, including flags such as executable
|
|
146
|
+
# or if it's a link. Sizes and so on are also included.
|
|
147
|
+
#
|
|
148
|
+
# @return [ManifestEntry] the manifest at this point in time
|
|
149
|
+
def manifest
|
|
150
|
+
@manifest ||= @repo.manifest.read(raw_changeset[0])
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
##
|
|
154
|
+
# Returns the change in the manifest at this revision. I don't entirely
|
|
155
|
+
# know what this is yet.
|
|
156
|
+
def manifest_delta
|
|
157
|
+
@manifest_delta ||= @repo.manifest.read_delta(raw_changeset[0])
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
##
|
|
161
|
+
# Returns the parents of this changeset as {Changeset}s.
|
|
162
|
+
#
|
|
163
|
+
# @return [[Changeset]] the parents of this changeset.
|
|
164
|
+
def parents
|
|
165
|
+
return @parents if @parents
|
|
166
|
+
|
|
167
|
+
p = @repo.changelog.parent_indices_for_index @revision
|
|
168
|
+
p = [p[0]] if p[1] == NULL_REV
|
|
169
|
+
|
|
170
|
+
@parents = p.map {|x| Changeset.new(@repo, x) }
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
##
|
|
174
|
+
# Returns the children of this changeset as {Changeset}s.
|
|
175
|
+
#
|
|
176
|
+
# @return [Array<Changeset>] the children of this changeset.
|
|
177
|
+
def children
|
|
178
|
+
@repo.changelog.children(node_id).map do |node|
|
|
179
|
+
Changeset.new(@repo, node)
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
##
|
|
184
|
+
# Iterates over each entry in the manifest.
|
|
185
|
+
def each(&block)
|
|
186
|
+
manifest.sort.each(&block)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
##
|
|
190
|
+
# Checks whether this changeset included a given file or not.
|
|
191
|
+
#
|
|
192
|
+
# @param [String] file the file to lookup
|
|
193
|
+
# @return [Boolean] whether the file is in this changeset's manifest
|
|
194
|
+
def include?(file)
|
|
195
|
+
manifest[file] != nil
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
##
|
|
199
|
+
# Gets the file with the given name, as a {VersionedFile}.
|
|
200
|
+
# @param file the path to the file to retrieve
|
|
201
|
+
# @return [VersionedFile] the file at this revision
|
|
202
|
+
#
|
|
203
|
+
def [](file)
|
|
204
|
+
get_file(file)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
##
|
|
208
|
+
# Returns the file's info, namely it's node_id and flags it may
|
|
209
|
+
# have at this point in time, such as "x" for executable.
|
|
210
|
+
#
|
|
211
|
+
# @param path the path to the file
|
|
212
|
+
# @return [[String, String]] the [node_id, flags] pair for this file
|
|
213
|
+
def file_info(path)
|
|
214
|
+
if manifest # have we loaded our manifest yet? if so, use that sucker
|
|
215
|
+
result = [manifest[path], manifest.flags[path]]
|
|
216
|
+
if result[0].nil?
|
|
217
|
+
return [NULL_ID, '']
|
|
218
|
+
else
|
|
219
|
+
return result
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
if manifest_delta || files[path] # check if it's in the delta... i dunno
|
|
223
|
+
if manifest_delta[path]
|
|
224
|
+
return [manifest_delta[path], manifest_delta.flags[path]]
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
# Give us, just look it up the long way in the manifest. not fun. slow.
|
|
228
|
+
node, flag = @repo.manifest.find(raw_changeset[0], path)
|
|
229
|
+
if node.nil?
|
|
230
|
+
return [NULL_ID, '']
|
|
231
|
+
end
|
|
232
|
+
return [node, flag]
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
##
|
|
236
|
+
# Gets the flags for the file at the given path at this revision.
|
|
237
|
+
# @param path the path to the file in question
|
|
238
|
+
# @return [String] the flags for the file, such as "x" or "l".
|
|
239
|
+
#
|
|
240
|
+
def flags(path)
|
|
241
|
+
info = file_info(path)[1]
|
|
242
|
+
return "" if info.nil?
|
|
243
|
+
info
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
##
|
|
247
|
+
# Gets the node_id in the manifest for the file at this path, for this
|
|
248
|
+
# specific revision.
|
|
249
|
+
#
|
|
250
|
+
# @param path the path to the file
|
|
251
|
+
# @return [String] the node's ID in the manifest, which we'll use every
|
|
252
|
+
# where we need a node_id.
|
|
253
|
+
def file_node(path)
|
|
254
|
+
file_info(path).first[0..19]
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
##
|
|
258
|
+
# Creates a versioned file for the file at the given path, for the frame
|
|
259
|
+
# of reference of this revision.
|
|
260
|
+
# @param path the path to the file
|
|
261
|
+
# @param [String] file_id the node_id, to save us some computation
|
|
262
|
+
# @param [FileLog] file_log the file_log to use, again to save us computation
|
|
263
|
+
# @return [VersionedFile] the file at this revision.
|
|
264
|
+
#
|
|
265
|
+
def get_file(path, file_id = nil, file_log = nil)
|
|
266
|
+
file_id = file_node(path) if file_id.nil?
|
|
267
|
+
VersionedFile.new(@repo, path, :file_id => file_id, :changeset => self,
|
|
268
|
+
:file_log => file_log)
|
|
269
|
+
end
|
|
270
|
+
#accessors
|
|
271
|
+
# revision index
|
|
272
|
+
def revision; @revision; end
|
|
273
|
+
alias_method :rev, :revision
|
|
274
|
+
# node_id
|
|
275
|
+
def node_id; @node_id; end
|
|
276
|
+
# @see node_id
|
|
277
|
+
alias_method :node, :node_id
|
|
278
|
+
# our node_id in sexy hexy
|
|
279
|
+
def hex; @node_id.hexlify; end
|
|
280
|
+
# the user who committed me!
|
|
281
|
+
def user; raw_changeset[1]; end
|
|
282
|
+
# the date i was committed!
|
|
283
|
+
def date; raw_changeset[2]; end
|
|
284
|
+
def easy_date; Time.at(raw_changeset[2].first); end
|
|
285
|
+
# the files affected in this commit!
|
|
286
|
+
def files; raw_changeset[3]; end
|
|
287
|
+
# the message with this commit
|
|
288
|
+
def description; raw_changeset[4]; end
|
|
289
|
+
# What branch i was committed onto
|
|
290
|
+
def branch
|
|
291
|
+
raw_changeset[5]["branch"]
|
|
292
|
+
end
|
|
293
|
+
# Any extra stuff I've got in me
|
|
294
|
+
def extra; raw_changeset[5]; end
|
|
295
|
+
# tags
|
|
296
|
+
def tags; @repo.tags_for_node node; end
|
|
297
|
+
|
|
298
|
+
##
|
|
299
|
+
# recursively walk
|
|
300
|
+
#
|
|
301
|
+
# @param [Amp::Matcher] match this is a custom object that knows files
|
|
302
|
+
# magically. Not your grampa's proc!
|
|
303
|
+
def walk(match) # calls DirState#walk
|
|
304
|
+
# just make it so the keys are there
|
|
305
|
+
results = []
|
|
306
|
+
|
|
307
|
+
hash = Hash.with_keys match.files
|
|
308
|
+
hash.delete '.'
|
|
309
|
+
|
|
310
|
+
each do |file|
|
|
311
|
+
hash.each {|f, val| (hash.delete file and break) if f == file }
|
|
312
|
+
|
|
313
|
+
results << file if match.call file # yield file if match.call file
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
hash.keys.sort.each do |file|
|
|
317
|
+
if match.bad file, "No such file in revision #{revision}" and match[file]
|
|
318
|
+
results << file # yield file
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
results
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def ancestor(other_changeset)
|
|
325
|
+
node = @repo.changelog.ancestor(self.node, other_changeset.node)
|
|
326
|
+
return Changeset.new(@repo, node)
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def ancestors
|
|
330
|
+
results = []
|
|
331
|
+
@repo.changelog.ancestors(revision)
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
##
|
|
336
|
+
# This is a special changeset that specifically works within the
|
|
337
|
+
# working directory. We sort of have to combine the old revision
|
|
338
|
+
# logs with the fact that files might be changed, and not in the
|
|
339
|
+
# revision logs! oh, mercy!
|
|
340
|
+
class WorkingDirectoryChangeset < Changeset
|
|
341
|
+
|
|
342
|
+
def initialize(repo, opts={:text => ""})
|
|
343
|
+
@repo = repo
|
|
344
|
+
@revision = nil
|
|
345
|
+
@node_id = nil
|
|
346
|
+
@text = opts[:text]
|
|
347
|
+
require 'time' if opts[:date].kind_of?(String)
|
|
348
|
+
@date = opts[:date].kind_of?(String) ? Time.parse(opts[:date]) : opts[:date]
|
|
349
|
+
@user = opts[:user] if opts[:user]
|
|
350
|
+
@parents = opts[:parents].map {|p| Changeset.new(@repo, p)} if opts[:parents]
|
|
351
|
+
@status = opts[:changes] if opts[:changes]
|
|
352
|
+
|
|
353
|
+
@extra = {}
|
|
354
|
+
@extra = opts[:extra].dup if opts[:extra]
|
|
355
|
+
unless @extra["branch"]
|
|
356
|
+
branch = @repo.dirstate.branch
|
|
357
|
+
# encoding - to UTF-8
|
|
358
|
+
@extra["branch"] = branch
|
|
359
|
+
end
|
|
360
|
+
@extra["branch"] = "default" if @extra["branch"] && @extra["branch"].empty?
|
|
361
|
+
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
##
|
|
365
|
+
# Converts to a string.
|
|
366
|
+
# I'm my first parent, plus a little extra.
|
|
367
|
+
def to_s
|
|
368
|
+
parents.first.to_s + "+"
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
##
|
|
372
|
+
# Am I nil? never!
|
|
373
|
+
def nil?; false; end
|
|
374
|
+
|
|
375
|
+
##
|
|
376
|
+
# Do I include a given file? (not sure this is ever used yet)
|
|
377
|
+
def include?(key); "?r".include?(@repo.dirstate[key].status.to_hg_letter); end
|
|
378
|
+
|
|
379
|
+
##
|
|
380
|
+
# What is the status of the working directory? This little
|
|
381
|
+
# method hides quite a bit of work!
|
|
382
|
+
def status
|
|
383
|
+
@status ||= @repo.status(:unknown => true)
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
##
|
|
387
|
+
# Who is the user working on me?
|
|
388
|
+
def user
|
|
389
|
+
@user ||= @repo.config.username
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
##
|
|
393
|
+
# Well, I guess the working directory's date is... right now!
|
|
394
|
+
def date
|
|
395
|
+
@date ||= Time.new
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
##
|
|
399
|
+
# Who is the working directory's father? Is it Chef? Mr. Garrison?
|
|
400
|
+
# the 1989 denver broncos?
|
|
401
|
+
#
|
|
402
|
+
# hahaha mike that's hilarious
|
|
403
|
+
def parents
|
|
404
|
+
return @parents if @parents
|
|
405
|
+
p = @repo.dirstate.parents
|
|
406
|
+
p = [p[0]] if p[1] == NULL_ID
|
|
407
|
+
@parents = p.map {|x| Changeset.new(@repo, x) }
|
|
408
|
+
@parents
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
##
|
|
412
|
+
# OK, so we've got the last revision's manifest, that part's simple and makes sense.
|
|
413
|
+
# except now, we need to get the status of the working directory, and
|
|
414
|
+
# add in all the other files, because they're in the "manifest" by being
|
|
415
|
+
# in existence. Oh, and we need to remove any files from the parent's
|
|
416
|
+
# manifest that don't exist anymore. Make sense?
|
|
417
|
+
def manifest
|
|
418
|
+
return @manifest if @manifest
|
|
419
|
+
|
|
420
|
+
# Start off with the last revision's manifest, that's safe.
|
|
421
|
+
man = parents()[0].manifest.dup
|
|
422
|
+
# Any copied files since the last revision?
|
|
423
|
+
copied = @repo.dirstate.copy_map
|
|
424
|
+
# Any modified, added, etc files since the last revision?
|
|
425
|
+
modified, added, removed = status[:modified], status[:added], status[:removed]
|
|
426
|
+
deleted, unknown = status[:deleted], status[:unknown]
|
|
427
|
+
# Merge these discoveries in!
|
|
428
|
+
{:a => added, :m => modified, :u => unknown}.each do |k, list|
|
|
429
|
+
list.each do |file|
|
|
430
|
+
copy_name = (copied[file] || file)
|
|
431
|
+
man[file] = (man.flags[copy_name] || NULL_ID) + k.to_s
|
|
432
|
+
man.flags[file] = @repo.dirstate.flags(file)
|
|
433
|
+
end
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
# Delete files from the real manifest that don't exist.
|
|
437
|
+
(deleted + removed).each do |file|
|
|
438
|
+
man.delete file if man[file]
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
man
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
##
|
|
445
|
+
# Returns a {VersionedWorkingFile} to represent the file at the given
|
|
446
|
+
# point in time. It represents a file in the working directory, which
|
|
447
|
+
# obvious don't read from the history, but from the actual file in
|
|
448
|
+
# question.
|
|
449
|
+
#
|
|
450
|
+
# @param path the path to the file
|
|
451
|
+
# @param file_log the log for the file to save some computation
|
|
452
|
+
# @return [Amp::VersionedWorkingFile] the file object we can work with
|
|
453
|
+
def get_file(path, file_log=nil)
|
|
454
|
+
VersionedWorkingFile.new(@repo, path, :working_changeset => self,
|
|
455
|
+
:file_log => file_log)
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
##
|
|
459
|
+
# Gets the flags for the file at current state in time
|
|
460
|
+
#
|
|
461
|
+
# @param [String] path the path to the file
|
|
462
|
+
# @return [String] the flags, such as "x", "l", or ""
|
|
463
|
+
def flags(path)
|
|
464
|
+
if @manifest
|
|
465
|
+
return manifest.flags[path] || ""
|
|
466
|
+
end
|
|
467
|
+
pnode = parents[0].raw_changeset[0]
|
|
468
|
+
|
|
469
|
+
orig = @repo.dirstate.copy_map[path] || path
|
|
470
|
+
node, flag = @repo.manifest.find(pnode, orig)
|
|
471
|
+
return @repo.dirstate.flags(@repo.working_join(path))
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
def useful_parents(log, revision)
|
|
475
|
+
parents = @parents.map {|p| p.revision}
|
|
476
|
+
if parents[1] == -1
|
|
477
|
+
if parents[0] >= @repo.size - 1
|
|
478
|
+
parents = []
|
|
479
|
+
else
|
|
480
|
+
parents = [parents[0]]
|
|
481
|
+
end
|
|
482
|
+
end
|
|
483
|
+
parents
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
##
|
|
487
|
+
# Recursively walk the directory tree, getting all files that +match+ says
|
|
488
|
+
# are good.
|
|
489
|
+
#
|
|
490
|
+
# @param [Amp::Match] match how to select the files in the tree
|
|
491
|
+
# @param [Boolean] check_ignored (false) should we check for ignored files?
|
|
492
|
+
# @return [Array<String>] an array of filenames in the tree that match +match+
|
|
493
|
+
def walk(match, check_ignored = false)
|
|
494
|
+
tree = @repo.dirstate.walk true, check_ignored, match
|
|
495
|
+
tree.keys.sort
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
# If there's a description, ok then
|
|
499
|
+
def description; @text; end
|
|
500
|
+
# Files affected in this transaction: modified, added, removed.
|
|
501
|
+
def files; (status[:modified] + status[:added] + status[:removed]).sort; end
|
|
502
|
+
# What files have changed?
|
|
503
|
+
def modified; status[:modified]; end
|
|
504
|
+
# What files have we added?
|
|
505
|
+
def added; status[:added]; end
|
|
506
|
+
# What files have been removed?
|
|
507
|
+
def removed; status[:removed]; end
|
|
508
|
+
# What files have been deleted (but not officially)?
|
|
509
|
+
def deleted; status[:deleted]; end
|
|
510
|
+
# What files are hanging out, but untracked?
|
|
511
|
+
def unknown; status[:unknown]; end
|
|
512
|
+
# What files are pristine since the last revision?
|
|
513
|
+
def clean; status[:normal]; end
|
|
514
|
+
# What branch are we in?
|
|
515
|
+
def branch; @extra["branch"]; end
|
|
516
|
+
# Any other extra data? i'd like to hear it
|
|
517
|
+
def extra; @extra; end
|
|
518
|
+
# No children. Returns the empty array.
|
|
519
|
+
def children; []; end
|
|
520
|
+
end
|
|
521
|
+
end
|