piston 1.1.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +15 -0
- data/lib/core_ext/string.rb +4 -0
- data/lib/piston/command.rb +2 -1
- data/lib/piston/commands/import.rb +3 -2
- data/lib/piston/commands/status.rb +79 -0
- data/lib/piston/commands/update.rb +42 -17
- data/lib/piston/ui/command_line.rb +1 -0
- data/lib/piston/version.rb +2 -2
- metadata +3 -2
data/CHANGELOG
CHANGED
@@ -1,4 +1,19 @@
|
|
1
1
|
*SVN*
|
2
|
+
* New status subcommand. Shows M if locally or remotely modified. Applies to
|
3
|
+
one, many, all folders. This subcommand *requires* the use of a Subversion
|
4
|
+
1.2.0 client. Thanks to Chris Wanstrath for the inspiration. His Rake
|
5
|
+
tasks are available at http://errtheblog.com/post/38.
|
6
|
+
* Minor patch by Miguel Ibero Carreras to make Subversion always use the
|
7
|
+
C locale, instead of the current one. This allows Piston to be used
|
8
|
+
with internationalized versions of Subversion. David Bittencourt later
|
9
|
+
reported the same problem. Thanks!
|
10
|
+
* Better handle how update finds it's latest local revision to prevent
|
11
|
+
conflicts. If you had never locally changed your vendor repositories,
|
12
|
+
this fix will change nothing for you. This helps prevent local conflicts
|
13
|
+
if you had ever applied a local patch.
|
14
|
+
*CAVEAT*: See the release announcement at
|
15
|
+
http://blog.teksol.info/articles/2006/11/17/piston-1-2-0-status-better-update
|
16
|
+
for a required local operation.
|
2
17
|
|
3
18
|
2006-08-30 1.1.1
|
4
19
|
* Add contrib/piston [Michael Schuerig]
|
data/lib/core_ext/string.rb
CHANGED
data/lib/piston/command.rb
CHANGED
@@ -2,7 +2,7 @@ module Piston
|
|
2
2
|
# The base class which all commands subclass to obtain services from.
|
3
3
|
class Command
|
4
4
|
attr_accessor :revision, :dry_run, :quiet, :verbose, :force, :lock,
|
5
|
-
:recursive, :logging_stream
|
5
|
+
:recursive, :logging_stream, :show_updates
|
6
6
|
|
7
7
|
# Execute this command. The arguments are pre-processed to expand any
|
8
8
|
# wildcards using Dir#[]. This is because the Windows shell does not
|
@@ -32,6 +32,7 @@ module Piston
|
|
32
32
|
command = "svn #{args.join(' ')}"
|
33
33
|
logging_stream.puts command if verbose
|
34
34
|
return if dry_run
|
35
|
+
ENV['LC_ALL'] = 'C'
|
35
36
|
result = `#{command}`
|
36
37
|
logging_stream.puts result if verbose
|
37
38
|
raise "Command #{command} resulted in an error:\n\n#{result}" unless $?.exitstatus.zero?
|
@@ -21,6 +21,7 @@ module Piston
|
|
21
21
|
info = YAML::load(svn(:info, repos))
|
22
22
|
options = [:export]
|
23
23
|
options << ['--revision', revision] if revision
|
24
|
+
options << '--quiet'
|
24
25
|
options << repos
|
25
26
|
options << dir
|
26
27
|
export = svn options
|
@@ -31,7 +32,7 @@ module Piston
|
|
31
32
|
end
|
32
33
|
|
33
34
|
# Add so we can set properties
|
34
|
-
svn :add, '--non-recursive', '--force', dir
|
35
|
+
svn :add, '--non-recursive', '--force', '--quiet', dir
|
35
36
|
|
36
37
|
# Set the properties
|
37
38
|
svn :propset, Piston::ROOT, repos, dir
|
@@ -42,7 +43,7 @@ module Piston
|
|
42
43
|
|
43
44
|
# Finish adding. If we get an error, at least the properties will be
|
44
45
|
# set and the user can handle the rest
|
45
|
-
svn :add, '--force', dir
|
46
|
+
svn :add, '--force', '--quiet', dir
|
46
47
|
|
47
48
|
logging_stream.puts "Exported r#{revision} from '#{repos}' to '#{dir}'"
|
48
49
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'pp'
|
2
|
+
|
3
|
+
module Piston
|
4
|
+
module Commands
|
5
|
+
class Status < Piston::Command
|
6
|
+
def run(args)
|
7
|
+
# First, find the list of pistoned folders
|
8
|
+
folders = svn(:propget, '--recursive', Piston::ROOT, *args)
|
9
|
+
repos = Hash.new
|
10
|
+
repo = nil
|
11
|
+
folders.each_line do |line|
|
12
|
+
next unless line =~ /(\w.*) - /
|
13
|
+
repos[$1] = Hash.new
|
14
|
+
end
|
15
|
+
|
16
|
+
# Then, get their properties
|
17
|
+
repo = nil
|
18
|
+
svn(:proplist, '--verbose', *repos.keys).each_line do |line|
|
19
|
+
case line
|
20
|
+
when /'([^']+)'/
|
21
|
+
repo = repos[$1]
|
22
|
+
when /(piston:[-\w]+)\s*:\s*(.*)$/
|
23
|
+
repo[$1] = $2
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Determine their local status
|
28
|
+
repos.each_pair do |path, props|
|
29
|
+
log = svn(:log, '--revision', "#{props[Piston::LOCAL_REV]}:HEAD", '--quiet', '--limit', '2', path)
|
30
|
+
props[:locally_modified] = 'M' if log.count("\n") > 3
|
31
|
+
end
|
32
|
+
|
33
|
+
# And their remote status, if required
|
34
|
+
repos.each_pair do |path, props|
|
35
|
+
log = svn(:log, '--revision', "#{props[Piston::REMOTE_REV]}:HEAD", '--quiet', '--limit', '2', props[Piston::ROOT])
|
36
|
+
props[:remotely_modified] = 'M' if log.count("\n") > 3
|
37
|
+
end if show_updates
|
38
|
+
|
39
|
+
# Display the results
|
40
|
+
repos.each_pair do |path, props|
|
41
|
+
logging_stream.printf "%1s%1s %s (%s)\n", props[:locally_modified],
|
42
|
+
props[:remotely_modified], path, props[Piston::ROOT]
|
43
|
+
end
|
44
|
+
|
45
|
+
logging_stream.puts "No pistoned folders found" if repos.empty?
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.help
|
49
|
+
"Determines the current status of each pistoned directory"
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.detailed_help(stream)
|
53
|
+
stream.puts <<EOF
|
54
|
+
status: #{help}
|
55
|
+
usage: status [DIR [DIR...]]
|
56
|
+
|
57
|
+
Shows the status of one, many or all pistoned folders. The status is
|
58
|
+
returned in columns.
|
59
|
+
|
60
|
+
The first column's values are:
|
61
|
+
: Locally unchanged (space)
|
62
|
+
M: Locally modified since importing
|
63
|
+
|
64
|
+
The second column's values are blanks, unless the --show-updates is passed.
|
65
|
+
M: Remotely modified since importing
|
66
|
+
|
67
|
+
Valid options:
|
68
|
+
--show-updates : Queries the remote repositories to determine
|
69
|
+
if they have been updated from our revision.
|
70
|
+
|
71
|
+
EOF
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.aliases
|
75
|
+
%w(status st)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -22,27 +22,42 @@ module Piston
|
|
22
22
|
def update(dir)
|
23
23
|
return unless File.directory?(dir)
|
24
24
|
return skip(dir, "locked") unless svn(:propget, LOCKED, dir) == ''
|
25
|
-
status = svn
|
26
|
-
|
25
|
+
status = svn(:status, '--show-updates', dir)
|
26
|
+
new_local_rev = nil
|
27
|
+
new_status = Array.new
|
28
|
+
status.each_line do |line|
|
29
|
+
if line =~ /status.+\s(\d+)$/i then
|
30
|
+
new_local_rev = $1.to_i
|
31
|
+
else
|
32
|
+
new_status << line unless line =~ /^\?/
|
33
|
+
end
|
34
|
+
end
|
35
|
+
raise "Unable to parse status\n#{status}" unless new_local_rev
|
36
|
+
return skip(dir, "pending updates -- run \"svn update #{dir}\"\n#{new_status}") if new_status.size > 0
|
27
37
|
|
28
|
-
logging_stream.
|
38
|
+
logging_stream.puts "Processing '#{dir}'..."
|
29
39
|
repos = svn(:propget, Piston::ROOT, dir).chomp
|
30
40
|
uuid = svn(:propget, Piston::UUID, dir).chomp
|
31
41
|
remote_revision = svn(:propget, Piston::REMOTE_REV, dir).chomp.to_i
|
32
42
|
local_revision = svn(:propget, Piston::LOCAL_REV, dir).chomp.to_i
|
43
|
+
local_revision = local_revision.succ
|
33
44
|
|
45
|
+
logging_stream.puts " Fetching remote repository's latest revision and UUID"
|
34
46
|
info = YAML::load(svn(:info, repos))
|
35
47
|
return skip(dir, "Repository UUID changed\n Expected #{uuid}\n Found #{info['Repository UUID']}\n Repository: #{repos}") unless uuid == info['Repository UUID']
|
36
48
|
|
37
49
|
new_remote_rev = info['Last Changed Rev'].to_i
|
38
50
|
return skip(dir, "unchanged from revision #{remote_revision}", false) if remote_revision == new_remote_rev
|
39
51
|
|
40
|
-
info = YAML::load(svn(:info, File.join(dir, '..')))
|
41
|
-
new_local_rev = info['Revision']
|
42
|
-
|
43
52
|
revisions = (remote_revision .. (revision || new_remote_rev))
|
44
|
-
|
53
|
+
|
54
|
+
logging_stream.puts " Restoring remote repository to known state at r#{revisions.first}"
|
55
|
+
svn :checkout, '--ignore-externals', '--quiet', '--revision', revisions.first, repos, dir.tmp
|
56
|
+
|
57
|
+
logging_stream.puts " Updating remote repository to r#{revisions.last}"
|
45
58
|
updates = svn :update, '--ignore-externals', '--revision', revisions.last, dir.tmp
|
59
|
+
|
60
|
+
logging_stream.puts " Processing adds/deletes"
|
46
61
|
merges = Array.new
|
47
62
|
changes = 0
|
48
63
|
updates.each_line do |line|
|
@@ -53,34 +68,44 @@ module Piston
|
|
53
68
|
case op
|
54
69
|
when 'A'
|
55
70
|
if File.directory?(File.join(dir.tmp, file)) then
|
56
|
-
svn :mkdir, File.join(dir, file)
|
71
|
+
svn :mkdir, '--quiet', File.join(dir, file)
|
57
72
|
else
|
58
73
|
copy(dir, file)
|
59
|
-
svn :add, '--force', File.join(dir, file)
|
74
|
+
svn :add, '--quiet', '--force', File.join(dir, file)
|
60
75
|
end
|
61
76
|
when 'D'
|
62
|
-
svn :remove, '--force', File.join(dir, file)
|
77
|
+
svn :remove, '--quiet', '--force', File.join(dir, file)
|
63
78
|
else
|
64
79
|
copy(dir, file)
|
65
80
|
merges << file
|
66
81
|
end
|
67
82
|
end
|
68
83
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
84
|
+
# Determine if there are any local changes in the pistoned directory
|
85
|
+
log = svn(:log, '--quiet', '--revision', (local_revision .. new_local_rev).to_svn, '--limit', '2', dir)
|
86
|
+
|
87
|
+
# If none, we skip the merge process
|
88
|
+
if local_revision < new_local_rev && log.count("\n") > 3 then
|
89
|
+
logging_stream.puts " Merging local changes back in"
|
90
|
+
merges.each do |file|
|
91
|
+
begin
|
92
|
+
svn(:merge, '--quiet', '--revision', (local_revision .. new_local_rev).to_svn,
|
93
|
+
File.join(dir, file), File.join(dir, file))
|
94
|
+
rescue RuntimeError
|
95
|
+
next if $!.message =~ /Unable to find repository location for/
|
96
|
+
end
|
74
97
|
end
|
75
|
-
end
|
98
|
+
end
|
76
99
|
|
100
|
+
logging_stream.puts " Removing temporary files / folders"
|
77
101
|
FileUtils.rm_rf dir.tmp
|
78
102
|
|
103
|
+
logging_stream.puts " Updating Piston properties"
|
79
104
|
svn :propset, Piston::REMOTE_REV, revisions.last, dir
|
80
105
|
svn :propset, Piston::LOCAL_REV, new_local_rev, dir
|
81
106
|
svn :propset, Piston::LOCKED, revisions.last, dir if lock
|
82
107
|
|
83
|
-
logging_stream.puts "Updated to #{revisions.last} (#{changes} changes)"
|
108
|
+
logging_stream.puts " Updated to r#{revisions.last} (#{changes} changes)"
|
84
109
|
end
|
85
110
|
|
86
111
|
def copy(dir, file)
|
@@ -10,6 +10,7 @@ module Piston
|
|
10
10
|
[ '--dry-run', GetoptLong::NO_ARGUMENT ],
|
11
11
|
[ '--quiet', '-q', GetoptLong::NO_ARGUMENT ],
|
12
12
|
[ '--verbose', '-v', GetoptLong::NO_ARGUMENT ],
|
13
|
+
[ '--show-updates', '-u', GetoptLong::NO_ARGUMENT ],
|
13
14
|
[ '--lock', GetoptLong::NO_ARGUMENT ],
|
14
15
|
[ '--force', '-f', GetoptLong::NO_ARGUMENT ],
|
15
16
|
[ '--version', GetoptLong::NO_ARGUMENT ]
|
data/lib/piston/version.rb
CHANGED
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
|
|
3
3
|
specification_version: 1
|
4
4
|
name: piston
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 1.
|
7
|
-
date: 2006-
|
6
|
+
version: 1.2.0
|
7
|
+
date: 2006-11-17 00:00:00 +00:00
|
8
8
|
summary: Piston is a utility that enables merge tracking of remote repositories.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -51,6 +51,7 @@ files:
|
|
51
51
|
- lib/piston/commands/import.rb
|
52
52
|
- lib/piston/commands/unlock.rb
|
53
53
|
- lib/piston/commands/convert.rb
|
54
|
+
- lib/piston/commands/status.rb
|
54
55
|
- lib/piston/ui/command_line.rb
|
55
56
|
test_files: []
|
56
57
|
|