piston 1.0.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/CHANGELOG ADDED
@@ -0,0 +1,4 @@
1
+ *SVN*
2
+
3
+ 2006-08-24 1.0.0
4
+ * Initial version
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2006 Francois Beausoleil <francois@teksol.info>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,114 @@
1
+ Piston is a utility that enables merge tracking of remote repositories.
2
+ This is similar to <tt>svn:externals</tt>, except you have a local copy of
3
+ the files, which you can modify at will. As long as the changes are
4
+ mergeable, you should have no problems.
5
+
6
+ This tool has a similar purpose than svnmerge.py which you can find in the
7
+ contrib/client-side folder of the main Subversion repository at
8
+ http://svn.collab.net/repos/svn/trunk/contrib/client-side/svnmerge.py.
9
+ The main difference is that Piston is designed to work with remote
10
+ repositories. Another tool you might want to look at, SVK, situated at
11
+ http://svk.elixus.org.
12
+
13
+ From Wikipedia's Piston page (http://en.wikipedia.org/wiki/Piston):
14
+ In general, a piston is a sliding plug that fits closely inside the bore
15
+ of a cylinder.
16
+
17
+ Its purpose is either to change the volume enclosed by the cylinder, or
18
+ to exert a force on a fluid inside the cylinder.
19
+
20
+ For this utility, I retain the second meaning, "to exert a force on a fluid
21
+ inside the cylinder." Piston forces the content of a remote repository
22
+ location back into our own.
23
+
24
+ = Installation
25
+
26
+ Nothing could be simpler:
27
+
28
+ $ gem install --include-dependencies piston
29
+
30
+
31
+ = Usage
32
+
33
+ First, you need to import the remote repository location:
34
+
35
+ $ piston import http://dev.rubyonrails.org/svn/rails/trunk vendor/rails
36
+ Exported r4720 from 'http://dev.rubyonrails.org/svn/rails/trunk' to 'vendor/rails'
37
+
38
+ $ svn commit -m "Importing local copy of Rails"
39
+
40
+ When you want to get the latest changes from the remote repository location:
41
+
42
+ $ piston update vendor/rails
43
+ Updated 'vendor/rails' to r4720.
44
+
45
+ $ svn commit -m "Updates vendor/rails to the latest revision"
46
+
47
+ You can prevent a local Piston-managed folder from updating by using the
48
+ +lock+ subcommand:
49
+
50
+ $ piston lock vendor/rails
51
+ 'vendor/rails' locked at r4720.
52
+
53
+ When you want to update again, you unlock:
54
+
55
+ $ piston unlock vendor/rails
56
+ 'vendor/rails' unlocked.
57
+
58
+
59
+ = Caveats
60
+
61
+ == Speed
62
+
63
+ This tool is SLOW. The update process particularly so. I use a brute force
64
+ approach. Subversion cannot merge from remote repositories, so instead I
65
+ checkout the folder at the initial revision, and then run svn update and
66
+ parse the results of that to determine what changes have occured.
67
+
68
+ If a local copy of a file was changed, it's changes will be merged back in.
69
+ If that introduces a conflict, Piston will not detect it. The commit will be
70
+ rejected by Subversion anyway.
71
+
72
+ == Copies / Renames
73
+
74
+ Piston *does not* track copies. Since Subversion does renames in two
75
+ phases (copy + delete), that is what Piston does.
76
+
77
+ == Local Operations Only
78
+
79
+ Piston only works if you have a working copy. It also never commits your
80
+ working copy directly. You are responsible for reviewing the changes and
81
+ applying any pending fixes.
82
+
83
+ == Remote Repository UUID
84
+
85
+ Piston caches the remote repository UUID, allowing it to know if the remote
86
+ repos is still the same. Piston refuses to work against a different
87
+ repository than the one we checked out from originally.
88
+
89
+
90
+ = Subversion Properties Used
91
+
92
+ * <tt>piston:uuid</tt>: The remote repository's UUID, which we always confirm
93
+ before doing any operations.
94
+ * <tt>piston:root</tt>: The repository root URL from which this Piston folder
95
+ was exported from.
96
+ * <tt>piston:remote-revision</tt>: The <tt>Last Changed Rev</tt> of the remote
97
+ repository.
98
+ * <tt>piston:local-revision</tt>: The <tt>Last Changed Rev</tt> of the Piston
99
+ managed folder, to enable us to know if we need to do any merging.
100
+ * <tt>piston:locked</tt>: The revision at which this folder is locked. If
101
+ this property is set and non-blank, Piston will skip the folder with
102
+ an appropriate message.
103
+
104
+
105
+ = Dependencies
106
+
107
+ Piston depends on the following libraries:
108
+
109
+ * yaml
110
+ * getoptlong
111
+ * uri
112
+ * fileutils
113
+
114
+ These dependencies are all included in a stock 1.8.4 Ruby distribution.
data/Rakefile ADDED
@@ -0,0 +1,63 @@
1
+ require 'rubygems'
2
+ require 'rake/testtask'
3
+ require 'rake/gempackagetask'
4
+ require 'rake/contrib/rubyforgepublisher'
5
+ require File.join(File.dirname(__FILE__), 'lib', 'piston', 'version')
6
+
7
+ PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
8
+ PKG_NAME = 'piston'
9
+ PKG_VERSION = Piston::VERSION::STRING + PKG_BUILD
10
+ PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
11
+
12
+ RELEASE_NAME = "REL #{PKG_VERSION}"
13
+
14
+ RUBY_FORGE_PROJECT = "piston"
15
+ RUBY_FORGE_USER = "fbos"
16
+
17
+ task :default => :test
18
+ Rake::TestTask.new { |t|
19
+ t.pattern = 'test/**/*_test.rb'
20
+ t.verbose = true
21
+ t.warning = false
22
+ }
23
+
24
+ # Create compressed packages
25
+ dist_dirs = [ "lib", "test"]
26
+
27
+ spec = Gem::Specification.new do |s|
28
+ s.name = PKG_NAME
29
+ s.version = PKG_VERSION
30
+ s.summary = "Piston is a utility that enables merge tracking of remote repositories."
31
+ s.description = %q{This is similar to svn:externals, except you have a local copy of the files, which you can modify at will. As long as the changes are mergeable, you should have no problems.}
32
+
33
+ s.bindir = "bin" # Use these for applications.
34
+ s.executables = ["piston"]
35
+ s.default_executable = "piston"
36
+
37
+ s.files = [ "CHANGELOG", "README", "LICENSE", "Rakefile" ] + FileList["{bin,test,lib}/**/*"].to_a
38
+
39
+ s.require_path = 'lib'
40
+ s.has_rdoc = false
41
+
42
+ s.author = "Francois Beausoleil"
43
+ s.email = "francois@teksol.info"
44
+ s.homepage = "http://piston.rubyforge.org/"
45
+ s.rubyforge_project = "piston"
46
+ end
47
+
48
+ Rake::GemPackageTask.new(spec) do |p|
49
+ p.gem_spec = spec
50
+ p.need_tar = true
51
+ p.need_zip = true
52
+ end
53
+
54
+ desc "Publish the release files to RubyForge."
55
+ task :release => [ :package ] do
56
+ `rubyforge login`
57
+
58
+ for ext in %w( gem tgz zip )
59
+ release_command = "rubyforge add_release #{PKG_NAME} #{PKG_NAME} 'REL #{PKG_VERSION}' pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}"
60
+ puts release_command
61
+ system(release_command)
62
+ end
63
+ end
data/bin/piston ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ begin
4
+ require 'rubygems'
5
+ rescue LoadError
6
+ # no rubygems to load, so we fail silently
7
+ end
8
+
9
+ require 'piston'
10
+ require 'piston/ui/command_line'
11
+
12
+ Piston::Ui::CommandLine.start
@@ -0,0 +1,5 @@
1
+ class Range
2
+ def to_svn
3
+ to_s.gsub(/\.{2,3}/, ':')
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class String
2
+ def tmp
3
+ self + ".tmp"
4
+ end
5
+ end
@@ -0,0 +1,46 @@
1
+ module Piston
2
+ # The base class which all commands subclass to obtain services from.
3
+ class Command
4
+ attr_accessor :revision, :dry_run, :quiet, :verbose, :force, :lock,
5
+ :logging_stream
6
+
7
+ # Execute this command. The arguments are pre-processed to expand any
8
+ # wildcards using Dir#[]. This is because the Windows shell does not
9
+ # know it should expand wildcards before calling an executable.
10
+ def execute(args)
11
+ # Because the Windows shell does not process wildcards, we must do it
12
+ # here ourselves
13
+ args.map do |arg|
14
+ next arg unless arg =~ /[*?]/
15
+ Dir[arg]
16
+ end
17
+
18
+ run(args.flatten)
19
+ end
20
+
21
+ # Run a Subversion command using the shell. If the Subversion command
22
+ # returns an existstatus different from zero, a RuntimeError is raised.
23
+ def svn(*args)
24
+ args = args.flatten.compact.map do |arg|
25
+ if arg.to_s =~ /[ *?@]/ then
26
+ %Q("#{arg}")
27
+ else
28
+ arg
29
+ end
30
+ end
31
+
32
+ command = "svn #{args.join(' ')}"
33
+ logging_stream.puts command if verbose
34
+ return if dry_run
35
+ result = `#{command}`
36
+ logging_stream.puts result if verbose
37
+ raise "Command #{command} resulted in an error:\n\n#{result}" unless $?.exitstatus.zero?
38
+ result
39
+ end
40
+
41
+ # Returns an IO-like object to which all information should be logged.
42
+ def logging_stream
43
+ @logging_stream ||= $stdout
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,4 @@
1
+ module Piston
2
+ # Raised whenever an argument is not correct during processing.
3
+ class CommandError < ArgumentError; end
4
+ end
@@ -0,0 +1,44 @@
1
+ module Piston
2
+ module Commands
3
+ class Help < Piston::Command
4
+ def run(targets=nil)
5
+ command = targets.shift
6
+
7
+ return help_on_command(command) if command
8
+ general_help
9
+ end
10
+
11
+ def help_on_command(command_name)
12
+ begin
13
+ require File.join(PISTON_ROOT, 'piston', 'commands', command_name)
14
+ command = Piston::Commands.const_get(command_name.capitalize)
15
+ command.detailed_help(logging_stream)
16
+ rescue LoadError
17
+ logging_stream.puts "No help available for '#{command_name}'"
18
+ general_help
19
+ end
20
+ end
21
+
22
+ def general_help
23
+ logging_stream.puts "Available commands are:"
24
+ commands = Array.new
25
+ Dir[File.join(PISTON_ROOT, 'piston', 'commands', '*.rb')].each do |file|
26
+ require file
27
+ commands << Piston::Commands.const_get(File.basename(file).gsub(/\.rb$/, '').capitalize)
28
+ end
29
+
30
+ commands.each do |command|
31
+ logging_stream.printf " %-12s %s\n", command.aliases.first, command.help
32
+ end
33
+ end
34
+
35
+ def self.help
36
+ "Returns detailed help on a specific command"
37
+ end
38
+
39
+ def self.aliases
40
+ %w(help)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,68 @@
1
+ module Piston
2
+ module Commands
3
+ class Import < Piston::Command
4
+ def run(args)
5
+ raise Piston::CommandError, "Missing REPOS_URL argument" if args.empty?
6
+
7
+ repos, dir = args.shift, args.shift
8
+ raise Piston::CommandError, "Too many arguments" unless args.empty?
9
+ dir = File.basename(URI.parse(repos).path) unless dir
10
+
11
+ if File.exists?(dir) then
12
+ raise Piston::CommandError, "Target folder already exists" unless force
13
+ svn :revert, '--recursive', dir
14
+ FileUtils.rm_rf(dir)
15
+ end
16
+
17
+ info = YAML::load(svn(:info, repos))
18
+ options = [:export]
19
+ options << ['--revision', revision] if revision
20
+ options << repos
21
+ options << dir
22
+ export = svn options
23
+ export.each_line do |line|
24
+ next unless line =~ /Exported revision (\d+)./i
25
+ @revision = $1
26
+ break
27
+ end
28
+
29
+ svn :add, '--force', dir
30
+ svn :propset, Piston::ROOT, repos, dir
31
+ svn :propset, Piston::UUID, info['Repository UUID'], dir
32
+ svn :propset, Piston::REMOTE_REV, revision, dir
33
+ svn :propset, Piston::LOCAL_REV, YAML::load(svn(:info))['Last Changed Rev'], dir
34
+ svn :propset, Piston::LOCKED, revision, dir if lock
35
+
36
+ logging_stream.puts "Exported r#{revision} from '#{repos}' to '#{dir}'"
37
+ end
38
+
39
+ def self.help
40
+ "Prepares a folder for merge tracking"
41
+ end
42
+
43
+ def self.detailed_help(stream)
44
+ stream.puts <<EOF
45
+ import (init): #{help}
46
+ usage: import REPOS_URL [DIR]
47
+
48
+ Exports the specified REPOS_URL (which must be a Subversion repository) to
49
+ DIR, defaulting to the last component of REPOS_URL if DIR is not present.
50
+
51
+ If the local folder already exists, this command will abort with an error.
52
+
53
+ Valid options:
54
+ -r [--revision] arg : Start merge tracking at ARG instead of HEAD
55
+ --lock : Close down and lock the folder from future
56
+ updates immediately
57
+ --verbose : Show Subversion commands and results as they
58
+ are executed
59
+
60
+ EOF
61
+ end
62
+
63
+ def self.aliases
64
+ %w(import init)
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,38 @@
1
+ module Piston
2
+ module Commands
3
+ class Lock < Piston::Command
4
+ def run(args)
5
+ raise Piston::CommandError, "No targets to run against" if args.empty?
6
+
7
+ args.each do |dir|
8
+ remote_rev = svn(:propget, Piston::REMOTE_REV, dir).chomp.to_i
9
+ svn :propset, Piston::LOCKED, remote_rev, dir
10
+ end
11
+
12
+ logging_stream.puts "Locked #{args.size} folder(s)"
13
+ end
14
+
15
+ def self.help
16
+ "Lock one or more folders to a specific revision"
17
+ end
18
+
19
+ def self.detailed_help(stream)
20
+ stream.puts <<EOF
21
+ lock: #{help}
22
+ usage: lock DIR [DIR [...]]
23
+
24
+ Locked folders will not be updated to the latest revision when updating.
25
+
26
+ Valid options:
27
+ --verbose : Show Subversion commands and results as they
28
+ are executed
29
+
30
+ EOF
31
+ end
32
+
33
+ def self.aliases
34
+ %w(lock)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,34 @@
1
+ module Piston
2
+ module Commands
3
+ class Unlock < Piston::Command
4
+ def run(args)
5
+ raise Piston::CommandError, "No targets to run against" if args.empty?
6
+ svn :propdel, Piston::LOCKED, *args
7
+ logging_stream.puts "Unlocked #{args.size} folder(s)"
8
+ end
9
+
10
+ def self.help
11
+ "Undoes the changes enabled by lock"
12
+ end
13
+
14
+ def self.detailed_help(stream)
15
+ stream.puts <<EOF
16
+ unlock: #{help}
17
+ usage: unlock DIR [DIR [...]]
18
+
19
+ Unlocked folders are free to be updated to the latest revision when
20
+ updating.
21
+
22
+ Valid options:
23
+ --verbose : Show Subversion commands and results as they
24
+ are executed
25
+
26
+ EOF
27
+ end
28
+
29
+ def self.aliases
30
+ %w(unlock)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,107 @@
1
+ module Piston
2
+ module Commands
3
+ class Update < Piston::Command
4
+ def run(args)
5
+ args.each do |dir|
6
+ update dir
7
+ end
8
+ end
9
+
10
+ def update(dir)
11
+ next unless File.directory?(dir)
12
+ next skip(dir, "locked") unless svn(:propget, LOCKED, dir) == ''
13
+ status = svn :status, '--show-updates', dir
14
+ next skip(dir, "pending updates -- run \"svn update #{dir}\"") if status.split("\n").size > 1
15
+
16
+ logging_stream.print "Processing '#{dir}'... "
17
+ repos = svn(:propget, Piston::ROOT, dir).chomp
18
+ uuid = svn(:propget, Piston::UUID, dir).chomp
19
+ remote_revision = svn(:propget, Piston::REMOTE_REV, dir).chomp.to_i
20
+ local_revision = svn(:propget, Piston::LOCAL_REV, dir).chomp.to_i
21
+
22
+ info = YAML::load(svn(:info, repos))
23
+ next skip(dir, "Repository UUID changed\n Expected #{uuid}\n Found #{info['Repository UUID']}\n Repository: #{repos}") unless uuid == info['Repository UUID']
24
+
25
+ new_remote_rev = info['Last Changed Rev'].to_i
26
+ next skip(dir, "unchanged from revision #{remote_revision}") if remote_revision == new_remote_rev
27
+
28
+ info = YAML::load(svn(:info))
29
+ new_local_rev = info['Last Changed Rev']
30
+
31
+ revisions = (remote_revision .. (revision || new_remote_rev))
32
+ svn :checkout, '--ignore-externals', '--revision', revisions.first, repos, dir.tmp
33
+ updates = svn :update, '--ignore-externals', '--revision', revisions.last, dir.tmp
34
+ merges = Array.new
35
+ changes = 0
36
+ updates.each_line do |line|
37
+ next unless line =~ %r{^([A-Z]).*\s+#{Regexp.escape(dir.tmp)}[\\/](.+)$}
38
+ op, file = $1, $2
39
+ changes += 1
40
+
41
+ case op
42
+ when 'A'
43
+ copy(dir, file)
44
+ svn :add, File.join(dir, file)
45
+ when 'D'
46
+ svn :remove, File.join(dir, file)
47
+ else
48
+ copy(dir, file)
49
+ merges << file
50
+ end
51
+ end
52
+
53
+ merges.each do |file|
54
+ svn :merge, '--revision', (local_revision.succ .. new_local_rev).to_svn, File.join(dir, file), File.join(dir, file)
55
+ end unless local_revision == new_local_rev
56
+
57
+ FileUtils.rm_rf dir.tmp
58
+
59
+ svn :propset, Piston::REMOTE_REV, revisions.last, dir
60
+ svn :propset, Piston::LOCAL_REV, new_local_rev, dir
61
+ svn :propset, Piston::LOCKED, revisions.last, dir if lock
62
+
63
+ logging_stream.puts "Updated to #{revisions.last} (#{changes} changed files)"
64
+ end
65
+
66
+ def copy(dir, file)
67
+ FileUtils.cp(File.join(dir.tmp, file), File.join(dir, file))
68
+ end
69
+
70
+ def skip(dir, msg)
71
+ logging_stream.puts "Skipping '#{dir}': #{msg}"
72
+ end
73
+
74
+ def self.help
75
+ "Updates one or more folders to the latest revision"
76
+ end
77
+
78
+ def self.detailed_help(stream)
79
+ stream.puts <<EOF
80
+ update: #{help}
81
+ usage: update DIR [...]
82
+
83
+ This operation has the effect of downloading all remote changes back to our
84
+ working copy. If any local modifications were done, they will be preserved.
85
+ If merge conflicts occur, they will not be taken care of, and your subsequent
86
+ commit will fail.
87
+
88
+ Piston will refuse to update a folder if it has pending updates. Run
89
+ 'svn update' on the target folder to update it before running Piston
90
+ again.
91
+
92
+ Valid options:
93
+ -r [--revision] arg : Update to ARG instead of HEAD
94
+ --lock : Close down and lock the folder from future
95
+ updates immediately
96
+ --verbose : Show Subversion commands and results as they
97
+ are executed
98
+
99
+ EOF
100
+ end
101
+
102
+ def self.aliases
103
+ %w(update up)
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,69 @@
1
+ require 'getoptlong'
2
+
3
+ module Piston
4
+ module Ui
5
+ module CommandLine
6
+ def self.start
7
+ opts = ::GetoptLong.new(
8
+ [ '--revision', '-r', GetoptLong::REQUIRED_ARGUMENT ],
9
+ [ '--help', '-h', GetoptLong::NO_ARGUMENT ],
10
+ [ '--dry-run', GetoptLong::NO_ARGUMENT ],
11
+ [ '--quiet', '-q', GetoptLong::NO_ARGUMENT ],
12
+ [ '--verbose', '-v', GetoptLong::NO_ARGUMENT ],
13
+ [ '--lock', GetoptLong::NO_ARGUMENT ],
14
+ [ '--force', '-f', GetoptLong::NO_ARGUMENT ],
15
+ [ '--version', GetoptLong::NO_ARGUMENT ]
16
+ )
17
+
18
+ options = Hash.new
19
+
20
+ opts.each do |opt, arg|
21
+ case opt
22
+ when '--revision'
23
+ options[:revision] = arg.to_i
24
+
25
+ when '--help'
26
+ return help
27
+
28
+ when '--version'
29
+ require 'piston/version'
30
+ puts "Piston #{Piston::VERSION::STRING}"
31
+ puts "Copyright 2006, Francois Beausoleil"
32
+ puts "\nSee the LICENSE file for details"
33
+ exit
34
+
35
+ when /--([-\w]+)$/
36
+ options[$1.gsub('-', '_').to_sym] = true
37
+ end
38
+ end
39
+
40
+ return help if ARGV.empty?
41
+
42
+ command_name = ARGV.shift.downcase
43
+ begin
44
+ require File.join(PISTON_ROOT, 'piston', 'commands', command_name)
45
+ rescue LoadError
46
+ return help
47
+ end
48
+
49
+ command_class = Piston::Commands.const_get("#{command_name.capitalize}")
50
+ command = command_class.new
51
+ options.each do |key, value|
52
+ command.send "#{key}=", value
53
+ end
54
+
55
+ begin
56
+ command.execute(ARGV)
57
+ rescue Piston::CommandError
58
+ $stderr.puts "ERROR: #{$!.message}"
59
+ exit 1
60
+ end
61
+ end
62
+
63
+ def self.help
64
+ require File.join(PISTON_ROOT, 'piston', 'commands', 'help')
65
+ Piston::Commands::Help.new.run(ARGV)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,9 @@
1
+ module Piston
2
+ module VERSION #:nodoc:
3
+ MAJOR = 1
4
+ MINOR = 0
5
+ TINY = 0
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
data/lib/piston.rb ADDED
@@ -0,0 +1,43 @@
1
+ # Copyright (c) 2006 Francois Beausoleil <francois@teksol.info>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ # $HeadURL: svn://rubyforge.org/var/svn/piston/tags/1.0.0/lib/piston.rb $
22
+ # $Id: piston.rb 11 2006-08-25 02:47:12Z fbos $
23
+
24
+ require 'yaml'
25
+ require 'uri'
26
+ require 'fileutils'
27
+
28
+ PISTON_ROOT = File.dirname(__FILE__)
29
+ Dir[File.join(PISTON_ROOT, 'core_ext', '*.rb')].each do |file|
30
+ require file
31
+ end
32
+
33
+ require File.join(PISTON_ROOT, 'piston', 'command')
34
+ require File.join(PISTON_ROOT, 'piston', 'command_error')
35
+ require File.join(PISTON_ROOT, 'piston', 'ui', 'command_line')
36
+
37
+ module Piston
38
+ ROOT = "piston:root"
39
+ UUID = "piston:uuid"
40
+ REMOTE_REV = "piston:remote-revision"
41
+ LOCAL_REV = "piston:local-revision"
42
+ LOCKED = "piston:locked"
43
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.0
3
+ specification_version: 1
4
+ name: piston
5
+ version: !ruby/object:Gem::Version
6
+ version: 1.0.0
7
+ date: 2006-08-25 00:00:00 +00:00
8
+ summary: Piston is a utility that enables merge tracking of remote repositories.
9
+ require_paths:
10
+ - lib
11
+ email: francois@teksol.info
12
+ homepage: http://piston.rubyforge.org/
13
+ rubyforge_project: piston
14
+ description: This is similar to svn:externals, except you have a local copy of the files, which you can modify at will. As long as the changes are mergeable, you should have no problems.
15
+ autorequire:
16
+ default_executable: piston
17
+ bindir: bin
18
+ has_rdoc: false
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Francois Beausoleil
31
+ files:
32
+ - CHANGELOG
33
+ - README
34
+ - LICENSE
35
+ - Rakefile
36
+ - bin/piston
37
+ - lib/core_ext
38
+ - lib/piston
39
+ - lib/piston.rb
40
+ - lib/core_ext/core_ext
41
+ - lib/core_ext/core_ext/string.rb
42
+ - lib/core_ext/core_ext/range.rb
43
+ - lib/piston/commands
44
+ - lib/piston/ui
45
+ - lib/piston/command_error.rb
46
+ - lib/piston/command.rb
47
+ - lib/piston/version.rb
48
+ - lib/piston/commands/update.rb
49
+ - lib/piston/commands/help.rb
50
+ - lib/piston/commands/lock.rb
51
+ - lib/piston/commands/import.rb
52
+ - lib/piston/commands/unlock.rb
53
+ - lib/piston/ui/command_line.rb
54
+ test_files: []
55
+
56
+ rdoc_options: []
57
+
58
+ extra_rdoc_files: []
59
+
60
+ executables:
61
+ - piston
62
+ extensions: []
63
+
64
+ requirements: []
65
+
66
+ dependencies: []
67
+