2016-04-06 19:06:34 +00:00
|
|
|
package commands
|
|
|
|
|
|
|
|
import (
|
2017-02-16 20:24:53 +00:00
|
|
|
"fmt"
|
2017-04-17 21:29:23 +00:00
|
|
|
"net/url"
|
2016-04-06 19:06:34 +00:00
|
|
|
"os"
|
2017-09-12 14:19:26 +00:00
|
|
|
"path/filepath"
|
2017-02-16 00:24:22 +00:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2017-01-13 22:57:58 +00:00
|
|
|
"sync"
|
2016-04-06 19:06:34 +00:00
|
|
|
|
2017-04-17 21:19:15 +00:00
|
|
|
"github.com/git-lfs/git-lfs/config"
|
2016-11-15 17:01:18 +00:00
|
|
|
"github.com/git-lfs/git-lfs/errors"
|
2017-02-16 00:24:22 +00:00
|
|
|
"github.com/git-lfs/git-lfs/git"
|
2016-11-15 17:01:18 +00:00
|
|
|
"github.com/git-lfs/git-lfs/lfs"
|
2017-02-16 00:24:22 +00:00
|
|
|
"github.com/git-lfs/git-lfs/lfsapi"
|
2017-01-12 23:39:53 +00:00
|
|
|
"github.com/git-lfs/git-lfs/locking"
|
2016-12-29 21:30:23 +00:00
|
|
|
"github.com/git-lfs/git-lfs/progress"
|
2016-11-15 17:01:18 +00:00
|
|
|
"github.com/git-lfs/git-lfs/tools"
|
2016-12-12 00:20:14 +00:00
|
|
|
"github.com/git-lfs/git-lfs/tq"
|
2017-02-16 00:24:22 +00:00
|
|
|
"github.com/rubyist/tracerx"
|
2016-04-06 19:06:34 +00:00
|
|
|
)
|
|
|
|
|
2017-02-16 20:24:53 +00:00
|
|
|
func uploadLeftOrAll(g *lfs.GitScanner, ctx *uploadContext, ref string) error {
|
|
|
|
if pushAll {
|
|
|
|
if err := g.ScanRefWithDeleted(ref, nil); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if err := g.ScanLeftToRemote(ref, nil); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ctx.scannerError()
|
|
|
|
}
|
|
|
|
|
2016-04-06 19:06:34 +00:00
|
|
|
type uploadContext struct {
|
2017-01-04 15:56:09 +00:00
|
|
|
Remote string
|
2016-04-06 19:06:34 +00:00
|
|
|
DryRun bool
|
2017-01-04 15:56:09 +00:00
|
|
|
Manifest *tq.Manifest
|
2016-07-07 16:16:13 +00:00
|
|
|
uploadedOids tools.StringSet
|
2016-12-29 21:30:23 +00:00
|
|
|
|
|
|
|
meter progress.Meter
|
|
|
|
tq *tq.TransferQueue
|
2017-01-12 23:39:53 +00:00
|
|
|
|
|
|
|
committerName string
|
|
|
|
committerEmail string
|
2017-01-13 22:57:58 +00:00
|
|
|
|
|
|
|
trackedLocksMu *sync.Mutex
|
2017-02-01 20:08:11 +00:00
|
|
|
|
|
|
|
// ALL verifiable locks
|
2017-04-21 21:33:30 +00:00
|
|
|
lockVerifyState verifyState
|
|
|
|
ourLocks map[string]locking.Lock
|
|
|
|
theirLocks map[string]locking.Lock
|
2017-02-01 20:08:11 +00:00
|
|
|
|
|
|
|
// locks from ourLocks that were modified in this push
|
2017-02-16 23:53:00 +00:00
|
|
|
ownedLocks []locking.Lock
|
2017-02-01 20:08:11 +00:00
|
|
|
|
|
|
|
// locks from theirLocks that were modified in this push
|
2017-02-16 23:53:00 +00:00
|
|
|
unownedLocks []locking.Lock
|
2017-02-16 20:24:53 +00:00
|
|
|
|
2017-05-02 18:24:59 +00:00
|
|
|
// allowMissing specifies whether pushes containing missing/corrupt
|
|
|
|
// pointers should allow pushing Git blobs
|
|
|
|
allowMissing bool
|
|
|
|
|
2017-02-16 20:24:53 +00:00
|
|
|
// tracks errors from gitscanner callbacks
|
|
|
|
scannerErr error
|
|
|
|
errMu sync.Mutex
|
2016-04-06 19:06:34 +00:00
|
|
|
}
|
|
|
|
|
2017-02-16 23:53:00 +00:00
|
|
|
// Determines if a filename is lockable. Serves as a wrapper around theirLocks
|
|
|
|
// that implements GitScannerSet.
|
|
|
|
type gitScannerLockables struct {
|
|
|
|
m map[string]locking.Lock
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *gitScannerLockables) Contains(name string) bool {
|
|
|
|
if l == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
_, ok := l.m[name]
|
|
|
|
return ok
|
|
|
|
}
|
|
|
|
|
2017-02-16 00:24:22 +00:00
|
|
|
type verifyState byte
|
|
|
|
|
|
|
|
const (
|
|
|
|
verifyStateUnknown verifyState = iota
|
|
|
|
verifyStateEnabled
|
|
|
|
verifyStateDisabled
|
|
|
|
)
|
|
|
|
|
2017-01-04 15:56:09 +00:00
|
|
|
func newUploadContext(remote string, dryRun bool) *uploadContext {
|
|
|
|
cfg.CurrentRemote = remote
|
2016-12-29 21:30:23 +00:00
|
|
|
|
2017-01-11 20:59:25 +00:00
|
|
|
ctx := &uploadContext{
|
2017-01-13 22:57:58 +00:00
|
|
|
Remote: remote,
|
2017-09-17 10:43:35 +00:00
|
|
|
Manifest: getTransferManifestOperationRemote("upload", remote),
|
2017-01-13 22:57:58 +00:00
|
|
|
DryRun: dryRun,
|
|
|
|
uploadedOids: tools.NewStringSet(),
|
2017-02-16 23:53:00 +00:00
|
|
|
ourLocks: make(map[string]locking.Lock),
|
|
|
|
theirLocks: make(map[string]locking.Lock),
|
2017-01-13 22:57:58 +00:00
|
|
|
trackedLocksMu: new(sync.Mutex),
|
2017-09-12 14:33:52 +00:00
|
|
|
allowMissing: cfg.Git.Bool("lfs.allowincompletepush", true),
|
2016-04-06 19:06:34 +00:00
|
|
|
}
|
2016-12-29 21:30:23 +00:00
|
|
|
|
|
|
|
ctx.meter = buildProgressMeter(ctx.DryRun)
|
2017-01-11 20:59:25 +00:00
|
|
|
ctx.tq = newUploadQueue(ctx.Manifest, ctx.Remote, tq.WithProgress(ctx.meter), tq.DryRun(ctx.DryRun))
|
2017-01-13 18:25:21 +00:00
|
|
|
ctx.committerName, ctx.committerEmail = cfg.CurrentCommitter()
|
2016-12-29 21:30:23 +00:00
|
|
|
|
2017-09-20 12:30:03 +00:00
|
|
|
// Do not check locks for standalone transfer, because there is no LFS
|
|
|
|
// server to ask.
|
|
|
|
if ctx.Manifest.IsStandaloneTransfer() {
|
|
|
|
ctx.lockVerifyState = verifyStateDisabled
|
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
2017-04-21 21:33:30 +00:00
|
|
|
ourLocks, theirLocks, verifyState := verifyLocks(remote)
|
|
|
|
ctx.lockVerifyState = verifyState
|
2017-02-16 00:24:22 +00:00
|
|
|
for _, l := range theirLocks {
|
2017-02-17 17:14:00 +00:00
|
|
|
ctx.theirLocks[l.Path] = l
|
2017-02-16 00:24:22 +00:00
|
|
|
}
|
|
|
|
for _, l := range ourLocks {
|
2017-02-17 17:14:00 +00:00
|
|
|
ctx.ourLocks[l.Path] = l
|
2017-02-16 00:24:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
2017-04-21 21:33:30 +00:00
|
|
|
func verifyLocks(remote string) (ours, theirs []locking.Lock, st verifyState) {
|
2017-02-16 00:24:22 +00:00
|
|
|
endpoint := getAPIClient().Endpoints.Endpoint("upload", remote)
|
|
|
|
state := getVerifyStateFor(endpoint)
|
2017-05-30 20:47:17 +00:00
|
|
|
if state == verifyStateDisabled {
|
|
|
|
return
|
|
|
|
}
|
2017-02-16 00:24:22 +00:00
|
|
|
|
2017-01-13 21:33:51 +00:00
|
|
|
lockClient := newLockClient(remote)
|
2017-02-16 00:24:22 +00:00
|
|
|
|
|
|
|
ours, theirs, err := lockClient.VerifiableLocks(0)
|
2017-01-13 21:33:51 +00:00
|
|
|
if err != nil {
|
2017-02-16 00:24:22 +00:00
|
|
|
if errors.IsNotImplementedError(err) {
|
|
|
|
disableFor(endpoint)
|
2017-04-26 19:43:59 +00:00
|
|
|
} else if state == verifyStateUnknown || state == verifyStateEnabled {
|
2017-04-27 16:49:15 +00:00
|
|
|
if errors.IsAuthError(err) {
|
2017-04-27 17:03:28 +00:00
|
|
|
if state == verifyStateUnknown {
|
|
|
|
Error("WARNING: Authentication error: %s", err)
|
|
|
|
} else if state == verifyStateEnabled {
|
|
|
|
Exit("ERROR: Authentication error: %s", err)
|
|
|
|
}
|
2017-04-27 16:49:15 +00:00
|
|
|
} else {
|
2017-04-26 19:43:59 +00:00
|
|
|
Print("Remote %q does not support the LFS locking API. Consider disabling it with:", remote)
|
2017-05-08 21:01:38 +00:00
|
|
|
Print(" $ git config lfs.%s.locksverify false", endpoint.Url)
|
2017-04-27 16:44:38 +00:00
|
|
|
if state == verifyStateEnabled {
|
|
|
|
ExitWithError(err)
|
|
|
|
}
|
2017-02-16 20:37:03 +00:00
|
|
|
}
|
2017-01-23 21:25:00 +00:00
|
|
|
}
|
2017-04-18 15:19:29 +00:00
|
|
|
} else if state == verifyStateUnknown {
|
2017-02-16 00:24:22 +00:00
|
|
|
Print("Locking support detected on remote %q. Consider enabling it with:", remote)
|
2017-05-08 21:01:38 +00:00
|
|
|
Print(" $ git config lfs.%s.locksverify true", endpoint.Url)
|
2017-01-13 21:33:51 +00:00
|
|
|
}
|
|
|
|
|
2017-04-21 21:33:30 +00:00
|
|
|
return ours, theirs, state
|
2016-04-06 19:06:34 +00:00
|
|
|
}
|
|
|
|
|
2017-02-16 20:24:53 +00:00
|
|
|
func (c *uploadContext) scannerError() error {
|
|
|
|
c.errMu.Lock()
|
|
|
|
defer c.errMu.Unlock()
|
|
|
|
|
|
|
|
return c.scannerErr
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *uploadContext) addScannerError(err error) {
|
|
|
|
c.errMu.Lock()
|
|
|
|
defer c.errMu.Unlock()
|
|
|
|
|
|
|
|
if c.scannerErr != nil {
|
|
|
|
c.scannerErr = fmt.Errorf("%v\n%v", c.scannerErr, err)
|
|
|
|
} else {
|
|
|
|
c.scannerErr = err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *uploadContext) buildGitScanner() (*lfs.GitScanner, error) {
|
|
|
|
gitscanner := lfs.NewGitScanner(func(p *lfs.WrappedPointer, err error) {
|
|
|
|
if err != nil {
|
|
|
|
c.addScannerError(err)
|
|
|
|
} else {
|
|
|
|
uploadPointers(c, p)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2017-02-16 23:53:00 +00:00
|
|
|
gitscanner.FoundLockable = func(name string) {
|
|
|
|
if lock, ok := c.theirLocks[name]; ok {
|
|
|
|
c.trackedLocksMu.Lock()
|
|
|
|
c.unownedLocks = append(c.unownedLocks, lock)
|
|
|
|
c.trackedLocksMu.Unlock()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gitscanner.PotentialLockables = &gitScannerLockables{m: c.theirLocks}
|
2017-02-16 20:24:53 +00:00
|
|
|
return gitscanner, gitscanner.RemoteForPush(c.Remote)
|
|
|
|
}
|
|
|
|
|
2016-04-07 16:52:24 +00:00
|
|
|
// AddUpload adds the given oid to the set of oids that have been uploaded in
|
|
|
|
// the current process.
|
2016-04-08 15:59:02 +00:00
|
|
|
func (c *uploadContext) SetUploaded(oid string) {
|
2016-04-07 16:52:24 +00:00
|
|
|
c.uploadedOids.Add(oid)
|
|
|
|
}
|
2016-04-06 19:06:34 +00:00
|
|
|
|
2016-04-07 16:52:24 +00:00
|
|
|
// HasUploaded determines if the given oid has already been uploaded in the
|
|
|
|
// current process.
|
|
|
|
func (c *uploadContext) HasUploaded(oid string) bool {
|
|
|
|
return c.uploadedOids.Contains(oid)
|
|
|
|
}
|
2016-04-06 19:06:34 +00:00
|
|
|
|
2016-12-29 21:36:28 +00:00
|
|
|
func (c *uploadContext) prepareUpload(unfiltered ...*lfs.WrappedPointer) (*tq.TransferQueue, []*lfs.WrappedPointer) {
|
2016-04-07 16:52:24 +00:00
|
|
|
numUnfiltered := len(unfiltered)
|
|
|
|
uploadables := make([]*lfs.WrappedPointer, 0, numUnfiltered)
|
2016-04-06 19:06:34 +00:00
|
|
|
|
2016-10-20 18:07:40 +00:00
|
|
|
// XXX(taylor): temporary measure to fix duplicate (broken) results from
|
|
|
|
// scanner
|
|
|
|
uniqOids := tools.NewStringSet()
|
|
|
|
|
2016-04-07 16:52:24 +00:00
|
|
|
// separate out objects that _should_ be uploaded, but don't exist in
|
|
|
|
// .git/lfs/objects. Those will skipped if the server already has them.
|
|
|
|
for _, p := range unfiltered {
|
2016-10-20 18:07:40 +00:00
|
|
|
// object already uploaded in this process, or we've already
|
|
|
|
// seen this OID (see above), skip!
|
|
|
|
if uniqOids.Contains(p.Oid) || c.HasUploaded(p.Oid) {
|
2016-04-07 16:52:24 +00:00
|
|
|
continue
|
2016-04-06 19:06:34 +00:00
|
|
|
}
|
2016-10-20 18:07:40 +00:00
|
|
|
uniqOids.Add(p.Oid)
|
2016-04-06 19:06:34 +00:00
|
|
|
|
2017-01-12 23:39:53 +00:00
|
|
|
// canUpload determines whether the current pointer "p" can be
|
|
|
|
// uploaded through the TransferQueue below. It is set to false
|
|
|
|
// only when the file is locked by someone other than the
|
|
|
|
// current committer.
|
|
|
|
var canUpload bool = true
|
2016-04-06 19:06:34 +00:00
|
|
|
|
2017-02-01 20:08:11 +00:00
|
|
|
if lock, ok := c.theirLocks[p.Name]; ok {
|
|
|
|
c.trackedLocksMu.Lock()
|
|
|
|
c.unownedLocks = append(c.unownedLocks, lock)
|
|
|
|
c.trackedLocksMu.Unlock()
|
2017-04-26 19:43:59 +00:00
|
|
|
|
2017-04-27 17:10:22 +00:00
|
|
|
// If the verification state is enabled, this failed
|
|
|
|
// locks verification means that the push should fail.
|
2017-04-26 19:43:59 +00:00
|
|
|
//
|
|
|
|
// If the state is disabled, the verification error is
|
|
|
|
// silent and the user can upload.
|
2017-04-27 17:10:22 +00:00
|
|
|
//
|
|
|
|
// If the state is undefined, the verification error is
|
|
|
|
// sent as a warning and the user can upload.
|
|
|
|
canUpload = c.lockVerifyState != verifyStateEnabled
|
2017-02-01 20:08:11 +00:00
|
|
|
}
|
2016-04-06 19:06:34 +00:00
|
|
|
|
2017-02-01 20:08:11 +00:00
|
|
|
if lock, ok := c.ourLocks[p.Name]; ok {
|
2017-01-13 22:57:58 +00:00
|
|
|
c.trackedLocksMu.Lock()
|
2017-02-01 20:08:11 +00:00
|
|
|
c.ownedLocks = append(c.ownedLocks, lock)
|
2017-01-13 22:57:58 +00:00
|
|
|
c.trackedLocksMu.Unlock()
|
2016-04-06 19:06:34 +00:00
|
|
|
}
|
2016-04-07 16:52:24 +00:00
|
|
|
|
2017-01-12 23:39:53 +00:00
|
|
|
if canUpload {
|
|
|
|
// estimate in meter early (even if it's not going into
|
|
|
|
// uploadables), since we will call Skip() based on the
|
|
|
|
// results of the download check queue.
|
|
|
|
c.meter.Add(p.Size)
|
|
|
|
|
|
|
|
uploadables = append(uploadables, p)
|
|
|
|
}
|
2016-11-17 15:08:18 +00:00
|
|
|
}
|
|
|
|
|
2016-12-29 21:30:23 +00:00
|
|
|
return c.tq, uploadables
|
2016-04-06 19:06:34 +00:00
|
|
|
}
|
|
|
|
|
2016-12-29 21:36:28 +00:00
|
|
|
func uploadPointers(c *uploadContext, unfiltered ...*lfs.WrappedPointer) {
|
2016-04-07 16:52:24 +00:00
|
|
|
if c.DryRun {
|
|
|
|
for _, p := range unfiltered {
|
|
|
|
if c.HasUploaded(p.Oid) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
Print("push %s => %s", p.Oid, p.Name)
|
2016-04-08 15:59:02 +00:00
|
|
|
c.SetUploaded(p.Oid)
|
2016-04-07 16:52:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-12-29 21:36:28 +00:00
|
|
|
q, pointers := c.prepareUpload(unfiltered...)
|
2016-04-07 16:52:24 +00:00
|
|
|
for _, p := range pointers {
|
2017-09-12 14:22:02 +00:00
|
|
|
t, err := uploadTransfer(p, c.allowMissing)
|
2017-01-11 20:59:25 +00:00
|
|
|
if err != nil && !errors.IsCleanPointerError(err) {
|
|
|
|
ExitWithError(err)
|
2016-04-07 16:52:24 +00:00
|
|
|
}
|
2016-04-06 19:06:34 +00:00
|
|
|
|
2016-12-15 20:49:04 +00:00
|
|
|
q.Add(t.Name, t.Path, t.Oid, t.Size)
|
2016-04-08 15:59:02 +00:00
|
|
|
c.SetUploaded(p.Oid)
|
2016-04-07 16:52:24 +00:00
|
|
|
}
|
2016-12-29 21:30:23 +00:00
|
|
|
}
|
2016-04-06 19:06:34 +00:00
|
|
|
|
2016-12-29 21:30:23 +00:00
|
|
|
func (c *uploadContext) Await() {
|
|
|
|
c.tq.Wait()
|
2016-04-06 19:06:34 +00:00
|
|
|
|
2017-03-24 20:37:29 +00:00
|
|
|
var missing = make(map[string]string)
|
|
|
|
var corrupt = make(map[string]string)
|
2017-03-24 22:05:48 +00:00
|
|
|
var others = make([]error, 0, len(c.tq.Errors()))
|
2017-03-24 20:37:29 +00:00
|
|
|
|
2016-12-29 21:30:23 +00:00
|
|
|
for _, err := range c.tq.Errors() {
|
2017-03-24 20:37:29 +00:00
|
|
|
if malformed, ok := err.(*tq.MalformedObjectError); ok {
|
|
|
|
if malformed.Missing() {
|
|
|
|
missing[malformed.Name] = malformed.Oid
|
|
|
|
} else if malformed.Corrupt() {
|
|
|
|
corrupt[malformed.Name] = malformed.Oid
|
|
|
|
}
|
|
|
|
} else {
|
2017-03-24 22:05:48 +00:00
|
|
|
others = append(others, err)
|
2017-03-24 20:37:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-02 18:22:06 +00:00
|
|
|
for _, err := range others {
|
|
|
|
FullError(err)
|
|
|
|
}
|
|
|
|
|
2017-03-24 20:37:29 +00:00
|
|
|
if len(missing) > 0 || len(corrupt) > 0 {
|
2017-05-02 18:27:17 +00:00
|
|
|
var action string
|
|
|
|
if c.allowMissing {
|
|
|
|
action = "missing objects"
|
|
|
|
} else {
|
|
|
|
action = "failed"
|
|
|
|
}
|
|
|
|
|
|
|
|
Print("LFS upload %s:", action)
|
2017-03-24 20:37:29 +00:00
|
|
|
for name, oid := range missing {
|
|
|
|
Print(" (missing) %s (%s)", name, oid)
|
|
|
|
}
|
|
|
|
for name, oid := range corrupt {
|
|
|
|
Print(" (corrupt) %s (%s)", name, oid)
|
|
|
|
}
|
2017-05-02 18:27:47 +00:00
|
|
|
|
|
|
|
if !c.allowMissing {
|
|
|
|
os.Exit(2)
|
|
|
|
}
|
2017-03-24 22:05:48 +00:00
|
|
|
}
|
2017-03-24 20:37:29 +00:00
|
|
|
|
2017-05-02 18:27:47 +00:00
|
|
|
if len(others) > 0 {
|
2017-01-23 21:25:17 +00:00
|
|
|
os.Exit(2)
|
2017-01-13 22:57:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
c.trackedLocksMu.Lock()
|
|
|
|
if ul := len(c.unownedLocks); ul > 0 {
|
|
|
|
Print("Unable to push %d locked file(s):", ul)
|
|
|
|
for _, unowned := range c.unownedLocks {
|
2017-02-03 17:48:31 +00:00
|
|
|
Print("* %s - %s", unowned.Path, unowned.Owner)
|
2017-01-13 22:57:58 +00:00
|
|
|
}
|
2017-01-12 23:39:53 +00:00
|
|
|
|
2017-04-21 21:33:30 +00:00
|
|
|
if c.lockVerifyState == verifyStateEnabled {
|
|
|
|
Exit("ERROR: Cannot update locked files.")
|
|
|
|
} else {
|
|
|
|
Error("WARNING: The above files would have halted this push.")
|
|
|
|
}
|
2017-04-26 19:43:59 +00:00
|
|
|
} else if len(c.ownedLocks) > 0 {
|
|
|
|
Print("Consider unlocking your own locked file(s): (`git lfs unlock <path>`)")
|
|
|
|
for _, owned := range c.ownedLocks {
|
|
|
|
Print("* %s", owned.Path)
|
|
|
|
}
|
2017-01-12 23:39:53 +00:00
|
|
|
}
|
2017-04-26 19:43:59 +00:00
|
|
|
c.trackedLocksMu.Unlock()
|
2016-04-07 16:52:24 +00:00
|
|
|
}
|
2017-02-16 00:24:22 +00:00
|
|
|
|
2017-04-18 15:19:43 +00:00
|
|
|
var (
|
2017-04-18 15:29:20 +00:00
|
|
|
githubHttps, _ = url.Parse("https://github.com")
|
|
|
|
githubSsh, _ = url.Parse("ssh://github.com")
|
|
|
|
|
2017-04-18 15:19:43 +00:00
|
|
|
// hostsWithKnownLockingSupport is a list of scheme-less hostnames
|
|
|
|
// (without port numbers) that are known to implement the LFS locking
|
|
|
|
// API.
|
|
|
|
//
|
|
|
|
// Additions are welcome.
|
2017-04-18 15:29:20 +00:00
|
|
|
hostsWithKnownLockingSupport = []*url.URL{
|
|
|
|
githubHttps, githubSsh,
|
2017-04-18 15:19:43 +00:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2017-09-12 14:22:02 +00:00
|
|
|
func uploadTransfer(p *lfs.WrappedPointer, allowMissing bool) (*tq.Transfer, error) {
|
2017-09-12 14:19:26 +00:00
|
|
|
filename := p.Name
|
|
|
|
oid := p.Oid
|
|
|
|
|
|
|
|
localMediaPath, err := lfs.LocalMediaPath(oid)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "Error uploading file %s (%s)", filename, oid)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(filename) > 0 {
|
2017-09-12 14:22:02 +00:00
|
|
|
if err = ensureFile(filename, localMediaPath, allowMissing); err != nil && !errors.IsCleanPointerError(err) {
|
2017-09-12 14:19:26 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return &tq.Transfer{
|
|
|
|
Name: filename,
|
|
|
|
Path: localMediaPath,
|
|
|
|
Oid: oid,
|
|
|
|
Size: p.Size,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ensureFile makes sure that the cleanPath exists before pushing it. If it
|
|
|
|
// does not exist, it attempts to clean it by reading the file at smudgePath.
|
2017-09-12 14:22:02 +00:00
|
|
|
func ensureFile(smudgePath, cleanPath string, allowMissing bool) error {
|
2017-09-12 14:19:26 +00:00
|
|
|
if _, err := os.Stat(cleanPath); err == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
localPath := filepath.Join(config.LocalWorkingDir, smudgePath)
|
|
|
|
file, err := os.Open(localPath)
|
|
|
|
if err != nil {
|
2017-09-12 14:22:02 +00:00
|
|
|
if allowMissing {
|
|
|
|
return nil
|
|
|
|
}
|
2017-09-12 14:19:26 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
defer file.Close()
|
|
|
|
|
|
|
|
stat, err := file.Stat()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
cleaned, err := lfs.PointerClean(file, file.Name(), stat.Size(), nil)
|
|
|
|
if cleaned != nil {
|
|
|
|
cleaned.Teardown()
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-02-16 00:24:22 +00:00
|
|
|
// getVerifyStateFor returns whether or not lock verification is enabled for the
|
|
|
|
// given "endpoint". If no state has been explicitly set, an "unknown" state
|
|
|
|
// will be returned instead.
|
|
|
|
func getVerifyStateFor(endpoint lfsapi.Endpoint) verifyState {
|
2017-04-17 21:19:15 +00:00
|
|
|
uc := config.NewURLConfig(cfg.Git)
|
2017-02-16 00:24:22 +00:00
|
|
|
|
2017-04-17 21:19:15 +00:00
|
|
|
v, ok := uc.Get("lfs", endpoint.Url, "locksverify")
|
2017-02-16 00:24:22 +00:00
|
|
|
if !ok {
|
2017-04-18 15:29:02 +00:00
|
|
|
if supportsLockingAPI(endpoint) {
|
2017-04-18 15:19:29 +00:00
|
|
|
return verifyStateEnabled
|
|
|
|
}
|
2017-02-16 00:24:22 +00:00
|
|
|
return verifyStateUnknown
|
|
|
|
}
|
|
|
|
|
|
|
|
if enabled, _ := strconv.ParseBool(v); enabled {
|
|
|
|
return verifyStateEnabled
|
|
|
|
}
|
|
|
|
return verifyStateDisabled
|
|
|
|
}
|
|
|
|
|
2017-04-18 15:19:43 +00:00
|
|
|
// supportsLockingAPI returns whether or not a given lfsapi.Endpoint "e"
|
|
|
|
// is known to support the LFS locking API by whether or not its hostname is
|
|
|
|
// included in the list above.
|
|
|
|
func supportsLockingAPI(e lfsapi.Endpoint) bool {
|
|
|
|
u, err := url.Parse(e.Url)
|
|
|
|
if err != nil {
|
|
|
|
tracerx.Printf("commands: unable to parse %q to determine locking support: %v", e.Url, err)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2017-04-18 15:29:20 +00:00
|
|
|
for _, supported := range hostsWithKnownLockingSupport {
|
|
|
|
if supported.Scheme == u.Scheme &&
|
|
|
|
supported.Hostname() == u.Hostname() &&
|
|
|
|
strings.HasPrefix(u.Path, supported.Path) {
|
2017-04-18 15:19:43 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2017-02-16 00:24:22 +00:00
|
|
|
// disableFor disables lock verification for the given lfsapi.Endpoint,
|
|
|
|
// "endpoint".
|
|
|
|
func disableFor(endpoint lfsapi.Endpoint) error {
|
|
|
|
tracerx.Printf("commands: disabling lock verification for %q", endpoint.Url)
|
|
|
|
|
|
|
|
key := strings.Join([]string{"lfs", endpoint.Url, "locksverify"}, ".")
|
|
|
|
|
|
|
|
_, err := git.Config.SetLocal("", key, "false")
|
|
|
|
return err
|
|
|
|
}
|