Full Code of alebedev/git-media for AI

master 09bde56ad0a0 cached
26 files
44.9 KB
12.3k tokens
112 symbols
1 requests
Download .txt
Repository: alebedev/git-media
Branch: master
Commit: 09bde56ad0a0
Files: 26
Total size: 44.9 KB

Directory structure:
gitextract_rveq7zvc/

├── .document
├── .gitignore
├── Gemfile
├── LICENSE
├── README.md
├── Rakefile
├── TODO
├── VERSION
├── bin/
│   └── git-media
├── git-media.gemspec
├── lib/
│   ├── git-media/
│   │   ├── clear.rb
│   │   ├── filter-branch.rb
│   │   ├── filter-clean.rb
│   │   ├── filter-smudge.rb
│   │   ├── status.rb
│   │   ├── sync.rb
│   │   ├── transport/
│   │   │   ├── atmos_client.rb
│   │   │   ├── box.rb
│   │   │   ├── local.rb
│   │   │   ├── s3.rb
│   │   │   ├── scp.rb
│   │   │   └── webdav.rb
│   │   └── transport.rb
│   └── git-media.rb
└── spec/
    ├── media_spec.rb
    └── spec_helper.rb

================================================
FILE CONTENTS
================================================

================================================
FILE: .document
================================================
README.rdoc
lib/**/*.rb
bin/*
features/**/*.feature
LICENSE


================================================
FILE: .gitignore
================================================
*.gem
*.rbc
/.config
/coverage/
/InstalledFiles
/pkg/
/spec/reports/
/spec/examples.txt
/test/tmp/
/test/version_tmp/
/tmp/

## Specific to RubyMotion:
.dat*
.repl_history
build/

## Documentation cache and generated files:
/.yardoc/
/_yardoc/
/doc/
/rdoc/

## Environment normalisation:
/.bundle/
/vendor/bundle
/lib/bundler/man/

# for a library or gem, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
Gemfile.lock
.ruby-version
.ruby-gemset

# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
.rvmrc


================================================
FILE: Gemfile
================================================
source 'https://rubygems.org'

gem 'trollop'
gem 's3'
gem 'ruby-atmos-pure'
gem 'right_aws'
gem 'net_dav', :git => 'https://github.com/devrandom/net_dav.git', :require => 'net/dav'
gem 'boxr'

gem 'netrc'
#gem 'curb', :require => false


================================================
FILE: LICENSE
================================================
Copyright (c) 2009 Scott Chacon

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


================================================
FILE: README.md
================================================
# git-media

GitMedia extension allows you to use Git with large media files
without storing the media in Git itself.

## Configuration

Setup the attributes filter settings.

	(once after install)
	$ git config filter.media.clean "git-media filter-clean"
	$ git config filter.media.smudge "git-media filter-smudge"

Setup the `.gitattributes` file to map extensions to the filter.

	(in repo - once)
	$ echo "*.mov filter=media -crlf" > .gitattributes

Staging files with those extensions will automatically copy them to the
media buffer area (.git/media) until you run 'git media sync' wherein they
are uploaded.  Checkouts that reference media you don't have yet will try to
be automatically downloaded, otherwise they are downloaded when you sync.

Next you need to configure git to tell it where you want to store the large files.
There are five options:

1. Storing remotely in Amazon's S3
2. Storing locally in a filesystem path
3. Storing remotely via SCP (should work with any SSH server)
4. Storing remotely in atmos
5. Storing remotely via WebDav

Here are the relevant sections that should go either in `~/.gitconfig` (for global settings)
or in `clone/.git/config` (for per-repo settings).

```ini
[git-media]
	transport = <scp|local|s3|atmos|webdav>
	autodownload = <true|false>

	# settings for scp transport
	scpuser = <user>
	scphost = <host>
	scppath = <path_on_remote_server>

	# settings for local transport
	localpath = <local_filesystem_path>

	# settings for s3 transport
	s3bucket = <name_of_bucket>
	s3key    = <s3 access key>
	s3secret = <s3 secret key>

	# settings for atmos transport
	endpoint = <atmos server>
	uid      = <atmos_uid>
	secret   = <atmos secret key>
	tag      = <atmos object tag>

	# settings for webdav transport
	webdavurl = <webdav root url>
	# user and password are taken from netrc if omitted
	webdavuser = <user for basic auth, optional>
	webdavpassword = <password for basic auth>

	webdavverifyserver = <Net::Dav.verify_server setting, true by default>
	webdavbinarytransfer = <Net::Dav.new :curl option value, false by default>

```


## Usage

	(in repo - repeatedly)
	$ (hack, stage, commit)
	$ git media sync

You can also check the status of your media files via

	$ git media status

Which will show you files that are waiting to be uploaded and how much data
that is. If you want to upload & delete the local cache of media files, run:

	$ git media clear

If you want to replace file in git-media with changed version (for example, video file has been edited),
you need to explicitly tell git that some media files has changed:

    $ git update-index --really-refresh


## Config Settings

If autodownload is set to true, required files will automatically be
downloaded when checking out or pulling. Default is false

	$ git config --global media.autodownload true


## Installing

    $ git clone git@github.com:alebedev/git-media.git
    $ cd git-media
    $ sudo gem install bundler
    $ bundle install
    $ gem build git-media.gemspec
    $ sudo gem install git-media-*.gem

## Notes for Windows

It is important to switch off git smart newline character support for media files.
Use `-crlf` switch in `.gitattributes` (for example `*.mov filter=media -crlf`) or config option `core.autocrlf = false`.

