On Tue, Nov 27, 2012 at 12:43 AM, Eric S. Raymond <e...@thyrsus.com> 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 <felipe.contre...@gmail.com>
  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
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to