Module: SvnCommandHelper::Svn

Extended by:
SystemCommandHelper
Defined in:
lib/svn_command_helper.rb

Overview

Subversion native command and some utilities

Class Method Summary collapse

Class Method Details

.base_uri_of(uris) ⇒ String

find common part of the given uris

Parameters:

  • uris (Array<uri string like>)

    target uri

Returns:

  • (String)

    common part of the given uris



227
228
229
230
231
232
233
# File 'lib/svn_command_helper.rb', line 227

def base_uri_of(uris)
  uris.reduce(Pathname.new(uris.first.to_s)) do |base_uri, uri|
    rel = Pathname.new(uri).relative_path_from(base_uri)
    to_parent = rel.to_s.match(/(?:\.\.\/)*/).to_s
    to_parent.empty? ? base_uri : base_uri + to_parent
  end.to_s
end

.cat(path) ⇒ String

svn cat

Parameters:

  • path (path string like)

    target path

Returns:

  • (String)

    file contents



213
214
215
# File 'lib/svn_command_helper.rb', line 213

def cat(path)
  cap("svn cat #{path}")
end

.check_exists(transaction, raise_if_from_not_found = true) ⇒ Boolean

check transaction from file exists

Parameters:

  • transaction (SvnFileCopyTransaction)

    from and to info

  • raise_if_from_not_found (Boolean) (defaults to: true)

    raise if from not found

Returns:

  • (Boolean)

    true if file exists



344
345
346
347
348
349
350
351
352
353
354
355
356
357
# File 'lib/svn_command_helper.rb', line 344

def check_exists(transaction, raise_if_from_not_found = true)
  unless transaction.from_exist?
    if !raise_if_from_not_found
      false
    elsif transaction.to_exist?
      puts "[WARNING] File:#{file}はコピー先のみにあります"
      false
    else
      raise "[Error] File:#{file}が見つかりません!"
    end
  else
    true
  end
end

.commit(message, path = ".") ⇒ Object

svn commit

Parameters:

  • message (String)

    commit message

  • path (path string like) (defaults to: ".")

    target path



21
22
23
24
25
26
27
28
29
# File 'lib/svn_command_helper.rb', line 21

def commit(message, path = ".")
  if cap("svn status #{path}").empty?
    sys "svn revert -R #{path}"
    puts "[WARNING] no change: #{message}"
  else
    sys "svn commit -m '#{message}' #{path}"
  end
  sys "svn update #{path}"
end

.copied_revision(uri = ".") ⇒ Object

stop-on-copy revision of uri return [Integer] revision number

Parameters:

  • uri (uri string like) (defaults to: ".")

    target uri



134
135
136
# File 'lib/svn_command_helper.rb', line 134

def copied_revision(uri = ".")
  log(uri, stop_on_copy: true).first.revision
end

.copy_multi(transactions, message) ⇒ Object

copy multi transactions

Parameters:



315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'lib/svn_command_helper.rb', line 315

def copy_multi(transactions, message)
  base_uri = base_uri_of(transactions.map(&:from_base) + transactions.map(&:to_base))
  transactions.each do |transaction|
    raise "copy_multi: #{transaction.from} not exists" unless transaction.from_exist?
  end
  Dir.mktmpdir do |dir|
    Dir.chdir(dir) do
      sys "svn checkout --depth empty #{base_uri} ."
      transactions.each do |transaction|
        relative_to = transaction.relative_to(base_uri)

        if transaction.to_exist?  # toがある場合マージ
          Svn.update_deep(relative_to, "empty", false)
          sys "svn export --force #{transaction.from} #{relative_to}"
          sys "svn add --force #{relative_to}"
        else # toがない場合コピー
          Svn.update_deep(File.dirname(relative_to), "empty", false) # mkpath的な なくてもエラーにはならないので
          sys "svn copy --parents #{transaction.from} #{relative_to}"
        end
      end
      Svn.commit(message, ".")
    end
  end
end

.copy_single(transaction, message, recursive = false) ⇒ Object

copy single transaction

Parameters:

  • transaction (SvnFileCopyTransaction)

    from and to info

  • message (String)

    commit message

  • recursive (Boolean) (defaults to: false)

    list –recursive



285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/svn_command_helper.rb', line 285

