Git Product home page Git Product logo

git-lfs's Introduction

Git Large File Storage

CI status

Git LFS is a command line extension and specification for managing large files with Git.

The client is written in Go, with pre-compiled binaries available for Mac, Windows, Linux, and FreeBSD. Check out the website for an overview of features.

Getting Started

Installing

On Linux

Debian and RPM packages are available from packagecloud, see the Linux installation instructions.

On macOS

Homebrew bottles are distributed and can be installed via brew install git-lfs.

On Windows

Git LFS is included in the distribution of Git for Windows. Alternatively, you can install a recent version of Git LFS from the Chocolatey package manager.

From binary

Binary packages are available for Linux, macOS, Windows, and FreeBSD. The binary packages include a script which will:

  • Install Git LFS binaries onto the system $PATH. On Windows in particular, you may need to restart your command shell so any change to $PATH will take effect and Git can locate the Git LFS binary.
  • Run git lfs install to perform required global configuration changes.
$ ./install.sh

Note that Debian and RPM packages are built for multiple Linux distributions and versions for both amd64 and i386. For arm64, only Debian packages are built and only for recent versions due to the cost of building in emulation.

From source

  • Ensure you have the latest version of Go, GNU make, and a standard Unix-compatible build environment installed.
  • On Windows, install goversioninfo with go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@latest.
  • Run make.
  • Place the git-lfs binary, which can be found in bin, on your system’s executable $PATH or equivalent.
  • Git LFS requires global configuration changes once per-machine. This can be done by running: git lfs install

Verifying releases

Releases are signed with the OpenPGP key of one of the core team members. To get these keys, you can run the following command, which will print them to standard output:

$ curl -L https://api.github.com/repos/git-lfs/git-lfs/tarball/core-gpg-keys | tar -Ozxf -

Once you have the keys, you can download the sha256sums.asc file and verify the file you want like so:

$ gpg -d sha256sums.asc | grep git-lfs-linux-amd64-v2.10.0.tar.gz | shasum -a 256 -c

For the convenience of distributors, we also provide a wider variety of signed hashes in the hashes.asc file. Those hashes are in the tagged BSD format, but can be verified with Perl's shasum or the GNU hash utilities, just like the ones in sha256sums.asc.

Example Usage

To begin using Git LFS within a Git repository that is not already configured for Git LFS, you can indicate which files you would like Git LFS to manage. This can be done by running the following from within a Git repository:

$ git lfs track "*.psd"

(Where *.psd is the pattern of filenames that you wish to track. You can read more about this pattern syntax here).

Note: the quotation marks surrounding the pattern are important to prevent the glob pattern from being expanded by the shell.

After any invocation of git-lfs-track(1) or git-lfs-untrack(1), you must commit changes to your .gitattributes file. This can be done by running:

$ git add .gitattributes
$ git commit -m "track *.psd files using Git LFS"

You can now interact with your Git repository as usual, and Git LFS will take care of managing your large files. For example, changing a file named my.psd (tracked above via *.psd):

$ git add my.psd
$ git commit -m "add psd"

Tip: if you have large files already in your repository's history, git lfs track will not track them retroactively. To migrate existing large files in your history to use Git LFS, use git lfs migrate. For example:

$ git lfs migrate import --include="*.psd" --everything

Note that this will rewrite history and change all of the Git object IDs in your repository, just like the export version of this command.

For more information, read git-lfs-migrate(1).

You can confirm that Git LFS is managing your PSD file:

$ git lfs ls-files
3c2f7aedfb * my.psd

Once you've made your commits, push your files to the Git remote:

$ git push origin main
Uploading LFS objects: 100% (1/1), 810 B, 1.2 KB/s
# ...
To https://github.com/git-lfs/git-lfs-test
   67fcf6a..47b2002  main -> main

Note: Git LFS requires at least Git 1.8.2 on Linux or 1.8.5 on macOS.

Uninstalling

If you've decided that Git LFS isn't right for you, you can convert your repository back to a plain Git repository with git lfs migrate as well. For example:

$ git lfs migrate export --include="*.psd" --everything

Note that this will rewrite history and change all of the Git object IDs in your repository, just like the import version of this command.

If there's some reason that things aren't working out for you, please let us know in an issue, and we'll definitely try to help or get it fixed.

Limitations

Git LFS maintains a list of currently known limitations, which you can find and edit here.

Git LFS source code utilizes Go modules in its build system, and therefore this project contains a go.mod file with a defined Go module path. However, we do not maintain a stable Go language API or ABI, as Git LFS is intended to be used solely as a compiled binary utility. Please do not import the git-lfs module into other Go code and do not rely on it as a source code dependency.

Need Help?

You can get help on specific commands directly:

$ git lfs help <subcommand>

The official documentation has command references and specifications for the tool. There's also a FAQ shipped with Git LFS which answers some common questions.

If you have a question on how to use Git LFS, aren't sure about something, or are looking for input from others on tips about best practices or use cases, feel free to start a discussion.

You can always open an issue, and one of the Core Team members will respond to you. Please be sure to include:

  1. The output of git lfs env, which displays helpful information about your Git repository useful in debugging.
  2. Any failed commands re-run with GIT_TRACE=1 in the environment, which displays additional information pertaining to why a command crashed.

Contributing