If installing on windows, you might run into a problem verifying certificates
for S3 or something. If that happens, see the [instructions in this Gist for how
to update your RubyGems to the proper certificates](https://gist.github.com/luislavena/f064211759ee0f806c88).

## Copyright

Copyright (c) 2009 Scott Chacon. See LICENSE for details.


================================================
FILE: Rakefile
================================================
require 'rubygems'
require 'rake'

begin
  require 'jeweler'
  Jeweler::Tasks.new do |gem|
    gem.name = "git-media"
    gem.summary = %Q{git-media}
    gem.email = "schacon@gmail.com"
    gem.homepage = "http://github.com/schacon/git-media"
    gem.authors = ["Scott Chacon"]
    # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
  end

rescue LoadError
  puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
end

require 'spec/rake/spectask'
Spec::Rake::SpecTask.new(:spec) do |spec|
  spec.libs << 'lib' << 'spec'
  spec.spec_files = FileList['spec/**/*_spec.rb']
end

Spec::Rake::SpecTask.new(:rcov) do |spec|
  spec.libs << 'lib' << 'spec'
  spec.pattern = 'spec/**/*_spec.rb'
  spec.rcov = true
end


task :default => :spec

require 'rake/rdoctask'
Rake::RDocTask.new do |rdoc|
  if File.exist?('VERSION.yml')
    config = YAML.load(File.read('VERSION.yml'))
    version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
  else
    version = ""
  end

  rdoc.rdoc_dir = 'rdoc'
  rdoc.title = "git-media #{version}"
  rdoc.rdoc_files.include('README*')
  rdoc.rdoc_files.include('lib/**/*.rb')
end



================================================
FILE: TODO
================================================
== Tools

	* tool to clean large files out of existing repo (filter-branch?)
	  - can also just re-do the last commit with a new filter

	* git media add (file) - adds it to the .gitattributes file

== Transports

	* Local
	* Amazon S3
	* SCP
	* SFTP
	* FTP


================================================
FILE: VERSION
================================================
0.1.4


================================================
FILE: bin/git-media
================================================
#!/usr/bin/env ruby
require 'optparse'

$:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
require 'git-media'

GitMedia::Application.run!


================================================
FILE: git-media.gemspec
================================================
# Generated by jeweler
# DO NOT EDIT THIS FILE DIRECTLY
# Instead, edit Jeweler::Tasks in rakefile, and run 'rake gemspec'
# -*- encoding: utf-8 -*-

Gem::Specification.new do |s|
  s.name = "git-media"
  s.version = "0.1.5"

  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
  s.authors = ["Scott Chacon", "Alexander Lebedev"]
  s.date = "2014-10-20"
  s.email = "alexander.lebedev@gmail.com"
  s.executables = ["git-media"]
  s.extra_rdoc_files = [
    "LICENSE",
    "README.md",
    "TODO"
  ]
  s.files = [
    ".document",
    "Gemfile",
    "Gemfile.lock",
    "LICENSE",
    "README.md",
    "Rakefile",
    "TODO",
    "VERSION",
    "bin/git-media",
    "git-media.gemspec",
    "lib/git-media.rb",
    "lib/git-media/clear.rb",
    "lib/git-media/filter-clean.rb",
    "lib/git-media/filter-smudge.rb",
    "lib/git-media/filter-branch.rb",
    "lib/git-media/status.rb",
    "lib/git-media/sync.rb",
    "lib/git-media/transport.rb",
    "lib/git-media/transport/atmos_client.rb",
    "lib/git-media/transport/box.rb",
    "lib/git-media/transport/local.rb",
    "lib/git-media/transport/s3.rb",
    "lib/git-media/transport/scp.rb",
    "lib/git-media/transport/webdav.rb",
    "spec/media_spec.rb",
    "spec/spec_helper.rb"
  ]
  s.homepage = "http://github.com/alebedev/git-media"
  s.require_paths = ["lib"]
  s.rubygems_version = "1.8.28"
  s.summary = "git-media"

  if s.respond_to? :specification_version then
    s.specification_version = 3

    if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
      s.add_runtime_dependency(%q<trollop>, [">= 0"])
    else
      s.add_dependency(%q<trollop>, [">= 0"])
    end
  else
    s.add_dependency(%q<trollop>, [">= 0"])
  end
end



================================================
FILE: lib/git-media/clear.rb
================================================
require 'git-media/status'

module GitMedia
  module Clear

    def self.run!
      @push = GitMedia.get_push_transport
      self.clear_local_cache
    end
    
    def self.clear_local_cache
      # find files in media buffer and delete all pushed files
      all_cache = Dir.chdir(GitMedia.get_media_buffer) { Dir.glob('*') }
      unpushed_files = @push.get_unpushed(all_cache)
      pushed_files = all_cache - unpushed_files
      pushed_files.each do |sha|
        puts "Removing " + sha[0, 8]
        File.unlink(File.join(GitMedia.get_media_buffer, sha))
      end
    end
    
  end
end

================================================
FILE: lib/git-media/filter-branch.rb
================================================
require 'set'
require 'git-media/filter-clean'
require 'fileutils'

include Process

module GitMedia
  module FilterBranch

    def self.get_temp_buffer
      @@git_dir ||= `git rev-parse --git-dir`.chomp
      temp_buffer = File.join(@@git_dir, 'media/filter-branch')
      FileUtils.mkdir_p(temp_buffer) if !File.exist?(temp_buffer)
      return temp_buffer
    end

    def self.clean!
      tmp_buffer = get_temp_buffer
      FileUtils.rm_r (tmp_buffer)
      FileUtils.rmdir (tmp_buffer)
    end

    def self.run!
      # Rewriting of history
      # Inspired by how git-fat does it

      inputfiles = ARGF.read.split("\n").map { |s| s.downcase }.to_set
      all_files = `git ls-files -s`.split("\n")
      filecount = all_files.length.to_s

      # determine and initialize our media buffer directory
      media_buffer = GitMedia.get_media_buffer

      tmp_buffer = get_temp_buffer

      STDOUT.write ("  ")

      index = 0
      prevLength = 0


      fileLists = [[],[],[],[]]
      all_files.each_with_index do |f, i|
        fileLists[i % fileLists.length].push (f)
      end

      update_index_reader, update_index_writer = IO.pipe
      update_index_pid = spawn("git update-index --index-info", :in=>update_index_reader)
      update_index_reader.close
      mutex = Mutex.new
          
      threads = []
      fileLists.each_with_index do |files, thread_index|

        fls = files
        thread = Thread.new do

          fls.each do |line|
            index += 1

            head, filepath = line.split("\t")
            filepath.strip!

            if not inputfiles.include? (filepath.downcase)
              next
            end

            mode, blob, stagenumber = head.split()

            # Skip symlinks
            if mode == "120000"
              next
            end

            # 1   Find cached git-hash of the media stub
            # 1.2 If not found, calculate it
            # 1.3 store object in media buffer
            # 1.4 save the hash in the cache
            # 2   Replace object with git-hash of the stub
            
            #1
            hash_file_path = File.join(tmp_buffer, blob)

            hash_of_stub = nil
            if File.exists?(hash_file_path)

              File.open(hash_file_path, "rb") do |f|
                hash_of_stub = f.read.strip()
              end
            else

              # Only show progress output for thread 0 because otherwise the thread
              # output might get messed up by multiple threads writing at the same time
              if thread_index == 0
                # Erase previous output text
                # \b is backspace
                prevLength.times {
                  STDOUT.write("\b")
                  STDOUT.write(" ")
                  STDOUT.write("\b")
                }

                line = "Filtering " + index.to_s + " of " + filecount + " : " + filepath
                prevLength = line.length
                STDOUT.write (line)
              end

              # pipes roughly equivalent to
              # cat-file | clean | hash | update-index
              # 1.2, 1.3

              gitcat_reader, gitcat_writer= IO.pipe
              gitcat_pid = spawn("git cat-file blob " + blob, :out=>gitcat_writer, :close_others=>true)

              # We are not using it, so close it
              gitcat_writer.close

              githash_reader, githash_writer= IO.pipe
              githash_output_reader, githash_output_writer= IO.pipe
              githash_pid = spawn("git hash-object -w --stdin", :in=>githash_reader, :out=>githash_output_writer)
              githash_output_writer.close
              githash_reader.close

              GitMedia::FilterClean.run!(gitcat_reader, githash_writer, false)
              
              gitcat_reader.close
              githash_writer.close

              hash_of_stub = githash_output_reader.read().strip()

              # 1.4
              cache = File.new(hash_file_path, File::CREAT|File::RDWR|File::BINARY)
              cache.write(hash_of_stub)
              cache.close

              wait (githash_pid)
              wait (gitcat_pid)
            end

            # 2
            update = mode + " " + hash_of_stub + " " + stagenumber + "\t" + filepath + "\n"

            # Synchronize with a mutex to avoid multiple
            # threads writing to the pipe at the same time
            mutex.synchronize do
              update_index_writer.write(update)
            end
          end
        end

        threads.push(thread)
      end

      threads.each do |thread|
        thread.join
      end

      update_index_writer.close()
      wait(update_index_pid)
    end
  end
end

================================================
FILE: lib/git-media/filter-clean.rb
================================================
require 'digest/sha1'
require 'fileutils'
require 'tempfile'

module GitMedia
  module FilterClean

    def self.run!(input=STDIN, output=STDOUT, info_output=true)
      
      input.binmode
      # Read first 42 bytes
      # If the file is only 41 bytes long (as in the case of a stub)
      # it will only return a string with a length of 41
      data = input.read(42)
      output.binmode

      if data != nil && data.length == 41 && data.match(/^[0-9a-fA-F]+\n$/)
        
        # Exactly 41 bytes long and matches the hex string regex
        # This is most likely a stub
        # TODO: Maybe add some additional marker in the files like
        # "[hex string]:git-media"
        # to really be able to say that a file is a stub

        output.write (data)

        if info_output
          STDERR.puts("Skipping unexpanded stub : " + data[0, 8])
        end

      else

        # determine and initialize our media buffer directory
        media_buffer = GitMedia.get_media_buffer

        hashfunc = Digest::SHA1.new
        start = Time.now

        # read in buffered chunks of the data
        #  calculating the SHA and copying to a tempfile
        tempfile = Tempfile.new('media', :binmode => true)

        # Write the first 42 bytes
        if data != nil
          hashfunc.update(data)
          tempfile.write(data)
        end

        while data = input.read(4096)
          hashfunc.update(data)
          tempfile.write(data)
        end
        tempfile.close

        # calculate and print the SHA of the data
        output.print hx = hashfunc.hexdigest 
        output.write("\n")

        # move the tempfile to our media buffer area
        media_file = File.join(media_buffer, hx)
        FileUtils.mv(tempfile.path, media_file)

        elapsed = Time.now - start

        if info_output
          STDERR.puts('Saving media : ' + hx + ' : ' + elapsed.to_s)
        end
      end
    end

  end
end

================================================
FILE: lib/git-media/filter-smudge.rb
================================================
module GitMedia
  module FilterSmudge

    def self.print_stream(stream)
      # create a binary stream to write to stdout
      # this avoids messing up line endings on windows
      outstream = IO.try_convert(STDOUT)
      outstream.binmode

      while data = stream.read(4096) do
        print data
      end
    end

    def self.run!
      media_buffer = GitMedia.get_media_buffer
      
      # read checksum size
      STDIN.binmode
      STDOUT.binmode
      orig = STDIN.readline(64)
      sha = orig.strip # read no more than 64 bytes
      if STDIN.eof? && sha.length == 40 && sha.match(/^[0-9a-fA-F]+$/) != nil
        # this is a media file
        media_file = File.join(media_buffer, sha.chomp)
        if File.exists?(media_file)
          STDERR.puts('Recovering media : ' + sha)
          File.open(media_file, 'rb') do |f|
            print_stream(f)
          end
        else
          # Read key from config
          auto_download = `git config git-media.autodownload`.chomp.downcase == "true"

          if auto_download

            pull = GitMedia.get_pull_transport

            cache_file = GitMedia.media_path(sha)
            if !File.exist?(cache_file)
              STDERR.puts ("Downloading : " + sha[0,8])
              # Download the file from backend storage
              # We have no idea what the final file will be (therefore nil)
              pull.pull(nil, sha)
            end

            STDERR.puts ("Expanding : " + sha[0,8])

            if File.exist?(cache_file)
              File.open(media_file, 'rb') do |f|
                print_stream(f)
              end
            else
              STDERR.puts ("Could not get media, saving placeholder : " + sha)
              puts orig
            end

          else
            STDERR.puts('Media missing, saving placeholder : ' + sha)
            # Print orig and not sha to preserve eventual newlines at end of file
            # To avoid git thinking the file has changed
            puts orig
          end
        end
      else
        # if it is not a 40 character long hash, just output
        STDERR.puts('Unknown git-media file format')
        print orig
        print_stream(STDIN)
      end
    end

  end
end


================================================
FILE: lib/git-media/status.rb
================================================
require 'pp'
Encoding.default_external = Encoding::UTF_8

module GitMedia
  module Status

    def self.run!(opts)
      @push = GitMedia.get_push_transport
      r = self.find_references
      self.print_references(r, opts[:short])
      r = self.local_cache_status
      self.print_cache_status(r, opts[:short])
    end

    # find tree entries that are likely media references
    def self.find_references
      references = {:to_expand => [], :expanded => [], :deleted => []}
      files = `git ls-tree -l -r HEAD | tr "\\000" \\\\n`.split("\n")
      files = files.map { |f| s = f.split("\t"); [s[0].split(' ').last, s[1]] }
      files = files.select { |f| f[0] == '41' } # it's the right size
      files.each do |tree_size, fname|
        if File.exists?(fname)
          size = File.size(fname)

          # Windows newlines can offset file size by 1
          if size == tree_size.to_i or size == tree_size.to_i + 1
            # TODO: read in the data and verify that it's a sha + newline
            fname = fname.tr("\\","") #remove backslash
            sha = File.read(fname).strip
            if sha.length == 40 && sha =~ /^[0-9a-f]+$/
              references[:to_expand] << [fname, sha]
            end
          else
            references[:expanded] << fname
          end
        else
          # file was deleted
          references[:deleted] << fname
        end
      end
      references
    end

    def self.print_references(refs, short=false)

      if refs[:to_expand].size > 0
        puts "== Unexpanded Media =="
        if short
          puts "Count: " + refs[:to_expand].size.to_s
        else
          refs[:to_expand].each do |file, sha|
            puts "   " + sha[0, 8] + " " + file
          end
          puts
        end
      end
      if refs[:expanded].size > 0
        puts "== Expanded Media =="
        if short
          puts "Count: " + refs[:expanded].size.to_s
        else
          refs[:expanded].each do |file|
            size = File.size(file)
            puts "   " + "(#{self.to_human(size)})".ljust(8) + " #{file}"
          end
          puts
        end
      end
      if refs[:deleted].size > 0
        puts "== Deleted Media =="
        if short
          puts "Count: " + refs[:deleted].size.to_s
        else
          refs[:deleted].each do |file|
            puts "           " + " #{file}"
          end
          puts
        end
      end
    end

    def self.print_cache_status(refs, short)
      if refs[:unpushed].size > 0
        puts "== Unpushed Media =="
        if short
          puts "Count: " + refs[:unpushed].size.to_s
        else
          refs[:unpushed].each do |sha|
            cache_file = GitMedia.media_path(sha)
            size = File.size(cache_file)
            puts "   " + "(#{self.to_human(size)})".ljust(8) + ' ' + sha[0, 8]
          end
          puts
        end
      end
      if refs[:pushed].size > 0
        puts "== Already Pushed Media =="
        if short
          puts "Count: " + refs[:pushed].size.to_s
        else
          refs[:pushed].each do |sha|
            cache_file = GitMedia.media_path(sha)
            size = File.size(cache_file)
            puts "   " + "(#{self.to_human(size)})".ljust(8) + ' ' + sha[0, 8]
          end
          puts
        end
      end
    end

    def self.local_cache_status
      # find files in media buffer and upload them
      references = {:unpushed => [], :pushed => []}
      all_cache = Dir.chdir(GitMedia.get_media_buffer) { Dir.glob('*') }
      unpushed_files = @push.get_unpushed(all_cache) || []
      references[:unpushed] = unpushed_files
      references[:pushed] = all_cache - unpushed_files rescue []
      references
    end


    def self.to_human(size)
      if size < 1024
        return size.to_s + 'b'
      elsif size < 1048576
        return (size / 1024).to_s + 'k'
      else
        return (size / 1048576).to_s + 'm'
      end
    end

  end
end


================================================
FILE: lib/git-media/sync.rb
================================================
# find files that are placeholders (41 char) and download them
# upload files in media buffer that are not in offsite bin
require 'git-media/status'

module GitMedia
  module Sync

    def self.run!
      @push = GitMedia.get_push_transport
      @pull = GitMedia.get_pull_transport

      self.expand_references
      self.update_index
      self.upload_local_cache
    end

    def self.expand_references
      status = GitMedia::Status.find_references
      status[:to_expand].each_with_index do |tuple, index|
        file = tuple[0]
        sha = tuple[1]
        cache_file = GitMedia.media_path(sha)
        if !File.exist?(cache_file)
          puts "Downloading " + sha[0,8] + " : " + file
          @pull.pull(file, sha)
        end

        puts "Expanding " + (index+1).to_s + " of " + status[:to_expand].length.to_s + " : " + sha[0,8] + " : " + file

        if File.exist?(cache_file)
          FileUtils.cp(cache_file, file)
        else
          puts 'Could not get media from storage'
        end
      end
    end

    def self.update_index
      refs = GitMedia::Status.find_references

      # Split references up into lists of at most 500
      # because most OSs have limits on the size of the argument list
      # TODO: Could probably use the --stdin flag on git update-index to be
      # able to update it in a single call
      refLists = refs[:expanded].each_slice(500).to_a

      refLists.each {
        |refList|

        refList = refList.map { |v| "\"" + v + "\""}

        `git update-index --assume-unchanged -- #{refList.join(' ')}`
      }
      
      puts "Updated git index"
    end

    def self.upload_local_cache
      # find files in media buffer and upload them
      all_cache = Dir.chdir(GitMedia.get_media_buffer) { Dir.glob('*') }
      unpushed_files = @push.get_unpushed(all_cache)
      unpushed_files.each_with_index do |sha, index|
        puts "Uploading " + sha[0, 8] + " " + (index+1).to_s + " of " + unpushed_files.length.to_s
        @push.push(sha)
      end
      # TODO: if --clean, remove them
    end

  end
end

================================================
FILE: lib/git-media/transport/atmos_client.rb
================================================
require 'git-media/transport'
require 'ruby-atmos-pure'
require 'atmos'


# git-media.transport atmos
# git-media.endpoint
# git-media.uid
# git-media.secret
# git-media.tag (optional)

module GitMedia
  module Transport
    class AtmosClient < Base

      def initialize(endpoint, uid, secret, tag)
        atmos_options = {
          :url => endpoint,
          :uid => uid,
          :secret => secret
        }
        @tag = tag
        @atmos_client = Atmos::Store.new(atmos_options)
      end

      def read?
        reachable?
      end

      def get_file(sha, to_file)
        dst_file = File.new(to_file, File::CREAT|File::RDWR|File::BINARY)
        @atmos_client.get(:namespace => sha).data_as_stream do |chunck|
          dst_file.write(chunck)
        end
      end

      def write
        reachable?
      end

      def put_file(sha, from_file)
        src_file = File.open(from_file,"rb")
        obj_conf = {:data => src_file, :length => File.size(from_file), :namespace => sha}
        obj_conf[:listable_metadata] = {@tag => true} if @tag
        @atmos_client.create(obj_conf)
      end

      def get_unpushed(files)
        unpushed = []
        files.each do |file|
          begin
            @atmos_client.get(:namespace => file)
          rescue Atmos::Exceptions::AtmosException
            unpushed << file
          end
        end
        unpushed
      end

      private
      # dummy function to test connectivity to atmos
      def reachable?
        @atmos_client.server_version
        true
      rescue
        false
      end

    end
  end
end




================================================
FILE: lib/git-media/transport/box.rb
================================================
require 'git-media/transport'
require 'boxr'
require 'shellwords'

# git-media.transport box
# git-media.boxclientid
# git-media.boxclientsecret
# git-media.boxredirecturi
# git-media.boxfolderid
# git-media.boxaccesstoken
# git-media.boxrefreshtoken

module GitMedia
  module Transport
    class Box < Base

      def initialize(client_id, client_secret, redirect_uri, folder_id, access_token, refresh_token)
        if access_token == "" || refresh_token == ""
          uri = Boxr::oauth_url(redirect_uri, box_client_id: client_id)
          print "(1) Paste following URL to your browser, and get your access code:\n\n#{uri}\n\n(2) Enter your access code: "
          code = STDIN.gets.chomp
          token =  Boxr::get_tokens(code, box_client_id: client_id, box_client_secret: client_secret)

          access_token = token.access_token
          refresh_token = token.refresh_token

          `git config git-media.boxaccesstoken #{access_token.shellescape}`
          `git config git-media.boxrefreshtoken #{refresh_token.shellescape}`
        end

        token_refresh_callback = lambda {|at, rt, id|
          `git config git-media.boxaccesstoken #{at.shellescape}`
          `git config git-media.boxrefreshtoken #{rt.shellescape}`
        }
        @box = Boxr::Client.new(access_token, refresh_token: refresh_token, box_client_id: client_id, box_client_secret: client_secret, &token_refresh_callback)
        @folder = @box.folder_from_id(folder_id)
      end

      def read?
        true
      end

      def get_file(sha, to_file)
        files = get_files(true)
        if files.has_key?(sha) == false
          files = get_files()
        end

        file_id = files[sha]
        if file_id == nil
          STDERR.puts("Storage backend (box) did not contain file : "+sha+", have you run 'git media sync' from all repos?")
          return false
        end

        file = @box.file_from_id(file_id)
        content = @box.download_file(file)
        File::open(to_file, "wb") do |f|
          f.write(content)
        end
      end

      def write?
        true
      end

      def put_file(sha, from_file)
        @box.upload_file(from_file, @folder)
      end

      def get_unpushed(files)
        remote_files = get_files()

        files.select do |f|
          !remote_files.has_key?(f)
        end
      end

      def get_files(use_cache = false)
        media_buffer = GitMedia.get_media_buffer
        cache_file = File.join(media_buffer, "cache")
        files = {}

        if use_cache
          File::exists?(cache_file) && File::open(cache_file) do |f|
            f.each do |s|
              r = s.strip.split(",")
              files[r[0]] = r[1]
            end
          end

          return files if files.length > 0
        end

        offset = 0
        limit = 100
        while (items = @box.folder_items(@folder, fields: [:id, :name], offset: offset, limit: limit)).length > 0
          items.each do |f|
            files[f[:name]] = f[:id]
          end
          offset = offset + limit
        end

        # cache update
        f = File::open(cache_file, "w")
        files.each do |name, id|
          f.puts "#{name},#{id}"
        end
        f.close

        return files
      end
    end
  end
end


================================================
FILE: lib/git-media/transport/local.rb
================================================
require 'git-media/transport'

# move large media to local bin

# git-media.transport local
# git-media.localpath /opt/media

module GitMedia
  module Transport
    class Local < Base

      def initialize(path)
        @path = path
      end

      def read?
        File.exist?(@path)
      end

      def get_file(sha, to_file)
        from_file = File.join(@path, sha)
        if File.exists?(from_file)
          FileUtils.cp(from_file, to_file)
          return true
        end
        return false
      end

      def write?
        File.exist?(@path)
      end

      def put_file(sha, from_file)
        to_file = File.join(@path, sha)
        if File.exists?(from_file)
          FileUtils.cp(from_file, to_file)
          return true
        end
        return false
      end
      
      def get_unpushed(files)
        files.select do |f|
          !File.exist?(File.join(@path, f))
        end
      end
      
    end
  end
end


================================================
FILE: lib/git-media/transport/s3.rb
================================================
require 'git-media/transport'
require 's3'
require 'right_aws'

# git-media.transport s3
# git-media.s3bucket
# git-media.s3key
# git-media.s3secret

module GitMedia
  module Transport
    class S3 < Base

      def initialize(bucket, access_key_id = nil, secret_access_key = nil)    
        @s3 = RightAws::S3Interface.new(access_key_id, secret_access_key,
              {:multi_thread => true, :logger => Logger.new(File.expand_path('~/.git-media.s3.log'))})
    
        @bucket = bucket

        begin
          @buckets = @s3.list_all_my_buckets.map { |a| a[:name] }
        rescue RightAws::AwsError
          # Need to use STDERR because this might be called inside a filter
          STDERR.puts ("Failed to connect to storage backend (S3)")
          raise
        end

        if !@buckets.include?(bucket)
          # Need to use STDERR because this might be called inside a filter
          STDERR.puts ("Creating New Bucket")
          if @s3.create_bucket(bucket)
            @buckets << bucket
          end
        end
      end

      def read?
        @buckets.size > 0
      end

      def get_file(sha, to_file)
        to = File.new(to_file, File::CREAT|File::RDWR|File::BINARY)
        begin
          @s3.get(@bucket, sha) do |chunk|
            to.write(chunk)
          end
          to.close
          return true
        rescue RightAws::AwsError => e
          # Delete the file to make sure it is not expanded
          to.close
          File.delete(to_file)

          # Ugly, but AwsError does not seem to give me much choice
          if e.message.include?('NoSuchKey')
            STDERR.puts("Storage backend (S3) did not contain file : "+sha+", have you run 'git media sync' from all repos?")
            return false
          else
            # Need to use STDERR because this might be called inside a filter
            STDERR.puts ("Downloading file from S3 failed with error:\n" + e.message)
            return false
          end
        end
      end

      def write?
        @buckets.size > 0
      end

      def put_file(sha, from_file)
        @s3.put(@bucket, sha,  File.open(from_file,"rb"))
      end

      def get_unpushed(files)
        # Using a set instead of a list improves performance a lot
        # since it reduces the complexity from O(n^2) to O(n)
        keys = Set.new()

        # Apparently the list_bucket method only returns the first 1000 elements
        # This method however will continue to give back results until all elements
        # have been listed
        @s3.incrementally_list_bucket(@bucket) { |contents| 
          contents[:contents].each { |element|
            keys.add (element[:key])
          }
        }

        files.select do |f|
          !keys.include?(f)
        end
      end
    end
  end
end

================================================
FILE: lib/git-media/transport/scp.rb
================================================
require 'git-media/transport'

# move large media to remote server via SCP

# git-media.transport scp
# git-media.scpuser someuser
# git-media.scphost remoteserver.com
# git-media.scppath /opt/media

module GitMedia
  module Transport
    class Scp < Base

      def initialize(user, host, path, port)
        @user = user
        @host = host
        @path = path
        unless port === ""
          @sshport = "-p#{port}"
        end
        unless port === ""
          @scpport = "-P#{port}"
        end
      end

      def exist?(file)
        if `ssh #{@user}@#{@host} #{@sshport} [ -f "#{file}" ] && echo 1 || echo 0`.chomp == "1"
          puts file + " exists"
          return true
        else
          puts file + " doesn't exists"
          return false
        end
      end

      def read?
        return true
      end

      def get_file(sha, to_file)
        from_file = @user+"@"+@host+":"+File.join(@path, sha)
        `scp #{@scpport} "#{from_file}" "#{to_file}"`
        if $? == 0
          puts sha+" downloaded"
          return true
        end
        puts sha+" download fail"
        return false
      end

      def write?
        return true
      end

      def put_file(sha, from_file)
        to_file = @user+"@"+@host+":"+File.join(@path, sha)
        `scp #{@scpport} "#{from_file}" "#{to_file}"`
        if $? == 0
          puts sha+" uploaded"
          return true
        end
        puts sha+" upload fail"
        return false
      end
      
      def get_unpushed(files)
        files.select do |f|
          !self.exist?(File.join(@path, f))
        end
      end
      
    end
  end
end


================================================
FILE: lib/git-media/transport/webdav.rb
================================================
require 'git-media/transport'

require 'uri'
require 'net/dav'


module GitMedia
  module Transport
    class WebDav < Base
      def initialize(url, user, password, verify_server=true, binary_transfer=false)
        @uri = URI(url)
        # Faster binary transport requires curb gem
        @dav = Net::DAV.new(url, :curl => (binary_transfer))
        @dav.verify_server = verify_server
        @dav.credentials(user, password)
        print 'checking connection... '
        @has_connection = @dav.exists?('.')
        puts (if @has_connection then 'ok' else 'failed' end)
      end

      def read?
        @has_connection
      end

      def write?
        @has_connection
      end

      def get_path(path)
        @uri.merge(path).path
      end

      def exists?(file)
        @dav.exists?(get_path(file))
      end

      def get_file(sha, to_file)
        to = File.new(to_file, File::CREAT|File::RDWR|File::BINARY)
        begin
          @dav.get(get_path(sha)) do |chunk|
            to.write(chunk)
          end
          true
        ensure
          to.close
        end
      end

      def put_file(sha, from_file)
        @dav.put(get_path(sha), File.open(from_file, "rb"), File.size(from_file))
      end

      def get_unpushed(files)
        files.select do |f|
          !self.exists?(f)
        end
      end

    end
  end
end


================================================
FILE: lib/git-media/transport.rb
================================================
module GitMedia
  module Transport
    class Base

      def pull(final_file, sha)
        to_file = GitMedia.media_path(sha)
        get_file(sha, to_file)
      end

      def push(sha)
        from_file = GitMedia.media_path(sha)
        put_file(sha, from_file)
      end

      ## OVERWRITE ##
      
      def read?
        false
      end

      def write?
        false
      end

      def get_file(sha, to_file)
        false
      end

      def put_file(sha, to_file)
        false
      end
      
      def get_unpushed(files)
        files
      end
      
    end
  end
end

================================================
FILE: lib/git-media.rb
================================================
require 'rubygems'
require 'bundler/setup'

require 'trollop'
require 'fileutils'
#

module GitMedia

  def self.get_media_buffer
    @@git_dir ||= `git rev-parse --git-dir`.chomp
    media_buffer = File.join(@@git_dir, 'media/objects')
    FileUtils.mkdir_p(media_buffer) if !File.exist?(media_buffer)
    return media_buffer
  end

  def self.media_path(sha)
    buf = self.get_media_buffer
    File.join(buf, sha)
  end

  # TODO: select the proper transports based on settings
  def self.get_push_transport
    self.get_transport
  end

  def self.get_credentials_from_netrc(url)
    require 'uri'
    require 'netrc'

    uri = URI(url)
    hostname = uri.host
    unless hostname
      raise "Cannot identify hostname within git-media.webdavurl value"
    end
    netrc = Netrc.read
    netrc[hostname]
  end

  def self.get_transport
    transport = `git config git-media.transport`.chomp
    case transport
    when ""
      raise "git-media.transport not set"

    when "scp"
      require 'git-media/transport/scp'

      user = `git config git-media.scpuser`.chomp
      host = `git config git-media.scphost`.chomp
      path = `git config git-media.scppath`.chomp
      port = `git config git-media.scpport`.chomp
      if user === ""
        raise "git-media.scpuser not set for scp transport"
      end
      if host === ""
        raise "git-media.scphost not set for scp transport"
      end
      if path === ""
        raise "git-media.scppath not set for scp transport"
      end
      GitMedia::Transport::Scp.new(user, host, path, port)

    when "local"
      require 'git-media/transport/local'

      path = `git config git-media.localpath`.chomp
      if path === ""
        raise "git-media.localpath not set for local transport"
      end
      GitMedia::Transport::Local.new(path)

    when "s3"
      require 'git-media/transport/s3'

      bucket = `git config git-media.s3bucket`.chomp
      key = `git config git-media.s3key`.chomp
      secret = `git config git-media.s3secret`.chomp
      if bucket === ""
        raise "git-media.s3bucket not set for s3 transport"
      end
      if key === ""
        raise "git-media.s3key not set for s3 transport"
      end
      if secret === ""
        raise "git-media.s3secret not set for s3 transport"
      end
      GitMedia::Transport::S3.new(bucket, key, secret)

    when "atmos"
      require 'git-media/transport/atmos_client'

      endpoint = `git config git-media.endpoint`.chomp
      uid = `git config git-media.uid`.chomp
      secret = `git config git-media.secret`.chomp
      tag = `git config git-media.tag`.chomp

      if endpoint == ""
        raise "git-media.endpoint not set for atmos transport"
      end

      if uid == ""
        raise "git-media.uid not set for atmos transport"
      end

      if secret == ""
        raise "git-media.secret not set for atmos transport"
      end
      GitMedia::Transport::AtmosClient.new(endpoint, uid, secret, tag)
    when "webdav"
      require 'git-media/transport/webdav'

      url = `git config git-media.webdavurl`.chomp
      user = `git config git-media.webdavuser`.chomp
      password = `git config git-media.webdavpassword`.chomp
      verify_server = `git config git-media.webdavverifyserver`.chomp == 'true'
      binary_transfer = `git config git-media.webdavbinarytransfer`.chomp == 'true'
      if url == ""
        raise "git-media.webdavurl not set for webdav transport"
      end
      if user == ""
        user, password = self.get_credentials_from_netrc(url)
      end
      if !user
        raise "git-media.webdavuser not set for webdav transport"
      end
      if !password
        raise "git-media.webdavpassword not set for webdav transport"
      end
      GitMedia::Transport::WebDav.new(url, user, password, verify_server, binary_transfer)
    when "box"
      require 'git-media/transport/box'

      client_id = `git config git-media.boxclientid`.chomp
      client_secret = `git config git-media.boxclientsecret`.chomp
      redirect_uri = `git config git-media.boxredirecturi`.chomp
      folder_id = `git config git-media.boxfolderid`.chomp

      access_token = `git config git-media.boxaccesstoken`.chomp
      refresh_token = `git config git-media.boxrefreshtoken`.chomp
      if client_id == ""
        raise "git-media.boxclientid not set for box transport"
      end
      if client_secret == ""
        raise "git-media.boxclientsecret not set for box transport"
      end
      if redirect_uri == ""
        raise "git-media.boxredirecturi not set for box transport"
      end
      if folder_id == ""
        raise "git-media.boxfolderid not set for box transport"
      end
      GitMedia::Transport::Box.new(client_id, client_secret, redirect_uri, folder_id, access_token, refresh_token)
    else
      raise "Invalid transport #{transport}"
    end
  end

  def self.get_pull_transport
    self.get_transport
  end

  module Application
    def self.run!

      if !system('git rev-parse')
        return
      end

      cmd = ARGV.shift # get the subcommand
      cmd_opts = case cmd
        when "filter-clean" # parse delete options
          require 'git-media/filter-clean'
          GitMedia::FilterClean.run!
        when "filter-smudge"
          require 'git-media/filter-smudge'
          GitMedia::FilterSmudge.run!
        when "clear" # parse delete options
          require 'git-media/clear'
          GitMedia::Clear.run!
        when "sync"
          require 'git-media/sync'
          GitMedia::Sync.run!
        when 'status'
          require 'git-media/status'
          opts = Trollop::options do
            opt :force, "Force status"
            opt :short, "Short status"
          end
          GitMedia::Status.run!(opts)
        when 'retroactively-apply'
          require 'git-media/filter-branch'
          GitMedia::FilterBranch.clean!
          arg2 = "--index-filter 'git media index-filter #{ARGV.shift}'"
          system("git filter-branch #{arg2} --tag-name-filter cat -- --all")
          GitMedia::FilterBranch.clean!
        when 'index-filter'
          require 'git-media/filter-branch'
          GitMedia::FilterBranch.run!
        else
    print <<EOF
usage: git media sync|status|clear

  sync                 Sync files with remote server

  status               Show files that are waiting to be uploaded and file size
                       --short:  Displays a shorter status message

  clear                Upload and delete the local cache of media files

  retroactively-apply  [Experimental] Rewrite history to add files from previous commits to git-media
                       Takes a single argument which is an absolute path to a file which should contain all file paths to rewrite
                       This file could for example be generated using
                       'git log --pretty=format: --name-only --diff-filter=A | sort -u | egrep ".*\.(jpg|png)" > to_rewrite'

EOF
        end

    end
  end
end


================================================
FILE: spec/media_spec.rb
================================================
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')

# I realize this is horrible, horrible rspec but I want to run the actual
# git commands and it takes forever to setup the test env each time, so
# i'm squeezing a bunch of tests into each 'it' - don't judge me

describe "Media" do
    
  it "should clean and smudge and save data in buffer area" do
    in_temp_git_w_media do
      git('add .')
      git("commit -m 'testing'")
      
      # check that we saved the sha and not the data
      size = git("cat-file -s master:testing1.mov")
      size.should eql('41')
      
      # check that the data is in our buffer area
      Dir.chdir('.git/media/objects') do
        objects = Dir.glob('*')
        objects.should include('20eabe5d64b0e216796e834f52d61fd0b70332fc')
      end
      
      # check that removing the file and checking out returns the data
      File.unlink('testing1.mov')
      git('checkout testing1.mov')
      File.size('testing1.mov').should eql(7)
      
      # check that removing the file and checking out sans data returns the sha
      File.unlink('testing1.mov')
      File.unlink('.git/media/objects/20eabe5d64b0e216796e834f52d61fd0b70332fc')
      git('checkout testing1.mov')
      File.size('testing1.mov').should eql(41)
    end
  end
  
  it "should show me the status of my directory"
  
  it "should sync with a local transport"
  
end


================================================
FILE: spec/spec_helper.rb
================================================
require 'rubygems'
require 'spec'
require 'tempfile'
require 'pp'

$LOAD_PATH.unshift(File.dirname(__FILE__))
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
require 'git-media'

Spec::Runner.configure do |config|
end

def in_temp_git
  tf = Tempfile.new('gitdir')
  temppath = tf.path
  tf.unlink
  FileUtils.mkdir(temppath)
  Dir.chdir(temppath) do
    `git init`
    yield
  end
end

def in_temp_git_w_media
  bin = File.join(File.dirname(__FILE__), '..', 'bin', 'git-media')  
  in_temp_git do
    append_file('testing1.mov', '1234567')
    append_file('testing2.mov', '123456789')
    append_file('normal.txt', 'hello world')
    append_file('.gitattributes', '*.mov filter=media')
    `git config filter.media.clean "#{bin} clean"`
    `git config filter.media.smudge "#{bin} smudge"`
    yield
  end
end

def append_file(filename, content)
  File.open(filename, 'w+') do |f|
    f.print content
  end
end

def git(command)
  output = `git #{command} 2>/dev/null`.strip
end
Download .txt
gitextract_rveq7zvc/

├── .document
├── .gitignore
├── Gemfile
├── LICENSE
├── README.md
├── Rakefile
├── TODO
├── VERSION
├── bin/
│   └── git-media
├── git-media.gemspec
├── lib/
│   ├── git-media/
│   │   ├── clear.rb
│   │   ├── filter-branch.rb
│   │   ├── filter-clean.rb
│   │   ├── filter-smudge.rb
│   │   ├── status.rb
│   │   ├── sync.rb
│   │   ├── transport/
│   │   │   ├── atmos_client.rb
│   │   │   ├── box.rb
│   │   │   ├── local.rb
│   │   │   ├── s3.rb
│   │   │   ├── scp.rb
│   │   │   └── webdav.rb
│   │   └── transport.rb
│   └── git-media.rb
└── spec/
    ├── media_spec.rb
    └── spec_helper.rb
Download .txt
SYMBOL INDEX (112 symbols across 15 files)

FILE: lib/git-media.rb
  type GitMedia (line 8) | module GitMedia
    function get_media_buffer (line 10) | def self.get_media_buffer
    function media_path (line 17) | def self.media_path(sha)
    function get_push_transport (line 23) | def self.get_push_transport
    function get_credentials_from_netrc (line 27) | def self.get_credentials_from_netrc(url)
    function get_transport (line 40) | def self.get_transport
    function get_pull_transport (line 159) | def self.get_pull_transport
    type Application (line 163) | module Application
      function run! (line 164) | def self.run!

FILE: lib/git-media/clear.rb
  type GitMedia (line 3) | module GitMedia
    type Clear (line 4) | module Clear
      function run! (line 6) | def self.run!
      function clear_local_cache (line 11) | def self.clear_local_cache

FILE: lib/git-media/filter-branch.rb
  type GitMedia (line 7) | module GitMedia
    type FilterBranch (line 8) | module FilterBranch
      function get_temp_buffer (line 10) | def self.get_temp_buffer
      function clean! (line 17) | def self.clean!
      function run! (line 23) | def self.run!

FILE: lib/git-media/filter-clean.rb
  type GitMedia (line 5) | module GitMedia
    type FilterClean (line 6) | module FilterClean
      function run! (line 8) | def self.run!(input=STDIN, output=STDOUT, info_output=true)

FILE: lib/git-media/filter-smudge.rb
  type GitMedia (line 1) | module GitMedia
    type FilterSmudge (line 2) | module FilterSmudge
      function print_stream (line 4) | def self.print_stream(stream)
      function run! (line 15) | def self.run!

FILE: lib/git-media/status.rb
  type GitMedia (line 4) | module GitMedia
    type Status (line 5) | module Status
      function run! (line 7) | def self.run!(opts)
      function find_references (line 16) | def self.find_references
      function print_references (line 44) | def self.print_references(refs, short=false)
      function print_cache_status (line 82) | def self.print_cache_status(refs, short)
      function local_cache_status (line 111) | def self.local_cache_status
      function to_human (line 122) | def self.to_human(size)

FILE: lib/git-media/sync.rb
  type GitMedia (line 5) | module GitMedia
    type Sync (line 6) | module Sync
      function run! (line 8) | def self.run!
      function expand_references (line 17) | def self.expand_references
      function update_index (line 38) | def self.update_index
      function upload_local_cache (line 58) | def self.upload_local_cache

FILE: lib/git-media/transport.rb
  type GitMedia (line 1) | module GitMedia
    type Transport (line 2) | module Transport
      class Base (line 3) | class Base
        method pull (line 5) | def pull(final_file, sha)
        method push (line 10) | def push(sha)
        method read? (line 17) | def read?
        method write? (line 21) | def write?
        method get_file (line 25) | def get_file(sha, to_file)
        method put_file (line 29) | def put_file(sha, to_file)
        method get_unpushed (line 33) | def get_unpushed(files)

FILE: lib/git-media/transport/atmos_client.rb
  type GitMedia (line 12) | module GitMedia
    type Transport (line 13) | module Transport
      class AtmosClient (line 14) | class AtmosClient < Base
        method initialize (line 16) | def initialize(endpoint, uid, secret, tag)
        method read? (line 26) | def read?
        method get_file (line 30) | def get_file(sha, to_file)
        method write (line 37) | def write
        method put_file (line 41) | def put_file(sha, from_file)
        method get_unpushed (line 48) | def get_unpushed(files)
        method reachable? (line 62) | def reachable?

FILE: lib/git-media/transport/box.rb
  type GitMedia (line 13) | module GitMedia
    type Transport (line 14) | module Transport
      class Box (line 15) | class Box < Base
        method initialize (line 17) | def initialize(client_id, client_secret, redirect_uri, folder_id, ...
        method read? (line 39) | def read?
        method get_file (line 43) | def get_file(sha, to_file)
        method write? (line 62) | def write?
        method put_file (line 66) | def put_file(sha, from_file)
        method get_unpushed (line 70) | def get_unpushed(files)
        method get_files (line 78) | def get_files(use_cache = false)

FILE: lib/git-media/transport/local.rb
  type GitMedia (line 8) | module GitMedia
    type Transport (line 9) | module Transport
      class Local (line 10) | class Local < Base
        method initialize (line 12) | def initialize(path)
        method read? (line 16) | def read?
        method get_file (line 20) | def get_file(sha, to_file)
        method write? (line 29) | def write?
        method put_file (line 33) | def put_file(sha, from_file)
        method get_unpushed (line 42) | def get_unpushed(files)

FILE: lib/git-media/transport/s3.rb
  type GitMedia (line 10) | module GitMedia
    type Transport (line 11) | module Transport
      class S3 (line 12) | class S3 < Base
        method initialize (line 14) | def initialize(bucket, access_key_id = nil, secret_access_key = nil)
        method read? (line 37) | def read?
        method get_file (line 41) | def get_file(sha, to_file)
        method write? (line 66) | def write?
        method put_file (line 70) | def put_file(sha, from_file)
        method get_unpushed (line 74) | def get_unpushed(files)

FILE: lib/git-media/transport/scp.rb
  type GitMedia (line 10) | module GitMedia
    type Transport (line 11) | module Transport
      class Scp (line 12) | class Scp < Base
        method initialize (line 14) | def initialize(user, host, path, port)
        method exist? (line 26) | def exist?(file)
        method read? (line 36) | def read?
        method get_file (line 40) | def get_file(sha, to_file)
        method write? (line 51) | def write?
        method put_file (line 55) | def put_file(sha, from_file)
        method get_unpushed (line 66) | def get_unpushed(files)

FILE: lib/git-media/transport/webdav.rb
  type GitMedia (line 7) | module GitMedia
    type Transport (line 8) | module Transport
      class WebDav (line 9) | class WebDav < Base
        method initialize (line 10) | def initialize(url, user, password, verify_server=true, binary_tra...
        method read? (line 21) | def read?
        method write? (line 25) | def write?
        method get_path (line 29) | def get_path(path)
        method exists? (line 33) | def exists?(file)
        method get_file (line 37) | def get_file(sha, to_file)
        method put_file (line 49) | def put_file(sha, from_file)
        method get_unpushed (line 53) | def get_unpushed(files)

FILE: spec/spec_helper.rb
  function in_temp_git (line 13) | def in_temp_git
  function in_temp_git_w_media (line 24) | def in_temp_git_w_media
  function append_file (line 37) | def append_file(filename, content)
  function git (line 43) | def git(command)
Condensed preview — 26 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (50K chars).
[
  {
    "path": ".document",
    "chars": 60,
    "preview": "README.rdoc\nlib/**/*.rb\nbin/*\nfeatures/**/*.feature\nLICENSE\n"
  },
  {
    "path": ".gitignore",
    "chars": 601,
    "preview": "*.gem\n*.rbc\n/.config\n/coverage/\n/InstalledFiles\n/pkg/\n/spec/reports/\n/spec/examples.txt\n/test/tmp/\n/test/version_tmp/\n/t"
  },
  {
    "path": "Gemfile",
    "chars": 247,
    "preview": "source 'https://rubygems.org'\r\n\r\ngem 'trollop'\r\ngem 's3'\r\ngem 'ruby-atmos-pure'\r\ngem 'right_aws'\r\ngem 'net_dav', :git =>"
  },
  {
    "path": "LICENSE",
    "chars": 1056,
    "preview": "Copyright (c) 2009 Scott Chacon\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this so"
  },
  {
    "path": "README.md",
    "chars": 3614,
    "preview": "# git-media\n\nGitMedia extension allows you to use Git with large media files\nwithout storing the media in Git itself.\n\n#"
  },
  {
    "path": "Rakefile",
    "chars": 1201,
    "preview": "require 'rubygems'\nrequire 'rake'\n\nbegin\n  require 'jeweler'\n  Jeweler::Tasks.new do |gem|\n    gem.name = \"git-media\"\n  "
  },
  {
    "path": "TODO",
    "chars": 258,
    "preview": "== Tools\n\n\t* tool to clean large files out of existing repo (filter-branch?)\n\t  - can also just re-do the last commit wi"
  },
  {
    "path": "VERSION",
    "chars": 6,
    "preview": "0.1.4\n"
  },
  {
    "path": "bin/git-media",
    "chars": 146,
    "preview": "#!/usr/bin/env ruby\nrequire 'optparse'\n\n$:.unshift File.join(File.dirname(__FILE__), '..', 'lib')\nrequire 'git-media'\n\nG"
  },
  {
    "path": "git-media.gemspec",
    "chars": 1836,
    "preview": "# Generated by jeweler\r\n# DO NOT EDIT THIS FILE DIRECTLY\r\n# Instead, edit Jeweler::Tasks in rakefile, and run 'rake gems"
  },
  {
    "path": "lib/git-media/clear.rb",
    "chars": 595,
    "preview": "require 'git-media/status'\n\nmodule GitMedia\n  module Clear\n\n    def self.run!\n      @push = GitMedia.get_push_transport\n"
  },
  {
    "path": "lib/git-media/filter-branch.rb",
    "chars": 4688,
    "preview": "require 'set'\nrequire 'git-media/filter-clean'\nrequire 'fileutils'\n\ninclude Process\n\nmodule GitMedia\n  module FilterBran"
  },
  {
    "path": "lib/git-media/filter-clean.rb",
    "chars": 1936,
    "preview": "require 'digest/sha1'\nrequire 'fileutils'\nrequire 'tempfile'\n\nmodule GitMedia\n  module FilterClean\n\n    def self.run!(in"
  },
  {
    "path": "lib/git-media/filter-smudge.rb",
    "chars": 2224,
    "preview": "module GitMedia\n  module FilterSmudge\n\n    def self.print_stream(stream)\n      # create a binary stream to write to stdo"
  },
  {
    "path": "lib/git-media/status.rb",
    "chars": 3941,
    "preview": "require 'pp'\nEncoding.default_external = Encoding::UTF_8\n\nmodule GitMedia\n  module Status\n\n    def self.run!(opts)\n     "
  },
  {
    "path": "lib/git-media/sync.rb",
    "chars": 2076,
    "preview": "# find files that are placeholders (41 char) and download them\n# upload files in media buffer that are not in offsite bi"
  },
  {
    "path": "lib/git-media/transport/atmos_client.rb",
    "chars": 1588,
    "preview": "require 'git-media/transport'\nrequire 'ruby-atmos-pure'\nrequire 'atmos'\n\n\n# git-media.transport atmos\n# git-media.endpoi"
  },
  {
    "path": "lib/git-media/transport/box.rb",
    "chars": 3261,
    "preview": "require 'git-media/transport'\nrequire 'boxr'\nrequire 'shellwords'\n\n# git-media.transport box\n# git-media.boxclientid\n# g"
  },
  {
    "path": "lib/git-media/transport/local.rb",
    "chars": 946,
    "preview": "require 'git-media/transport'\n\n# move large media to local bin\n\n# git-media.transport local\n# git-media.localpath /opt/m"
  },
  {
    "path": "lib/git-media/transport/s3.rb",
    "chars": 2794,
    "preview": "require 'git-media/transport'\nrequire 's3'\nrequire 'right_aws'\n\n# git-media.transport s3\n# git-media.s3bucket\n# git-medi"
  },
  {
    "path": "lib/git-media/transport/scp.rb",
    "chars": 1641,
    "preview": "require 'git-media/transport'\n\n# move large media to remote server via SCP\n\n# git-media.transport scp\n# git-media.scpuse"
  },
  {
    "path": "lib/git-media/transport/webdav.rb",
    "chars": 1356,
    "preview": "require 'git-media/transport'\n\nrequire 'uri'\nrequire 'net/dav'\n\n\nmodule GitMedia\n  module Transport\n    class WebDav < B"
  },
  {
    "path": "lib/git-media/transport.rb",
    "chars": 589,
    "preview": "module GitMedia\n  module Transport\n    class Base\n\n      def pull(final_file, sha)\n        to_file = GitMedia.media_path"
  },
  {
    "path": "lib/git-media.rb",
    "chars": 6967,
    "preview": "require 'rubygems'\nrequire 'bundler/setup'\n\nrequire 'trollop'\nrequire 'fileutils'\n#\n\nmodule GitMedia\n\n  def self.get_med"
  },
  {
    "path": "spec/media_spec.rb",
    "chars": 1396,
    "preview": "require File.expand_path(File.dirname(__FILE__) + '/spec_helper')\n\n# I realize this is horrible, horrible rspec but I wa"
  },
  {
    "path": "spec/spec_helper.rb",
    "chars": 998,
    "preview": "require 'rubygems'\nrequire 'spec'\nrequire 'tempfile'\nrequire 'pp'\n\n$LOAD_PATH.unshift(File.dirname(__FILE__))\n$LOAD_PATH"
  }
]

About this extraction

This page contains the full source code of the alebedev/git-media GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 26 files (44.9 KB), approximately 12.3k tokens, and a symbol index with 112 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!