diff --git a/lib/git.rb b/lib/git.rb
index 9dcc79ce..2dd91752 100644
--- a/lib/git.rb
+++ b/lib/git.rb
@@ -26,6 +26,8 @@
 require 'git/stashes'
 require 'git/stash'
 
+require 'git/stream'
+
 
 # Git/Ruby Library
 #
diff --git a/lib/git/author.rb b/lib/git/author.rb
index 545abb9b..e9476f79 100644
--- a/lib/git/author.rb
+++ b/lib/git/author.rb
@@ -2,7 +2,7 @@ module Git
   class Author
     attr_accessor :name, :email, :date
     
-    def initialize(author_string)
+    def initialize(author_string = nil)
       if m = /(.*?) <(.*?)> (\d+) (.*)/.match(author_string)
         @name = m[1]
         @email = m[2]
@@ -10,5 +10,12 @@ def initialize(author_string)
       end
     end
     
+    def self.from_parts(name, email, date = Time.now)
+      a = Author.new
+      a.name = name
+      a.email = email
+      a.date = date
+      return a
+    end
   end
 end
\ No newline at end of file
diff --git a/lib/git/base.rb b/lib/git/base.rb
index 342796e2..e7c3fd03 100644
--- a/lib/git/base.rb
+++ b/lib/git/base.rb
@@ -364,6 +364,14 @@ def archive(treeish, file = nil, opts = {})
       self.object(treeish).archive(file, opts)
     end
     
+    # use a stream to import into the repository
+    def import_stream 
+      stream = Git::Stream.new
+      yield stream
+                 
+      self.lib.fast_import(stream)
+    end
+    
     # repacks the repository
     def repack
       self.lib.repack
diff --git a/lib/git/lib.rb b/lib/git/lib.rb
index cffeffbd..437786aa 100644
--- a/lib/git/lib.rb
+++ b/lib/git/lib.rb
@@ -581,6 +581,17 @@ def checkout_index(opts = {})
       command('checkout-index', arr_opts)
     end
     
+    def fast_import(stream)
+      tmp = Tempfile.new("stream-file")
+      tmp.write(stream.to_s)
+      tmp.flush
+      begin
+        command('fast-import',"--date-format=rfc2822",true,"< #{escape tmp.path}")
+      ensure
+        tmp.close(true)
+      end
+    end
+    
     # creates an archive file
     #
     # options
