Module: GitDump::Repo::Git

Included in:
Rugged
Defined in:
lib/git_dump/repo/git.rb

Overview

Interface to git using system calls and pipes

Defined Under Namespace

Modules: ClassMethods Classes: InitException

Constant Summary collapse

TAG_ENTRIES_FIELDS =
%w[
  %(objecttype)%00
  %(objectname)%00
  %(refname)%00
  %(authordate:rfc2822)%(*authordate:rfc2822)%00
  %(committerdate:rfc2822)%(*committerdate:rfc2822)%00
  %(contents)%00
  %(*contents)%00
].freeze

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object



13
14
15
# File 'lib/git_dump/repo/git.rb', line 13

def self.included(base)
  base.extend(ClassMethods)
end

Instance Method Details

#blob_pipe(sha, &block) ⇒ Object

Return pipe with contents of blob identified by sha



120
121
122
# File 'lib/git_dump/repo/git.rb', line 120

def blob_pipe(sha, &block)
  git('cat-file', 'blob', sha).popen('rb', &block)
end

#blob_read(sha, io = nil) ⇒ Object

Return contents of blob identified by sha If io is specified, then content will be written to io



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/git_dump/repo/git.rb', line 126

def blob_read(sha, io = nil)
  @blob_read_pipe ||= git(*%w[cat-file --batch]).popen('rb+')
  @blob_read_pipe.puts(sha)
  size = @blob_read_pipe.gets.split(' ')[2].to_i
  result = if io
    while size > 0
      chunk = [size, 4096].min
      io.write(@blob_read_pipe.read(chunk))
      size -= chunk
    end
    io
  else
    @blob_read_pipe.read(size)
  end
  @blob_read_pipe.gets
  result
end

#blob_unpack(sha, path, mode) ⇒ Object

Write contents of blob to file at path and set its mode



145
146
147
148
149
150
151
152
153
154
# File 'lib/git_dump/repo/git.rb', line 145

def blob_unpack(sha, path, mode)
  Tempfile.open('git_dump', File.dirname(path)) do |temp|
    temp.binmode
    blob_read(sha, temp)
    temp.close

    File.chmod(mode, temp.path)
    File.rename(temp.path, path)
  end
end

#commit(tree_sha, options = {}) ⇒ Object

Create commit for tree_sha, return sha options:

:time => author date (by default now)
:message => commit message (by default empty)


73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/git_dump/repo/git.rb', line 73

def commit(tree_sha, options = {})
  env = git_env({
    :author_date => options[:time],
    :author_name => options[:name],
    :author_email => options[:email],
    :committer_name => options[:name],
    :committer_email => options[:email],
  })

  args = %w[commit-tree]
  args << '-F' << '-' if options[:message]
  args << tree_sha << {:env => env, :no_stdin => !options[:message]}

  git(*args).pipe(options[:message]).chomp
end

#data_sha(content) ⇒ Object

Add blob for content to repository, return sha



31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/git_dump/repo/git.rb', line 31

def data_sha(content)
  @data_sha_command ||= git(*%w[hash-object -w --no-filters --stdin])
  @data_sha_command.popen('rb+') do |f|
    if content.respond_to?(:read)
      f.write(content.read(4096)) until content.eof?
    else
      f.write(content)
    end
    f.close_write
    f.read.chomp
  end
end

#fetch(url, id, options = {}) ⇒ Object

Receive tag with name id from repo at url Use :progress => true to show progress



208
209
210
# File 'lib/git_dump/repo/git.rb', line 208

def fetch(url, id, options = {})
  transfer(:fetch, url, id, options)
end

#gc(options = {}) ⇒ Object

Run garbage collection Use :auto => true to run only if GC is required Use :aggressive => true to run GC more aggressively



221
222
223
224
225
226
# File 'lib/git_dump/repo/git.rb', line 221

def gc(options = {})
  args = %w[gc --quiet]
  args << '--auto' if options[:auto]
  args << '--aggressive' if options[:aggressive]
  git(*args).run
