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,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
|