🏹

Gitコマンド入門::Gitオブジェクト(RubyでBLOB作成)第五十三回

2021/03/19に公開

みなさんこんにちは! 今回も前回に引き続き、さらに深堀して、BLOBオブジェクトを、Ruby で作成してみますね。とはいっても、そんなに難しいことでもなく、Gitのドキュメントの通りに、実際に自分でも試してみるだけですけどね。(笑)

前回の記事はこちらから!

https://zenn.dev/shiozumi/articles/79cd009e307074

git本家本元の情報はこちらから!

https://git-scm.com/book/ja/v2

今日の課題も前回同様、ここを参考にしています!

https://git-scm.com/book/ja/v2/Gitの内側-Gitオブジェクト

10.2 Gitの内側 - Gitオブジェクト

Figure 150は、ページの下の方にあります! 今日はここを学習していきますね!

それでは早速いつもの~ git initから!

$ mkdir func0053 && cd $_
// フォルダー作成と移動を同時に実行、
// 第53回なので、func0053

$ git init
// 毎度のイニシャル!

Rubyをインストールして置きましょう!

yum install ruby

$ ruby -v
ruby 2.0.0p648 (2015-12-16) [x86_64-linux]

https://www.ruby-lang.org/ja/documentation/installation/

インストールが分からない方は、こちらのサイトからどうぞ!!

では、久しぶりの作図です!

ヘッダー構造について

  1. 先頭は、blob とか、tree, commit などの種別の文字列が入ります。
  2. その次は、コンテンツのデーターサイズですね。
  3. 最後は、¥0の区切り文字です。ヘッダーとデーターを区別するため。

データー構造について

  1. こちらは、テキスト、バイナリ、どちらでも当然OK!
  2. Zlib ライブラリを使って圧縮しています。「ヘッダーも含めて圧縮!」

ハッシュ値について

  1. ヘッダーのあとにデーターを連結させてから、SHA1でハッシュ値を作成
  2. 40文字のハッシュ値の最初の2文字がフォルダー名、残りの38文字がファイル名

例:bd9dbf5aae1a3862dd1526723246b20206e5fc37

カレント フォルダ名 ファイル名
.git/objects/ bd 9dbf5aae1a3862dd1526723246b20206e5fc37

では、Rubyでの処理は、こんな感じです!

  1. コンテンツを作成
  2. コンテンツのサイズを取得
  3. ヘッダーを作成
  4. ヘッダーとコンテンツを結合して、一つにまとめる。
  5. 4番でまとめたデーターから、SHA1ハッシュ値を作成
  6. ハッシュ値から、フォルダー名とファイル名を作成
  7. 4番でまとめたデーターを、Zlibで圧縮する。
  8. 7番で圧縮したデーターを、6番のファイル名で保存

本家のドキュメントサイトでは、irb コマンドで行っていますので、そちらでお試しください。

$ irb
>> content = "what is up, doc?"
=> "what is up, doc?"

上記のように、irbコマンドで始まるところです。そこを参照してお試しください!

それでは、処理の解説しま~す!

# 1. コンテンツを作成
content = "what is up, doc?" + "\n"

# 2. コンテンツのサイズを取得 #{content.length}
# 3. ヘッダーを作成 blob or tree , commit 
header = "blob #{content.length}\0"

# 4. ヘッダーとコンテンツを結合して、一つにまとめる。
store = header + content

# 5. 4番でまとめたデーター[store]から、SHA1ハッシュ値を作成
sha1 = Digest::SHA1.hexdigest(store)

# 6. ハッシュ値から、フォルダー名とファイル名を作成
path = '.git/objects/' + sha1[0,2] + '/' + sha1[2,38]

# 7. 4番でまとめたデーター[store]を、Zlibで圧縮する。
zlib_content = Zlib::Deflate.deflate(store)

# 8. 7番で圧縮したデーターzlib_content を、6番のファイル名[path]で保存
FileUtils.mkdir_p(File.dirname(path))
File.open(path, 'w') { 
    |f| f.write zlib_content
}

さあ~、いかがでしょうか? いたってシンプルなプログラムですね!
サンプルページでは、コンテンツテキストが、"what is up, doc?" となり、
ハッシュ値は、bd9dbf5aae1a3862dd1526723246b20206e5fc37 ですね。

実際に確認してみましょう!

