Re: gitpacker progress report and a question
From: Felipe Contreras <hidden>
Date: 2016-06-15 22:55:25
Subsystem:
the rest · Maintainer:
Linus Torvalds
On Tue, Nov 27, 2012 at 12:43 AM, Eric S. Raymond [off-list ref] wrote:
----------------------------------- commit 1 directory foo-1.1 Release 1.1 of project foo . commit 2 directory foo-1.2 ..This is an example of a byte-stuffed line. Release 1.2 of project foo . commit 3 directory foo-1.3 Release 1.3 of project foo . ----------------------------------- The main objective of the logfile design is to make hand-crafting these easy.
Here's another version with YAML:
---
-
author: &me Felipe Contreras [off-list ref]
date: 2011-1-1
msg: one
- tag v0.1
-
author: *me
date: 2011-1-2
msg: extra
-
author: *me
date: 2011-1-3
msg: |
with
spaces
-
author: *me
date: 2011-1-4
msg: |
dot
.
-
author: *me
date: 2011-1-5
msg: remove
ref: remove
- checkout devel
-
author: *me
date: 2011-1-6
msg: dev
- checkout master
-
author: *me
date: 2011-1-7
msg: bump
- tag v0.2
- checkout test remove
---
I believe that log file is much more human readable. Yet I still fail
to see why would anybody want so much detail only to import tarballs.
diff --git a/contrib/weave/git-weave b/contrib/weave/git-weave
new file mode 100755
index 0000000..646aeaa
--- /dev/null
+++ b/contrib/weave/git-weave@@ -0,0 +1,234 @@ +#!/usr/bin/env ruby + +require 'optparse' +require 'find' +require 'fileutils' +require 'yaml' + +$last = nil +$branches = {} +$branch = 'master' +$refs = {} + +class Commit + + attr_reader :id, :parents, :author, :committer, :date, :msg, :ref + + @@num = 0 + + def initialize(args) + @id = @@num += 1 + @parents = [] + args.each do |key, value| + instance_variable_set("@#{key}", value) + end + if @author =~ /(.+ <.+>) (.+)/ + @author = $1 + end + if @committer =~ /(.+ <.+>) (.+)/ + @committer = $1 + @date = DateTime.strptime($2, '%s %z') + end + $refs[@ref] = @id if @ref + end + +end + +def export_commit(cmd, indir, out) + + c = Commit.new(cmd) + $last = c.id + + # commit + out.puts 'commit refs/heads/%s' % $branch + out.puts 'mark :%u' % c.id + if c.author and c.committer + out.puts 'author %s %s' % [c.author, c.date.strftime('%s %z')] + out.puts 'committer %s %s' % [c.committer, c.date.strftime('%s %z')] + else + out.puts 'committer %s %s' % [c.author, c.date.strftime('%s %z')] + end + out.puts 'data %u' % c.msg.bytesize + out.puts c.msg + + # parents + c.parents.each_with_index do |p, i| + ref = $refs[p] + if i == 0 + out.puts 'from :%u' % ref + else + out.puts 'merge :%u' % ref + end + end + + # files + out.puts 'deleteall' + FileUtils.cd(File.join(indir, c.id.to_s)) do + Find.find('.') do |e| + next unless File.file?(e) + content = File.read(e) + filename = e.split(File::SEPARATOR).slice(1..-1).join(File::SEPARATOR) + if File.symlink?(e) + mode = '120000' + content = File.readlink(e) + else + mode = File.executable?(e) ? '100755' : '100644' + end + out.puts 'M %s inline %s' % [mode, filename] + out.puts 'data %u' % content.bytesize + out.puts content + end + end + out.puts + +end + +def do_reset(out, ref, from) + out.puts "reset %s" % ref + out.puts "from :%u" % from + out.puts +end + +def export_reset(cmd, indir, out) + _, ref, from = cmd.split + do_reset(out, ref, from) +end + +def export_checkout(cmd, indir, out) + _, $branch, from = cmd.split + from = ':%u' % $last if not $branches[$branch] + do_reset(out, 'refs/heads/%s' % $branch, from) if from + $branches[$branch] = true +end + +def export_tag(cmd, indir, out) + _, tag = cmd.split + do_reset(out, 'refs/tags/%s' % tag, $last) +end + +def export(indir = '.', out = STDOUT) + + $branches['master'] = true + + YAML.load_file(File.join(indir, 'log')).each do |e| + case e + when Hash + export_commit(e, indir, out) + when /^checkout / + export_checkout(e, indir, out) + when /^tag / + export_tag(e, indir, out) + when /^reset / + export_reset(e, indir, out) + end + end + +end + +def import(outdir, out) + format = 'format:commit %H%nauthor %an <%ae> %ad%ncommitter %cn
<%ce> %cd%nparents %P%n%n%B'
+ cmd = ['git', 'log', '-z', '-s', '--date=raw', '--format=%s' %
format, '--reverse', '--all']
+ commits = {}
+
+ cmds = []
+
+ IO.popen(cmd).each_with_index("\0") do |data, i|
+ @msg = nil
+ @parents = []
+ data.chomp("\0").each_line do |l|
+ if not @msg
+ case l
+ when /^commit (.+)$/
+ @id = $1
+ when /^author (.+)$/
+ @author = $1
+ when /^committer (.+)$/
+ @committer = $1
+ when /^parents (.+)$/
+ @parents = $1.split(" ")
+ when /^$/
+ @msg = ""
+ end
+ else
+ @msg << l
+ end
+ end
+
+ num = i + 1
+ commits[@id] = num
+
+ cmds << {
+ :author => @author,
+ :committer => @committer,
+ :msg => @msg,
+ :ref => num,
+ :parents => @parents.map { |e| commits[e] },
+ }
+
+ wd = File.join(outdir, num.to_s)
+ FileUtils.mkdir_p(wd)
+ system('git', '--work-tree', wd, 'checkout', '-f', '-q', @id)
+ end
+
+ IO.popen(['git', 'show-ref', '--tags', '--heads']).each do |e|
+ id, ref = e.chomp.split
+ cmds << 'reset %s %s' % [ref, commits[id]]
+ end
+
+ out.write(cmds.to_yaml)
+end
+
+def git_pack(indir, outdir)
+ indir = File.absolute_path(indir)
+ system('git', 'init', '--quiet', outdir)
+ FileUtils.cd(outdir) do
+ IO.popen(['git', 'fast-import', '--quiet'], 'w') do |io|
+ export(indir, io)
+ end
+ system('git', 'reset', '--quiet', '--hard')
+ end
+end
+
+def git_unpack(indir, outdir)
+ begin
+ FileUtils.mkdir_p(outdir)
+ log = File.open(File.join(outdir, 'log'), 'w')
+ ENV['GIT_DIR'] = File.join(indir, '.git')
+ oldref = %x[git symbolic-ref HEAD]
+ import(outdir, log)
+ ensure
+ system('git', 'symbolic-ref', 'HEAD', oldref) if oldref
+ ENV.delete('GIT_DIR')
+ log.close if log
+ end
+end
+
+$indir = '.'
+
+begin
+ OptionParser.new do |opts|
+ opts.on('-x') do
+ $mode = 'unpack'
+ end
+ opts.on('-c') do
+ $mode = 'pack'
+ end
+ opts.on('-o', '--outdir DIR') do |v|
+ $outdir = v
+ end
+ opts.on('-i', '--indir DIR') do |v|
+ $indir = v
+ end
+ end.parse!
+rescue OptionParser::InvalidOption
+end
+
+$mode = File.exists?(File.join($indir, '.git')) ? 'unpack' : 'pack'
unless $mode
+$outdir = File.join($indir, $mode == 'pack' ? 'packed' : 'unpacked2')
unless $outdir
+
+case $mode
+when 'pack'
+ git_pack($indir, $outdir)
+when 'unpack'
+ git_unpack($indir, $outdir)
+end
--
Felipe Contreras