module SvnCommandHelper::Svn

Subversion native command and some utilities

Public Class Methods

base_uri_of(uris) click to toggle source

find common part of the given uris @param [Array<uri string like>] uris target uri @return [String] common part of the given uris

# File lib/svn_command_helper.rb, line 287
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) click to toggle source

svn cat @param [path string like] path target path @return [String] file contents

# File lib/svn_command_helper.rb, line 273
def cat(path)
  cap("svn cat #{path}")
end
check_exists(transaction, raise_if_from_not_found = true) click to toggle source

check transaction from file exists @param [SvnFileCopyTransaction] transaction from and to info @param [Boolean] raise_if_from_not_found raise if from not found @return [Boolean] true if file exists

# File lib/svn_command_helper.rb, line 436
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 = ".") click to toggle source

svn commit @param [String] message commit message @param [path string like] path target path

# File lib/svn_command_helper.rb, line 20
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 = ".") click to toggle source

stop-on-copy revision of uri @param [uri string like] uri target uri return [Integer] revision number

# File lib/svn_command_helper.rb, line 154
def copied_revision(uri = ".")
  log(uri, stop_on_copy: true).first.revision
end
copy_multi(transactions, message) click to toggle source

copy multi transactions @param [Array<SvnFileCopyTransaction>] transactions from and to info list @param [String] message commit message

# File lib/svn_command_helper.rb, line 403
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} ."
      root = Svn.working_copy_root_path(".")
      transactions.each do |transaction|
        relative_to = transaction.relative_to(base_uri)

        if transaction.to_exist?  # toがある場合マージ
          Svn.update_deep(relative_to, "empty", false, root: root)
          begin
            Svn.merge1(1, "HEAD", transaction.from, relative_to, "--accept theirs-full")
          rescue
            sys "svn export --force #{transaction.from} #{relative_to}"
          end
        else # toがない場合コピー
          Svn.update_deep(File.dirname(relative_to), "empty", false, root: root) # mkpath的な なくてもエラーにはならないので
          sys "svn copy --parents #{transaction.from} #{relative_to}"
        end
      end
      Svn.commit(message, ".")
    end
  end
end
copy_single(transaction, message, recursive = false) click to toggle source

copy single transaction @param [SvnFileCopyTransaction] transaction from and to info @param [String] message commit message @param [Boolean] recursive list –recursive

# File lib/svn_command_helper.rb, line 369
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} ."
        # なくてもエラーにならないので全部update
        sys "svn update --set-depth infinity #{transactions.map(&:file).join(' ')}"
        unless only_from_transactions.empty?
          sys "svn copy --parents #{only_from_transactions.map(&:from).join(' ')} ."
        end
        to_exist_transactions.each do |_transaction|
          begin
            Svn.merge1(1, "HEAD", _transaction.from, _transaction.file, "--accept theirs-full")
          rescue
            sys "svn export --force #{_transaction.from} #{_transaction.file}"
          end
        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) click to toggle source

svn diff @param [String] from_uri from uri @param [String] to_uri to uri @param [Boolean] ignore_properties –ignore-properties @param [Boolean] ignore_eol_style -x –ignore-eol-style @param [Boolean] ignore_space_change -x –ignore-space-change @param [Boolean] ignore_all_space -x –ignore-all-space @return [String] raw diff str

# File lib/svn_command_helper.rb, line 303
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) click to toggle source

check svn uri exists or not @param [uri string like] uri target uri @return [Boolean] true if exists

# File lib/svn_command_helper.rb, line 97
def exist?(uri)
  basename = File.basename(uri)
  !list(File.dirname(uri)).find{|entry| File.fnmatch(basename, entry.path)}.nil?
end
exist_file?(uri) click to toggle source

check svn uri file exists or not @param [uri string like] uri target uri @return [Boolean] true if exists

# File lib/svn_command_helper.rb, line 105
def exist_file?(uri)
  file = File.basename(uri)
  !list_files(File.dirname(uri)).find{|entry| File.fnmatch(file, entry.path)}.nil?
end
info(path = ".") click to toggle source

svn info -> yaml parse @param [path string like] path target path @return [Hash<String, String>] svn info contents

# File lib/svn_command_helper.rb, line 266
def info(path = ".")
  YAML.load(cap("svn info #{path}"))
end
list(uri, recursive = false) click to toggle source

