Skip to content

Fix Git::Lib#commit_data for GPG-signed commits #610

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Feb 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 29 additions & 26 deletions lib/git/lib.rb
Original file line number Diff line number Diff line change
Expand Up @@ -221,54 +221,57 @@ def object_size(sha)
def commit_data(sha)
sha = sha.to_s
cdata = command_lines('cat-file', 'commit', sha)
process_commit_data(cdata, sha, 0)
process_commit_data(cdata, sha)
end

def process_commit_data(data, sha = nil, indent = 4)
def process_commit_data(data, sha)
hsh = {
'sha' => sha,
'message' => '',
'parent' => []
'sha' => sha,
'parent' => []
}

loop do
key, *value = data.shift.split

break if key.nil?

each_cat_file_header(data) do |key, value|
if key == 'parent'
hsh['parent'] << value.join(' ')
hsh['parent'] << value
else
hsh[key] = value.join(' ')
hsh[key] = value
end
end

hsh['message'] = data.collect {|line| line[indent..-1]}.join("\n") + "\n"
hsh['message'] = data.join("\n") + "\n"

return hsh
end

CAT_FILE_HEADER_LINE = /\A(?<key>\w+) (?<value>.*)\z/

def each_cat_file_header(data)
while (match = CAT_FILE_HEADER_LINE.match(data.shift))
key = match[:key]
value_lines = [match[:value]]

while data.first.start_with?(' ')
value_lines << data.shift.lstrip
end

yield key, value_lines.join("\n")
end
end

def tag_data(name)
sha = sha.to_s
tdata = command_lines('cat-file', 'tag', name)
process_tag_data(tdata, name, 0)
process_tag_data(tdata, name)
end

def process_tag_data(data, name, indent=4)
hsh = {
'name' => name,
'message' => ''
}

loop do
key, *value = data.shift.split

break if key.nil?
def process_tag_data(data, name)
hsh = { 'name' => name }

hsh[key] = value.join(' ')
each_cat_file_header(data) do |key, value|
hsh[key] = value
end

hsh['message'] = data.collect {|line| line[indent..-1]}.join("\n") + "\n"
hsh['message'] = data.join("\n") + "\n"

return hsh
end
Expand Down
1 change: 1 addition & 0 deletions tests/files/signed_commits/dot_git/HEAD
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ref: refs/heads/main
7 changes: 7 additions & 0 deletions tests/files/signed_commits/dot_git/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
Binary file added tests/files/signed_commits/dot_git/index
Binary file not shown.
1 change: 1 addition & 0 deletions tests/files/signed_commits/dot_git/logs/HEAD
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0000000000000000000000000000000000000000 a043c677c93d9f2b285771a29742cc73715e41ea Simon Coffey <simon.coffey@futurelearn.com> 1673868871 +0000 commit (initial): Signed commit
1 change: 1 addition & 0 deletions tests/files/signed_commits/dot_git/logs/refs/heads/main
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0000000000000000000000000000000000000000 a043c677c93d9f2b285771a29742cc73715e41ea Simon Coffey <simon.coffey@futurelearn.com> 1673868871 +0000 commit (initial): Signed commit
Binary file not shown.
Binary file not shown.
Binary file not shown.
1 change: 1 addition & 0 deletions tests/files/signed_commits/dot_git/refs/heads/main
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a043c677c93d9f2b285771a29742cc73715e41ea
1 change: 1 addition & 0 deletions tests/files/signed_commits/notes.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
it is very important that changes to this file are verified
58 changes: 58 additions & 0 deletions tests/units/test_signed_commits.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/env ruby

require File.dirname(__FILE__) + '/../test_helper'
require "fileutils"

class TestSignedCommits < Test::Unit::TestCase
def git_working_dir
cwd = FileUtils.pwd
if File.directory?(File.join(cwd, 'files'))
test_dir = File.join(cwd, 'files')
elsif File.directory?(File.join(cwd, '..', 'files'))
test_dir = File.join(cwd, '..', 'files')
elsif File.directory?(File.join(cwd, 'tests', 'files'))
test_dir = File.join(cwd, 'tests', 'files')
end

create_temp_repo(File.expand_path(File.join(test_dir, 'signed_commits')))
end

def create_temp_repo(clone_path)
filename = 'git_test' + Time.now.to_i.to_s + rand(300).to_s.rjust(3, '0')
@tmp_path = File.join("/tmp/", filename)
FileUtils.mkdir_p(@tmp_path)
FileUtils.cp_r(clone_path, @tmp_path)
tmp_path = File.join(@tmp_path, File.basename(clone_path))
Dir.chdir(tmp_path) do
FileUtils.mv('dot_git', '.git')
end
tmp_path
end

def setup
@lib = Git.open(git_working_dir).lib
end

def test_commit_data
data = @lib.commit_data('a043c677c93d9f2b')

assert_equal('Simon Coffey <simon.coffey@futurelearn.com> 1673868871 +0000', data['author'])
assert_equal('92fd5b7c1aeb6a4e2602799f01608b3deebbad2d', data['tree'])
assert_equal(<<~EOS.chomp, data['gpgsig'])
-----BEGIN PGP SIGNATURE-----

iHUEABYKAB0WIQRmiEtd91BkbBpcgV2yCJ+VnJz/iQUCY8U2cgAKCRCyCJ+VnJz/
ibjyAP48dGdoFgWL2BjV3CnmebdVjEjTCQtF2QGUybJsyJhhcwEAwbzAAGt3YHfS
uuLNH9ki9Sqd+/CH+L8Q2dPM5F4l3gg=
=3ATn
-----END PGP SIGNATURE-----
EOS
assert_equal(<<~EOS, data['message'])
Signed commit

This will allow me to test commit data extraction for signed commits.
I'm making the message multiline to make sure that message extraction is
preserved.
EOS
end
end