Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,47 @@ $ git push --set-upstream ipld:// master
Note: Some features like remote tracking are still missing, though the plugin is
quite usable. IPNS helper is WIP and doesn't yet do what it should

## Custom API URL

It is possible to pass the API URL of the running ipfs daemon for cases where the
daemon is running as another user, in a container, in another machine, or
behind a proxy. One can either specify the API URL through the `IPFS_API_URL`
environment variable:

```
$ export IPFS_API_URL=http://localhost:5001

other git commands...
```

Or through the remote URL itself:

```
Clone an example repository:
$ git clone ipld://localhost:5001/2347e110c29742a1783134ef45f5bff58b29e40e

Pull a commit:
$ git pull ipld://localhost:5001/2347e110c29742a1783134ef45f5bff58b29e40e

Push (remember to end with slash if there is no previous hash):
$ git push --set-upstream ipld://localhost:5001/ master
```

If the API URL is **behind an https proxy**, then one can set `IPFS_API_URL` with the proper https URL. It is also possible to use the `ipld+https://` schema on the git command itself:

```
Clone an example repository:
$ git clone ipld+https://my.ipfs.domain/my_api/2347e110c29742a1783134ef45f5bff58b29e40e

Pull a commit:
$ git pull ipld+https://my.ipfs.domain/my_api/2347e110c29742a1783134ef45f5bff58b29e40e

Push (remember to end with slash if there is no previous hash):
$ git push --set-upstream ipld+https://my.ipfs.domain/my_api/ master
```

**Note:** The link `git-remote-ipld+https` must be created manually for this to work.

## Installation
1. `go get github.com/ipfs-shipyard/git-remote-ipld`
2. `make install`
Expand All @@ -32,6 +73,9 @@ quite usable. IPNS helper is WIP and doesn't yet do what it should
* `fetch: manifest has unsupported version: 2 (we support 3)` on any command
- This usually means that tracker data format has changed
- Simply do `rm -rf .git/ipld`
* Invalid hash when pushing to a new remote while specifying a custom API URL
- The remote URL **must** end with a forward slash if there is no hash specified:
- `git push --set-upstream ipld://127.0.0.1:5001/ master`

