Help us understand the problem. What is going on with this article?

Ruby で git リポジトリを操作する rugged を使ってみた

More than 1 year has passed since last update.

rugged は libgit2 を ruby にバインドした gem です。

gitの細かい操作が出来て便利。

自分が前に使っていた時のメモです。(1年前のなのでちょっと古いかも)

https://github.com/siman-man/rugged-examples

colorize は見栄えを良くするために使っています。

git clone

require 'rugged'

url = 'https://github.com/siman-man/rugged-examples'
local_path = File.expand_path('repos/rugged-examples', Dir.pwd)

repo = Rugged::Repository.clone_at(url, local_path)

private なリポジトリを取ってくる時は access_token を発行して、credentials 情報を追加してあげる。

require 'rugged'

url = 'https://github.com/siman-man/rugged-examples'
local_path = File.expand_path('repos/rugged-examples', Dir.pwd)

credentials = Rugged::Credentials::UserPassword.new(
  username: "access_token",
  password: ""
)

repo = Rugged::Repository.clone_at(url, local_path, credentials: credentials)

git reflog

require 'colorize'
require 'rugged'

repo = Rugged::Repository.new('.')

ref = repo.head

ref.log.reverse_each do |entry|
  sha = entry[:id_new]
  message = entry[:message]
  committer = entry[:committer]
  author = committer[:name]
  email = committer[:email]
  time = committer[:time]

  puts <<-GITLOG
#{"commit\t#{sha}".yellow}
Author:\t#{author} <#{email}>
Date:\t#{time}"
    #{message}"
  GITLOG
end

git branch

require 'colorize'
require 'rugged'

repo = Rugged::Repository.new('.')

repo.branches.each do |branch|
  name = branch.name
  remote_name = branch.remote_name

  next if name.include?('/')

  if branch.head?
    puts "* #{name.green}"
  else
    puts "  #{name}"
  end
end

git branch [name]

ブランチの作成

require 'rugged'

repo = Rugged::Repository.new('.')

repo.create_branch('test')

git log

require 'colorize'
require 'rugged'

repo = Rugged::Repository.new('.')

repo.walk(repo.last_commit).take(5).each do |entry|
  sha = entry.oid
  message = entry.message.chomp
  committer = entry.author
  author = committer[:name]
  email = committer[:email]
  time = committer[:time]

  puts "commit #{sha}".yellow
  puts "Author:\t#{author} <#{email}>"
  puts "Date:\t#{time}"
  puts ""
  puts "#{message}"
  puts ""
end

git ls-tree

require 'rugged'

repo = Rugged::Repository.new('.')
ref = repo.head
commit = ref.target
tree = commit.tree

tree.each do |obj|
  puts "%06o #{obj[:type]} #{obj[:oid]}\t#{obj[:name]}" % [obj[:filemode]]
end

git diff

require 'colorize'
require 'rugged'

def color_diff_message(message)
  message.split("\n").map {|line|
    case line
    when /^\+/
      line.green
    when /^\-/
      line.red
    when /^@@.*@@$/
      line.cyan
    else
      line
    end
  }.join("\n")
end

repo = Rugged::Repository.new('.')
diff = repo.diff_workdir(repo.last_commit)

diff.each_patch do |patch|
  puts color_diff_message(patch.to_s)
end

git status [WIP]

結講実装が面倒

require 'rugged'
require 'colorize'

# TODO: not yet implemented
repo = Rugged::Repository.new('.')


repo_status = Hash.new{|h,k| h[k] = []}

index_files = []
worktree_files = []

repo.status {|file, status_data|
  repo_status[status_data.first] << file
  status = status_data.first

  if %i(index_new index_modified index_deleted).include?(status_data.first)
    index_files << [file, status]
  elsif %i(worktree_new worktree_modified worktree_deleted).include?(status_data.first)
    worktree_files << [file, status]
  end
}

puts "On branch master"
puts "Your branch is up-to-date with 'origin/master'."
puts '  Changes to be committed:'
puts '    (use "git reset HEAD <file>..." to unstage)'
puts ''

index_files.each do |file, status|
  status = status.to_s.gsub('index_', '')
  puts "\t#{status} file:  #{file}".green
end

puts ''
puts 'Untracked files:'
puts '  (use "git add <file>..." to include in what will be committed)'
puts ''

worktree_files.each do |file, status|
  status = status.to_s.gsub('worktree_', '')
  puts "\t#{file}".red
end

puts ''

