piston 1.0.0

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