See CONTRIBUTING.md for info on working on Git LFS and sending patches. Related projects are listed on the Implementations wiki page.

See also SECURITY.md for info on how to submit reports of security vulnerabilities.

Core Team

These are the humans that form the Git LFS core team, which runs the project.

In alphabetical order:

@bk2204 @chrisd8088 @larsxschneider
PGP 0223B187 PGP 088335A9 PGP A5795889

Alumni

These are the humans that have in the past formed the Git LFS core team, or have otherwise contributed a significant amount to the project. Git LFS would not be possible without them.

In alphabetical order:

@andyneff @PastelMobileSuit @rubyist @sinbad @technoweenie @ttaylorr

git-lfs's People

Contributors

ajh16 avatar andyneff avatar b-camacho avatar bk2204 avatar bkeepers avatar bozaro avatar chrisd8088 avatar dependabot[bot] avatar dpursehouse avatar jasminlapalme avatar javabrett avatar jochenhz avatar joshvera avatar larsxschneider avatar mhagger avatar michael-k avatar mstrap avatar pastelmobilesuit avatar pluehne avatar rubyist avatar shevron avatar shiftkey avatar sinbad avatar slonopotamus avatar sprohaska avatar sschuberth avatar ssgelm avatar strich avatar technoweenie avatar ttaylorr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

git-lfs's Issues

Large Asset content

If you missed my team update from last week, I made some progress on large assets on Friday. I have functioning git-media filters for dealing with large assets. This is how it works in a nut shell:

  • When you git add a file, Git passes them through a "clean" filter. git-media cleans large files by replacing them with meta data pointing to the file.
  • When Git checks out a file to the working directory, it runs them through a "smudge" filter. Files are untouched unless it detects large asset meta data.

I want to get some comments on the metadata as early as possible. Once meta data is committed, we can't easily change it. I'm taking cues from @schacon's git-media and assman projects. I want a format that is ascii friendly (works fine with plain Git), but easily parsed. Currently it's simply a JSON file with one or more # comment lines. This format needs to be something easily parsed so that every interface to GitHub understands how to read or write the real file as needed.

# This is a placeholder for large media....
{"media_type": "application/vnd.github.large-asset", "size": 123, "md5": "....", "sha1": "..."}

Assman only records the SHA. Though when talking to @nickh about Subversion integration, he mentioned MD5. This seems to be a good place to get any meta data or signatures that we need. Unfortunately, the filters only work through STDIN data. They have no idea what the file name or type is.

I'm currently writing this in Go. It's really easy to cross compile static binaries for every major platform, (even Windows). The git media stuff should be easy to port to c if needed though. I would just need a patient c buddy.

/cc @github/mac @github/windows @github/importexport @arrbee @peff

Please let me know if I missed someone or you don't want to be spammed by this stuff. Or better yet, join the @github/large-assets team :) We can create a large-assets-integration team too.

List of git media commands

I'm used to things like git with nothing else spitting out some really brief output and summary of commands. I'd love some way to get this from git media as well.