一番最初のコミットを取得

require 'rugged'

repo = Rugged::Repository.new('.')

first_commit = repo.walk(repo.last_commit).to_a.last

puts first_commit.oid
puts first_commit.message

一番最後のコミットを取得

Repository#last_commit で取得可能

require 'rugged'

repo = Rugged::Repository.new('.')

commit = repo.last_commit

puts commit.oid
puts commit.message

指定した範囲のコミットオブジェクトを取得

require 'rugged'

repo = Rugged::Repository.new('.')

def range_log(repo, from, to)
  logs = []
  commits = repo.walk(from)

  commits.each do |commit|
    break if to.oid == commit.oid
    logs << commit
  end

  if commits.size == logs.size
    raise 'Object not found' if commits.size == logs.size
  else
    logs
  end
end

commits = repo.walk(repo.last_commit).to_a

logs = range_log(repo, commits[0], commits[5])

p logs.size

最新コミットからある特定の時間までのコミットを取得

require 'rugged'

repo = Rugged::Repository.new('.')

def get_timerange_log(repo, time)
  repo.walk(repo.last_commit).take_while do |commit|
    commit.time > time
  end
end

# 1 hour's commits
hour_logs = get_timerange_log(repo, Time.now - 3600)

# 1 day's commits
day_logs = get_timerange_log(repo, Time.now - 3600 * 24)

# 1 week's commits
week_logs = get_timerange_log(repo, Time.now - 3600 * 24 * 7)

puts "#{hour_logs.size} commits 1 hour ago"
puts "#{day_logs.size} commits 1 day ago"
puts "#{week_logs.size} commits 1 week ago"

ファイルの中身を閲覧

git cat-file -p <object_id> に相当するやつ、事前に対象のobject_idが判明している場合は Rugged::Blob.lookup(repo, object_id) が良さそう

require 'rugged'

repo = Rugged::Repository.new('.')

revision = repo.last_commit.oid
path = 'Gemfile'

blob = repo.blob_at(revision, path)

puts blob.text

puts ""

puts blob.content

Blob#content でも同様の処理が可能、#text#content には引数が渡せて、#text の方では「最大n行まで」、#content では「最大n bytes」までの取得を行う操作が可能

ブランチ属性でフィルタリング

ローカルブランチとリモートブランチのフィルタリングが可能

require 'colorize'
require 'rugged'

repo = Rugged::Repository.new('.')

repo.branches.each(:local) do |local_branch|
  puts local_branch.name
end

repo.branches.each(:remote) do |remote_branch|
  puts remote_branch.name
end

puts "\nlocal branche list"

repo.branches.each_name(:local) do |name|
  puts "  #{name}"
end

puts "\nremote branch list"

repo.branches.each_name(:remote) do |name|
  puts "  #{name}"
end

ブランチを削除する

強制削除

require 'rugged'

repo = Rugged::Repository.new('.')

branch_name = 'sample_branch'

# git branch -D branch_name
repo.branches.delete(branch_name)

ブランチオブジェクトを取得する

BranchCollection#[] にブランチ名を渡して上げると取れる

require 'rugged'

repo = Rugged::Repository.new('.')

branch_name = 'master'
branches = repo.branches

branch = branches[branch_name]

puts branch.name

ブランチが存在するかどうかを調べる

BranchCollection#existBranchCollection#exists が使える

require 'rugged'

repo = Rugged::Repository.new('.')

branches = repo.branches

p branches.exist?('hoge') #=> false
p branches.exist?('master') #=> true

p branches.exists?('hoge') #=> false
p branches.exists?('master') #=> true

コミット情報の出力

git log コマンドに parent_id と Committer 情報を足したぐらい

require 'rugged'
require 'colorize'

repo = Rugged::Repository.new('.')

commit = repo.last_commit

author = commit.author
committer = commit.committer

puts <<-COMMIT_LOG
parents: #{commit.parent_ids.join(', ').blue}
commit #{commit.oid.yellow}
Author: #{author[:name]} <#{author[:email]}>
Committer: #{committer[:name]} <#{committer[:email]}>
Date: #{committer[:time]}
    #{commit.message}
COMMIT_LOG

最新コミットのファイル一覧表示

最後の表示部分は puts [root, entry[:name]].join でもOK

require 'rugged'
require 'pathname'

repo = Rugged::Repository.new('.')

repo.last_commit.tree.walk_blobs do |root, entry|
  puts Pathname.new(root).join(entry[:name])
end
Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away