Class: Confgit::Repo
Constant Summary collapse
- ROOT_KEY =
'confgit.root'
Constants included from WithColor
Class Method Summary collapse
-
.define_command(command, *opts) ⇒ Object
外部コマンドを定義する.
Instance Method Summary collapse
-
#arg_last_options(args) ⇒ Object
引数の最後が Hash ならオプションとして取出す.
-
#chrepo(repo) ⇒ Object
リポジトリの変更.
-
#confgit_add(options, *files) ⇒ Object
ファイルを管理対象に追加.
-
#confgit_backup(options, *args) ⇒ Object
バックアップする.
-
#confgit_init(options) ⇒ Object
リポジトリの初期化.
-
#confgit_list(options, *args) ⇒ Object
一覧表示する.
-
#confgit_path(options, subdir = '.') ⇒ Object
リポジトリのパスを表示.
-
#confgit_repo(options, repo = nil) ⇒ Object
カレントリポジトリの表示・変更.
-
#confgit_restore(options, *args) ⇒ Object
リストアする.
-
#confgit_rm(options, *args) ⇒ Object
ファイルを管理対象から削除.
-
#confgit_root(options, value = nil) ⇒ Object
ルートの表示・変更.
-
#default_config ⇒ Object
設定の初期値.
-
#dir_each(subdir = '.') ⇒ Object
ディレクトリ内のファイルを繰返す.
-
#expand_path(path, dir = nil) ⇒ Object
パスを展開する.
-
#file_directory?(path) ⇒ Boolean
ファイルがディレクトリか?(シンボリックリンクの場合は対象外).
-
#file_exist?(path) ⇒ Boolean
ファイルが存在するか?(シンボリックリンクの場合も対象にする).
-
#file_writable_real?(path) ⇒ Boolean
ファイルが書込み可能か?(シンボリックリンクの場合はわからないのでとりあえず true を返す).
-
#filecopy(from, to, exiting = false) ⇒ Object
ファイルのコピー(属性は維持する).
-
#getargs(args, force = false) ⇒ Object
引数を利用可能にする.
-
#getopts(args) ⇒ Object
オプションを取出す.
-
#git(*args) ⇒ Object
git を呼出す.
-
#git_args(args) ⇒ Object
git コマンドの引数を生成する.
-
#git_each(*args) ⇒ Object
git に管理されているファイルを繰返す.
-
#hash_object(file) ⇒ Object
ファイルの hash値を求める.
-
#hostname ⇒ Object
ホスト名.
-
#initialize(path = '~/.etc/confgit') ⇒ Repo
constructor
A new instance of Repo.
-
#method_missing(name, *args, &block) ⇒ Object
メソッドがない場合.
-
#mode2str(bits) ⇒ Object
ファイル属性を文字列にする.
-
#modfile?(from, to) ⇒ Boolean
ファイルの更新チェック.
-
#read_config(file) ⇒ Object
設定の読込み.
-
#relative_path(path) ⇒ Object
ルートからの相対パス.
-
#repo_each ⇒ Object
リポジトリを繰返す.
-
#rmrepo(repo, force = false) ⇒ Object
リポジトリの削除.
-
#root ⇒ Object
ルートのパスを取得する.
-
#root=(value) ⇒ Object
ルートのパスを設定する.
-
#system_(command, *args) ⇒ Object
オプションに応じて外部呼出しを行う.
-
#valid_repo ⇒ Object
カレントリポジトリがない場合の処理.
-
#yes?(prompt, y = true) ⇒ Boolean
確認プロンプトを表示する.
Methods included from WithColor
Constructor Details
#initialize(path = '~/.etc/confgit') ⇒ Repo
Returns a new instance of Repo.
22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/confgit/repo.rb', line 22 def initialize(path = '~/.etc/confgit') @base_path = File.(path) @repos_path = File.join(@base_path, 'repos') FileUtils.mkpath(@repos_path) @config = read_config(File.join(@base_path, 'confgit.conf')) @repo_path = File.('current', @repos_path) valid_repo unless File.symlink?(@repo_path) end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(name, *args, &block) ⇒ Object
メソッドがない場合
135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/confgit/repo.rb', line 135 def method_missing(name, *args, &block) if name.to_s =~ /^confgit_(.+)$/ = args.shift args = git_args(args).push() command = $1.gsub(/_/, '-') git(command, *args) # abort "#{CMD} '#{$'}' is not a git command. See '#{CMD} --help'.\n" else super end end |
Class Method Details
.define_command(command, *opts) ⇒ Object
外部コマンドを定義する
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/confgit/repo.rb', line 118 def self.define_command(command, *opts) define_method "confgit_#{command}" do |, *args| args = getargs(args) Dir.chdir(@repo_path) { |path| begin args = opts + args args.push() system_(command, *args) rescue => e abort e.to_s end } end end |
Instance Method Details
#arg_last_options(args) ⇒ Object
引数の最後が Hash ならオプションとして取出す
173 174 175 176 177 178 179 |
# File 'lib/confgit/repo.rb', line 173 def (args) if args.last && args.last.kind_of?(Hash) args.pop else {} end end |
#chrepo(repo) ⇒ Object
リポジトリの変更
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/confgit/repo.rb', line 52 def chrepo(repo) Dir.chdir(@repos_path) { |path| begin if File.symlink?('current') return if File.readlink('current') == repo File.unlink('current') end unless File.exist?(repo) FileUtils.mkpath(repo) Dir.chdir(repo) { |path| begin out, err, status = Open3.capture3('git', 'init') $stderr.puts err unless err.empty? rescue => e FileUtils.remove_entry_secure(repo) abort e.to_s end } end File.symlink(repo, 'current') rescue => e abort e.to_s end } end |
#confgit_add(options, *files) ⇒ Object
ファイルを管理対象に追加
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 |
# File 'lib/confgit/repo.rb', line 500 def confgit_add(, *files) confgit_init unless File.exist?(@repo_path) repo = File.realpath(@repo_path) files.each { |path| path = (path) if relative_path(path) =~ /^[.]{2}/ $stderr.puts "'#{path}' is outside directory" next end if file_directory?(path) dir_each(path) { |file| next if File.directory?(file) from = File.join(path, file) rel = relative_path(from) to = File.join(repo, rel) if filecopy(from, to) git('add', rel) end } else from = path rel = relative_path(from) to = File.join(repo, rel) if filecopy(from, to) git('add', rel) end end } end |
#confgit_backup(options, *args) ⇒ Object
バックアップする
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 |
# File 'lib/confgit/repo.rb', line 551 def confgit_backup(, *args) git_each(*args) { |file, hash| next if file_directory?(file) from = File.join(root, file) to = File.join(@repo_path, file) unless file_exist?(from) with_color(:fg_red) { print "[?] #{file}" } puts next end if [:force] || modfile?(from, to) with_color(:fg_blue) { print "--> #{file}" } write = [:yes] if write == nil # 書込みが決定していない場合 write = yes?(nil, false) else puts end filecopy(from, to) if write end } git('status', :interactive => false) end |
#confgit_init(options) ⇒ Object
リポジトリの初期化
494 495 496 497 |
# File 'lib/confgit/repo.rb', line 494 def confgit_init() FileUtils.mkpath(@repo_path) git('init') end |
#confgit_list(options, *args) ⇒ Object
一覧表示する
614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 |
# File 'lib/confgit/repo.rb', line 614 def confgit_list(, *args) git_each(*args) { |file, hash| next if file_directory?(file) from = File.join(root, file) to = File.join(@repo_path, file) if File.symlink?(from) mode = [:octal] ? '120000' : 'l---------' user = '-' group = '-' elsif File.exist?(from) stat = File.stat(from) mode = [:octal] ? stat.mode.to_s(8) : mode2str(stat.mode) user = Etc.getpwuid(stat.uid).name group = Etc.getgrgid(stat.gid).name else mode = ' ' * ([:octal] ? 6 : 10) user = '-' group = '-' end print "#{mode}\t#{user}\t#{group}\t#{from}\n" } end |
#confgit_path(options, subdir = '.') ⇒ Object
リポジトリのパスを表示
641 642 643 644 |
# File 'lib/confgit/repo.rb', line 641 def confgit_path(, subdir = '.') path = File.realpath(File.(subdir, @repo_path)) print path, "\n" end |
#confgit_repo(options, repo = nil) ⇒ Object
カレントリポジトリの表示・変更
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 |
# File 'lib/confgit/repo.rb', line 462 def confgit_repo(, repo = nil) if repo # 変更 if [:remove] rmrepo(repo, [:force]) else chrepo(repo) end else # 表示 repo_each { |file, is_current| mark = is_current ? '*' : ' ' print "#{mark} #{file}\n" } end end |
#confgit_restore(options, *args) ⇒ Object
リストアする
583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 |
# File 'lib/confgit/repo.rb', line 583 def confgit_restore(, *args) git_each(*args) { |file, hash| next if file_directory?(file) from = File.join(@repo_path, file) to = File.join(root, file) unless file_exist?(from) with_color(:fg_red) { print "[?] #{file}" } puts next end if [:force] || modfile?(from, to) color = file_writable_real?(to) ? :fg_blue : :fg_magenta with_color(color) { print "<-- #{file}" } write = [:yes] if write == nil # 書込みが決定していない場合 write = yes?(nil, false) else puts end filecopy(from, to) if write end } end |
#confgit_rm(options, *args) ⇒ Object
ファイルを管理対象から削除
537 538 539 540 541 542 543 544 545 546 547 548 |
# File 'lib/confgit/repo.rb', line 537 def confgit_rm(, *args) return unless File.exist?(@repo_path) = getopts(args) repo = File.realpath(@repo_path) files = args.collect { |from| relative_path((from)) } git('rm', *( + files), :interactive => false) end |
#confgit_root(options, value = nil) ⇒ Object
ルートの表示・変更
480 481 482 483 484 485 486 487 488 489 490 491 |
# File 'lib/confgit/repo.rb', line 480 def confgit_root(, value = nil) if [:remove] # 削除 self.root = nil elsif value # 変更 self.root = value else # 表示 puts root end end |
#default_config ⇒ Object
設定の初期値
100 101 102 |
# File 'lib/confgit/repo.rb', line 100 def default_config {} end |
#dir_each(subdir = '.') ⇒ Object
ディレクトリ内のファイルを繰返す
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 |
# File 'lib/confgit/repo.rb', line 328 def dir_each(subdir = '.') Dir.chdir(File.(subdir, @repo_path)) { |path| Dir.foreach('.') { |file| next if /^(\.git|\.$|\.\.$)/ =~ file yield(file) if File.directory?(file) Dir.glob("#{file}/**/*", File::FNM_DOTMATCH) { |file| if /(^|\/)(\.git|\.|\.\.)$/ !~ file yield(file) end } end } } end |
#expand_path(path, dir = nil) ⇒ Object
パスを展開する
392 393 394 395 396 397 398 399 400 401 402 |
# File 'lib/confgit/repo.rb', line 392 def (path, dir = nil) File.(path, dir).gsub(%r|^/private(/[^/]+)|) { |m| begin subdir = $1 m = subdir if File.realpath(subdir) == m rescue end m } end |
#file_directory?(path) ⇒ Boolean
ファイルがディレクトリか?(シンボリックリンクの場合は対象外)
286 287 288 |
# File 'lib/confgit/repo.rb', line 286 def file_directory?(path) File.directory?(path) && ! File.symlink?(path) end |
#file_exist?(path) ⇒ Boolean
ファイルが存在するか?(シンボリックリンクの場合も対象にする)
281 282 283 |
# File 'lib/confgit/repo.rb', line 281 def file_exist?(path) File.exist?(path) || File.symlink?(path) end |
#file_writable_real?(path) ⇒ Boolean
ファイルが書込み可能か?(シンボリックリンクの場合はわからないのでとりあえず true を返す)
291 292 293 294 295 296 |
# File 'lib/confgit/repo.rb', line 291 def file_writable_real?(path) return true if File.symlink?(path) return File.writable_real?(File.dirname(path)) unless File.exist?(path) File.writable_real?(path) end |
#filecopy(from, to, exiting = false) ⇒ Object
ファイルのコピー(属性は維持する)
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
# File 'lib/confgit/repo.rb', line 299 def filecopy(from, to, exiting = false) begin to_dir = File.dirname(to) FileUtils.mkpath(to_dir) if File.symlink?(to) # シンボリックリンクの場合は削除する File.unlink(to) elsif File.exist?(to) && ! File.writable_real?(to) # 書込みできない場合は削除を試みる File.unlink(to) end FileUtils.copy_entry(from, to) unless File.symlink?(from) || File.symlink?(to) stat = File.stat(from) File.utime(stat.atime, stat.mtime, to) File.chmod(stat.mode, to) end return true rescue => e abort e.to_s if exiting $stderr.puts e.to_s end end |
#getargs(args, force = false) ⇒ Object
引数を利用可能にする
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/confgit/repo.rb', line 150 def getargs(args, force = false) args.collect { |x| run = false case x when /^-/ when /\// run = true else run = force end if run repo = File.realpath(@repo_path) path = File.join(repo, x) x = Pathname(path).relative_path_from(Pathname(repo)).to_s end x } end |
#getopts(args) ⇒ Object
オプションを取出す
405 406 407 408 409 410 411 412 413 |
# File 'lib/confgit/repo.rb', line 405 def getopts(args) = [] args.each { |opt| break unless /^-/ =~ opt << args.shift } end |
#git(*args) ⇒ Object
git を呼出す
200 201 202 203 204 205 206 207 208 |
# File 'lib/confgit/repo.rb', line 200 def git(*args) Dir.chdir(@repo_path) { |path| begin system_('git', *args) rescue => e abort e.to_s end } end |
#git_args(args) ⇒ Object
git コマンドの引数を生成する
211 212 213 214 215 216 |
# File 'lib/confgit/repo.rb', line 211 def git_args(args) args.collect { |item| item = $' if item.kind_of?(String) && %r|^/| =~ item item } end |
#git_each(*args) ⇒ Object
git に管理されているファイルを繰返す
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 |
# File 'lib/confgit/repo.rb', line 347 def git_each(*args) args = getargs(args, true) files = args.collect { |f| f.shellescape } Dir.chdir(@repo_path) { |path| open("| git ls-files --stage --full-name " + files.join(' ')) {|f| while line = f.gets mode, hash, stage, file = line.split # file = line.chomp next if /^\.git/ =~ file next if file_directory?(file) yield(file, hash) end } } end |
#hash_object(file) ⇒ Object
ファイルの hash値を求める
248 249 250 251 252 253 254 255 256 257 258 259 260 |
# File 'lib/confgit/repo.rb', line 248 def hash_object(file) path = File.(file) if File.symlink?(path) mode = '120000' hash = `readlink "#{path}" | git hash-object --stdin`.chomp else mode = File.executable?(path) ? '100755' : '100644' hash = `git hash-object "#{path}"`.chomp end "#{mode} #{hash}" end |
#hostname ⇒ Object
ホスト名
35 36 37 |
# File 'lib/confgit/repo.rb', line 35 def hostname `hostname`.chomp end |
#mode2str(bits) ⇒ Object
ファイル属性を文字列にする
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 |
# File 'lib/confgit/repo.rb', line 421 def mode2str(bits) case bits & 0170000 # S_IFMT when 0010000 # S_IFIFO パイプ mode = 'p' when 0020000 # S_IFCHR キャラクタ・デバイス mode = 'c' when 0040000 # S_IFDIR ディレクトリ mode = 'd' when 0060000 # S_IFBLK ブロック・デバイス mode = 'b' when 0100000 # S_IFREG 通常ファイル mode = '-' when 0120000 # S_IFLNK シンボリックリンク mode = 'l' when 0140000 # S_IFSOCK ソケット mode = 's' when 0160000 # S_IFWHT BSD空白ファイル mode = 'w' end mode += 'rwx'*3 (0..8).each { |i| mask = 1<<i mode[-(i+1)] = '-' if (bits & mask) == 0 } if (bits & 0001000) != 0 # S_ISVTX スティッキービット if mode[-1] == '-' mode[-1] = 'T' else mode[-1] = 't' end end mode end |
#modfile?(from, to) ⇒ Boolean
ファイルの更新チェック
416 417 418 |
# File 'lib/confgit/repo.rb', line 416 def modfile?(from, to) ! file_exist?(to) || hash_object(from) != hash_object(to) end |
#read_config(file) ⇒ Object
設定の読込み
105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/confgit/repo.rb', line 105 def read_config(file) if File.exist?(file) config = JSON.parse(File.read(file)) config = default_config.merge(config) else config = default_config File.write(file, JSON.pretty_generate(config)+"\n") end return config end |
#relative_path(path) ⇒ Object
ルートからの相対パス
242 243 244 245 |
# File 'lib/confgit/repo.rb', line 242 def relative_path(path) root_path = Pathname.new(root) Pathname.new(path).relative_path_from(root_path).to_s end |
#repo_each ⇒ Object
リポジトリを繰返す
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 |
# File 'lib/confgit/repo.rb', line 367 def repo_each Dir.chdir(@repos_path) { |path| begin current = File.(File.readlink('current')) rescue end Dir.glob('*') { |file| next if /^current$/ =~ file if current && File.realpath(file) == current is_current = true current = nil else is_current = false end yield(file, is_current) } yield(File.readlink('current'), true) if current } end |
#rmrepo(repo, force = false) ⇒ Object
リポジトリの削除
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/confgit/repo.rb', line 82 def rmrepo(repo, force = false) Dir.chdir(@repos_path) { |path| begin if File.symlink?('current') && File.readlink('current') == repo abort "'#{repo}' is current repository!" unless force File.unlink('current') end FileUtils.remove_entry_secure(repo) valid_repo unless File.symlink?('current') rescue => e abort e.to_s end } end |
#root ⇒ Object
ルートのパスを取得する
219 220 221 222 223 224 225 226 227 228 |
# File 'lib/confgit/repo.rb', line 219 def root return @root if @root # 表示 out, err, status = git('config', '--path', '--local', ROOT_KEY, :capture => true) out.chomp! out = '/' if out.empty? @root = out end |
#root=(value) ⇒ Object
ルートのパスを設定する
231 232 233 234 235 236 237 238 239 |
# File 'lib/confgit/repo.rb', line 231 def root=(value) if value && ! value.empty? git('config', '--path', '--local', ROOT_KEY, value) else git('config', '--unset', '--local', ROOT_KEY) end @root = nil end |
#system_(command, *args) ⇒ Object
オプションに応じて外部呼出しを行う
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
# File 'lib/confgit/repo.rb', line 182 def system_(command, *args) = (args) if [:interactive] == false out, err, status = Open3.capture3(command, *args) $stdout.print out unless out.empty? $stderr.print err unless err.empty? status elsif [:capture] Open3.capture3(command, *args) else system(command, *args) end end |
#valid_repo ⇒ Object
カレントリポジトリがない場合の処理
40 41 42 43 44 45 46 47 48 49 |
# File 'lib/confgit/repo.rb', line 40 def valid_repo repo = nil repo_each { |file, is_current| repo = file break } chrepo(repo || hostname) end |
#yes?(prompt, y = true) ⇒ Boolean
確認プロンプトを表示する
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
# File 'lib/confgit/repo.rb', line 263 def yes?(prompt, y = true) yn = y ? 'Yn' : 'yN' print "#{prompt} [#{yn}]: " result = $stdin.gets.chomp return y if result.empty? if /^(y|yes)$/i =~ result y = true else y = false end y end |