$ find .git/objects/ -type f
.git/objects/bd/9dbf5aae1a3862dd1526723246b20206e5fc37
// ファイルが作成されました!

$ git cat-file -p bd9dbf5aae1a3862dd1526723246b20206e5fc37
what is up, doc?
// テキスト内容

$ git cat-file -s bd9dbf5aae1a3862dd1526723246b20206e5fc37
16
// ファイルサイズは、16バイト

$ git cat-file -t bd9dbf5aae1a3862dd1526723246b20206e5fc37
blob
// 属性は、blob 

私が作成したオリジナルですが、宜しければどうぞ!

ファイルから読み込むようにしてあります。ruby sample.rb <任意のファイル> 実行可能!

sample.rb
#! /bin/ruby

require 'zlib'
require 'digest/sha1'
require 'fileutils'

argvc = ARGV.size()

if argvc == 0 then
	print "ruby sample.rb <input-file> \n";
	exit
end	

$global = ''

f = open(ARGV[0])

while line = f.gets
  $global = $global + line
end
f.close

#
#	コンテンツテキスト
#
content = $global
# content = "what is up, doc?"

#
# 	BLOB のヘッダー
#
header = "blob #{content.length}\0"

#
#	ヘッダー+コンテンツ
#
store = header + content

print "Data: " + store + "\n"

#
#	ハッシュ値の取得
#
sha1 = Digest::SHA1.hexdigest(store)

print "SHA1: " + sha1 + "\n\n"

#
#	フォルダー2文字/ファイル名38文字
#
path = '.git/objects/' + sha1[0,2] + '/' + sha1[2,38]

print "PATH: " + path + "\n"

#
#	Zlib で圧縮!
#
zlib_content = Zlib::Deflate.deflate(store)


#
#	フォルダー作成
#
FileUtils.mkdir_p(File.dirname(path))

#
#	ファイル保存
#
File.open(path, 'w') { 
	|f| f.write zlib_content
}

#
#	git cat-file で確認!	
#
print "\n"

print "git cat-file -t => "
system('git cat-file -t ' + sha1 )

print "git cat-file -s => "
system('git cat-file -s ' + sha1 )

print "git cat-file -p => "
system('git cat-file -p ' + sha1 )
print "\n"

system('find .git/objects -type f')
print "\n"

exit

sample.rb の実行結果!

$ echo "Ruby BLOB" > README.md
// ファイルの中身は、Ruby BLOB

$ ./sample.rb README.md <!-- 必ず、ファイル名を指定してください!
// ruby sample.rb README.md でも可能!

Data: blob 10Ruby BLOB // <!-- blob + Size + コンテンツテキスト
SHA1: 4571358b3c1294d24cde428fd6babdac9810da18
PATH: .git/objects/45/71358b3c1294d24cde428fd6babdac9810da18 

// cat-file で確認した結果!
git cat-file -t => blob
git cat-file -s => 10
git cat-file -p => Ruby BLOB

// find .git/objects/ -type f でファイルの存在確認
.git/objects/45/71358b3c1294d24cde428fd6babdac9810da18
.git/objects/bd/9dbf5aae1a3862dd1526723246b20206e5fc37

今回のまとめ

みなさんも実際に試していただくと、より、gitの中心部に近づいた気がすると思います。私のような古いプログラマーにとっては、データとかソースコードが大好物です。正直なところ、時間の余裕があれば、gitのソースコードの中身を追いかけたいぐらいですけどね。ま、今時は、そんなことをしていたら、いくら時間があっても足りませんね。(^▽^;)

ただ、ファミコン、スーファミの時代は、OSも含めて、全てフルスクラッチ、しかもアセンブラで書いていましたから、どうしてもそういう発想になってしまう。あ、、、セガサターンも提供されたライブラリーが使えなかったから、あの時も、SH2のRISCアセンブラで、フルスクラッチでしたよ!(笑)・・・完全に余談でした。

本気のまとめ・・・

ここでのポイントは、実際にデータに触れることで、Gitへの恐怖感を取り除くことかな? 私もそうですけど、何か問題やトラブルが起こったとき、結構、気が動転しまうタイプです。そんなときに一番役立つのが、知識とか経験ですからね。

それでは、今回はここまで、お疲れ様でした!

https://zenn.dev/shiozumi/articles/1b64fe7a7446e0
https://twitter.com/esmile2013

Discussion