diff --git a/lib/git/stream.rb b/lib/git/stream.rb
new file mode 100644
index 00000000..00f7e106
--- /dev/null
+++ b/lib/git/stream.rb
@@ -0,0 +1,189 @@
+module Git
+  
+  require 'time'
+  
+  #====================
+  # Stream Classes
+  # 
+  # These classes must support the following methods
+  # 
+  #  to_s - write the command to the git protocol stream
+  #====================
+  
+  class StreamCommit
+    
+    
+    attr_accessor :branch, :mark, :author, :committer, :message, :ancestor, :changes
+    
+    def initialize()
+      @branch = nil
+      @mark = StreamMark.new
+      @author = nil
+      @committer = nil
+      @message = nil
+      @ancestor = nil
+      @changes = []
+    end
+    
+    def modify_file(repos_path, data, mode = nil)
+      sfm = StreamFileModify.new(repos_path, data)
+      sfm.mode = mode unless mode == nil
+      changes << sfm
+    end
+    
+    def delete_file(repos_path)
+      changes << StreamFileDelete.new(repos_path)
+    end
+    
+    def rename_file(repos_path_from, repos_path_to)
+      changes << StreamFileRename.new(repos_path_form, repos_path_to)
+    end
+
+    def copy_file(repos_path_from, repos_path_to)
+      changes << StreamFileCopy.new(repos_path_form, repos_path_to)
+    end
+    
+    def delete_all_files()
+      changes << StreamFileDeleteAll.new
+    end
+    
+    def to_s 
+      out = "commit refs/heads/#{branch.to_s}\n"
+      out << "mark #{mark}\n" 
+      out << "author #{author.name} <#{author.email}> #{author.date.rfc2822}\n" unless author == nil
+      out << "committer #{committer.name} <#{committer.email}> #{committer.date.rfc2822}\n" unless committer == nil
+      if (message == nil)
+        out << StreamData.emit_empty_data
+      else
+        out << StreamData.emit_inline_data(message)
+      end
+      out << "from #{ancestor}\n" unless ancestor == nil
+      changes.each do |c|
+        out << c.to_s
+      end
+      out << "\n"
+    end
+  end
+  
+  class StreamMark
+
+    @@mark_counter = 1
+    
+    def initialize(id = (@@mark_counter += 1))
+      @id = id
+    end
+    
+    def to_s
+      ":#{@id}"
+    end
+  end
+  
+  # This class is used in the filemodify change on the commit stream
+  # At this time only the inline mode data stream is supported
+  class StreamFileModify
+    
+    attr_accessor :mode, :repository_path, :inline_data
+    
+    def initialize(repository_path, data)
+      @mode = 100644
+      @repository_path = repository_path
+      @inline_data = data
+    end
+    
+    def to_s
+      "M #{mode} inline #{repository_path}\n#{StreamData.emit_inline_data(inline_data)}"
+    end
+  end
+  
+  class StreamFileDelete
+    
+    attr_accessor :repository_path
+    
+    def initialize(repository_path)
+      @repository_path = repository_path
+    end
+    
+    def to_s
+      "D #{repository_path}\n"
+    end
+  end
+  
+  class StreamFileCopy
+    
+    attr_accessor :repository_path_from, :repository_path_to
+    
+    def initialize(repository_path_from, repository_path_to)
+      @repository_path_from = repository_path_from
+      @repository_path_to = repository_path_to
+    end
+    
+    def to_s
+      "C #{repository_path_from} #{repository_path_to}\n"
+    end
+    
+  end
+  
+  class StreamFileRename
+    
+    attr_accessor :repository_path_from, :repository_path_to
+    
+    def initialize(repository_path_from,repository_path_to)
+      @repository_path_from = repository_path_from
+      @repository_path_to = repository_path_to
+    end
+
+    def to_s
+      "R #{repository_path_from} #{repository_path_to}\n"
+    end
+
+  end
+  
+  class StreamFileDeleteAll
+    
+    def to_s
+      "deleteall\n"
+    end
+  end
+  
+  # Represents a stream of data bytes in the git stream
+  class StreamData
+    
+    def self.emit_inline_data(data_string)
+      "data #{data_string.length}\n#{data_string}\n" 
+    end
+    
+    def self.emit_empty_data
+      "data 0\n\n"
+    end
+  end
+  
+  #====================
+  # Stream Implementation
+  #====================
+
+  # This is an initial implementation of git fast-import/export streams
+  # It is not complete!
+  class Stream
+    
+    attr_reader :commands
+    
+    def initialize
+      @commands = []
+    end
+    
+    def commit
+      cmt = Git::StreamCommit.new
+      yield cmt
+      commands << cmt
+    end
+    
+    def to_s
+      s = ""
+      commands.each do |cmd|
+        s << cmd.to_s
+      end
+      return s
+    end
+  end
+  
+end
\ No newline at end of file
diff --git a/ruby-git.gemspec b/ruby-git.gemspec
index e68d909b..ffb4d611 100644
--- a/ruby-git.gemspec
+++ b/ruby-git.gemspec
@@ -1,11 +1,11 @@
 spec = Gem::Specification.new do |s|
     s.platform  =   Gem::Platform::RUBY
     s.name      =   "git"
-    s.version   =   "1.1.1"
+    s.version   =   "1.1.4"
     s.author    =   "Scott Chacon"
     s.email     =   "schacon@gmail.com"
     s.summary   =   "A package for using Git in Ruby code."