svn list @param [uri string like] uri target uri @param [Boolean] recursive –recursive @return [Array<ListItem>] paths

# File lib/svn_command_helper.rb, line 34
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 = LibXML::XML::Document.string(list_str).find("//lists/list/entry").map do |entry|
      commit = entry.find_first("commit")
      ListItem.new(
        kind: entry["kind"],
        path: entry.find_first("name").content,
        revision: commit["revision"].to_i,
        author: commit.find_first("author").content,
        date: Time.iso8601(commit.find_first("date").content)
      )
    end
    @list_cache[recursive][uri] = list if @list_cache
    list
  end
end
list_cache(&block) click to toggle source

svn list cache block

# File lib/svn_command_helper.rb, line 88
def list_cache(&block)
  @list_cache = {true => {}, false => {}}
  block.call
  @list_cache = nil
end
list_files(uri, recursive = false) click to toggle source

svn list -> grep only files @param [uri string like] uri target uri @param [Boolean] recursive –recursive @return [Array<String>] file paths

# File lib/svn_command_helper.rb, line 76
def list_files(uri, recursive = false)
  list(uri, recursive).select {|entry| entry.kind == "file"}
end
list_files_recursive(uri) click to toggle source

svn list –recursive -> grep only files @param [uri string like] uri target uri @return [Array<String>] file paths

# File lib/svn_command_helper.rb, line 83
def list_files_recursive(uri)
  list_files(uri, true)
end
list_recursive(uri) click to toggle source

svn list –recursive @param [uri string like] uri target uri @return [Array<String>] paths

# File lib/svn_command_helper.rb, line 68
def list_recursive(uri)
  list(uri, true)
end
log(uri = ".", limit: nil, stop_on_copy: false) click to toggle source

svn log @param [uri string like] uri target uri @param [Integer] limit –limit @param [Boolean] stop_on_copy –stop-on-copy @return [Array<LogItem>] log (old to new order)

# File lib/svn_command_helper.rb, line 122
def log(uri = ".", limit: nil, stop_on_copy: false)
  log = cap "svn log --xml #{limit ? "--limit #{limit}" : ""} #{stop_on_copy ? "--stop-on-copy" : ""} #{uri}"
  LibXML::XML::Document.string(log).find("//log/logentry").map do |entry|
    LogItem.new(
      revision: entry["revision"].to_i,
      author: entry.find_first("author").content,
      date: Time.iso8601(entry.find_first("date").content),
      msg: entry.find_first("msg").content
    )
  end.reverse
end
merge1(start_rev, end_rev, from_uri, to_path = ".", extra = "") click to toggle source

svn merge -r start_rev:end_rev from_uri to_path @param [Integer] start_rev start revision @param [Integer] end_rev end revision @param [String] from_uri from uri @param [String] to_path to local path @param [String] extra extra options

# File lib/svn_command_helper.rb, line 189
def merge1(start_rev, end_rev, from_uri, to_path = ".", extra = "")
  safe_merge merge1_command(start_rev, end_rev, from_uri, to_path, extra)
end
merge1_command(start_rev, end_rev, from_uri, to_path = ".", extra = "", dry_run: false) click to toggle source

“svn merge -r start_rev:end_rev from_uri to_path” @param [Integer] start_rev start revision @param [Integer] end_rev end revision @param [String] from_uri from uri @param [String] to_path to local path @param [String] extra extra options @param [Boolean] dry_run –dry-run

# File lib/svn_command_helper.rb, line 210
def merge1_command(start_rev, end_rev, from_uri, to_path = ".", extra = "", dry_run: false)
  "svn merge -r #{start_rev}:#{end_rev} #{from_uri} #{to_path} #{extra} #{dry_run ? "--dry-run" : ""}"
end
merge1_dry_run(start_rev, end_rev, from_uri, to_path = ".", extra = "") click to toggle source

svn merge -r start_rev:end_rev from_uri to_path –dry-run @param [Integer] start_rev start revision @param [Integer] end_rev end revision @param [String] from_uri from uri @param [String] to_path to local path @param [String] extra extra options

# File lib/svn_command_helper.rb, line 199
def merge1_dry_run(start_rev, end_rev, from_uri, to_path = ".", extra = "")
  merge_dry_run merge1_command(start_rev, end_rev, from_uri, to_path, extra)
end
merge_branch_to_trunk(from_uri, to_path = ".") click to toggle source

