diff --git a/lib/git/lib.rb b/lib/git/lib.rb index b4d16152..35791c02 100644 --- a/lib/git/lib.rb +++ b/lib/git/lib.rb @@ -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(?\w+) (?.*)\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 diff --git a/tests/files/signed_commits/dot_git/HEAD b/tests/files/signed_commits/dot_git/HEAD new file mode 100644 index 00000000..b870d826 --- /dev/null +++ b/tests/files/signed_commits/dot_git/HEAD @@ -0,0 +1 @@ +ref: refs/heads/main diff --git a/tests/files/signed_commits/dot_git/config b/tests/files/signed_commits/dot_git/config new file mode 100644 index 00000000..6c9406b7 --- /dev/null +++ b/tests/files/signed_commits/dot_git/config @@ -0,0 +1,7 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true + ignorecase = true + precomposeunicode = true diff --git a/tests/files/signed_commits/dot_git/index b/tests/files/signed_commits/dot_git/index new file mode 100644 index 00000000..8ad08658 Binary files /dev/null and b/tests/files/signed_commits/dot_git/index differ diff --git a/tests/files/signed_commits/dot_git/logs/HEAD b/tests/files/signed_commits/dot_git/logs/HEAD new file mode 100644 index 00000000..130aef50 --- /dev/null +++ b/tests/files/signed_commits/dot_git/logs/HEAD @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 a043c677c93d9f2b285771a29742cc73715e41ea Simon Coffey 1673868871 +0000 commit (initial): Signed commit diff --git a/tests/files/signed_commits/dot_git/logs/refs/heads/main b/tests/files/signed_commits/dot_git/logs/refs/heads/main new file mode 100644 index 00000000..130aef50 --- /dev/null +++ b/tests/files/signed_commits/dot_git/logs/refs/heads/main @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 a043c677c93d9f2b285771a29742cc73715e41ea Simon Coffey 1673868871 +0000 commit (initial): Signed commit diff --git a/tests/files/signed_commits/dot_git/objects/4a/7f2717e7c6b029dcadf62aac0e5e811332f39d b/tests/files/signed_commits/dot_git/objects/4a/7f2717e7c6b029dcadf62aac0e5e811332f39d new file mode 100644 index 00000000..57463231 Binary files /dev/null and b/tests/files/signed_commits/dot_git/objects/4a/7f2717e7c6b029dcadf62aac0e5e811332f39d differ diff --git a/tests/files/signed_commits/dot_git/objects/92/fd5b7c1aeb6a4e2602799f01608b3deebbad2d b/tests/files/signed_commits/dot_git/objects/92/fd5b7c1aeb6a4e2602799f01608b3deebbad2d new file mode 100644 index 00000000..dedfeed8 Binary files /dev/null and b/tests/files/signed_commits/dot_git/objects/92/fd5b7c1aeb6a4e2602799f01608b3deebbad2d differ diff --git a/tests/files/signed_commits/dot_git/objects/a0/43c677c93d9f2b285771a29742cc73715e41ea b/tests/files/signed_commits/dot_git/objects/a0/43c677c93d9f2b285771a29742cc73715e41ea new file mode 100644 index 00000000..1463622a Binary files /dev/null and b/tests/files/signed_commits/dot_git/objects/a0/43c677c93d9f2b285771a29742cc73715e41ea differ diff --git a/tests/files/signed_commits/dot_git/refs/heads/main b/tests/files/signed_commits/dot_git/refs/heads/main new file mode 100644 index 00000000..6315edb3 --- /dev/null +++ b/tests/files/signed_commits/dot_git/refs/heads/main @@ -0,0 +1 @@ +a043c677c93d9f2b285771a29742cc73715e41ea diff --git a/tests/files/signed_commits/notes.txt b/tests/files/signed_commits/notes.txt new file mode 100644 index 00000000..4a7f2717 --- /dev/null +++ b/tests/files/signed_commits/notes.txt @@ -0,0 +1 @@ +it is very important that changes to this file are verified diff --git a/tests/units/test_signed_commits.rb b/tests/units/test_signed_commits.rb new file mode 100644 index 00000000..578b082c --- /dev/null +++ b/tests/units/test_signed_commits.rb @@ -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 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