def copy_single(transaction, message, recursive = false)
  transactions = transaction.glob_transactions(recursive)
  raise "copy_single: #{transaction.from} not exists" if transactions.empty?
  to_exist_transactions = Svn.list_files(transaction.to_base).map do |entry|
    transactions.find {|_transaction| _transaction.file == entry.path}
  end.compact
  only_from_transactions = transactions - to_exist_transactions
  if to_exist_transactions.empty? # toにファイルがない
    sys "svn copy --parents #{only_from_transactions.map(&:from).join(' ')} #{transaction.to_base} -m '#{message}'"
  else
    Dir.mktmpdir do |dir|
      Dir.chdir(dir) do
        sys "svn checkout --depth empty #{transaction.to_base} ."
        unless only_from_transactions.empty?
          sys "svn copy --parents #{only_from_transactions.map(&:from).join(' ')} ."
        end
        sys "svn update --set-depth infinity #{to_exist_transactions.map(&:file).join(' ')}"
        to_exist_transactions.each do |_transaction|
          sys "svn export --force #{_transaction.from} #{_transaction.file}"
          sys "svn add --force #{_transaction.file}"
        end
        Svn.commit(message, ".")
      end
    end
  end
end

.diff(from_uri, to_uri, ignore_properties: false, ignore_eol_style: false, ignore_space_change: false, ignore_all_space: false) ⇒ String

svn diff

Parameters:

  • from_uri (String)

    from uri

  • to_uri (String)

    to uri

  • ignore_properties (Boolean) (defaults to: false)

    –ignore-properties

  • ignore_eol_style (Boolean) (defaults to: false)

    -x –ignore-eol-style

  • ignore_space_change (Boolean) (defaults to: false)

    -x –ignore-space-change

  • ignore_all_space (Boolean) (defaults to: false)

    -x –ignore-all-space

Returns:

  • (String)

    raw diff str



243
244
245
246
247
248
249
250
# File 'lib/svn_command_helper.rb', line 243

def diff(from_uri, to_uri, ignore_properties: false, ignore_eol_style: false, ignore_space_change: false, ignore_all_space: false)
  options = []
  options << "-x --ignore-eol-style" if ignore_eol_style
  options << "-x --ignore-space-change" if ignore_space_change
  options << "-x --ignore-all-space" if ignore_all_space
  options << "--ignore-properties" if ignore_properties
  cap("svn diff #{from_uri} #{to_uri} #{options.join(' ')}")
end

.exist?(uri) ⇒ Boolean

check svn uri exists or not

Parameters:

  • uri (uri string like)

    target uri

Returns:

  • (Boolean)

    true if exists



87
88
89
90
# File 'lib/svn_command_helper.rb', line 87

def exist?(uri)
  basename = File.basename(uri)
  !list(File.dirname(uri)).find{|entry| File.fnmatch(basename, entry.path)}.nil?
end

.exist_file?(uri) ⇒ Boolean

check svn uri file exists or not

Parameters:

  • uri (uri string like)

    target uri

Returns:

  • (Boolean)

    true if exists



95
96
97
98
# File 'lib/svn_command_helper.rb', line 95

def exist_file?(uri)
  file = File.basename(uri)
  !list_files(File.dirname(uri)).find{|entry| File.fnmatch(file, entry.path)}.nil?
end

.info(path = ".") ⇒ Hash<String, String>

svn info -> yaml parse

Parameters:

  • path (path string like) (defaults to: ".")

    target path

Returns:

  • (Hash<String, String>)

    svn info contents



206
207
208
# File 'lib/svn_command_helper.rb', line 206

def info(path = ".")
  YAML.load(cap("svn info #{path}"))
end

.list(uri, recursive = false) ⇒ Array<String>

svn list

Parameters:

  • uri (uri string like)

    target uri

  • recursive (Boolean) (defaults to: false)

    –recursive

Returns:

  • (Array<String>)

    paths



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/svn_command_helper.rb', line 35

def list(uri, recursive = false)
  if @list_cache && @list_cache[recursive][uri]
    @list_cache[recursive][uri]
  else
    list_str = cap("svn list --xml #{recursive ? '-R' : ''} #{uri}")
    list = REXML::Document.new(list_str).elements.collect("/lists/list/entry") do |entry|
      commit = entry.elements["commit"]
      OpenStruct.new({
        kind: entry.attribute("kind").value,
        path: entry.elements["name"].text,
        revision: commit.attribute("revision").value.to_i,
        author: commit.elements["author"].text,
        date: Time.iso8601(commit.elements["date"].text),
      })
    end
    @list_cache[recursive][uri] = list if @list_cache
    list
  end