svn merge branch to trunk with detecting revision range @param [String] from_uri from uri @param [String] to_path to local path

# File lib/svn_command_helper.rb, line 245
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
merge_dry_run(command) click to toggle source

merge dry-run conflict check result @param [String] command svn merge full command

# File lib/svn_command_helper.rb, line 228
def merge_dry_run(command)
  cap("#{command} --dry-run")
    .each_line.map(&:chomp).reject {|line| line[4] != " "}
    .map {|line| MergeStatusItem.new(status: line[0...4], path: line[5..-1])}
end
reverse_merge(start_rev, end_rev = nil, path = ".") click to toggle source

reverse merge single revision @param [Integer] start_rev start revision @param [Integer] end_rev end revision (if no end_rev then “-c start_rev”) @param [String] path local path

# File lib/svn_command_helper.rb, line 255
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 = ".") click to toggle source

head revision of uri @param [uri string like] uri target uri return [Integer] revision number

# File lib/svn_command_helper.rb, line 147
def revision(uri = ".")
  log(uri, limit: 1).last.revision
end
safe_merge(command) click to toggle source

merge after dry-run conflict check @param [String] command svn merge full command

# File lib/svn_command_helper.rb, line 216
def safe_merge(command)
  dry_run = merge_dry_run(command)
  if dry_run.any? {|entry| entry.status.include?("C")}
    dry_run_str = dry_run.map {|entry| "#{entry.status} #{entry.path}"}.join("\n")
    raise "[ERROR] merge_branch_to_trunk: `#{command}` has conflict!\n#{dry_run_str}"
  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, with_list_info: false) click to toggle source

svn diff –summarize @param [String] from_uri from uri @param [String] to_uri to uri @param [Boolean] ignore_properties | grep -v '^ ' @param [Boolean] ignore_eol_style -x –ignore-eol-style @param [Boolean] ignore_space_change -x –ignore-space-change @param [Boolean] ignore_all_space -x –ignore-all-space @param [Boolean] with_list_info with svn list info @return [Array] diff files list

# File lib/svn_command_helper.rb, line 321
def summarize_diff(from_uri, to_uri, ignore_properties: false, ignore_eol_style: false, ignore_space_change: false, ignore_all_space: false, with_list_info: 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 = LibXML::XML::Document.string(diff_str).find("//diff/paths/path").map do |path|
    DiffItem.new(
      kind: path["kind"],
      item: path["item"],
      props: path["props"],
      path: path.content
    )
  end
  if ignore_properties
    diff_list.reject! {|diff| diff.item == "none"}
  end
  if with_list_info
    from_entries = Svn.list_recursive(from_uri)
    to_entries = Svn.list_recursive(to_uri)
    from_entries_hash = from_entries.each.with_object({}) {|file, entries_hash| entries_hash[file.path] = file}
    to_entries_hash = to_entries.each.with_object({}) {|file, entries_hash| entries_hash[file.path] = file}
    from_uri_path = Pathname.new(from_uri)
    diff_list.each do |diff|
      path = Pathname.new(diff.path).relative_path_from(from_uri_path).to_s
      diff.from = from_entries_hash[path]
      diff.to = to_entries_hash[path]
    end
  end
  diff_list
end
update(path = ".", depth = nil) click to toggle source

svn update @param [path string like] path target path @param [depth] depth –set-depth

# File lib/svn_command_helper.rb, line 113
def update(path = ".", depth = nil)
  sys "svn update #{depth ? "--set-depth #{depth}" : ""} #{path}"
end
update_deep(path, depth = nil, exist_path_update = true, root: nil) click to toggle source

svn update to deep path recursive @param [path string like] path target path @param [Integer] depth –set-depth for only new updated dirs @param [Boolean] exist_path_update middle path update flag @param [String] root working copy root path

# File lib/svn_command_helper.rb, line 163
def update_deep(path, depth = nil, exist_path_update = true, root: nil)
  exist_path = path
  until File.exist?(exist_path)
    exist_path = File.dirname(exist_path)
  end
  root = Pathname.new(root || 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 = ".") click to toggle source

Working Copy Root Path from svn info @param [path string like] path target path @return [String] Working Copy Root Path

# File lib/svn_command_helper.rb, line 280
def working_copy_root_path(path = ".")
  info(path)["Working Copy Root Path"]
end