renderables/ on master β€Ί git  
usage: git [--version] [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
           [-p|--paginate|--no-pager] [--no-replace-objects] [--bare]
           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
           [-c name=value] [--help]
           <command> [<args>]

Basic Commands:
   init       Create an empty git repository or reinitialize an existing one
   add        Add new or modified files to the staging area
   rm         Remove files from the working directory and staging area
   mv         Move or rename a file, a directory, or a symlink
   status     Show the status of the working directory and staging area
   commit     Record changes to the repository

History Commands:
   log        Show the commit history log
   diff       Show changes between commits, commit and working tree, etc
   show       Show information about commits, tags or files

Branching Commands:
   branch     List, create, or delete branches
   checkout   Switch the active branch to another branch
   merge      Join two or more development histories (branches) together
   tag        Create, list, delete, sign or verify a tag object

Remote Commands:
   clone      Clone a remote repository into a new directory
   fetch      Download data, tags and branches from a remote repository
   pull       Fetch from and merge with another repository or a local branch
   push       Upload data, tags and branches to a remote repository
   remote     View and manage a set of remote repositories

Advanced Commands:
   reset      Reset your staging area or working directory to another point
   rebase     Re-apply a series of patches in one branch onto another
   bisect     Find by binary search the change that introduced a bug
   grep       Print files with lines matching a pattern in your codebase

GitHub Commands:
   pull-request   Open a pull request on GitHub
   fork           Make a fork of a remote repository on GitHub and add as remote
   create         Create this repository on GitHub and add GitHub as origin
   browse         Open a GitHub page in the default browser
   compare        Open a compare page on GitHub
   ci-status      Show the CI status of a commit

See 'git help <command>' for more information on a specific command.

renderables/ on master β€Ί git media
git-media v0.1.0

renderables/ on master β€Ί git help media
No manual entry for git-media

`git media sync` panics on new branches in submodules

Steps:

  1. cd into the submodule of a repository
  2. Create a new branch with git checkout -b
  3. Run git media sync

Results:

Something like:

Error setting up queue
Unable to log panic:
panic: mkdir /Users/justin/Programming/ReactiveCocoa/.git: not a directory

goroutine 1 [running]:
_/Users/rick/github/git-media.handlePanic(0xc2000baae0, 0xc2000baba0, 0x0, 0x0)
    /Users/rick/github/git-media/logging.go:81 +0x1ce
_/Users/rick/github/git-media.Panic(0xc2000baae0, 0xc2000baba0, 0x29e590, 0x16, 0x0, ...)
    /Users/rick/github/git-media/logging.go:47 +0x87
_/Users/rick/github/git-media.UploadQueue(0x254cb)
    /Users/rick/github/git-media/queues.go:30 +0x99
_/Users/rick/github/git-media/commands.(*SyncCommand).Run(0xc2000001e0)
    /Users/rick/github/git-media/commands/command_sync.go:13 +0x1c
_/Users/rick/github/git-media/commands.Run()
    /Users/rick/github/git-media/commands/commands.go:30 +0x1cb
main.main()
    /Users/rick/github/git-media/cmd/git-media.go:8 +0x18

goroutine 2 [syscall]:

goroutine 3 [runnable]:

Looks like it's upset that .git is actually just a pointer to the parent repo (instead of a folder with Git metadata).

getting the filename during git smudge/clean

Git's smudge/clean filters work with binary data flowing through STDIN and STDOUT. Is there any way to get the filename? I want this to print out while it's downloading files. This is what it looks like now:

Cloning into 'gitmediatest'...
remote: Counting objects: 21, done.
remote: Compressing objects: 100% (12/12), done.
remote: Total 21 (delta 5), reused 15 (delta 4)
Unpacking objects: 100% (21/21), done.
Downloading media: 8eeb1515c98ecd46dc38f10953dadd1f5d0f9a4ed707cb7421db95ae7f738a31
Downloading media: 17ab91cebd2972fa7f01b6e77b28c59d341534d1d8d6fffacb6f3ea9d3aabff4

Based on what @schacon wrote here, it may be possible.

This would be git-media style where we use .gitattributes in the client to smudge and clean content - it can be much smarter too, now that smudge and clean have the file path option instead of just stdin - we can stat the file and see if it's big rather than having to read it all in.

Any thoughts @peff?

General usability concerns

  • Need lots of .gitattributes docs, and possibly a default example file. Very confusing for the first time experience.
  • Attempting to git add a file that's already staged doesn't add it to Git Media.

`git media paths` should scan other git attributes sources

Right now it just scans .gitattributes in the repo directory. However, you can stick a .gitattributes file in any subdirectory, as well as into .git/ configuration. We should scan all of these to show in the path list since the git will filter them through git media.

Let users push assets to a new endpoint

You should be able to assign different git media endpoints to different remotes. It should be as easy to push git media to a new remote as it is to push a git repository.

git media env can work better with submodules

In this example, media-subs is a plain old git repo, rubyistmedia2 is github/rubyistmedia2 added as a submodule:

rubyist:rubyistmedia2 scott$ pwd
/Users/scott/media-subs/rubyistmedia2
rubyist:rubyistmedia2 scott$ git media env
Endpoint=https://github.com/github/rubyistmedia2.git/info/media
LocalWorkingDir=/Users/scott/media-subs
LocalGitDir=/Users/scott/media-subs/.git
LocalMediaDir=/Users/scott/media-subs/.git/media
TempDir=/var/folders/6f/rpq18l455zj67tqr__nr564w0000gn/T/git-media

Git media is using the parent repo's directory as its LocalWirkingDir and the parent repo's .git directory as its LocalGitDir. It does get the endpoint right, though, because git will calculate the correct remote.origin.url.

Git submodules gives us a few things we can use to help us figure this stuff out. First, when we're looking for the .git directory, we can check if it's a file or a directory. When a repo is a submodule, it will be a file containing something like this:

rubyist:rubyistmedia2 scott$ cat .git
gitdir: ../.git/modules/rubyistmedia2

And there's also the .gitmodules file in the parent repo:

rubyist:media-subs scott$ cat .gitmodules
[submodule "rubyistmedia2"]
    path = rubyistmedia2
    url = https://github.com/github/rubyistmedia2

We can use these to know the appropriate working and git dirs from within the submodule.

Convert repo to use large assets

It'd be awesome if git-media had a way to convert a repo and throw every large file in Git Media. It should output a completely new repository with rebuilt history. Two great example repositories include:

  • Desktop - Short history, mostly big images.
  • GitHub.com- I'd say if this repository can be converted, all of our bases are covered.

Massive bonus points if it can create a map of old to new commits that could be used to update pull requests and commit comments.

logging

We need better error handling.

The git filter scripts (git-media-clean and git-media-smudge) can't use fmt.Println, because Git does something with STDOUT. If there are issues, it's best to use STDERR or exit with a non 0 code.

  • I have some rough Debug() and Panic() functions that could use some more thought.
  • Logs could be written to something like .git/logs/media/refs/heads/master.
  • Key/value logs using grohl might be good for easier parsing.

Upload queue not reset when resetting commit staging

I added some files via git add they then show up in git media queues. However if I decide to not commit those files and git reset HEAD ., they remain in git media queues.

renderables/ on master β€Ί git add git-media/ 

renderables/ β€Ί git media queues
upload
  git-media/psds/100.psd
  git-media/psds/1000-2.psd
  git-media/psds/300.psd

renderables/ β€Ί git reset HEAD .  

renderables/ on master β€Ί git media queues
upload
  git-media/psds/100.psd
  git-media/psds/1000-2.psd
  git-media/psds/300.psd

renderables/ on master β€Ί git st
On branch master
Your branch is up-to-date with 'origin/master'.

Untracked files:
  (use "git add <file>..." to include in what will be committed)

    .gitattributes
    git-media/

nothing added to commit but untracked files present (use "git add" to track)

Git rid of git-media-*

There's no good reason to have git-media-clean and git-media-smudge. They should be rolled into sub commands of git-media.

Git media security?

Talking to a potential customer about Git LFS they raised a couple of questions I didn't immediately know the answer to, mostly around security:

  • Are the files moved around via encryption?
  • Also does anyone else have access to the media files?

Do we have any docs about security we could share?

pre-push hook does not install in submodules

Due to the issues in #74, when running any git media commands from the submodule, the pre-push filter will be installed in the parent repo. To install it into the sub repo, we need to look at the .git file:

rubyist:rubyistmedia2 scott$ cat .git
gitdir: ../.git/modules/rubyistmedia2

And install the hook into that directory.

Git media client will hang when using ssh to access repositories

Steps to reproduce:

Use the cache credential helper
git config --global credental.helper cache

Clone a git media repository via ssh. You will then be prompted for github credentials and the git media client will hang:

~$ git clone [email protected]:user/mediarepo.git
Cloning into 'mediarepo'...
remote: Counting objects: 95, done.
remote: Total 95 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (95/95), 8.88 KiB | 0 bytes/s, done.
Resolving deltas: 100% (25/25), done.
Checking connectivity... done.
Username for 'https://github.com': user
Password for 'https://[email protected]':

reliability in syncing assets

What should we do in the event of failures? There are bound to be issues when uploading a bunch of data over HTTPS.

  • Progress updates in the console for...
    • Downloading
    • Uploading
  • Retries and resumes...
    • Downloading (Range header)
    • Uploading (tus.io)

Can't rely on git config values for media url

Since git config values are not replicated across clones we cannot solely rely on them for storing the media url. New clones of the repository will fail to sync because the URL is not present.

Proposed options at this point:

Define a process to check for these values:

  1. Check git config
  2. Look for .gitmedia
  3. Build the URL based on a remote, e.g. https://github.com/foo/bar.git => https://github.com/foo/bar.git/info/media

Another option is to use a more generic .gitconfig file. This file would be similar to .git/config and could be used by other tooling we build.

/cc @technoweenie @github/desktop

Support for git-media metadata and versions

This issue stems from the desire to know the byte size of a git-media file before downloading the resource from the git-media server. I am proposing to add this information to the pointer blob.

# git-media
# size: 1234
SHA1

That format looks horrible, and it is horrible. Backwards compatibility will be a horrendous mass of spaghetti code and special cases; pity the developer who would have to implement and maintain that mess.

I am also proposing a parseable format for the git-media pointer information.

{
  "git-media": "1.0.0",
  "size-in-bytes": 1234,
  "sha1": "abc123def456"
}

A version string is attached to the "git-media" key. This allows implementations to maintain backwards compatibility and to detect newer git-media pointers.

Thoughts?

/cc @github/git-media

run "git media" in a directory which isn't a git repository -> boomtown

Version: 0.2.0
Platform: Windows 8.1 x64

Repro:

  • launch Git Shell with git-media.exe in $PATH
  • change directory to a path that doesn't contain a git repository
  • run git media

Expected: message similar to git's "not a git repository" message
Actual: after a long wait without any output, a stack overflow error.

Stacktrace:

C:\Users\brendanforster\Documents\GìtHūb> git media
runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow

runtime stack:
runtime.throw(0x9910ae)
        /opt/boxen/chgo/versions/1.3/src/pkg/runtime/panic.c:520 +0x7e
runtime.newstack()
        /opt/boxen/chgo/versions/1.3/src/pkg/runtime/stack.c:770 +0x524
runtime.morestack()
        /opt/boxen/chgo/versions/1.3/src/pkg/runtime/asm_amd64.s:228 +0x64

goroutine 16 [stack growth]:
strings.Replace(0xc095fba410, 0x7, 0x79d6a0, 0x1, 0x7a4e00, 0x1, 0xffffffffffffffff, 0x2, 0xc0a2101178)
        /opt/boxen/chgo/versions/1.3/src/pkg/strings/strings.go:638 fp=0xc0e2101128 sp=0xc0e2101120
path/filepath.FromSlash(0xc095fba410, 0x7, 0x0, 0x0)
        /opt/boxen/chgo/versions/1.3/src/pkg/path/filepath/path.go:172 +0x7e fp=0xc0e2101178 sp=0xc0e2101128
path/filepath.Clean(0xc095fba3fa, 0x6, 0x0, 0x0)
        /opt/boxen/chgo/versions/1.3/src/pkg/path/filepath/path.go:152 +0x2ea fp=0xc0e2101280 sp=0xc0e2101178
path/filepath.Join(0xc0e21013f0, 0x2, 0x2, 0x0, 0x0)
        /opt/boxen/chgo/versions/1.3/src/pkg/path/filepath/path.go:202 +0x137 fp=0xc0e2101340 sp=0xc0e2101280
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fba3d8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:103 +0x27d fp=0xc0e210
1418 sp=0xc0e2101340
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fba3a0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
14f0 sp=0xc0e2101418
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fba348, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
15c8 sp=0xc0e21014f0
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fba310, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
16a0 sp=0xc0e21015c8
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fba2b8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
1778 sp=0xc0e21016a0
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fba280, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
1850 sp=0xc0e2101778
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fba228, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
1928 sp=0xc0e2101850
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fba1f0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
1a00 sp=0xc0e2101928
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fba198, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
1ad8 sp=0xc0e2101a00
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fba160, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
1bb0 sp=0xc0e2101ad8
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fba108, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
1c88 sp=0xc0e2101bb0
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fba0d0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
1d60 sp=0xc0e2101c88
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fba078, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
1e38 sp=0xc0e2101d60
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fba040, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
1f10 sp=0xc0e2101e38
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5fe8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
1fe8 sp=0xc0e2101f10
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5fb0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
20c0 sp=0xc0e2101fe8
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5f58, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
2198 sp=0xc0e21020c0
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5f20, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
2270 sp=0xc0e2102198
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5ec8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
2348 sp=0xc0e2102270
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5e90, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
2420 sp=0xc0e2102348
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5e38, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
24f8 sp=0xc0e2102420
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5e00, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
25d0 sp=0xc0e21024f8
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5da8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
26a8 sp=0xc0e21025d0
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5d70, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
2780 sp=0xc0e21026a8
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5d18, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
2858 sp=0xc0e2102780
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5ce0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
2930 sp=0xc0e2102858
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5c88, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
2a08 sp=0xc0e2102930
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5c50, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
2ae0 sp=0xc0e2102a08
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5bf8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
2bb8 sp=0xc0e2102ae0
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5bc0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
2c90 sp=0xc0e2102bb8
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5b68, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
2d68 sp=0xc0e2102c90
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5b30, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
2e40 sp=0xc0e2102d68
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5ad8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
2f18 sp=0xc0e2102e40
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5aa0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
2ff0 sp=0xc0e2102f18
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5a48, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
30c8 sp=0xc0e2102ff0
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5a10, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
31a0 sp=0xc0e21030c8
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb59b8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
3278 sp=0xc0e21031a0
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5980, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
3350 sp=0xc0e2103278
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5928, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
3428 sp=0xc0e2103350
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb58f0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
3500 sp=0xc0e2103428
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5898, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
35d8 sp=0xc0e2103500
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5860, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
36b0 sp=0xc0e21035d8
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5808, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
3788 sp=0xc0e21036b0
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb57d0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
3860 sp=0xc0e2103788
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5778, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
3938 sp=0xc0e2103860
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5740, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
3a10 sp=0xc0e2103938
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb56e8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
3ae8 sp=0xc0e2103a10
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb56b0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
3bc0 sp=0xc0e2103ae8
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5658, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
3c98 sp=0xc0e2103bc0
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5620, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
3d70 sp=0xc0e2103c98
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb55c8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
3e48 sp=0xc0e2103d70
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5590, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
3f20 sp=0xc0e2103e48
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5538, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
3ff8 sp=0xc0e2103f20
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5500, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
40d0 sp=0xc0e2103ff8
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb54a8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
41a8 sp=0xc0e21040d0
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5470, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
4280 sp=0xc0e21041a8
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5418, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
4358 sp=0xc0e2104280
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb53e0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
4430 sp=0xc0e2104358
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5388, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
4508 sp=0xc0e2104430
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5350, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
45e0 sp=0xc0e2104508
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb52f8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
46b8 sp=0xc0e21045e0
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb52c0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
4790 sp=0xc0e21046b8
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5268, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
4868 sp=0xc0e2104790
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5230, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
4940 sp=0xc0e2104868
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb51d8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
4a18 sp=0xc0e2104940
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb51a0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
4af0 sp=0xc0e2104a18
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5148, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
4bc8 sp=0xc0e2104af0
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5110, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
4ca0 sp=0xc0e2104bc8
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb50b8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
4d78 sp=0xc0e2104ca0
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5080, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
4e50 sp=0xc0e2104d78
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb5028, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
4f28 sp=0xc0e2104e50
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb4ff0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
5000 sp=0xc0e2104f28
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb4f98, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
50d8 sp=0xc0e2105000
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb4f60, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
51b0 sp=0xc0e21050d8
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb4f08, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
5288 sp=0xc0e21051b0
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb4ed0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
5360 sp=0xc0e2105288
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb4e78, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
5438 sp=0xc0e2105360
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb4e40, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
5510 sp=0xc0e2105438
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb4de8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
55e8 sp=0xc0e2105510
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb4db0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
56c0 sp=0xc0e21055e8
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb4d58, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
5798 sp=0xc0e21056c0
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb4d20, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
5870 sp=0xc0e2105798
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb4cc8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
5948 sp=0xc0e2105870
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb4c90, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
5a20 sp=0xc0e2105948
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb4c38, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
5af8 sp=0xc0e2105a20
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb4c00, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
5bd0 sp=0xc0e2105af8
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb4ba8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
5ca8 sp=0xc0e2105bd0
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb4b70, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
5d80 sp=0xc0e2105ca8
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb4b18, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
5e58 sp=0xc0e2105d80
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb4ae0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
5f30 sp=0xc0e2105e58
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb4a88, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
6008 sp=0xc0e2105f30
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb4a50, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
60e0 sp=0xc0e2106008
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb49f8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
61b8 sp=0xc0e21060e0
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb49c0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
6290 sp=0xc0e21061b8
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb4968, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
6368 sp=0xc0e2106290
github.com/github/git-media/gitmedia.recursiveResolveGitDir(0xc095fb4930, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/rick/github/git-media/.vendor/src/github.com/github/git-media/gitmedia/gitmedia.go:112 +0x3e7 fp=0xc0e210
6440 sp=0xc0e2106368
...additional frames elided...
created by _rt0_go
        /opt/boxen/chgo/versions/1.3/src/pkg/runtime/asm_amd64.s:97 +0x12c

goroutine 19 [finalizer wait]:
runtime.park(0x414b30, 0x993618, 0x991789)
        /opt/boxen/chgo/versions/1.3/src/pkg/runtime/proc.c:1369 +0xac
runtime.parkunlock(0x993618, 0x991789)
        /opt/boxen/chgo/versions/1.3/src/pkg/runtime/proc.c:1385 +0x42
runfinq()
        /opt/boxen/chgo/versions/1.3/src/pkg/runtime/mgc0.c:2644 +0xdd
runtime.goexit()
        /opt/boxen/chgo/versions/1.3/src/pkg/runtime/proc.c:1445

git media queues shows wrong paths when in submodules

In the following example, media-subs is a plain old git repo, rubyistmedia2 is github/rubyistmedia2 added as a submodule:

rubyist:rubyistmedia2 scott$ pwd
/Users/scott/media-subs/rubyistmedia2
rubyist:rubyistmedia2 scott$ git add headstock-back.jpg
rubyist:rubyistmedia2 scott$ git media queues
upload
  ../headstock-back.jpg

Write a man page

rubyist:git-media scott$ git help media
No manual entry for git-media

git-media path removals are rude

To add a point about the .gitattributes file, git media will ignore and preserve any lines in the file that aren't related to git media when adding/removing paths. It currently looks for "filter=media" in the line, but I think we can tighten that up a little because that would also match "filter=mediafoo".

Also, it looks like if you find a line with filter=media, you delete the whole line. But it might be that the line contains other, unrelated attributes, like

*.mp3 -crlf filter=media -text -diff

It would be rude to delete this whole line; instead it should be changed to

*.mp3 -crlf -text -diff

(i.e., only the one attribute should be removed).

via #102 (comment)

Installer should ensure $PREFIX exists

Just tried to run the install.sh on a fresh(-ish) osx install, which did not have a /usr/local/bin and it failed with:

$ sudo ./install.sh 
Password:
cp: /usr/local/bin/git-media: No such file or directory
git: 'media' is not a git command. See 'git --help'.

We should perhaps mkdir -p (or windows equivalent) in the scripts, or at least do a check and give a better error.

Queue listing fails in a non-root dir

When I run git media queues in the repository root, it reports the queue as expected. For example:

..[$] <( (git)-[master]-)> git media queues
upload
  c85d2d9ba03452788dcef6ad1421b886d436d9738d8500193aed57280d75a0a7
  50552252a127ecddce2d02844f70b79a7a24c82ddf5144a1d1c3c4ce9e0ec948
  b35dd0cb5a95ccf7bde2b8f29574a21331b71e02c5117a8b99ba8f40e05ae59d
  79d71829ac2df92cb3e204ec85f56a6bfbf659caabe5dc93cc82e1e41b2897e1
  93fe8e63ef24fcd7dbd61b7826f8b4c3bbfc77bfc5b3f008d7158fd752e63f10

When I run it from a subdirectory, it gives me this:

..[$] <( (git)-[master]-)> git media queues
upload

Easily workable-around, but recorded here for great justice.

investigate pre-push hook.

Git 1.8.2 has a pre_push hook. We should use that to run git media push. Let's use that so we don't have to worry about wrapping git push.

  • Add pre-push hook #62
  • git media init adds hook if run inside a repository. #62
  • The git media clean/smudge filters also install hooks when run inside a repository. #65

The goal is for this to work:

# the smudge filter downloads media files, and installs the hook locally.
$ git clone https://github.com/github/big-repo

# the clean filter queues the file to be uploaded, and installs the hook locally.
# this is useful in case the `clone` didn't have any big media files.
$ git add some-big-file.mov

$ git commit -m "whatevs"
$ git push # runs pre-push

improve push command, kill the queue

It's time to beef up the queue.

  • Don't queue the same file twice.
  • If a queued file is not in the push, don't push it.
  • Add a command to gc the queue.
  • Look for other Git Media objects in the queue to push.

These features are too much for the queuedir package. It'd be awesome to find a good Go based k/v store that'll work cross compiled to every platform.

Fixes #82 and #99. /cc #102 (comment)

high level command integration tests

I realized while hacking on #23 that I have no automated way to see if it worked. We need to be testing the high level git-media commands. Just off the top of my head I can think of three possible dummy repositories that would need tests:

  • repository with no git media
  • repository with git media
  • repository with submodule that has git media

GitHub.com embeds bare git repositories in the code. But this makes working with the git fixtures more complicated. However, having the test fixtures with the code being tested is nice.

Get local path for an object

I'm working on support for the Mac app and it seems like we need a way of getting the path for an object. Here's the situation I'm thinking of:

  1. User asks to diff image1@sha1 image1@sha2.
  2. It's possible/probable we don't have either of those actual objects downloaded yet.
  3. If they want to download them, we can use git-media-smudge blob@sha.
  4. How do we get the path? We could make assumptions about the directory structure of git-media, but it seems more forward-thinking if git-media provides us with the path instead.

Thoughts? Am I thinking about this right?

Progress reporting

Something along the lines of Git's built-in progress reporting would be really helpful.

Users should see progress that updates in-place (the X% bits are the important ones):

remote: Counting objects: 14, done.
remote: Compressing objects: 100% (14/14), done.
remote: Total 14 (delta 3), reused 7 (delta 0)
Unpacking objects: 100% (14/14), done.

When connected to something that's not a terminal, Git will increment the progress over multiple lines:

Unpacking objects: 14% (2/14), done.
Unpacking objects: 21% (3/14), done.
Unpacking objects: 57% (8/14), done.
Unpacking objects: 100% (14/14), done.

This will be really important for building clients on top of Git Media.

Add some kind of "doctor" command.

Whether it's part of git media init, or a separate sub command, Git Media should verify that its requirements are met, such as having the correct version of Git.

Here's how homebrew does it:

$ brew doctor
Please note that these warnings are just used to help the Homebrew maintainers
with debugging if you file an issue. If everything you use Homebrew for is
working fine: please don't worry and just ignore them. Thanks!

Warning: Your Homebrew is outdated.
You haven't updated for at least 24 hours, this is a long time in brewland!
To update Homebrew, run `brew update`.

It'd be cool to teach Git Media how to file issues to us somehow too.

General comments and questions about git-media spec

I made a bunch of notes while reading the git-media documentation, and I'm not really sure what to do with them. So I'm throwing them in this issue.

Some of these are general questions that were unanswered by the documentation. It would be nice if the docs were expanded to cover these topics.

Some are questions about the design, including points where I wonder if you've thought of this detail or the other. Probably most of these can just be waived away, but others might give you ideas for work that is needed.

I hope this is useful!

  • Can git media path add and git media path remove handle fully general .gitattributes files? Can they handle lines that have other (perhaps unrelated) attributes on them?
  • What if .gitattributes files are present at lower levels in the tree? Can git media path add and git media path remove still handle the situation somehow?
  • What .gitattributes files are used? Are they the ones in the working copy, or in the index, or HEAD, or what? I believe that this is determined by Git and not by git-media, but the answer could affect usability.
  • How does git media init work? I imagine it adds configuration to the user's global Git config, because the docs show it being used before git init or git clone. Is this polite? E.g., will there be cases where the user wants different git-media configurations for different repositories?
  • In docs/spec.md, section "The Server", the precedence of the three places where the Git Media server is sought should be specified explicitly (I think the order is currently incorrect).
  • In the same section, the sample Git config file has a key media.endpoint. Should that be media.url? (Maybe that is old nomenclature that has since been changed?) The term "endpoint" also appears in the text.
  • It seems to me that the layout of git-media-related data in the local repository (i.e., the data under $GIT_DIR) needs to be documented to enable other Git implementations and tools to work with the data correctly.
  • Is it always guaranteed that the media files are pushed to the server before commits that use them? Are media files only pushed if they are used in the specific commits being pushed, or are all media blobs pushed even if they are used in other commits that haven't been pushed yet? How is it determined whether a media blob is already present on the server? (Is this verified every time a commit referencing the asset is pushed to the server?) What happens if the server URI is changed? Is there a way to migrate the media assets to a different media server?
  • Is it possible to un-media-ize a file in a later commit without confusing earlier history?
  • Is it possible to discard media files from the local repo to save space (e.g., I only want the media assets associated with the current revision)? Is there a tool to help with this?
  • Is it possible to GC media files from the remote media server if, for example, the commits referencing them have been discarded? Would other clients handle this situation gracefully?
  • Is there a tool analogous to git cat-file blob to access the contents of a media-ized file (downloading it if necessary) without having to check out the commit containing it? (I don't think that git cat-file respects smudge filters but I might be wrong.)
  • Is there any provision for sharding the local media asset cache directory? (If the number of assets becomes large, the performance might suffer from too many files in a single directory.)
  • Is there a reference implementation of a git media server?
  • Is there provision to compress the media files somehow (e.g., on disk and on the wire), or are they assumed to be incompressible? Can they be stored as deltas somehow? (Some large file formats delta well.)
  • Regarding the API: Is it standard practice to use the Accept: header to distinguish between fetching the blob and the JSON metadata? (I would have expected that distinction to be encoded in the URI.)

git media init does not run outside of a git repo

If it's run from, e.g., your home directory it will not work:

rubyist:~ scott$ git media init
Not in a git repository

This command should be able to be run from anywhere. However, initialization is all done in the gitmedia package's init() function, well before we know what command we're going to run.

plumbing

  • upload file by name or oid
  • download file by oid
  • scan repository commits for git media oids

media filter should have a required attribute

Proposal:

From the gitattributes documentation:

One use of the content filtering is to massage the content into a shape that is
more convenient for the platform, filesystem, and the user to use. For this
mode of operation, the key phrase here is "more convenient" and not "turning
something unusable into usable". In other words, the intent is that if someone
unsets the filter driver definition, or does not have the appropriate filter
program, the project should still be usable.

Another use of the content filtering is to store the content that cannot be
directly used in the repository (e.g. a UUID that refers to the true content
stored outside git, or an encrypted content) and turn it into a usable form
upon checkout (e.g. download the external content, or decrypt the encrypted
content).

These two filters behave differently, and by default, a filter is taken as the
former, massaging the contents into more convenient shape. A missing filter
driver definition in the config, or a filter driver that exits with a non-zero
status, is not an error but makes the filter a no-op passthru.

You can declare that a filter turns a content that by itself is unusable into a
usable content by setting the filter.<driver>.required configuration variable
to true.


[...]

If a filter must succeed in order to make the stored contents usable, you can
declare that the filter is required, in the configuration: [...]

The git media meta files are of the second variety, storing content that cannot be directly used. One effect that it has is that if, e.g., git media is not installed it fails and bails out on the first attempt:

rubyist:~ scott$ git clone https://github.com/github/rubyistmedia2
Cloning into 'rubyistmedia2'...
remote: Reusing existing pack: 40, done.
remote: Counting objects: 9, done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 49 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (49/49), done.
Checking connectivity... done.
git-media-smudge '1206-cheeseburger-wallpaper.jpg': git-media-smudge: command not found
error: external filter git-media-smudge %f failed -1
error: external filter git-media-smudge %f failed
fatal: 1206-cheeseburger-wallpaper.jpg: smudge filter media failed
warning: Clone succeeded, but checkout failed.
You can inspect what was checked out with 'git status'
and retry the checkout with 'git checkout -f HEAD'

Whereas if the filter is not required it still tries it for every file:

rubyist:~ scott$ rm -rf rubyistmedia2/
rubyist:~ scott$ git clone https://github.com/github/rubyistmedia2
Cloning into 'rubyistmedia2'...
remote: Reusing existing pack: 40, done.
remote: Counting objects: 9, done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 49 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (49/49), done.
Checking connectivity... done.
git-media-smudge '1206-cheeseburger-wallpaper.jpg': git-media-smudge: command not found
error: external filter git-media-smudge %f failed -1
error: external filter git-media-smudge %f failed
git-media-smudge '79009_large.jpg': git-media-smudge: command not found
error: external filter git-media-smudge %f failed -1
error: external filter git-media-smudge %f failed
git-media-smudge '7KmMxb3.gif': git-media-smudge: command not found
error: external filter git-media-smudge %f failed -1
error: external filter git-media-smudge %f failed
git-media-smudge 'PONOPLS.jpg': git-media-smudge: command not found
error: external filter git-media-smudge %f failed -1
error: external filter git-media-smudge %f failed
git-media-smudge 'Salad_platter.jpg': git-media-smudge: command not found
error: external filter git-media-smudge %f failed -1
error: external filter git-media-smudge %f failed
git-media-smudge 'bear.jpg': git-media-smudge: command not found
error: external filter git-media-smudge %f failed -1
error: external filter git-media-smudge %f failed
git-media-smudge 'chilidog.jpg': git-media-smudge: command not found
error: external filter git-media-smudge %f failed -1
error: external filter git-media-smudge %f failed
git-media-smudge 'ocelot.jpg': git-media-smudge: command not found
error: external filter git-media-smudge %f failed -1
error: external filter git-media-smudge %f failed
git-media-smudge 'octocat.jpg': git-media-smudge: command not found
error: external filter git-media-smudge %f failed -1
error: external filter git-media-smudge %f failed
git-media-smudge 'pickle.png': git-media-smudge: command not found
error: external filter git-media-smudge %f failed -1
error: external filter git-media-smudge %f failed
git-media-smudge 'pipe-wrench-1.jpg': git-media-smudge: command not found
error: external filter git-media-smudge %f failed -1
error: external filter git-media-smudge %f failed
git-media-smudge 'taco.jpg': git-media-smudge: command not found
error: external filter git-media-smudge %f failed -1
error: external filter git-media-smudge %f failed
git-media-smudge 'tp.gif': git-media-smudge: command not found
error: external filter git-media-smudge %f failed -1
error: external filter git-media-smudge %f failed

When the filter is required, the clone succeeds but the checkout is not, allowing you to correct the problem then just checkout from the repo. If the filter is not required, then you have a bunch of git media metadata files which we currently don't have a good way of re-syncing.

So the proposal is to set the required bit when git media init sets up the filters:

[filter "media"]
        clean = git-media-clean %f
        smudge = git-media-smudge %f
        required

What do you think? πŸ‘ / πŸ‘Ž ?

`clean` adds a file multiple times to the upload queue

Any time git media clean is run, it adds the Git Media file to the upload queue. You can replicate this:

  1. Add/Edit a file. Note: You may not have to run git add {file} manually, git status or git diff seem to run the clean hook.
  2. Confirm that it's listed in git media queues.
  3. Edit the file again. Then run git add or check the diff again.
  4. git media queues should list the file twice.

We should clear out old jobs matching the filename.

/cc #82

Improve unit test error reporting

There are scenarios where a unit test fails and the only error line reported is commands_test.go:200, making it difficult to find the actual failure. The failure message should indicate the file and line number of the assertion that is triggering the failure.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.