end

.list_cache(&block) ⇒ Object

svn list cache block



78
79
80
81
82
# File 'lib/svn_command_helper.rb', line 78

def list_cache(&block)
  @list_cache = {true => {}, false => {}}
  block.call
  @list_cache = nil
end

.list_files(uri, recursive = false) ⇒ Array<String>

svn list -> grep only files

Parameters:

  • uri (uri string like)

    target uri

  • recursive (Boolean) (defaults to: false)

    –recursive

Returns:

  • (Array<String>)

    file paths



66
67
68
# File 'lib/svn_command_helper.rb', line 66

def list_files(uri, recursive = false)
  list(uri, recursive).select {|entry| entry.kind == "file"}
end

.list_files_recursive(uri) ⇒ Array<String>

svn list –recursive -> grep only files

Parameters:

  • uri (uri string like)

    target uri

Returns:

  • (Array<String>)

    file paths



73
74
75
# File 'lib/svn_command_helper.rb', line 73

def list_files_recursive(uri)
  list_files(uri, true)
end

.list_recursive(uri) ⇒ Array<String>

svn list –recursive

Parameters:

  • uri (uri string like)

    target uri

Returns:

  • (Array<String>)

    paths



58
59
60
# File 'lib/svn_command_helper.rb', line 58

def list_recursive(uri)
  list(uri, true)
end

.log(uri = ".", limit: nil, stop_on_copy: false) ⇒ Array<OpenStruct>

svn log

Parameters:

  • uri (uri string like) (defaults to: ".")

    target uri

  • limit (Integer) (defaults to: nil)

    –limit

  • stop_on_copy (Boolean) (defaults to: false)

    –stop-on-copy

Returns:

  • (Array<OpenStruct>)

    log (old to new order)



112
113
114
115
116
117
118
119
120
121
122
# File 'lib/svn_command_helper.rb', line 112

def log(uri = ".", limit: nil, stop_on_copy: false)
  log = cap "svn log --xml #{limit ? "--limit #{limit}" : ""} #{stop_on_copy ? "--stop-on-copy" : ""} #{uri}"
  REXML::Document.new(log).elements.collect("/log/logentry") do |entry|
    OpenStruct.new({
      revision: entry.attribute("revision").value.to_i,
      author: entry.elements["author"].text,
      date: Time.iso8601(entry.elements["date"].text),
      msg: entry.elements["msg"].text,
    })
  end.reverse
end

.merge1(start_rev, end_rev, from_uri, to_path = ".") ⇒ Object

svn merge -r start_rev:end_rev from_uri to_path

Parameters:

  • start_rev (Integer)

    start revision

  • end_rev (Integer)

    end revision

  • from_uri (String)

    from uri

  • to_path (String) (defaults to: ".")

    to local path



167
168
169
# File 'lib/svn_command_helper.rb', line 167

def merge1(start_rev, end_rev, from_uri, to_path = ".")
  safe_merge "svn merge -r #{start_rev}:#{end_rev} #{from_uri} #{to_path}"
end

.merge_branch_to_trunk(from_uri, to_path = ".") ⇒ Object

svn merge branch to trunk with detecting revision range

Parameters:

  • from_uri (String)

    from uri

  • to_path (String) (defaults to: ".")

    to local path



185
186
187
188
189
# File 'lib/svn_command_helper.rb', line 185

def merge_branch_to_trunk(from_uri, to_path = ".")
  start_rev = copied_revision(from_uri)
  end_rev = revision(from_uri)
  merge1(start_rev, end_rev, from_uri, to_path)
end

.reverse_merge(start_rev, end_rev = nil, path = ".") ⇒ Object

reverse merge single revision

Parameters:

  • start_rev (Integer)

    start revision

  • end_rev (Integer) (defaults to: nil)

    end revision (if no end_rev then “-c start_rev”)

  • path (String) (defaults to: ".")

    local path



195
196
197
198
199
200
201
# File 'lib/svn_command_helper.rb', line 195