end

#path_sha(path) ⇒ Object

Add blob for content at path to repository, return sha



45
46
47
48
49
50
# File 'lib/git_dump/repo/git.rb', line 45

def path_sha(path)
  @path_sha_pipe ||=
    git(*%w[hash-object -w --no-filters --stdin-paths]).popen('r+')
  @path_sha_pipe.puts(path)
  @path_sha_pipe.gets.chomp
end

#push(url, id, options = {}) ⇒ Object

Send tag with name id to repo at url Use :progress => true to show progress



214
215
216
# File 'lib/git_dump/repo/git.rb', line 214

def push(url, id, options = {})
  transfer(:push, url, id, options)
end

#remove_tag(id) ⇒ Object

Remove tag with name id



200
201
202
203
204
# File 'lib/git_dump/repo/git.rb', line 200

def remove_tag(id)
  args = %W[tag --delete #{id}]
  args << {:no_stdout => true}
  git(*args).run
end

#size(sha) ⇒ Object

Return size of object identified by sha



113
114
115
116
117
# File 'lib/git_dump/repo/git.rb', line 113

def size(sha)
  @size_pipe ||= git(*%w[cat-file --batch-check]).popen('r+')
  @size_pipe.puts(sha)
  @size_pipe.gets.split(' ')[2].to_i
end

#tag(commit_sha, name_parts, options = {}) ⇒ Object

Create tag for commit_sha with name constructed from name_parts, return name. name_parts can be an array or a string separated by / options:

:time => tagger date
:message => tag message (by default empty)


94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/git_dump/repo/git.rb', line 94

def tag(commit_sha, name_parts, options = {})
  name = tag_name_from_parts(name_parts)

  env = git_env({
    :committer_date => options[:time],
    :committer_name => options[:name],
    :committer_email => options[:email],
  })

  args = %w[tag]
  args << '-F' << '-' << '--cleanup=verbatim' if options[:message]
  args << name << commit_sha << {:env => env}

  git(*args).pipe(options[:message])

  name
end

#tag_entriesObject

Return list of entries per tag ref Each entry is a hash with following keys:

:sha => tag or commit sha
:name => ref name


186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/git_dump/repo/git.rb', line 186

def tag_entries
  ref_fields(TAG_ENTRIES_FIELDS, 'refs/tags').map do |values|
    {
      :sha => values[1],
      :name => values[2].sub(%r{\Arefs/tags/}, ''),
      :author_time => Time.rfc2822(values[3]),
      :commit_time => Time.rfc2822(values[4]),
      :tag_message => values[0] == 'tag' ? values[5] : nil,
      :commit_message => values[0] == 'tag' ? values[6] : values[5],
    }
  end
end

#tree_entries(sha) ⇒ Object

Read tree at sha returning list of entries Each entry is a hash like one for treeify



158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/git_dump/repo/git.rb', line 158

def tree_entries(sha)
  git('ls-tree', sha).stripped_lines.map do |line|
    m = /^(\d{6}) (blob|tree) ([0-9a-f]{40})\t(.*)$/.match(line)
    fail "Unexpected: #{line}" unless m

    {
      :mode => m[1].to_i(8),
      :type => m[2].to_sym,
      :sha => m[3],
      :name => m[4],
    }
  end
end

#treeify(entries) ⇒ Object

Add blob for entries to repository, return sha Each entry is a hash with following keys:

:type => :blob or :tree
:name => name string
:sha  => sha of content
:mode => last three octets of mode


58
59
60
61
62
63
64
65
66
67
# File 'lib/git_dump/repo/git.rb', line 58

def treeify(entries)
  @treefier ||= git('mktree', '--batch').popen('r+')
  entries.map do |entry|
    values = normalize_entry(entry).values_at(:mode, :type, :sha, :name)
    line = format("%06o %s %s\t%s", *values)
    @treefier.puts line
  end
  @treefier.puts
  @treefier.gets.chomp
end