-    s.files     =   ["lib/git", "lib/git/author.rb", "lib/git/base.rb", "lib/git/branch.rb", "lib/git/branches.rb", "lib/git/diff.rb", "lib/git/index.rb", "lib/git/lib.rb", "lib/git/log.rb", "lib/git/object.rb", "lib/git/path.rb", "lib/git/remote.rb", "lib/git/repository.rb", "lib/git/stash.rb", "lib/git/stashes.rb", "lib/git/status.rb", "lib/git/working_directory.rb", "lib/git.rb"]
+    s.files     =   ["lib/git", "lib/git/author.rb", "lib/git/base.rb", "lib/git/branch.rb", "lib/git/branches.rb", "lib/git/diff.rb", "lib/git/index.rb", "lib/git/lib.rb", "lib/git/log.rb", "lib/git/object.rb", "lib/git/path.rb", "lib/git/remote.rb", "lib/git/repository.rb", "lib/git/stash.rb", "lib/git/stashes.rb", "lib/git/status.rb", "lib/git/stream.rb", "lib/git/working_directory.rb", "lib/git.rb"]
     s.require_path  =   "lib"
     s.autorequire   =   "git"
     s.test_files = Dir.glob('tests/*.rb')
diff --git a/tests/units/test_stream.rb b/tests/units/test_stream.rb
new file mode 100644
index 00000000..d58f178e
--- /dev/null
+++ b/tests/units/test_stream.rb
@@ -0,0 +1,172 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../test_helper'
+
+class TestStream < Test::Unit::TestCase
+  def setup
+    set_file_paths
+  end
+  
+  def test_minimal_commit_stream
+    t = Time.now
+    
+    commit = Git::StreamCommit.new
+    commit.branch = "master"
+    commit.committer = Git::Author.from_parts("Arthur Developer","arthur@example.com",t)
+    
+    str = "commit refs/heads/master\n"
+    str << "mark #{commit.mark}\n"
+    str << "committer Arthur Developer <arthur@example.com> #{t.rfc2822}\n"
+    str << "data 0\n"
+    str << "\n\n"
+    
+    assert_equal str, commit.to_s
+  end
+  
+  def test_single_file_add
+    t = Time.now
+    
+    commit = Git::StreamCommit.new
+    commit.branch = "master"
+    commit.author = Git::Author.from_parts("Arthur Developer","arthur@example.com",t)
+    commit.committer = Git::Author.from_parts("Jane Developer","jane@example.com",t+10)
+    commit.message = "Adding a single file."
+    commit.ancestor = "2e937ac5d5a6e95f4abb9f636273eaa6528f5dae"
+    commit.changes << Git::StreamFileModify.new("p/e/added-file.txt","This is the contents of the\nadded file.")
+    
+    str = "commit refs/heads/master\n"
+    str << "mark #{commit.mark}\n"
+    str << "author Arthur Developer <arthur@example.com> #{t.rfc2822}\n"
+    str << "committer Jane Developer <jane@example.com> #{(t+10).rfc2822}\n"
+    str << "data 21\n"
+    str << "Adding a single file.\n"
+    str << "from 2e937ac5d5a6e95f4abb9f636273eaa6528f5dae\n"
+    str << "M 100644 inline p/e/added-file.txt\n"
+    str << "data 39\n"
+    str << "This is the contents of the\nadded file.\n"
+    str << "\n"
+    
+    assert_equal str, commit.to_s
+  end
+  
+  def test_single_file_add
+    t = Time.now
+    
+    commit = Git::StreamCommit.new
+    commit.branch = "master"
+    commit.author = Git::Author.from_parts("Arthur Developer","arthur@example.com",t)
+    commit.committer = Git::Author.from_parts("Jane Developer","jane@example.com",t+10)
+    commit.message = "Adding a single file."
+    commit.ancestor = "2e937ac5d5a6e95f4abb9f636273eaa6528f5dae"
+    commit.changes << Git::StreamFileModify.new("p/e/added-file.txt","This is the contents of the\nadded file.")
+    
+    str = "commit refs/heads/master\n"
+    str << "mark #{commit.mark}\n"
+    str << "author Arthur Developer <arthur@example.com> #{t.rfc2822}\n"
+    str << "committer Jane Developer <jane@example.com> #{(t+10).rfc2822}\n"
+    str << "data 21\n"
+    str << "Adding a single file.\n"
+    str << "from 2e937ac5d5a6e95f4abb9f636273eaa6528f5dae\n"
+    str << "M 100644 inline p/e/added-file.txt\n"
+    str << "data 39\n"
+    str << "This is the contents of the\nadded file.\n"
+    str << "\n"
+    
+    assert_equal str, commit.to_s
+  end
+
+  def test_multiple_changes
+    t = Time.now
+    
+    commit = Git::StreamCommit.new
+    commit.branch = "master"
+    commit.author = Git::Author.from_parts("Arthur Developer","arthur@example.com",t)
+    commit.committer = Git::Author.from_parts("Jane Developer","jane@example.com",t+10)
+    commit.message = "Add/Delete/Rename/Copy/DeleteAll."
+    commit.ancestor = "2e937ac5d5a6e95f4abb9f636273eaa6528f5dae"
+    commit.changes << Git::StreamFileModify.new("p/e/added-file.txt","This is the contents of the\nadded file.")
+    commit.changes << Git::StreamFileDelete.new("del-file.txt")
+    commit.changes << Git::StreamFileRename.new("file1.txt","file2.txt")
+    commit.changes << Git::StreamFileCopy.new("file2.txt","file3.txt")
+    commit.changes << Git::StreamFileDeleteAll.new
+    
+    str = "commit refs/heads/master\n"
+    str << "mark #{commit.mark}\n"
+    str << "author Arthur Developer <arthur@example.com> #{t.rfc2822}\n"
+    str << "committer Jane Developer <jane@example.com> #{(t+10).rfc2822}\n"
+    str << "data 33\n"
+    str << "Add/Delete/Rename/Copy/DeleteAll.\n"
+    str << "from 2e937ac5d5a6e95f4abb9f636273eaa6528f5dae\n"
+    str << "M 100644 inline p/e/added-file.txt\n"
+    str << "data 39\n"
+    str << "This is the contents of the\nadded file.\n"
+    str << "D del-file.txt\n"
+    str << "R file1.txt file2.txt\n"
+    str << "C file2.txt file3.txt\n"
+    str << "deleteall\n"
+    str << "\n"
+    
+    assert_equal str, commit.to_s
+  end
+  
+  def test_temp_repo_baseline
+    create_temp_repo_with_branched_data do |g|
+      assert_equal 'blahblahblah3', g.cat_file('other_branch:test-file1')
+      assert_equal 'blahblahblah2', g.cat_file('other_branch:test-file2')
+      assert_equal 'blahblahblah1', g.cat_file('new_branch:test-file1')
+      assert_equal 'blahblahblah2', g.cat_file('new_branch:test-file2')      
+    end
+  end
+
+  def test_add_file_to_different_branch
+    create_temp_repo_with_branched_data do |g|
+
+      t = Time.new
+      
+      # make sure we are on 'new_branch'
+      g.branch('new_branch').checkout
+
+      # import a file into the 'other_branch'
+      g.import_stream do |stream|
+        stream.commit do |c|
+          c.branch = g.branch('other_branch')
+          c.committer = Git::Author.from_parts("Jane Developer","jane@example.com",t)
+          c.message = "Test adding a single file to a different branch, without switching."
+          c.modify_file("test-file1","testtesttesttest5")
+          c.ancestor = g.log.object('other_branch').first
+        end
+      end
+      
+      assert_equal 'blahblahblah2', g.cat_file('other_branch:test-file2')
+      assert_equal 'blahblahblah1', g.cat_file('new_branch:test-file1')
+      assert_equal 'blahblahblah2', g.cat_file('new_branch:test-file2')      
+      assert_equal "testtesttesttest5", g.cat_file('other_branch:test-file1')
+    end
+  end
+  
+  def create_temp_repo_with_branched_data
+    in_temp_dir do |path|
+      g = Git.clone(@wbare, 'branch_test')
+      Dir.chdir('branch_test') do
+
+        # create a basic repo with two branches and some content
+        g.branch('new_branch').checkout
+
+        new_file('test-file1', 'blahblahblah1')
+        new_file('test-file2', 'blahblahblah2')
+        
+        g.add(['test-file1', 'test-file2'])
+        g.commit("Initial commit.")
+        
+        g.branch('other_branch').checkout 
+        
+        new_file('test-file1', 'blahblahblah3')
+        g.add(['test-file1'])
+        g.commit("Second commit.")
+
+        yield g     
+      end
+    end
+  end
+  
+end