def reverse_merge(start_rev, end_rev = nil, path = ".")
  if end_rev
    safe_merge "svn merge -r #{end_rev}:#{start_rev} #{path}"
  else
    safe_merge "svn merge -c #{start_rev} #{path}"
  end
end

.revision(uri = ".") ⇒ Object

head revision of uri return [Integer] revision number

Parameters:

  • uri (uri string like) (defaults to: ".")

    target uri



127
128
129
# File 'lib/svn_command_helper.rb', line 127

def revision(uri = ".")
  log(uri, limit: 1).last.revision
end

.safe_merge(command) ⇒ Object

merge after dry-run conflict check

Parameters:

  • command (String)

    svn merge full command



173
174
175
176
177
178
179
180
# File 'lib/svn_command_helper.rb', line 173

def safe_merge(command)
  dry_run = cap("#{command} --dry-run")
  if dry_run.each_line.any? {|line| line.start_with?("C")}
    raise "[ERROR] merge_branch_to_trunk: `#{command}` has conflict!\n#{dry_run}"
  else
    sys command
  end
end

.summarize_diff(from_uri, to_uri, ignore_properties: false, ignore_eol_style: false, ignore_space_change: false, ignore_all_space: false) ⇒ Array

svn diff –summarize

Parameters:

  • from_uri (String)

    from uri

  • to_uri (String)

    to uri

  • ignore_properties (Boolean) (defaults to: false)

    | grep -v ‘^ ’

  • ignore_eol_style (Boolean) (defaults to: false)

    -x –ignore-eol-style

  • ignore_space_change (Boolean) (defaults to: false)

    -x –ignore-space-change

  • ignore_all_space (Boolean) (defaults to: false)

    -x –ignore-all-space

Returns:

  • (Array)

    diff files list



260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/svn_command_helper.rb', line 260

def summarize_diff(from_uri, to_uri, ignore_properties: false, ignore_eol_style: false, ignore_space_change: false, ignore_all_space: false)
  options = []
  options << "-x --ignore-eol-style" if ignore_eol_style
  options << "-x --ignore-space-change" if ignore_space_change
  options << "-x --ignore-all-space" if ignore_all_space

  diff_str = cap("svn diff --xml --summarize #{from_uri} #{to_uri} #{options.join(' ')}")
  diff_list = REXML::Document.new(diff_str).elements.collect("/diff/paths/path") do |path|
    OpenStruct.new({
      kind: path.attribute("kind").value,
      item: path.attribute("item").value,
      props: path.attribute("props").value,
      path: path.text,
    })
  end
  if ignore_properties
    diff_list.reject! {|diff| diff.item == "none"}
  end
  diff_list
end

.update(path = ".", depth = nil) ⇒ Object

svn update

Parameters:

  • path (path string like) (defaults to: ".")

    target path

  • depth (depth) (defaults to: nil)

    –set-depth



103
104
105
# File 'lib/svn_command_helper.rb', line 103

def update(path = ".", depth = nil)
  sys "svn update #{depth ? "--set-depth #{depth}" : ""} #{path}"
end

.update_deep(path, depth = nil, exist_path_update = true) ⇒ Object

svn update to deep path recursive

Parameters:

  • path (path string like)

    target path

  • depth (Integer) (defaults to: nil)

    –set-depth for only new updated dirs

  • exist_path_update (Boolean) (defaults to: true)

    middle path update flag



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/svn_command_helper.rb', line 142

def update_deep(path, depth = nil, exist_path_update = true)
  exist_path = path
  until File.exist?(exist_path)
    exist_path = File.dirname(exist_path)
  end
  root = Pathname.new(Svn.working_copy_root_path(exist_path))
  end_path = Pathname.new(path.to_s).expand_path
  parents = [end_path]
  while parents.first != root
    parents.unshift(parents.first.parent)
  end
  parents.each do |dir|
    if dir.exist?
      sys "svn update #{dir}" if exist_path_update
    else
      sys "svn update #{depth ? "--set-depth #{depth}" : ""} #{dir}"
    end
  end
end

.working_copy_root_path(path = ".") ⇒ String

Working Copy Root Path from svn info

Parameters:

  • path (path string like) (defaults to: ".")

    target path

Returns:

  • (String)

    Working Copy Root Path



220
221
222
# File 'lib/svn_command_helper.rb', line 220

def working_copy_root_path(path = ".")
  info(path)["Working Copy Root Path"]
end