## License
MIT
7 changes: 6 additions & 1 deletion cmd/git-remote-ipld/ipld.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type refPath struct {
type IpnsHandler struct {
api *ipfs.Shell

apiURL string
remoteName string
currentHash string

Expand All @@ -44,7 +45,11 @@ type IpnsHandler struct {
}

func (h *IpnsHandler) Initialize(remote *core.Remote) error {
h.api = ipfs.NewLocalShell()
if len(h.apiURL) == 0 {
h.api = ipfs.NewLocalShell()
} else {
h.api = ipfs.NewShell(h.apiURL)
}
h.currentHash = h.remoteName
return nil
}
Expand Down
52 changes: 47 additions & 5 deletions cmd/git-remote-ipld/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import (
)

const (
IPLD_PREFIX = "ipld://"
IPFS_PREFIX = "ipfs://"
PREFIX_SEP = "://"

IPLD_PREFIX = "ipld" + PREFIX_SEP
IPFS_PREFIX = "ipfs" + PREFIX_SEP

EMPTY_REPO = "QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn"
)
Expand All @@ -23,15 +25,55 @@ func Main(args []string, reader io.Reader, writer io.Writer, logger *log.Logger)
}

remoteName := args[2]
if strings.HasPrefix(remoteName, IPLD_PREFIX) || strings.HasPrefix(remoteName, IPFS_PREFIX) {
remoteName = remoteName[len(IPLD_PREFIX):]

sepIdx := strings.Index(remoteName, PREFIX_SEP)
prefix := ""
if sepIdx > 0 {
prefix = remoteName[:sepIdx + len(PREFIX_SEP)]
remoteName = remoteName[len(prefix):]
} else {
return fmt.Errorf("Invalid URL: %s", remoteName)
}

apiPrefixIdx := strings.Index(prefix, "+")
apiPrefix := ""
if apiPrefixIdx == 0 {
return fmt.Errorf("Invalid URL prefix: %s", prefix)
} else if apiPrefixIdx > 0 {
apiPrefix = prefix[apiPrefixIdx+1:]
prefix = prefix[:apiPrefixIdx] + PREFIX_SEP
}

if prefix != IPLD_PREFIX && prefix != IPFS_PREFIX {
return fmt.Errorf("Unknown URL prefix: %s", prefix)
}

slashIdx := strings.LastIndex(remoteName, "/")

apiURL := os.Getenv("IPFS_API_URL")
if slashIdx < 0 {
if len(apiPrefix) != 0 {
return fmt.Errorf("Prefix has been provided for the API, but not an API URL")
}
} else {
apiURL = apiPrefix + remoteName[:slashIdx+1]
}

// Quirk: The go-ipfs-api throws unexpected redirect if the API URL ends with /
if strings.HasSuffix(apiURL, "/") {
apiURL = apiURL[:len(apiURL)-1]
}

if len(apiURL) > 0 {
fmt.Fprintf(os.Stderr, "Using API URL: %s\n", apiURL)
}

remoteName = remoteName[slashIdx+1:]
if remoteName == "" {
remoteName = EMPTY_REPO
}

remote, err := core.NewRemote(&IpnsHandler{remoteName: remoteName}, reader, writer, logger)
remote, err := core.NewRemote(&IpnsHandler{apiURL: apiURL, remoteName: remoteName}, reader, writer, logger, apiURL)
if err != nil {
return err
}
Expand Down
10 changes: 8 additions & 2 deletions core/fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,13 @@ type Fetch struct {
api *ipfs.Shell
}

func NewFetch(gitDir string, tracker *Tracker, provider ObjectProvider) *Fetch {
func NewFetch(gitDir string, tracker *Tracker, provider ObjectProvider, apiURL string) *Fetch {
var api *ipfs.Shell
if len(apiURL) == 0 {
api = ipfs.NewLocalShell()
} else {
api = ipfs.NewShell(apiURL)
}
return &Fetch{
objectDir: path.Join(gitDir, "objects"),
gitDir: gitDir,
Expand All @@ -60,7 +66,7 @@ func NewFetch(gitDir string, tracker *Tracker, provider ObjectProvider) *Fetch {
doneCh: make(chan []byte),

provider: provider,
api: ipfs.NewLocalShell(),
api: api,
}
}

Expand Down
11 changes: 9 additions & 2 deletions core/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
type Push struct {
objectDir string
gitDir string
apiURL string

done uint64
todoc uint64
Expand All @@ -40,10 +41,11 @@ type Push struct {
NewNode func(hash cid.Cid, data []byte) error
}

func NewPush(gitDir string, tracker *Tracker, repo *git.Repository) *Push {
func NewPush(gitDir string, tracker *Tracker, repo *git.Repository, apiURL string) *Push {
return &Push{
objectDir: path.Join(gitDir, "objects"),
gitDir: gitDir,
apiURL: apiURL,

todo: list.New(),
log: log.New(os.Stderr, "push: ", 0),
Expand All @@ -67,7 +69,12 @@ func (p *Push) PushHash(hash string) error {
func (p *Push) doWork() error {
defer p.wg.Wait()

api := ipfs.NewLocalShell()
var api *ipfs.Shell
if len(p.apiURL) == 0 {
api = ipfs.NewLocalShell()
} else {
api = ipfs.NewShell(p.apiURL)
}

intch := make(chan os.Signal, 1)
signal.Notify(intch, os.Interrupt)
Expand Down
8 changes: 5 additions & 3 deletions core/remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ type Remote struct {
Logger *log.Logger
localDir string

apiURL string

Repo *git.Repository
Tracker *Tracker

Expand All @@ -37,7 +39,7 @@ type Remote struct {
todo []func() (string, error)
}

func NewRemote(handler RemoteHandler, reader io.Reader, writer io.Writer, logger *log.Logger) (*Remote, error) {
func NewRemote(handler RemoteHandler, reader io.Reader, writer io.Writer, logger *log.Logger, apiURL string) (*Remote, error) {
localDir, err := GetLocalDir()
if err != nil {
return nil, err
Expand Down Expand Up @@ -87,11 +89,11 @@ func (r *Remote) Printf(format string, a ...interface{}) (n int, err error) {
}

func (r *Remote) NewPush() *Push {
return NewPush(r.localDir, r.Tracker, r.Repo)
return NewPush(r.localDir, r.Tracker, r.Repo, r.apiURL)
}

func (r *Remote) NewFetch() *Fetch {
return NewFetch(r.localDir, r.Tracker, r.Handler.ProvideBlock)
return NewFetch(r.localDir, r.Tracker, r.Handler.ProvideBlock, r.apiURL)
}

func (r *Remote) Close() error {
Expand Down