amp 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,90 @@
|
|
1
|
+
# With ziprequire you can load ruby modules from a zip file. This means
|
2
|
+
# ruby's module include path can include zip-files.
|
3
|
+
#
|
4
|
+
# The following example creates a zip file with a single entry
|
5
|
+
# <code>log/simplelog.rb</code> that contains a single function
|
6
|
+
# <code>simpleLog</code>:
|
7
|
+
#
|
8
|
+
# require 'zip/zipfilesystem'
|
9
|
+
#
|
10
|
+
# Zip::ZipFile.open("my.zip", true) {
|
11
|
+
# |zf|
|
12
|
+
# zf.file.open("log/simplelog.rb", "w") {
|
13
|
+
# |f|
|
14
|
+
# f.puts "def simpleLog(v)"
|
15
|
+
# f.puts ' Kernel.puts "INFO: #{v}"'
|
16
|
+
# f.puts "end"
|
17
|
+
# }
|
18
|
+
# }
|
19
|
+
#
|
20
|
+
# To use the ruby module stored in the zip archive simply require
|
21
|
+
# <code>zip/ziprequire</code> and include the <code>my.zip</code> zip
|
22
|
+
# file in the module search path. The following command shows one
|
23
|
+
# way to do this:
|
24
|
+
#
|
25
|
+
# ruby -rzip/ziprequire -Imy.zip -e " require 'log/simplelog'; simpleLog 'Hello world' "
|
26
|
+
|
27
|
+
#$: << 'data/rubycode.zip' << 'data/rubycode2.zip'
|
28
|
+
|
29
|
+
|
30
|
+
need{ 'zip' }
|
31
|
+
|
32
|
+
class ZipList #:nodoc:all
|
33
|
+
def initialize(zipFileList)
|
34
|
+
@zipFileList = zipFileList
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_input_stream(entry, &aProc)
|
38
|
+
@zipFileList.each {
|
39
|
+
|zfName|
|
40
|
+
Zip::ZipFile.open(zfName) {
|
41
|
+
|zf|
|
42
|
+
begin
|
43
|
+
return zf.get_input_stream(entry, &aProc)
|
44
|
+
rescue Errno::ENOENT
|
45
|
+
end
|
46
|
+
}
|
47
|
+
}
|
48
|
+
raise Errno::ENOENT,
|
49
|
+
"No matching entry found in zip files '#{@zipFileList.join(', ')}' "+
|
50
|
+
" for '#{entry}'"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
module Kernel #:nodoc:all
|
56
|
+
alias :oldRequire :require
|
57
|
+
|
58
|
+
def require(moduleName)
|
59
|
+
zip_require(moduleName) || oldRequire(moduleName)
|
60
|
+
end
|
61
|
+
|
62
|
+
def zip_require(moduleName)
|
63
|
+
return false if already_loaded?(moduleName)
|
64
|
+
get_resource(ensure_rb_extension(moduleName)) {
|
65
|
+
|zis|
|
66
|
+
eval(zis.read); $" << moduleName
|
67
|
+
}
|
68
|
+
return true
|
69
|
+
rescue Errno::ENOENT => ex
|
70
|
+
return false
|
71
|
+
end
|
72
|
+
|
73
|
+
def get_resource(resourceName, &aProc)
|
74
|
+
zl = ZipList.new($:.grep(/\.zip$/))
|
75
|
+
zl.get_input_stream(resourceName, &aProc)
|
76
|
+
end
|
77
|
+
|
78
|
+
def already_loaded?(moduleName)
|
79
|
+
moduleRE = Regexp.new("^"+moduleName+"(\.rb|\.so|\.dll|\.o)?$")
|
80
|
+
$".detect { |e| e =~ moduleRE } != nil
|
81
|
+
end
|
82
|
+
|
83
|
+
def ensure_rb_extension(aString)
|
84
|
+
aString.sub(/(\.rb)?$/i, ".rb")
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Copyright (C) 2002 Thomas Sondergaard
|
89
|
+
# rubyzip is free software; you can redistribute it and/or
|
90
|
+
# modify it under the terms of the ruby license.
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# = Amp
|
2
|
+
module Amp
|
3
|
+
# = Encoding
|
4
|
+
module Encoding
|
5
|
+
# This class provides methods for encoding and decoding to base85, a storage format used
|
6
|
+
# by Mercurial. Base85 is like base64, only with some extra characters to improve compression.
|
7
|
+
# This is a direct port of the python file base85.py in the Mercurial distribution.
|
8
|
+
class Base85
|
9
|
+
# The allowable Base 85 characters (encoding)
|
10
|
+
B85chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~'
|
11
|
+
# The lookup table to go from character -> decimal (decoding)
|
12
|
+
B85dec = {}
|
13
|
+
# Prepare the decoding table
|
14
|
+
B85chars.size.times do |i|
|
15
|
+
B85dec[B85chars[i,1]] = i
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# Encodes the given string in Base85, and possibly pad it. This code makes sense to me. Fuck I hate
|
20
|
+
# python.
|
21
|
+
#
|
22
|
+
# @param [String] str the string to be encoded
|
23
|
+
# @param [Boolean] pad whether or not to pad the resulting output
|
24
|
+
# @return [String] Base85 encoded string
|
25
|
+
def self.encode str, pad=false
|
26
|
+
l = str.size
|
27
|
+
r = l % 4
|
28
|
+
str += "\0" * (4-r)
|
29
|
+
longs = str.size >> 2
|
30
|
+
out = []
|
31
|
+
words = str.unpack("N#{longs}")
|
32
|
+
|
33
|
+
words.each do |word|
|
34
|
+
word, r = word.divmod 85
|
35
|
+
e = B85chars[r].chr
|
36
|
+
word, r = word.divmod 85
|
37
|
+
d = B85chars[r].chr
|
38
|
+
word, r = word.divmod 85
|
39
|
+
c = B85chars[r].chr
|
40
|
+
word, r = word.divmod 85
|
41
|
+
b = B85chars[r].chr
|
42
|
+
word, r = word.divmod 85
|
43
|
+
a = B85chars[r].chr
|
44
|
+
|
45
|
+
out += [a,b,c,d,e]
|
46
|
+
end
|
47
|
+
|
48
|
+
out = out.join("")
|
49
|
+
return out if pad
|
50
|
+
|
51
|
+
olen = l % 4
|
52
|
+
olen += 1 if olen > 0
|
53
|
+
olen += l / 4 * 5
|
54
|
+
|
55
|
+
out[0 .. olen-1]
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# Decodes a base85 encoded string and returns it. This code sort of mystifies me.
|
60
|
+
# Slash looking at it I'm not sure why we don't just code it in C. Maybe we will eventually.
|
61
|
+
# Fucking python coders.
|
62
|
+
#
|
63
|
+
# @param [String] text the base85 encoded string to decode
|
64
|
+
# @return [String] the decoded text
|
65
|
+
def self.decode text
|
66
|
+
l = text.size
|
67
|
+
out = []
|
68
|
+
i = 0
|
69
|
+
while i < text.size
|
70
|
+
chunk = text[i .. i+4]
|
71
|
+
acc = 0
|
72
|
+
chunk.size.times do |j|
|
73
|
+
acc = acc * 85 + B85dec[chunk[j].chr]
|
74
|
+
end
|
75
|
+
out << acc
|
76
|
+
i += 5
|
77
|
+
end
|
78
|
+
|
79
|
+
cl = l % 5
|
80
|
+
if cl > 0
|
81
|
+
acc *= 85 ** (5 - cl)
|
82
|
+
if cl > 1
|
83
|
+
acc += 0xffffff >> (cl - 2) * 8
|
84
|
+
end
|
85
|
+
out[-1] = acc
|
86
|
+
end
|
87
|
+
|
88
|
+
out = out.pack("N#{out.size}")
|
89
|
+
if cl > 0
|
90
|
+
out = out[0 .. (-1 * (5-cl) - 1)]
|
91
|
+
end
|
92
|
+
out
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Amp
|
2
|
+
##
|
3
|
+
# Binary diffs
|
4
|
+
module Diffs
|
5
|
+
|
6
|
+
##
|
7
|
+
# Methods for producing a binary diff file for 2 input strings. Direct port
|
8
|
+
# from pure/bdiff.py in the mercurial source.
|
9
|
+
module BinaryDiff
|
10
|
+
|
11
|
+
##
|
12
|
+
# Produces a binary diff file from the input strings, str1 and str2. Works by
|
13
|
+
# getting the list of matching blocks (using {SequenceMatcher}) and filling in
|
14
|
+
# the gaps between them. Basically.
|
15
|
+
#
|
16
|
+
# @param [String] str1 the source string/file
|
17
|
+
# @param [String] str2 the destination string/file
|
18
|
+
# @return [String] A binary string representing the diff between the two strings/files
|
19
|
+
def bdiff(str1, str2)
|
20
|
+
# break 'em up into lines
|
21
|
+
a = []
|
22
|
+
str1.each_line {|l| a << l}
|
23
|
+
b = []
|
24
|
+
str2.each_line {|l| b << l}
|
25
|
+
|
26
|
+
if a.nil? || a.empty?
|
27
|
+
s = b.join
|
28
|
+
return [0,0,s.size].pack("NNN") + s
|
29
|
+
end
|
30
|
+
|
31
|
+
bin = []
|
32
|
+
byte_offsets = [0]
|
33
|
+
a.each {|line| byte_offsets << (byte_offsets.last + line.size) }
|
34
|
+
# Get all the sections of a and b that actually match each other.
|
35
|
+
matched_blocks = SequenceMatcher.new(a, b).get_matching_blocks
|
36
|
+
la = lb = 0
|
37
|
+
matched_blocks.each do |block|
|
38
|
+
am, bm, size = block[:start_a], block[:start_b], block[:length]
|
39
|
+
|
40
|
+
# At this point, a[la..am-1] does NOT equal b[lb..bm-1]. We know this
|
41
|
+
# because the block we just got is a *matching* block. It tells us where
|
42
|
+
# the two strings are the *same*. So a[la..am-1] is the source text, and
|
43
|
+
# b[lb..bm-1] is the destination text of our diff. Thus, we say "replace
|
44
|
+
# the text in a from (la..am) with the following text we got from b".
|
45
|
+
# Since the array byte_offsets[] contains the actual byte offsets of each line,
|
46
|
+
# our diff is stored as [start_a_text_to_replace, end_a_text_to_replace,
|
47
|
+
# size_of_replacement_text, replacement_text].
|
48
|
+
s = b[lb .. (bm-1)].join unless lb == bm && lb == 0
|
49
|
+
s = "" if lb == bm && lb == 0
|
50
|
+
bin << [byte_offsets[la], byte_offsets[am], s.size].pack("NNN") + s if am > la || s.any?
|
51
|
+
la = am + size
|
52
|
+
lb = bm + size
|
53
|
+
end
|
54
|
+
bin.join
|
55
|
+
end
|
56
|
+
module_function :bdiff
|
57
|
+
|
58
|
+
##
|
59
|
+
# Breaks the 2 input strings into blocks that match each other. Uses
|
60
|
+
# {SequenceMatcher} and just manipulates the output a little.
|
61
|
+
#
|
62
|
+
# @param [String] str1 the source string
|
63
|
+
# @param [String] str2 the destination string
|
64
|
+
# @return [[Hash]] The matching blocks, with keys :start_a, :start_b, :end_a, :end_b
|
65
|
+
def blocks(str1, str2)
|
66
|
+
an = str1.split_lines_better
|
67
|
+
bn = str2.split_lines_better
|
68
|
+
|
69
|
+
matches = Diffs::SequenceMatcher.new(an, bn).get_matching_blocks
|
70
|
+
matches.map do |match|
|
71
|
+
{:start_a => match[:start_a], :end_a => match[:start_a] + match[:length],
|
72
|
+
:start_b => match[:start_b], :end_b => match[:start_b] + match[:length] }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
module_function :blocks
|
76
|
+
|
77
|
+
def self.blocks_as_array(str1, str2)
|
78
|
+
blocks(str1,str2).map {|h| [h[:start_a], h[:end_a], h[:start_b], h[:end_b]]}
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
module Amp
|
2
|
+
##
|
3
|
+
# = Difflib
|
4
|
+
# Port of the Python Difflib. Only ports what is necessary. The cool part is that
|
5
|
+
# this works for anything that has []! So it'll find matches in an array of lines
|
6
|
+
# or just strings.
|
7
|
+
module Diffs
|
8
|
+
##
|
9
|
+
# Port of the Python SequenceMatcher, leaving out parts that Mercurial doesn't use.
|
10
|
+
class SequenceMatcher
|
11
|
+
|
12
|
+
##
|
13
|
+
# Initializes the sequence matcher.
|
14
|
+
#
|
15
|
+
# @param [String] seq1 The source data
|
16
|
+
# @param [String] seq2 The "destination" data
|
17
|
+
def initialize(seq1='', seq2='')
|
18
|
+
@a = @b = nil
|
19
|
+
set_seqs(seq1,seq2)
|
20
|
+
end
|
21
|
+
##
|
22
|
+
# Initializes the 2 sequences and prepares the rest of the matcher
|
23
|
+
#
|
24
|
+
# @param [#each] seq1 The source input sequence. Can be anything responding to #each.
|
25
|
+
# @param [#each] seq2 The destination sequence - what we transform seq1 into.
|
26
|
+
def set_seqs(seq1,seq2); set_seq1(seq1); set_seq2(seq2); end
|
27
|
+
|
28
|
+
##
|
29
|
+
# Initializes the source sequence and resets the matching blocks.
|
30
|
+
#
|
31
|
+
# @param [#each] seq1 The source input sequence.
|
32
|
+
def set_seq1(seq1)
|
33
|
+
return if @a == seq1
|
34
|
+
@a = seq1
|
35
|
+
@matching_blocks = nil
|
36
|
+
end
|
37
|
+
##
|
38
|
+
# Initializes the destination sequence and resets the matching blocks. It also prepares
|
39
|
+
# some optimization data.
|
40
|
+
#
|
41
|
+
# @param [#each] seq2 The destination input sequence.
|
42
|
+
def set_seq2(seq2)
|
43
|
+
return if @b == seq2
|
44
|
+
@b = seq2
|
45
|
+
@matching_blocks = nil
|
46
|
+
build_b2j
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# Sets up some optimization stuff. internal use only. I fucking hate python but ok here's what it
|
51
|
+
# does... it goes through the destination sequence, setting the indices into the destination where
|
52
|
+
# you can find each line. So it'll save the fact that "return nil" appears on lines 3, 14, and 20.
|
53
|
+
# at the same time it's looking for "popular" entries, that are ignored to save runtime later. They
|
54
|
+
# get removed from the list.
|
55
|
+
# The original source respected a "junk" parameter, which was also removed from the list. But
|
56
|
+
# Mercurial doesn't use the junk parameter, so I stripped that code.
|
57
|
+
def build_b2j
|
58
|
+
n = @b.size
|
59
|
+
@b2j = {}
|
60
|
+
populardict = {}
|
61
|
+
n.times do |i|
|
62
|
+
elt = @b[i,1]
|
63
|
+
if @b2j[elt]
|
64
|
+
indices = @b2j[elt]
|
65
|
+
if n >= 2000 && (indices.size * 100 > n)
|
66
|
+
populardict[elt] = true
|
67
|
+
indices.clear
|
68
|
+
else
|
69
|
+
indices << i
|
70
|
+
end
|
71
|
+
else
|
72
|
+
@b2j[elt] = [i]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
populardict.each do |k,v|
|
77
|
+
@b2j.delete k
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
##
|
82
|
+
# Finds the longest match in the 2 sequences between the 2 ranges provided.
|
83
|
+
#
|
84
|
+
# @param alo don't look at any part of the source before this
|
85
|
+
# @param ahi don't look at any part of the source after this
|
86
|
+
# @param blo don't look at any part of the destination before this
|
87
|
+
# @param bhi don't look at any part of the destination after this
|
88
|
+
# @return [[Integer,Integer,Integer]] The return is of the form
|
89
|
+
# [start_source, start_destination, length_of_common_sequence].
|
90
|
+
# source[start_source + i] == destination[start_destination + i] for all 0 <= i < length_of_common_sequence
|
91
|
+
def find_longest_match(alo, ahi, blo, bhi)
|
92
|
+
j2len = {}
|
93
|
+
besti, bestj, bestsize = alo, blo, 0
|
94
|
+
alo.upto(ahi-1) do |i|
|
95
|
+
newj2len = {}
|
96
|
+
@b2j[@a[i,1]] && @b2j[@a[i,1]].each do |j|
|
97
|
+
next if j < blo
|
98
|
+
break if j >= bhi
|
99
|
+
k = newj2len[j] = j2len[j-1].to_i + 1
|
100
|
+
besti, bestj, bestsize = i-k+1, j-k+1, k if k > bestsize
|
101
|
+
end
|
102
|
+
j2len = newj2len
|
103
|
+
end
|
104
|
+
# we ignored popular elements before. they're being added here.
|
105
|
+
while besti > alo && bestj > blo && @a[besti-1,1] == @b[bestj-1,1]
|
106
|
+
besti, bestj, bestsize = besti-1, bestj-1, bestsize + 1
|
107
|
+
end
|
108
|
+
# we ignored popular elements before. they're being added here.
|
109
|
+
while besti+bestsize < ahi && bestj+bestsize < bhi && @a[besti+bestsize,1] == @b[bestj+bestsize,1]
|
110
|
+
bestsize += 1
|
111
|
+
end
|
112
|
+
return [besti, bestj, bestsize]
|
113
|
+
end
|
114
|
+
|
115
|
+
##
|
116
|
+
# This will go through and find all the matching blocks in the
|
117
|
+
# 2 sequences, picking out the best sequences using find_longest_match.
|
118
|
+
#
|
119
|
+
# @return [[Hash]] A list of hashes, each of which represents 1 matching block.
|
120
|
+
def get_matching_blocks
|
121
|
+
return @matching_blocks if @matching_blocks
|
122
|
+
la, lb = @a.size, @b.size
|
123
|
+
|
124
|
+
# This is best done recursively, but if you have a large file it can blow
|
125
|
+
# the stack. We ignore popular lines here to speed things up. I guess.
|
126
|
+
queue = [[0, la, 0, lb]]
|
127
|
+
@matching_blocks = []
|
128
|
+
while queue.any?
|
129
|
+
alo, ahi, blo, bhi = queue.shift
|
130
|
+
i, j, k = x = find_longest_match(alo, ahi, blo, bhi)
|
131
|
+
if k > 0
|
132
|
+
@matching_blocks << x
|
133
|
+
if alo < i && blo < j
|
134
|
+
queue << [alo, i, blo, j]
|
135
|
+
end
|
136
|
+
if i+k < ahi && j+k < bhi
|
137
|
+
queue << [i+k, ahi, j+k, bhi]
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
# Sort 'em from beginning of the sequences to the end
|
142
|
+
@matching_blocks.sort!
|
143
|
+
i1 = j1 = k1 = 0
|
144
|
+
non_adjacent = []
|
145
|
+
# If any "popular" entries were ignored, let's add them to the sequence now.
|
146
|
+
@matching_blocks.each do |i2, j2, k2|
|
147
|
+
if i1 + k1 == i2 && j1 + k1 == j2
|
148
|
+
k1 += k2
|
149
|
+
else
|
150
|
+
non_adjacent << [i1, j1, k1] if k1 > 0
|
151
|
+
i1, j1, k1 = i2, j2, k2
|
152
|
+
end
|
153
|
+
end
|
154
|
+
# Add the last found sequence if there is one
|
155
|
+
non_adjacent << [i1, j1, k1] if k1 > 0
|
156
|
+
# Add the terminator sequence
|
157
|
+
non_adjacent << [la, lb, 0]
|
158
|
+
# Make the output ruby-like, we don't need fucking tuples
|
159
|
+
@matching_blocks = non_adjacent.map do |i, j, k|
|
160
|
+
{:start_a => i, :start_b => j, :length => k}
|
161
|
+
end
|
162
|
+
@matching_blocks
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
@@ -0,0 +1,378 @@
|
|
1
|
+
module Amp
|
2
|
+
module Diffs
|
3
|
+
##
|
4
|
+
# = MercurialDiff
|
5
|
+
# Mercurial has it's own implementation of the unified diff, because windows
|
6
|
+
# boxes don't have diff -u. Plus code is faster than the shell.
|
7
|
+
# Lame. That's ok, it's pretty easy to do. And we can also add flags and
|
8
|
+
# change default settings.
|
9
|
+
#
|
10
|
+
# Mainly, you're only going to use MercurialDiff.unified_diff(). It's usage
|
11
|
+
# is described below.
|
12
|
+
module MercurialDiff
|
13
|
+
extend self
|
14
|
+
##
|
15
|
+
# These are the default options you can modify. Grab them, clone them,
|
16
|
+
# change them. Notice: You have to *clone* this when you use it, or
|
17
|
+
# you will be changing the default options!
|
18
|
+
DEFAULT_OPTIONS = {:context => 3, :text => false, :show_func => false,
|
19
|
+
:git => false, :no_dates => false, :ignore_ws => false,
|
20
|
+
:ignore_ws_amount => false, :ignore_blank_lines => false,
|
21
|
+
:pretty => false}
|
22
|
+
|
23
|
+
##
|
24
|
+
# Clear up whitespace in the text if we have any options relating
|
25
|
+
# to getting rid of whitespace.
|
26
|
+
#
|
27
|
+
# @param [String] text the text to modify
|
28
|
+
# @param [Hash] options the options to use when deciding how to clean text
|
29
|
+
# @option [Boolean] options :ignore_ws (false) do we ignore all whitespace?
|
30
|
+
# this has the net effect of removing all whitespace.
|
31
|
+
# @option [Boolean] options :ignore_ws_amount (false) when this option is
|
32
|
+
# true, we only remove "excessive" whitespace - more than 1 space or tab.
|
33
|
+
# we then substitute it all with 1 space.
|
34
|
+
# @option [Boolean] options :ignore_blank_lines (false) when this option
|
35
|
+
# is true, we remove all extra blank lines.
|
36
|
+
def whitespace_clean(text, options=DEFAULT_OPTIONS)
|
37
|
+
if options[:ignore_ws]
|
38
|
+
text.gsub!(/[ \t]+/, "") #warnings made me use parens
|
39
|
+
elsif options[:ignore_ws_amount]
|
40
|
+
text.gsub!(/[ \t]+/, ' ')
|
41
|
+
text.gsub!(/[ \t]+\n/, "\n")
|
42
|
+
end
|
43
|
+
text.gsub!(/\n+/, '') if options[:ignore_blank_lines]
|
44
|
+
text
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Given a line, returns a string that represents "adding that line" in a diff,
|
49
|
+
# based on the options.
|
50
|
+
#
|
51
|
+
# @param [String] input the input line
|
52
|
+
# @return [String] the output line, in a format indicating it is "added"
|
53
|
+
def add_line(input, options)
|
54
|
+
options[:pretty] ? "+#{input.chomp}".green+"\n" : "+#{input}"
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# Given a line, returns a string that represents "removing that line" in a diff,
|
59
|
+
# based on the options.
|
60
|
+
#
|
61
|
+
# @param [String] input the input line
|
62
|
+
# @return [String] the output line, in a format indicating it is "removed"
|
63
|
+
def remove_line(input, options)
|
64
|
+
options[:pretty] ? "-#{input.chomp}".red+"\n" : "-#{input}"
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# Creates a header or something? Not sure what this is used for, no code
|
69
|
+
# references it. I think it's for git or something. eh.
|
70
|
+
def diff_line(revisions, a, b, options=DEFAULT_OPTIONS)
|
71
|
+
options = DEFAULT_OPTIONS.merge options
|
72
|
+
parts = ['diff']
|
73
|
+
|
74
|
+
parts << '--git' if options[:git]
|
75
|
+
parts << revisions.map {|r| "-r #{r}"}.join(' ') if revisions && !options[:git]
|
76
|
+
if options[:git]
|
77
|
+
parts << "a/#{a}"
|
78
|
+
parts << "b/#{b}"
|
79
|
+
else
|
80
|
+
parts << a
|
81
|
+
end
|
82
|
+
parts.join(' ') + "\n"
|
83
|
+
end
|
84
|
+
|
85
|
+
##
|
86
|
+
# Creates a date tag appropriate for diffs. Not all diff types use
|
87
|
+
# dates though (namely git, apparently), so the options matter.
|
88
|
+
#
|
89
|
+
# @param [Time] date the time that we want to make a spiffy date line for
|
90
|
+
# @param [String] fn1 the filename of the file being stamped. Only
|
91
|
+
# used if the addtab option is on.
|
92
|
+
# @param [Boolean] addtab (false) whether or not to add a tab in the
|
93
|
+
# line or not. Only used if we're in git mode or no-date mode.
|
94
|
+
# @param options the options to use while creating the date line.
|
95
|
+
# @option [Boolean] options :git (false) are we creating a git diff?
|
96
|
+
# this will deactivate dates.
|
97
|
+
# @option options [Boolean] :nodates (false) should we never print dates?
|
98
|
+
def date_tag(date, fn1, addtab = true, options = DEFAULT_OPTIONS)
|
99
|
+
return "\t#{date.to_diff}\n" if !(options[:git]) && !(options[:nodates])
|
100
|
+
return "\t\n" if addtab && fn1 =~ / /
|
101
|
+
return "\n"
|
102
|
+
end
|
103
|
+
|
104
|
+
##
|
105
|
+
# Returns a unified diff based on the 2 blocks of text, their modification
|
106
|
+
# times, their filenames, and the options.
|
107
|
+
#
|
108
|
+
# This is a self-contained replacement for diffs.
|
109
|
+
#
|
110
|
+
# @param a the original text
|
111
|
+
# @param [Time] ad the modification timestamp for the old file
|
112
|
+
# @param b the new text
|
113
|
+
# @param [Time] bd the modification timestamp for the new file
|
114
|
+
# @param fn1 the old filename
|
115
|
+
# @param fn2 the new filename
|
116
|
+
# @param r not sure what this does
|
117
|
+
# @param options the options we will be using. There's a lot of settings,
|
118
|
+
# see the descriptions for {whitespace_clean} and {date_tag}.
|
119
|
+
def unified_diff(a, ad, b, bd, fn1, fn2, r=nil, options=DEFAULT_OPTIONS)
|
120
|
+
return "" if (a.nil? || a.empty?) && (b.nil? || b.empty?)
|
121
|
+
epoch = Time.at(0)
|
122
|
+
if !options[:text] && (!a.nil? && a.binary? || !b.nil? && b.binary?)
|
123
|
+
return "" if a.any? && b.any? && a.size == b.size && a == b #DERR
|
124
|
+
l = ["Binary file #{fn1} has changed\n"]
|
125
|
+
elsif a.nil? || a.empty?
|
126
|
+
b = b.split_lines_better
|
127
|
+
header = []
|
128
|
+
if options[:pretty]
|
129
|
+
l1 = a.nil? ? "Added file " : "Changed file "
|
130
|
+
l1 += "#{fn2} at #{date_tag(bd,fn1,true,options)}"
|
131
|
+
l1 = l1.cyan
|
132
|
+
header << l1
|
133
|
+
else
|
134
|
+
if a.nil?
|
135
|
+
header << "--- /dev/null#{date_tag(epoch, fn1, false, options)}"
|
136
|
+
else
|
137
|
+
header << "--- #{"a/" + fn1}#{date_tag(ad,fn1,true,options)}"
|
138
|
+
end
|
139
|
+
header << "+++ #{"b/" + fn2}#{date_tag(bd,fn1,true,options)}"
|
140
|
+
header << "@@ -0,0 +1,#{b.size} @@\n"
|
141
|
+
end
|
142
|
+
l = header + (b.map {|line| add_line(line, options)})
|
143
|
+
elsif b.nil? || b.empty?
|
144
|
+
a = b.split_lines_better
|
145
|
+
header = []
|
146
|
+
if options[:pretty]
|
147
|
+
l1 = b.nil? ? "Removed file " : "Changed file "
|
148
|
+
l1 += "#{fn2} at #{date_tag(bd,fn1,true,options)}"
|
149
|
+
l1 = l1.cyan
|
150
|
+
header << l1
|
151
|
+
else
|
152
|
+
header << "--- #{"a/" + fn1}#{date_tag(ad,fn1,true,options)}"
|
153
|
+
if b.nil?
|
154
|
+
header << "+++ /dev/null#{date_tag(epoch, fn1, false, options)}"
|
155
|
+
else
|
156
|
+
header << "+++ #{"b/" + fn2}#{date_tag(bd,fn1,true,options)}"
|
157
|
+
end
|
158
|
+
header << "@@ -1,#{a.size} +0,0 @@\n"
|
159
|
+
end
|
160
|
+
l = header + (a.map {|line| remove_line(line, options)})
|
161
|
+
else
|
162
|
+
al = a.split_lines_better
|
163
|
+
bl = b.split_lines_better
|
164
|
+
l = bunidiff(a, b, al, bl, "a/"+fn1, "b/"+fn2, options)
|
165
|
+
return "" if l.nil? || l.empty?
|
166
|
+
if options[:pretty]
|
167
|
+
l.shift
|
168
|
+
if fn1 == fn2
|
169
|
+
l[0] = "Changed file #{fn1.cyan} at #{date_tag(bd,fn1,true,options).lstrip}"
|
170
|
+
else
|
171
|
+
l[0] = "Moved file from #{fn1.cyan} to #{fn2.cyan}"
|
172
|
+
end
|
173
|
+
else
|
174
|
+
l[0] = "#{l[0][0 .. -3]}#{date_tag(ad,fn1,true,options)}"
|
175
|
+
l[1] = "#{l[1][0 .. -3]}#{date_tag(bd,fn1,true,options)}"
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
l.size.times do |ln|
|
180
|
+
if l[ln][-1,1] != "\n"
|
181
|
+
l[ln] << "\n\\n"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
if r
|
186
|
+
l.unshift diff_line(r, fn1, fn2, options)
|
187
|
+
end
|
188
|
+
|
189
|
+
l.join
|
190
|
+
end
|
191
|
+
|
192
|
+
##
|
193
|
+
# Starts a block ending context for a change - part of the unified diff
|
194
|
+
# format.
|
195
|
+
def context_end(l, len, options)
|
196
|
+
ret = l + options[:context]
|
197
|
+
ret = len if ret > len
|
198
|
+
ret
|
199
|
+
end
|
200
|
+
|
201
|
+
##
|
202
|
+
# Starts a block starting context for a change - part of the unified diff
|
203
|
+
# format.
|
204
|
+
def context_start(l, options)
|
205
|
+
ret = l - options[:context]
|
206
|
+
return 0 if ret < 0
|
207
|
+
ret
|
208
|
+
end
|
209
|
+
|
210
|
+
##
|
211
|
+
# Given a hunk of changes, yield each line we need to write to the diff.
|
212
|
+
#
|
213
|
+
# @param [Hash] hunk specifies a block of lines that changed between
|
214
|
+
# the two files.
|
215
|
+
# @param header the header for the block, if we have one.
|
216
|
+
# @param l1 the original lines - used for context (unified diff format)
|
217
|
+
# @param delta the lines that have changed thus far
|
218
|
+
# @param options settings for the unified diff action. unused mostly here.
|
219
|
+
def yield_hunk(hunk, header, l1, delta, options)
|
220
|
+
header.each {|x| yield x} if header && header.any?
|
221
|
+
delta = hunk[:delta]
|
222
|
+
astart, a2, bstart, b2 = hunk[:start_a], hunk[:end_a], hunk[:start_b], hunk[:end_b]
|
223
|
+
aend = context_end(a2,l1.size,options)
|
224
|
+
alen = aend - astart
|
225
|
+
blen = b2 - bstart + aend - a2
|
226
|
+
|
227
|
+
# i seriously don't know what this does.
|
228
|
+
func = ""
|
229
|
+
if options[:show_func]
|
230
|
+
(astart - 1).downto(0) do |x|
|
231
|
+
t = l1[x].rstrip
|
232
|
+
if t =~ /\w/
|
233
|
+
func = ' ' + t[0 .. 39]
|
234
|
+
break
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
# yield the header
|
240
|
+
if options[:pretty]
|
241
|
+
yield "From original lines #{astart + 1}-#{alen+astart+1}".yellow + "\n"
|
242
|
+
else
|
243
|
+
yield "@@ -%d,%d +%d,%d @@%s\n" % [astart + 1, alen,
|
244
|
+
bstart + 1, blen, func]
|
245
|
+
end
|
246
|
+
|
247
|
+
# then yield each line of changes
|
248
|
+
delta.each {|x| yield x}
|
249
|
+
# then yield some context or something?
|
250
|
+
a2.upto(aend-1) {|x| yield ' ' + l1[x] }
|
251
|
+
end
|
252
|
+
|
253
|
+
##
|
254
|
+
# Helper method for creating unified diffs.
|
255
|
+
#
|
256
|
+
# @param [String] t1 original text
|
257
|
+
# @param [String] t2 new text
|
258
|
+
# @param [String] l1 the original text broke into lines?
|
259
|
+
# @param [String] l2 the new etxt broken into lines?
|
260
|
+
# @param [String] header1 the original file's header
|
261
|
+
# @param [String] header2 the new file's header
|
262
|
+
# @param opts options for the method
|
263
|
+
def bunidiff(t1,t2, l1, l2, header1, header2, opts=DEFAULT_OPTIONS)
|
264
|
+
header = [ "--- #{header1}\t\n", "+++ #{header2}\t\n" ]
|
265
|
+
|
266
|
+
diff = BinaryDiff.blocks(t1,t2)
|
267
|
+
hunk = nil
|
268
|
+
return_hunks = []
|
269
|
+
saved_delta = []
|
270
|
+
delta = []
|
271
|
+
diff.size.times do |i|
|
272
|
+
s = (i > 0) ? diff[i-1] : {:start_a => 0, :end_a => 0, :start_b => 0, :end_b => 0}
|
273
|
+
saved_delta += delta unless delta.empty?
|
274
|
+
delta = []
|
275
|
+
s1 = diff[i]
|
276
|
+
a1 = s[:end_a]
|
277
|
+
a2 = s1[:start_a]
|
278
|
+
b1 = s[:end_b]
|
279
|
+
b2 = s1[:start_b]
|
280
|
+
|
281
|
+
old = (a2 == 0) ? [] : l1[a1..(a2-1)]
|
282
|
+
newb = (b2 == 0) ? [] : l2[b1..(b2-1)] #stands for new "b"
|
283
|
+
|
284
|
+
next if old.empty? && newb.empty?
|
285
|
+
if opts[:ignore_ws] || opts[:ignore_blank_lines] || opts[:ignore_ws_amount]
|
286
|
+
next if whitespace_clean(old.join,opts) == whitespace_clean(newb.join,opts)
|
287
|
+
end
|
288
|
+
|
289
|
+
astart = context_start(a1,opts)
|
290
|
+
bstart = context_start(b1,opts)
|
291
|
+
prev = nil
|
292
|
+
if hunk
|
293
|
+
if astart < hunk[:end_a] + opts[:context] + 1
|
294
|
+
prev = hunk
|
295
|
+
astart = hunk[:end_a]
|
296
|
+
bstart = hunk[:end_b]
|
297
|
+
else
|
298
|
+
yield_hunk(hunk, header, l1, delta, opts) {|x| return_hunks << x}
|
299
|
+
|
300
|
+
header = nil
|
301
|
+
end
|
302
|
+
end
|
303
|
+
# move this inside previous nested if statements
|
304
|
+
if prev
|
305
|
+
hunk[:end_a] = a2
|
306
|
+
hunk[:end_b] = b2
|
307
|
+
delta = hunk[:delta]
|
308
|
+
else
|
309
|
+
hunk = {:start_a => astart, :end_a => a2, :start_b => bstart, :end_b => b2, :delta => delta}
|
310
|
+
end
|
311
|
+
|
312
|
+
hunk[:delta] += l1[astart..(a1-1)].map {|x| ' ' + x } if a1 > 0
|
313
|
+
hunk[:delta] += old.map {|x| remove_line(x, opts) }
|
314
|
+
hunk[:delta] += newb.map {|x| add_line(x, opts) }
|
315
|
+
|
316
|
+
end
|
317
|
+
saved_delta += delta
|
318
|
+
|
319
|
+
yield_hunk(hunk, header, l1, saved_delta, opts) {|x| return_hunks << x} if hunk
|
320
|
+
return_hunks
|
321
|
+
end
|
322
|
+
|
323
|
+
##
|
324
|
+
# Unpacks a binary-compressed patch.
|
325
|
+
#
|
326
|
+
# @param [String] binary the packed binary text to unpack
|
327
|
+
def patch_text(binary)
|
328
|
+
pos = 0
|
329
|
+
t = []
|
330
|
+
while pos < binary.size
|
331
|
+
p1, p2, l = binary[pos..(pos+11)].unpack("NNN")
|
332
|
+
pos += 12
|
333
|
+
t << binary[pos..(pos + l - 1)]
|
334
|
+
pos += l
|
335
|
+
end
|
336
|
+
t.join
|
337
|
+
end
|
338
|
+
|
339
|
+
##
|
340
|
+
# Applies the patch _bin_ to the text _a_.
|
341
|
+
#
|
342
|
+
# @param [String] a the text to patch
|
343
|
+
# @param [String] bin the binary patch to apply
|
344
|
+
def patch(a, bin)
|
345
|
+
MercurialPatch.apply_patches(a, [bin])
|
346
|
+
end
|
347
|
+
|
348
|
+
##
|
349
|
+
# Gets the matching blocks between the two texts.
|
350
|
+
#
|
351
|
+
# @param [String] a the original text
|
352
|
+
# @param [String] b the final text
|
353
|
+
# @return [[Hash]] The blocks of changes between the two
|
354
|
+
def get_matching_blocks(a, b)
|
355
|
+
an = a.split_lines_better
|
356
|
+
bn = b.split_lines_better
|
357
|
+
|
358
|
+
SequenceMatcher.new(an, bn).get_matching_blocks
|
359
|
+
end
|
360
|
+
|
361
|
+
##
|
362
|
+
# Returns the obvious header for when we create a new file
|
363
|
+
#
|
364
|
+
# @param [Fixnum] length the length of the file
|
365
|
+
# @return [String] the obvious header
|
366
|
+
def trivial_diff_header(length)
|
367
|
+
[0, 0, length].pack("NNN")
|
368
|
+
end
|
369
|
+
|
370
|
+
##
|
371
|
+
# Returns a text diff between a and b. This returns the packed, binary
|
372
|
+
# kind of diff.
|
373
|
+
def text_diff a,b
|
374
|
+
BinaryDiff.bdiff a,b
|
375
|
+
end
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|