diff options
Diffstat (limited to 'repository.go')
-rw-r--r-- | repository.go | 187 |
1 files changed, 155 insertions, 32 deletions
diff --git a/repository.go b/repository.go index 507ff44..de92d64 100644 --- a/repository.go +++ b/repository.go @@ -5,6 +5,7 @@ import ( "context" "errors" "fmt" + "io" stdioutil "io/ioutil" "os" "path" @@ -40,6 +41,8 @@ var ( ErrTagExists = errors.New("tag already exists") // ErrTagNotFound an error stating the specified tag does not exist ErrTagNotFound = errors.New("tag not found") + // ErrFetching is returned when the packfile could not be downloaded + ErrFetching = errors.New("unable to fetch packfile") ErrInvalidReference = errors.New("invalid reference, should be a tag or a branch") ErrRepositoryNotExists = errors.New("repository does not exist") @@ -341,13 +344,26 @@ func PlainClone(path string, isBare bool, o *CloneOptions) (*Repository, error) // transport operations. // // TODO(mcuadros): move isBare to CloneOptions in v5 +// TODO(smola): refuse upfront to clone on a non-empty directory in v5, see #1027 func PlainCloneContext(ctx context.Context, path string, isBare bool, o *CloneOptions) (*Repository, error) { + cleanup, cleanupParent, err := checkIfCleanupIsNeeded(path) + if err != nil { + return nil, err + } + r, err := PlainInit(path, isBare) if err != nil { return nil, err } - return r, r.clone(ctx, o) + err = r.clone(ctx, o) + if err != nil && err != ErrRepositoryAlreadyExists { + if cleanup { + cleanUpDir(path, cleanupParent) + } + } + + return r, err } func newRepository(s storage.Storer, worktree billy.Filesystem) *Repository { @@ -358,6 +374,65 @@ func newRepository(s storage.Storer, worktree billy.Filesystem) *Repository { } } +func checkIfCleanupIsNeeded(path string) (cleanup bool, cleanParent bool, err error) { + fi, err := os.Stat(path) + if err != nil { + if os.IsNotExist(err) { + return true, true, nil + } + + return false, false, err + } + + if !fi.IsDir() { + return false, false, fmt.Errorf("path is not a directory: %s", path) + } + + f, err := os.Open(path) + if err != nil { + return false, false, err + } + + defer ioutil.CheckClose(f, &err) + + _, err = f.Readdirnames(1) + if err == io.EOF { + return true, false, nil + } + + if err != nil { + return false, false, err + } + + return false, false, nil +} + +func cleanUpDir(path string, all bool) error { + if all { + return os.RemoveAll(path) + } + + f, err := os.Open(path) + if err != nil { + return err + } + + defer ioutil.CheckClose(f, &err) + + names, err := f.Readdirnames(-1) + if err != nil { + return err + } + + for _, name := range names { + if err := os.RemoveAll(filepath.Join(path, name)); err != nil { + return err + } + } + + return err +} + // Config return the repository config func (r *Repository) Config() (*config.Config, error) { return r.Storer.Config() @@ -640,8 +715,9 @@ func (r *Repository) clone(ctx context.Context, o *CloneOptions) error { } c := &config.RemoteConfig{ - Name: o.RemoteName, - URLs: []string{o.URL}, + Name: o.RemoteName, + URLs: []string{o.URL}, + Fetch: r.cloneRefSpec(o), } if _, err := r.CreateRemote(c); err != nil { @@ -649,7 +725,7 @@ func (r *Repository) clone(ctx context.Context, o *CloneOptions) error { } ref, err := r.fetchAndUpdateReferences(ctx, &FetchOptions{ - RefSpecs: r.cloneRefSpec(o, c), + RefSpecs: c.Fetch, Depth: o.Depth, Auth: o.Auth, Progress: o.Progress, @@ -719,21 +795,26 @@ const ( refspecSingleBranchHEAD = "+HEAD:refs/remotes/%s/HEAD" ) -func (r *Repository) cloneRefSpec(o *CloneOptions, c *config.RemoteConfig) []config.RefSpec { - var rs string - +func (r *Repository) cloneRefSpec(o *CloneOptions) []config.RefSpec { switch { case o.ReferenceName.IsTag(): - rs = fmt.Sprintf(refspecTag, o.ReferenceName.Short()) + return []config.RefSpec{ + config.RefSpec(fmt.Sprintf(refspecTag, o.ReferenceName.Short())), + } case o.SingleBranch && o.ReferenceName == plumbing.HEAD: - rs = fmt.Sprintf(refspecSingleBranchHEAD, c.Name) + return []config.RefSpec{ + config.RefSpec(fmt.Sprintf(refspecSingleBranchHEAD, o.RemoteName)), + config.RefSpec(fmt.Sprintf(refspecSingleBranch, plumbing.Master.Short(), o.RemoteName)), + } case o.SingleBranch: - rs = fmt.Sprintf(refspecSingleBranch, o.ReferenceName.Short(), c.Name) + return []config.RefSpec{ + config.RefSpec(fmt.Sprintf(refspecSingleBranch, o.ReferenceName.Short(), o.RemoteName)), + } default: - return c.Fetch + return []config.RefSpec{ + config.RefSpec(fmt.Sprintf(config.DefaultFetchRefSpec, o.RemoteName)), + } } - - return []config.RefSpec{config.RefSpec(rs)} } func (r *Repository) setIsBare(isBare bool) error { @@ -751,9 +832,7 @@ func (r *Repository) updateRemoteConfigIfNeeded(o *CloneOptions, c *config.Remot return nil } - c.Fetch = []config.RefSpec{config.RefSpec(fmt.Sprintf( - refspecSingleBranch, head.Name().Short(), c.Name, - ))} + c.Fetch = r.cloneRefSpec(o) cfg, err := r.Storer.Config() if err != nil { @@ -781,6 +860,8 @@ func (r *Repository) fetchAndUpdateReferences( remoteRefs, err := remote.fetch(ctx, o) if err == NoErrAlreadyUpToDate { objsUpdated = false + } else if err == packfile.ErrEmptyPackfile { + return nil, ErrFetching } else if err != nil { return nil, err } @@ -946,8 +1027,36 @@ func (r *Repository) PushContext(ctx context.Context, o *PushOptions) error { // Log returns the commit history from the given LogOptions. func (r *Repository) Log(o *LogOptions) (object.CommitIter, error) { - h := o.From - if o.From == plumbing.ZeroHash { + fn := commitIterFunc(o.Order) + if fn == nil { + return nil, fmt.Errorf("invalid Order=%v", o.Order) + } + + var ( + it object.CommitIter + err error + ) + if o.All { + it, err = r.logAll(fn) + } else { + it, err = r.log(o.From, fn) + } + + if err != nil { + return nil, err + } + + if o.FileName != nil { + // for `git log --all` also check parent (if the next commit comes from the real parent) + it = r.logWithFile(*o.FileName, it, o.All) + } + + return it, nil +} + +func (r *Repository) log(from plumbing.Hash, commitIterFunc func(*object.Commit) object.CommitIter) (object.CommitIter, error) { + h := from + if from == plumbing.ZeroHash { head, err := r.Head() if err != nil { return nil, err @@ -960,27 +1069,41 @@ func (r *Repository) Log(o *LogOptions) (object.CommitIter, error) { if err != nil { return nil, err } + return commitIterFunc(commit), nil +} + +func (r *Repository) logAll(commitIterFunc func(*object.Commit) object.CommitIter) (object.CommitIter, error) { + return object.NewCommitAllIter(r.Storer, commitIterFunc) +} + +func (*Repository) logWithFile(fileName string, commitIter object.CommitIter, checkParent bool) object.CommitIter { + return object.NewCommitFileIterFromIter(fileName, commitIter, checkParent) +} - var commitIter object.CommitIter - switch o.Order { +func commitIterFunc(order LogOrder) func(c *object.Commit) object.CommitIter { + switch order { case LogOrderDefault: - commitIter = object.NewCommitPreorderIter(commit, nil, nil) + return func(c *object.Commit) object.CommitIter { + return object.NewCommitPreorderIter(c, nil, nil) + } case LogOrderDFS: - commitIter = object.NewCommitPreorderIter(commit, nil, nil) + return func(c *object.Commit) object.CommitIter { + return object.NewCommitPreorderIter(c, nil, nil) + } case LogOrderDFSPost: - commitIter = object.NewCommitPostorderIter(commit, nil) + return func(c *object.Commit) object.CommitIter { + return object.NewCommitPostorderIter(c, nil) + } case LogOrderBSF: - commitIter = object.NewCommitIterBSF(commit, nil, nil) + return func(c *object.Commit) object.CommitIter { + return object.NewCommitIterBSF(c, nil, nil) + } case LogOrderCommitterTime: - commitIter = object.NewCommitIterCTime(commit, nil, nil) - default: - return nil, fmt.Errorf("invalid Order=%v", o.Order) - } - - if o.FileName == nil { - return commitIter, nil + return func(c *object.Commit) object.CommitIter { + return object.NewCommitIterCTime(c, nil, nil) + } } - return object.NewCommitFileIterFromIter(*o.FileName, commitIter), nil + return nil } // Tags returns all the tag References in a repository. |