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
パスを展開する.
-
#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
ファイルを管理対象に追加
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 |
# File 'lib/confgit/repo.rb', line 472 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) && ! File.symlink?(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
バックアップする
523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 |
# File 'lib/confgit/repo.rb', line 523 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
リポジトリの初期化
466 467 468 469 |
# File 'lib/confgit/repo.rb', line 466 def confgit_init() FileUtils.mkpath(@repo_path) git('init') end |
#confgit_list(options, *args) ⇒ Object
一覧表示する
586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 |
# File 'lib/confgit/repo.rb', line 586 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.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
リポジトリのパスを表示
609 610 611 612 |
# File 'lib/confgit/repo.rb', line 609 def confgit_path(, subdir = '.') path = File.realpath(File.(subdir, @repo_path)) print path, "\n" end |
#confgit_repo(options, repo = nil) ⇒ Object
カレントリポジトリの表示・変更
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 |
# File 'lib/confgit/repo.rb', line 434 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
リストアする
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 581 582 583 |
# File 'lib/confgit/repo.rb', line 555 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
ファイルを管理対象から削除
509 510 511 512 513 514 515 516 517 518 519 520 |
# File 'lib/confgit/repo.rb', line 509 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
ルートの表示・変更
452 453 454 455 456 457 458 459 460 461 462 463 |
# File 'lib/confgit/repo.rb', line 452 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
ディレクトリ内のファイルを繰返す
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 |
# File 'lib/confgit/repo.rb', line 300 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
パスを展開する
364 365 366 367 368 369 370 371 372 373 374 |
# File 'lib/confgit/repo.rb', line 364 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 |
#filecopy(from, to, exiting = false) ⇒ Object
ファイルのコピー(属性は維持する)
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 |
# File 'lib/confgit/repo.rb', line 274 def filecopy(from, to, exiting = false) begin to_dir = File.dirname(to) FileUtils.mkpath(to_dir) if File.exist?(to) && ! File.writable_real?(to) # 書込みできない場合は削除を試みる File.unlink(to) end FileUtils.copy_entry(from, to) stat = File.stat(from) unless File.symlink?(to) 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
オプションを取出す
377 378 379 380 381 382 383 384 385 |
# File 'lib/confgit/repo.rb', line 377 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 に管理されているファイルを繰返す
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 |
# File 'lib/confgit/repo.rb', line 319 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 |
# File 'lib/confgit/repo.rb', line 248 def hash_object(file) path = File.(file) open("| git hash-object \"#{path}\"") {|f| return f.gets.chomp } end |
#hostname ⇒ Object
ホスト名
35 36 37 |
# File 'lib/confgit/repo.rb', line 35 def hostname `hostname`.chomp end |
#mode2str(bits) ⇒ Object
ファイル属性を文字列にする
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 |
# File 'lib/confgit/repo.rb', line 393 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
ファイルの更新チェック
388 389 390 |
# File 'lib/confgit/repo.rb', line 388 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
リポジトリを繰返す
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 |
# File 'lib/confgit/repo.rb', line 339 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
確認プロンプトを表示する
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 |
# File 'lib/confgit/repo.rb', line 256 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 |