piston 1.1.1 → 1.2.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 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]
@@ -2,4 +2,8 @@ class String
2
2
  def tmp
3
3
  self + ".tmp"
4
4
  end
5
+
6
+ def blank?
7
+ self.empty? || self.strip.empty?
8
+ end
5
9
  end
@@ -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 :status, '--show-updates', dir
26
- return skip(dir, "pending updates -- run \"svn update #{dir}\"") if status.split("\n").size > 1
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.print "Processing '#{dir}'... "
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
- svn :checkout, '--ignore-externals', '--revision', revisions.first, repos, dir.tmp
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
- merges.each do |file|
70
- begin
71
- svn :merge, '--quiet', '--revision', (local_revision.succ .. new_local_rev).to_svn, File.join(dir, file), File.join(dir, file)
72
- rescue RuntimeError
73
- next if $!.message =~ /Unable to find repository location for/
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 unless local_revision.succ == new_local_rev
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 ]
@@ -1,8 +1,8 @@
1
1
  module Piston
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 1
4
- MINOR = 1
5
- TINY = 1
4
+ MINOR = 2
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
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.1.1
7
- date: 2006-08-31 00:00:00 +00:00
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