本站源代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1546 lines
38KB

  1. package git
  2. import (
  3. "bytes"
  4. "context"
  5. "errors"
  6. "fmt"
  7. "io"
  8. stdioutil "io/ioutil"
  9. "os"
  10. "path"
  11. "path/filepath"
  12. "strings"
  13. "time"
  14. "golang.org/x/crypto/openpgp"
  15. "gopkg.in/src-d/go-git.v4/config"
  16. "gopkg.in/src-d/go-git.v4/internal/revision"
  17. "gopkg.in/src-d/go-git.v4/plumbing"
  18. "gopkg.in/src-d/go-git.v4/plumbing/cache"
  19. "gopkg.in/src-d/go-git.v4/plumbing/format/packfile"
  20. "gopkg.in/src-d/go-git.v4/plumbing/object"
  21. "gopkg.in/src-d/go-git.v4/plumbing/storer"
  22. "gopkg.in/src-d/go-git.v4/storage"
  23. "gopkg.in/src-d/go-git.v4/storage/filesystem"
  24. "gopkg.in/src-d/go-git.v4/utils/ioutil"
  25. "gopkg.in/src-d/go-billy.v4"
  26. "gopkg.in/src-d/go-billy.v4/osfs"
  27. )
  28. // GitDirName this is a special folder where all the git stuff is.
  29. const GitDirName = ".git"
  30. var (
  31. // ErrBranchExists an error stating the specified branch already exists
  32. ErrBranchExists = errors.New("branch already exists")
  33. // ErrBranchNotFound an error stating the specified branch does not exist
  34. ErrBranchNotFound = errors.New("branch not found")
  35. // ErrTagExists an error stating the specified tag already exists
  36. ErrTagExists = errors.New("tag already exists")
  37. // ErrTagNotFound an error stating the specified tag does not exist
  38. ErrTagNotFound = errors.New("tag not found")
  39. // ErrFetching is returned when the packfile could not be downloaded
  40. ErrFetching = errors.New("unable to fetch packfile")
  41. ErrInvalidReference = errors.New("invalid reference, should be a tag or a branch")
  42. ErrRepositoryNotExists = errors.New("repository does not exist")
  43. ErrRepositoryAlreadyExists = errors.New("repository already exists")
  44. ErrRemoteNotFound = errors.New("remote not found")
  45. ErrRemoteExists = errors.New("remote already exists")
  46. ErrAnonymousRemoteName = errors.New("anonymous remote name must be 'anonymous'")
  47. ErrWorktreeNotProvided = errors.New("worktree should be provided")
  48. ErrIsBareRepository = errors.New("worktree not available in a bare repository")
  49. ErrUnableToResolveCommit = errors.New("unable to resolve commit")
  50. ErrPackedObjectsNotSupported = errors.New("Packed objects not supported")
  51. )
  52. // Repository represents a git repository
  53. type Repository struct {
  54. Storer storage.Storer
  55. r map[string]*Remote
  56. wt billy.Filesystem
  57. }
  58. // Init creates an empty git repository, based on the given Storer and worktree.
  59. // The worktree Filesystem is optional, if nil a bare repository is created. If
  60. // the given storer is not empty ErrRepositoryAlreadyExists is returned
  61. func Init(s storage.Storer, worktree billy.Filesystem) (*Repository, error) {
  62. if err := initStorer(s); err != nil {
  63. return nil, err
  64. }
  65. r := newRepository(s, worktree)
  66. _, err := r.Reference(plumbing.HEAD, false)
  67. switch err {
  68. case plumbing.ErrReferenceNotFound:
  69. case nil:
  70. return nil, ErrRepositoryAlreadyExists
  71. default:
  72. return nil, err
  73. }
  74. h := plumbing.NewSymbolicReference(plumbing.HEAD, plumbing.Master)
  75. if err := s.SetReference(h); err != nil {
  76. return nil, err
  77. }
  78. if worktree == nil {
  79. r.setIsBare(true)
  80. return r, nil
  81. }
  82. return r, setWorktreeAndStoragePaths(r, worktree)
  83. }
  84. func initStorer(s storer.Storer) error {
  85. i, ok := s.(storer.Initializer)
  86. if !ok {
  87. return nil
  88. }
  89. return i.Init()
  90. }
  91. func setWorktreeAndStoragePaths(r *Repository, worktree billy.Filesystem) error {
  92. type fsBased interface {
  93. Filesystem() billy.Filesystem
  94. }
  95. // .git file is only created if the storage is file based and the file
  96. // system is osfs.OS
  97. fs, isFSBased := r.Storer.(fsBased)
  98. if !isFSBased {
  99. return nil
  100. }
  101. if err := createDotGitFile(worktree, fs.Filesystem()); err != nil {
  102. return err
  103. }
  104. return setConfigWorktree(r, worktree, fs.Filesystem())
  105. }
  106. func createDotGitFile(worktree, storage billy.Filesystem) error {
  107. path, err := filepath.Rel(worktree.Root(), storage.Root())
  108. if err != nil {
  109. path = storage.Root()
  110. }
  111. if path == GitDirName {
  112. // not needed, since the folder is the default place
  113. return nil
  114. }
  115. f, err := worktree.Create(GitDirName)
  116. if err != nil {
  117. return err
  118. }
  119. defer f.Close()
  120. _, err = fmt.Fprintf(f, "gitdir: %s\n", path)
  121. return err
  122. }
  123. func setConfigWorktree(r *Repository, worktree, storage billy.Filesystem) error {
  124. path, err := filepath.Rel(storage.Root(), worktree.Root())
  125. if err != nil {
  126. path = worktree.Root()
  127. }
  128. if path == ".." {
  129. // not needed, since the folder is the default place
  130. return nil
  131. }
  132. cfg, err := r.Storer.Config()
  133. if err != nil {
  134. return err
  135. }
  136. cfg.Core.Worktree = path
  137. return r.Storer.SetConfig(cfg)
  138. }
  139. // Open opens a git repository using the given Storer and worktree filesystem,
  140. // if the given storer is complete empty ErrRepositoryNotExists is returned.
  141. // The worktree can be nil when the repository being opened is bare, if the
  142. // repository is a normal one (not bare) and worktree is nil the err
  143. // ErrWorktreeNotProvided is returned
  144. func Open(s storage.Storer, worktree billy.Filesystem) (*Repository, error) {
  145. _, err := s.Reference(plumbing.HEAD)
  146. if err == plumbing.ErrReferenceNotFound {
  147. return nil, ErrRepositoryNotExists
  148. }
  149. if err != nil {
  150. return nil, err
  151. }
  152. return newRepository(s, worktree), nil
  153. }
  154. // Clone a repository into the given Storer and worktree Filesystem with the
  155. // given options, if worktree is nil a bare repository is created. If the given
  156. // storer is not empty ErrRepositoryAlreadyExists is returned.
  157. //
  158. // The provided Context must be non-nil. If the context expires before the
  159. // operation is complete, an error is returned. The context only affects to the
  160. // transport operations.
  161. func Clone(s storage.Storer, worktree billy.Filesystem, o *CloneOptions) (*Repository, error) {
  162. return CloneContext(context.Background(), s, worktree, o)
  163. }
  164. // CloneContext a repository into the given Storer and worktree Filesystem with
  165. // the given options, if worktree is nil a bare repository is created. If the
  166. // given storer is not empty ErrRepositoryAlreadyExists is returned.
  167. //
  168. // The provided Context must be non-nil. If the context expires before the
  169. // operation is complete, an error is returned. The context only affects to the
  170. // transport operations.
  171. func CloneContext(
  172. ctx context.Context, s storage.Storer, worktree billy.Filesystem, o *CloneOptions,
  173. ) (*Repository, error) {
  174. r, err := Init(s, worktree)
  175. if err != nil {
  176. return nil, err
  177. }
  178. return r, r.clone(ctx, o)
  179. }
  180. // PlainInit create an empty git repository at the given path. isBare defines
  181. // if the repository will have worktree (non-bare) or not (bare), if the path
  182. // is not empty ErrRepositoryAlreadyExists is returned.
  183. func PlainInit(path string, isBare bool) (*Repository, error) {
  184. var wt, dot billy.Filesystem
  185. if isBare {
  186. dot = osfs.New(path)
  187. } else {
  188. wt = osfs.New(path)
  189. dot, _ = wt.Chroot(GitDirName)
  190. }
  191. s := filesystem.NewStorage(dot, cache.NewObjectLRUDefault())
  192. return Init(s, wt)
  193. }
  194. // PlainOpen opens a git repository from the given path. It detects if the
  195. // repository is bare or a normal one. If the path doesn't contain a valid
  196. // repository ErrRepositoryNotExists is returned
  197. func PlainOpen(path string) (*Repository, error) {
  198. return PlainOpenWithOptions(path, &PlainOpenOptions{})
  199. }
  200. // PlainOpenWithOptions opens a git repository from the given path with specific
  201. // options. See PlainOpen for more info.
  202. func PlainOpenWithOptions(path string, o *PlainOpenOptions) (*Repository, error) {
  203. dot, wt, err := dotGitToOSFilesystems(path, o.DetectDotGit)
  204. if err != nil {
  205. return nil, err
  206. }
  207. if _, err := dot.Stat(""); err != nil {
  208. if os.IsNotExist(err) {
  209. return nil, ErrRepositoryNotExists
  210. }
  211. return nil, err
  212. }
  213. s := filesystem.NewStorage(dot, cache.NewObjectLRUDefault())
  214. return Open(s, wt)
  215. }
  216. func dotGitToOSFilesystems(path string, detect bool) (dot, wt billy.Filesystem, err error) {
  217. if path, err = filepath.Abs(path); err != nil {
  218. return nil, nil, err
  219. }
  220. var fs billy.Filesystem
  221. var fi os.FileInfo
  222. for {
  223. fs = osfs.New(path)
  224. fi, err = fs.Stat(GitDirName)
  225. if err == nil {
  226. // no error; stop
  227. break
  228. }
  229. if !os.IsNotExist(err) {
  230. // unknown error; stop
  231. return nil, nil, err
  232. }
  233. if detect {
  234. // try its parent as long as we haven't reached
  235. // the root dir
  236. if dir := filepath.Dir(path); dir != path {
  237. path = dir
  238. continue
  239. }
  240. }
  241. // not detecting via parent dirs and the dir does not exist;
  242. // stop
  243. return fs, nil, nil
  244. }
  245. if fi.IsDir() {
  246. dot, err = fs.Chroot(GitDirName)
  247. return dot, fs, err
  248. }
  249. dot, err = dotGitFileToOSFilesystem(path, fs)
  250. if err != nil {
  251. return nil, nil, err
  252. }
  253. return dot, fs, nil
  254. }
  255. func dotGitFileToOSFilesystem(path string, fs billy.Filesystem) (bfs billy.Filesystem, err error) {
  256. f, err := fs.Open(GitDirName)
  257. if err != nil {
  258. return nil, err
  259. }
  260. defer ioutil.CheckClose(f, &err)
  261. b, err := stdioutil.ReadAll(f)
  262. if err != nil {
  263. return nil, err
  264. }
  265. line := string(b)
  266. const prefix = "gitdir: "
  267. if !strings.HasPrefix(line, prefix) {
  268. return nil, fmt.Errorf(".git file has no %s prefix", prefix)
  269. }
  270. gitdir := strings.Split(line[len(prefix):], "\n")[0]
  271. gitdir = strings.TrimSpace(gitdir)
  272. if filepath.IsAbs(gitdir) {
  273. return osfs.New(gitdir), nil
  274. }
  275. return osfs.New(fs.Join(path, gitdir)), nil
  276. }
  277. // PlainClone a repository into the path with the given options, isBare defines
  278. // if the new repository will be bare or normal. If the path is not empty
  279. // ErrRepositoryAlreadyExists is returned.
  280. //
  281. // TODO(mcuadros): move isBare to CloneOptions in v5
  282. func PlainClone(path string, isBare bool, o *CloneOptions) (*Repository, error) {
  283. return PlainCloneContext(context.Background(), path, isBare, o)
  284. }
  285. // PlainCloneContext a repository into the path with the given options, isBare
  286. // defines if the new repository will be bare or normal. If the path is not empty
  287. // ErrRepositoryAlreadyExists is returned.
  288. //
  289. // The provided Context must be non-nil. If the context expires before the
  290. // operation is complete, an error is returned. The context only affects to the
  291. // transport operations.
  292. //
  293. // TODO(mcuadros): move isBare to CloneOptions in v5
  294. // TODO(smola): refuse upfront to clone on a non-empty directory in v5, see #1027
  295. func PlainCloneContext(ctx context.Context, path string, isBare bool, o *CloneOptions) (*Repository, error) {
  296. cleanup, cleanupParent, err := checkIfCleanupIsNeeded(path)
  297. if err != nil {
  298. return nil, err
  299. }
  300. r, err := PlainInit(path, isBare)
  301. if err != nil {
  302. return nil, err
  303. }
  304. err = r.clone(ctx, o)
  305. if err != nil && err != ErrRepositoryAlreadyExists {
  306. if cleanup {
  307. cleanUpDir(path, cleanupParent)
  308. }
  309. }
  310. return r, err
  311. }
  312. func newRepository(s storage.Storer, worktree billy.Filesystem) *Repository {
  313. return &Repository{
  314. Storer: s,
  315. wt: worktree,
  316. r: make(map[string]*Remote),
  317. }
  318. }
  319. func checkIfCleanupIsNeeded(path string) (cleanup bool, cleanParent bool, err error) {
  320. fi, err := os.Stat(path)
  321. if err != nil {
  322. if os.IsNotExist(err) {
  323. return true, true, nil
  324. }
  325. return false, false, err
  326. }
  327. if !fi.IsDir() {
  328. return false, false, fmt.Errorf("path is not a directory: %s", path)
  329. }
  330. f, err := os.Open(path)
  331. if err != nil {
  332. return false, false, err
  333. }
  334. defer ioutil.CheckClose(f, &err)
  335. _, err = f.Readdirnames(1)
  336. if err == io.EOF {
  337. return true, false, nil
  338. }
  339. if err != nil {
  340. return false, false, err
  341. }
  342. return false, false, nil
  343. }
  344. func cleanUpDir(path string, all bool) error {
  345. if all {
  346. return os.RemoveAll(path)
  347. }
  348. f, err := os.Open(path)
  349. if err != nil {
  350. return err
  351. }
  352. defer ioutil.CheckClose(f, &err)
  353. names, err := f.Readdirnames(-1)
  354. if err != nil {
  355. return err
  356. }
  357. for _, name := range names {
  358. if err := os.RemoveAll(filepath.Join(path, name)); err != nil {
  359. return err
  360. }
  361. }
  362. return err
  363. }
  364. // Config return the repository config
  365. func (r *Repository) Config() (*config.Config, error) {
  366. return r.Storer.Config()
  367. }
  368. // Remote return a remote if exists
  369. func (r *Repository) Remote(name string) (*Remote, error) {
  370. cfg, err := r.Storer.Config()
  371. if err != nil {
  372. return nil, err
  373. }
  374. c, ok := cfg.Remotes[name]
  375. if !ok {
  376. return nil, ErrRemoteNotFound
  377. }
  378. return NewRemote(r.Storer, c), nil
  379. }
  380. // Remotes returns a list with all the remotes
  381. func (r *Repository) Remotes() ([]*Remote, error) {
  382. cfg, err := r.Storer.Config()
  383. if err != nil {
  384. return nil, err
  385. }
  386. remotes := make([]*Remote, len(cfg.Remotes))
  387. var i int
  388. for _, c := range cfg.Remotes {
  389. remotes[i] = NewRemote(r.Storer, c)
  390. i++
  391. }
  392. return remotes, nil
  393. }
  394. // CreateRemote creates a new remote
  395. func (r *Repository) CreateRemote(c *config.RemoteConfig) (*Remote, error) {
  396. if err := c.Validate(); err != nil {
  397. return nil, err
  398. }
  399. remote := NewRemote(r.Storer, c)
  400. cfg, err := r.Storer.Config()
  401. if err != nil {
  402. return nil, err
  403. }
  404. if _, ok := cfg.Remotes[c.Name]; ok {
  405. return nil, ErrRemoteExists
  406. }
  407. cfg.Remotes[c.Name] = c
  408. return remote, r.Storer.SetConfig(cfg)
  409. }
  410. // CreateRemoteAnonymous creates a new anonymous remote. c.Name must be "anonymous".
  411. // It's used like 'git fetch git@github.com:src-d/go-git.git master:master'.
  412. func (r *Repository) CreateRemoteAnonymous(c *config.RemoteConfig) (*Remote, error) {
  413. if err := c.Validate(); err != nil {
  414. return nil, err
  415. }
  416. if c.Name != "anonymous" {
  417. return nil, ErrAnonymousRemoteName
  418. }
  419. remote := NewRemote(r.Storer, c)
  420. return remote, nil
  421. }
  422. // DeleteRemote delete a remote from the repository and delete the config
  423. func (r *Repository) DeleteRemote(name string) error {
  424. cfg, err := r.Storer.Config()
  425. if err != nil {
  426. return err
  427. }
  428. if _, ok := cfg.Remotes[name]; !ok {
  429. return ErrRemoteNotFound
  430. }
  431. delete(cfg.Remotes, name)
  432. return r.Storer.SetConfig(cfg)
  433. }
  434. // Branch return a Branch if exists
  435. func (r *Repository) Branch(name string) (*config.Branch, error) {
  436. cfg, err := r.Storer.Config()
  437. if err != nil {
  438. return nil, err
  439. }
  440. b, ok := cfg.Branches[name]
  441. if !ok {
  442. return nil, ErrBranchNotFound
  443. }
  444. return b, nil
  445. }
  446. // CreateBranch creates a new Branch
  447. func (r *Repository) CreateBranch(c *config.Branch) error {
  448. if err := c.Validate(); err != nil {
  449. return err
  450. }
  451. cfg, err := r.Storer.Config()
  452. if err != nil {
  453. return err
  454. }
  455. if _, ok := cfg.Branches[c.Name]; ok {
  456. return ErrBranchExists
  457. }
  458. cfg.Branches[c.Name] = c
  459. return r.Storer.SetConfig(cfg)
  460. }
  461. // DeleteBranch delete a Branch from the repository and delete the config
  462. func (r *Repository) DeleteBranch(name string) error {
  463. cfg, err := r.Storer.Config()
  464. if err != nil {
  465. return err
  466. }
  467. if _, ok := cfg.Branches[name]; !ok {
  468. return ErrBranchNotFound
  469. }
  470. delete(cfg.Branches, name)
  471. return r.Storer.SetConfig(cfg)
  472. }
  473. // CreateTag creates a tag. If opts is included, the tag is an annotated tag,
  474. // otherwise a lightweight tag is created.
  475. func (r *Repository) CreateTag(name string, hash plumbing.Hash, opts *CreateTagOptions) (*plumbing.Reference, error) {
  476. rname := plumbing.ReferenceName(path.Join("refs", "tags", name))
  477. _, err := r.Storer.Reference(rname)
  478. switch err {
  479. case nil:
  480. // Tag exists, this is an error
  481. return nil, ErrTagExists
  482. case plumbing.ErrReferenceNotFound:
  483. // Tag missing, available for creation, pass this
  484. default:
  485. // Some other error
  486. return nil, err
  487. }
  488. var target plumbing.Hash
  489. if opts != nil {
  490. target, err = r.createTagObject(name, hash, opts)
  491. if err != nil {
  492. return nil, err
  493. }
  494. } else {
  495. target = hash
  496. }
  497. ref := plumbing.NewHashReference(rname, target)
  498. if err = r.Storer.SetReference(ref); err != nil {
  499. return nil, err
  500. }
  501. return ref, nil
  502. }
  503. func (r *Repository) createTagObject(name string, hash plumbing.Hash, opts *CreateTagOptions) (plumbing.Hash, error) {
  504. if err := opts.Validate(r, hash); err != nil {
  505. return plumbing.ZeroHash, err
  506. }
  507. rawobj, err := object.GetObject(r.Storer, hash)
  508. if err != nil {
  509. return plumbing.ZeroHash, err
  510. }
  511. tag := &object.Tag{
  512. Name: name,
  513. Tagger: *opts.Tagger,
  514. Message: opts.Message,
  515. TargetType: rawobj.Type(),
  516. Target: hash,
  517. }
  518. if opts.SignKey != nil {
  519. sig, err := r.buildTagSignature(tag, opts.SignKey)
  520. if err != nil {
  521. return plumbing.ZeroHash, err
  522. }
  523. tag.PGPSignature = sig
  524. }
  525. obj := r.Storer.NewEncodedObject()
  526. if err := tag.Encode(obj); err != nil {
  527. return plumbing.ZeroHash, err
  528. }
  529. return r.Storer.SetEncodedObject(obj)
  530. }
  531. func (r *Repository) buildTagSignature(tag *object.Tag, signKey *openpgp.Entity) (string, error) {
  532. encoded := &plumbing.MemoryObject{}
  533. if err := tag.Encode(encoded); err != nil {
  534. return "", err
  535. }
  536. rdr, err := encoded.Reader()
  537. if err != nil {
  538. return "", err
  539. }
  540. var b bytes.Buffer
  541. if err := openpgp.ArmoredDetachSign(&b, signKey, rdr, nil); err != nil {
  542. return "", err
  543. }
  544. return b.String(), nil
  545. }
  546. // Tag returns a tag from the repository.
  547. //
  548. // If you want to check to see if the tag is an annotated tag, you can call
  549. // TagObject on the hash of the reference in ForEach:
  550. //
  551. // ref, err := r.Tag("v0.1.0")
  552. // if err != nil {
  553. // // Handle error
  554. // }
  555. //
  556. // obj, err := r.TagObject(ref.Hash())
  557. // switch err {
  558. // case nil:
  559. // // Tag object present
  560. // case plumbing.ErrObjectNotFound:
  561. // // Not a tag object
  562. // default:
  563. // // Some other error
  564. // }
  565. //
  566. func (r *Repository) Tag(name string) (*plumbing.Reference, error) {
  567. ref, err := r.Reference(plumbing.ReferenceName(path.Join("refs", "tags", name)), false)
  568. if err != nil {
  569. if err == plumbing.ErrReferenceNotFound {
  570. // Return a friendly error for this one, versus just ReferenceNotFound.
  571. return nil, ErrTagNotFound
  572. }
  573. return nil, err
  574. }
  575. return ref, nil
  576. }
  577. // DeleteTag deletes a tag from the repository.
  578. func (r *Repository) DeleteTag(name string) error {
  579. _, err := r.Tag(name)
  580. if err != nil {
  581. return err
  582. }
  583. return r.Storer.RemoveReference(plumbing.ReferenceName(path.Join("refs", "tags", name)))
  584. }
  585. func (r *Repository) resolveToCommitHash(h plumbing.Hash) (plumbing.Hash, error) {
  586. obj, err := r.Storer.EncodedObject(plumbing.AnyObject, h)
  587. if err != nil {
  588. return plumbing.ZeroHash, err
  589. }
  590. switch obj.Type() {
  591. case plumbing.TagObject:
  592. t, err := object.DecodeTag(r.Storer, obj)
  593. if err != nil {
  594. return plumbing.ZeroHash, err
  595. }
  596. return r.resolveToCommitHash(t.Target)
  597. case plumbing.CommitObject:
  598. return h, nil
  599. default:
  600. return plumbing.ZeroHash, ErrUnableToResolveCommit
  601. }
  602. }
  603. // Clone clones a remote repository
  604. func (r *Repository) clone(ctx context.Context, o *CloneOptions) error {
  605. if err := o.Validate(); err != nil {
  606. return err
  607. }
  608. c := &config.RemoteConfig{
  609. Name: o.RemoteName,
  610. URLs: []string{o.URL},
  611. Fetch: r.cloneRefSpec(o),
  612. }
  613. if _, err := r.CreateRemote(c); err != nil {
  614. return err
  615. }
  616. ref, err := r.fetchAndUpdateReferences(ctx, &FetchOptions{
  617. RefSpecs: c.Fetch,
  618. Depth: o.Depth,
  619. Auth: o.Auth,
  620. Progress: o.Progress,
  621. Tags: o.Tags,
  622. RemoteName: o.RemoteName,
  623. }, o.ReferenceName)
  624. if err != nil {
  625. return err
  626. }
  627. if r.wt != nil && !o.NoCheckout {
  628. w, err := r.Worktree()
  629. if err != nil {
  630. return err
  631. }
  632. head, err := r.Head()
  633. if err != nil {
  634. return err
  635. }
  636. if err := w.Reset(&ResetOptions{
  637. Mode: MergeReset,
  638. Commit: head.Hash(),
  639. }); err != nil {
  640. return err
  641. }
  642. if o.RecurseSubmodules != NoRecurseSubmodules {
  643. if err := w.updateSubmodules(&SubmoduleUpdateOptions{
  644. RecurseSubmodules: o.RecurseSubmodules,
  645. Auth: o.Auth,
  646. }); err != nil {
  647. return err
  648. }
  649. }
  650. }
  651. if err := r.updateRemoteConfigIfNeeded(o, c, ref); err != nil {
  652. return err
  653. }
  654. if ref.Name().IsBranch() {
  655. branchRef := ref.Name()
  656. branchName := strings.Split(string(branchRef), "refs/heads/")[1]
  657. b := &config.Branch{
  658. Name: branchName,
  659. Merge: branchRef,
  660. }
  661. if o.RemoteName == "" {
  662. b.Remote = "origin"
  663. } else {
  664. b.Remote = o.RemoteName
  665. }
  666. if err := r.CreateBranch(b); err != nil {
  667. return err
  668. }
  669. }
  670. return nil
  671. }
  672. const (
  673. refspecTag = "+refs/tags/%s:refs/tags/%[1]s"
  674. refspecSingleBranch = "+refs/heads/%s:refs/remotes/%s/%[1]s"
  675. refspecSingleBranchHEAD = "+HEAD:refs/remotes/%s/HEAD"
  676. )
  677. func (r *Repository) cloneRefSpec(o *CloneOptions) []config.RefSpec {
  678. switch {
  679. case o.ReferenceName.IsTag():
  680. return []config.RefSpec{
  681. config.RefSpec(fmt.Sprintf(refspecTag, o.ReferenceName.Short())),
  682. }
  683. case o.SingleBranch && o.ReferenceName == plumbing.HEAD:
  684. return []config.RefSpec{
  685. config.RefSpec(fmt.Sprintf(refspecSingleBranchHEAD, o.RemoteName)),
  686. config.RefSpec(fmt.Sprintf(refspecSingleBranch, plumbing.Master.Short(), o.RemoteName)),
  687. }
  688. case o.SingleBranch:
  689. return []config.RefSpec{
  690. config.RefSpec(fmt.Sprintf(refspecSingleBranch, o.ReferenceName.Short(), o.RemoteName)),
  691. }
  692. default:
  693. return []config.RefSpec{
  694. config.RefSpec(fmt.Sprintf(config.DefaultFetchRefSpec, o.RemoteName)),
  695. }
  696. }
  697. }
  698. func (r *Repository) setIsBare(isBare bool) error {
  699. cfg, err := r.Storer.Config()
  700. if err != nil {
  701. return err
  702. }
  703. cfg.Core.IsBare = isBare
  704. return r.Storer.SetConfig(cfg)
  705. }
  706. func (r *Repository) updateRemoteConfigIfNeeded(o *CloneOptions, c *config.RemoteConfig, head *plumbing.Reference) error {
  707. if !o.SingleBranch {
  708. return nil
  709. }
  710. c.Fetch = r.cloneRefSpec(o)
  711. cfg, err := r.Storer.Config()
  712. if err != nil {
  713. return err
  714. }
  715. cfg.Remotes[c.Name] = c
  716. return r.Storer.SetConfig(cfg)
  717. }
  718. func (r *Repository) fetchAndUpdateReferences(
  719. ctx context.Context, o *FetchOptions, ref plumbing.ReferenceName,
  720. ) (*plumbing.Reference, error) {
  721. if err := o.Validate(); err != nil {
  722. return nil, err
  723. }
  724. remote, err := r.Remote(o.RemoteName)
  725. if err != nil {
  726. return nil, err
  727. }
  728. objsUpdated := true
  729. remoteRefs, err := remote.fetch(ctx, o)
  730. if err == NoErrAlreadyUpToDate {
  731. objsUpdated = false
  732. } else if err == packfile.ErrEmptyPackfile {
  733. return nil, ErrFetching
  734. } else if err != nil {
  735. return nil, err
  736. }
  737. resolvedRef, err := storer.ResolveReference(remoteRefs, ref)
  738. if err != nil {
  739. return nil, err
  740. }
  741. refsUpdated, err := r.updateReferences(remote.c.Fetch, resolvedRef)
  742. if err != nil {
  743. return nil, err
  744. }
  745. if !objsUpdated && !refsUpdated {
  746. return nil, NoErrAlreadyUpToDate
  747. }
  748. return resolvedRef, nil
  749. }
  750. func (r *Repository) updateReferences(spec []config.RefSpec,
  751. resolvedRef *plumbing.Reference) (updated bool, err error) {
  752. if !resolvedRef.Name().IsBranch() {
  753. // Detached HEAD mode
  754. h, err := r.resolveToCommitHash(resolvedRef.Hash())
  755. if err != nil {
  756. return false, err
  757. }
  758. head := plumbing.NewHashReference(plumbing.HEAD, h)
  759. return updateReferenceStorerIfNeeded(r.Storer, head)
  760. }
  761. refs := []*plumbing.Reference{
  762. // Create local reference for the resolved ref
  763. resolvedRef,
  764. // Create local symbolic HEAD
  765. plumbing.NewSymbolicReference(plumbing.HEAD, resolvedRef.Name()),
  766. }
  767. refs = append(refs, r.calculateRemoteHeadReference(spec, resolvedRef)...)
  768. for _, ref := range refs {
  769. u, err := updateReferenceStorerIfNeeded(r.Storer, ref)
  770. if err != nil {
  771. return updated, err
  772. }
  773. if u {
  774. updated = true
  775. }
  776. }
  777. return
  778. }
  779. func (r *Repository) calculateRemoteHeadReference(spec []config.RefSpec,
  780. resolvedHead *plumbing.Reference) []*plumbing.Reference {
  781. var refs []*plumbing.Reference
  782. // Create resolved HEAD reference with remote prefix if it does not
  783. // exist. This is needed when using single branch and HEAD.
  784. for _, rs := range spec {
  785. name := resolvedHead.Name()
  786. if !rs.Match(name) {
  787. continue
  788. }
  789. name = rs.Dst(name)
  790. _, err := r.Storer.Reference(name)
  791. if err == plumbing.ErrReferenceNotFound {
  792. refs = append(refs, plumbing.NewHashReference(name, resolvedHead.Hash()))
  793. }
  794. }
  795. return refs
  796. }
  797. func checkAndUpdateReferenceStorerIfNeeded(
  798. s storer.ReferenceStorer, r, old *plumbing.Reference) (
  799. updated bool, err error) {
  800. p, err := s.Reference(r.Name())
  801. if err != nil && err != plumbing.ErrReferenceNotFound {
  802. return false, err
  803. }
  804. // we use the string method to compare references, is the easiest way
  805. if err == plumbing.ErrReferenceNotFound || r.String() != p.String() {
  806. if err := s.CheckAndSetReference(r, old); err != nil {
  807. return false, err
  808. }
  809. return true, nil
  810. }
  811. return false, nil
  812. }
  813. func updateReferenceStorerIfNeeded(
  814. s storer.ReferenceStorer, r *plumbing.Reference) (updated bool, err error) {
  815. return checkAndUpdateReferenceStorerIfNeeded(s, r, nil)
  816. }
  817. // Fetch fetches references along with the objects necessary to complete
  818. // their histories, from the remote named as FetchOptions.RemoteName.
  819. //
  820. // Returns nil if the operation is successful, NoErrAlreadyUpToDate if there are
  821. // no changes to be fetched, or an error.
  822. func (r *Repository) Fetch(o *FetchOptions) error {
  823. return r.FetchContext(context.Background(), o)
  824. }
  825. // FetchContext fetches references along with the objects necessary to complete
  826. // their histories, from the remote named as FetchOptions.RemoteName.
  827. //
  828. // Returns nil if the operation is successful, NoErrAlreadyUpToDate if there are
  829. // no changes to be fetched, or an error.
  830. //
  831. // The provided Context must be non-nil. If the context expires before the
  832. // operation is complete, an error is returned. The context only affects to the
  833. // transport operations.
  834. func (r *Repository) FetchContext(ctx context.Context, o *FetchOptions) error {
  835. if err := o.Validate(); err != nil {
  836. return err
  837. }
  838. remote, err := r.Remote(o.RemoteName)
  839. if err != nil {
  840. return err
  841. }
  842. return remote.FetchContext(ctx, o)
  843. }
  844. // Push performs a push to the remote. Returns NoErrAlreadyUpToDate if
  845. // the remote was already up-to-date, from the remote named as
  846. // FetchOptions.RemoteName.
  847. func (r *Repository) Push(o *PushOptions) error {
  848. return r.PushContext(context.Background(), o)
  849. }
  850. // PushContext performs a push to the remote. Returns NoErrAlreadyUpToDate if
  851. // the remote was already up-to-date, from the remote named as
  852. // FetchOptions.RemoteName.
  853. //
  854. // The provided Context must be non-nil. If the context expires before the
  855. // operation is complete, an error is returned. The context only affects to the
  856. // transport operations.
  857. func (r *Repository) PushContext(ctx context.Context, o *PushOptions) error {
  858. if err := o.Validate(); err != nil {
  859. return err
  860. }
  861. remote, err := r.Remote(o.RemoteName)
  862. if err != nil {
  863. return err
  864. }
  865. return remote.PushContext(ctx, o)
  866. }
  867. // Log returns the commit history from the given LogOptions.
  868. func (r *Repository) Log(o *LogOptions) (object.CommitIter, error) {
  869. fn := commitIterFunc(o.Order)
  870. if fn == nil {
  871. return nil, fmt.Errorf("invalid Order=%v", o.Order)
  872. }
  873. var (
  874. it object.CommitIter
  875. err error
  876. )
  877. if o.All {
  878. it, err = r.logAll(fn)
  879. } else {
  880. it, err = r.log(o.From, fn)
  881. }
  882. if err != nil {
  883. return nil, err
  884. }
  885. if o.FileName != nil {
  886. // for `git log --all` also check parent (if the next commit comes from the real parent)
  887. it = r.logWithFile(*o.FileName, it, o.All)
  888. }
  889. return it, nil
  890. }
  891. func (r *Repository) log(from plumbing.Hash, commitIterFunc func(*object.Commit) object.CommitIter) (object.CommitIter, error) {
  892. h := from
  893. if from == plumbing.ZeroHash {
  894. head, err := r.Head()
  895. if err != nil {
  896. return nil, err
  897. }
  898. h = head.Hash()
  899. }
  900. commit, err := r.CommitObject(h)
  901. if err != nil {
  902. return nil, err
  903. }
  904. return commitIterFunc(commit), nil
  905. }
  906. func (r *Repository) logAll(commitIterFunc func(*object.Commit) object.CommitIter) (object.CommitIter, error) {
  907. return object.NewCommitAllIter(r.Storer, commitIterFunc)
  908. }
  909. func (*Repository) logWithFile(fileName string, commitIter object.CommitIter, checkParent bool) object.CommitIter {
  910. return object.NewCommitFileIterFromIter(fileName, commitIter, checkParent)
  911. }
  912. func commitIterFunc(order LogOrder) func(c *object.Commit) object.CommitIter {
  913. switch order {
  914. case LogOrderDefault:
  915. return func(c *object.Commit) object.CommitIter {
  916. return object.NewCommitPreorderIter(c, nil, nil)
  917. }
  918. case LogOrderDFS:
  919. return func(c *object.Commit) object.CommitIter {
  920. return object.NewCommitPreorderIter(c, nil, nil)
  921. }
  922. case LogOrderDFSPost:
  923. return func(c *object.Commit) object.CommitIter {
  924. return object.NewCommitPostorderIter(c, nil)
  925. }
  926. case LogOrderBSF:
  927. return func(c *object.Commit) object.CommitIter {
  928. return object.NewCommitIterBSF(c, nil, nil)
  929. }
  930. case LogOrderCommitterTime:
  931. return func(c *object.Commit) object.CommitIter {
  932. return object.NewCommitIterCTime(c, nil, nil)
  933. }
  934. }
  935. return nil
  936. }
  937. // Tags returns all the tag References in a repository.
  938. //
  939. // If you want to check to see if the tag is an annotated tag, you can call
  940. // TagObject on the hash Reference passed in through ForEach:
  941. //
  942. // iter, err := r.Tags()
  943. // if err != nil {
  944. // // Handle error
  945. // }
  946. //
  947. // if err := iter.ForEach(func (ref *plumbing.Reference) error {
  948. // obj, err := r.TagObject(ref.Hash())
  949. // switch err {
  950. // case nil:
  951. // // Tag object present
  952. // case plumbing.ErrObjectNotFound:
  953. // // Not a tag object
  954. // default:
  955. // // Some other error
  956. // return err
  957. // }
  958. // }); err != nil {
  959. // // Handle outer iterator error
  960. // }
  961. //
  962. func (r *Repository) Tags() (storer.ReferenceIter, error) {
  963. refIter, err := r.Storer.IterReferences()
  964. if err != nil {
  965. return nil, err
  966. }
  967. return storer.NewReferenceFilteredIter(
  968. func(r *plumbing.Reference) bool {
  969. return r.Name().IsTag()
  970. }, refIter), nil
  971. }
  972. // Branches returns all the References that are Branches.
  973. func (r *Repository) Branches() (storer.ReferenceIter, error) {
  974. refIter, err := r.Storer.IterReferences()
  975. if err != nil {
  976. return nil, err
  977. }
  978. return storer.NewReferenceFilteredIter(
  979. func(r *plumbing.Reference) bool {
  980. return r.Name().IsBranch()
  981. }, refIter), nil
  982. }
  983. // Notes returns all the References that are notes. For more information:
  984. // https://git-scm.com/docs/git-notes
  985. func (r *Repository) Notes() (storer.ReferenceIter, error) {
  986. refIter, err := r.Storer.IterReferences()
  987. if err != nil {
  988. return nil, err
  989. }
  990. return storer.NewReferenceFilteredIter(
  991. func(r *plumbing.Reference) bool {
  992. return r.Name().IsNote()
  993. }, refIter), nil
  994. }
  995. // TreeObject return a Tree with the given hash. If not found
  996. // plumbing.ErrObjectNotFound is returned
  997. func (r *Repository) TreeObject(h plumbing.Hash) (*object.Tree, error) {
  998. return object.GetTree(r.Storer, h)
  999. }
  1000. // TreeObjects returns an unsorted TreeIter with all the trees in the repository
  1001. func (r *Repository) TreeObjects() (*object.TreeIter, error) {
  1002. iter, err := r.Storer.IterEncodedObjects(plumbing.TreeObject)
  1003. if err != nil {
  1004. return nil, err
  1005. }
  1006. return object.NewTreeIter(r.Storer, iter), nil
  1007. }
  1008. // CommitObject return a Commit with the given hash. If not found
  1009. // plumbing.ErrObjectNotFound is returned.
  1010. func (r *Repository) CommitObject(h plumbing.Hash) (*object.Commit, error) {
  1011. return object.GetCommit(r.Storer, h)
  1012. }
  1013. // CommitObjects returns an unsorted CommitIter with all the commits in the repository.
  1014. func (r *Repository) CommitObjects() (object.CommitIter, error) {
  1015. iter, err := r.Storer.IterEncodedObjects(plumbing.CommitObject)
  1016. if err != nil {
  1017. return nil, err
  1018. }
  1019. return object.NewCommitIter(r.Storer, iter), nil
  1020. }
  1021. // BlobObject returns a Blob with the given hash. If not found
  1022. // plumbing.ErrObjectNotFound is returned.
  1023. func (r *Repository) BlobObject(h plumbing.Hash) (*object.Blob, error) {
  1024. return object.GetBlob(r.Storer, h)
  1025. }
  1026. // BlobObjects returns an unsorted BlobIter with all the blobs in the repository.
  1027. func (r *Repository) BlobObjects() (*object.BlobIter, error) {
  1028. iter, err := r.Storer.IterEncodedObjects(plumbing.BlobObject)
  1029. if err != nil {
  1030. return nil, err
  1031. }
  1032. return object.NewBlobIter(r.Storer, iter), nil
  1033. }
  1034. // TagObject returns a Tag with the given hash. If not found
  1035. // plumbing.ErrObjectNotFound is returned. This method only returns
  1036. // annotated Tags, no lightweight Tags.
  1037. func (r *Repository) TagObject(h plumbing.Hash) (*object.Tag, error) {
  1038. return object.GetTag(r.Storer, h)
  1039. }
  1040. // TagObjects returns a unsorted TagIter that can step through all of the annotated
  1041. // tags in the repository.
  1042. func (r *Repository) TagObjects() (*object.TagIter, error) {
  1043. iter, err := r.Storer.IterEncodedObjects(plumbing.TagObject)
  1044. if err != nil {
  1045. return nil, err
  1046. }
  1047. return object.NewTagIter(r.Storer, iter), nil
  1048. }
  1049. // Object returns an Object with the given hash. If not found
  1050. // plumbing.ErrObjectNotFound is returned.
  1051. func (r *Repository) Object(t plumbing.ObjectType, h plumbing.Hash) (object.Object, error) {
  1052. obj, err := r.Storer.EncodedObject(t, h)
  1053. if err != nil {
  1054. return nil, err
  1055. }
  1056. return object.DecodeObject(r.Storer, obj)
  1057. }
  1058. // Objects returns an unsorted ObjectIter with all the objects in the repository.
  1059. func (r *Repository) Objects() (*object.ObjectIter, error) {
  1060. iter, err := r.Storer.IterEncodedObjects(plumbing.AnyObject)
  1061. if err != nil {
  1062. return nil, err
  1063. }
  1064. return object.NewObjectIter(r.Storer, iter), nil
  1065. }
  1066. // Head returns the reference where HEAD is pointing to.
  1067. func (r *Repository) Head() (*plumbing.Reference, error) {
  1068. return storer.ResolveReference(r.Storer, plumbing.HEAD)
  1069. }
  1070. // Reference returns the reference for a given reference name. If resolved is
  1071. // true, any symbolic reference will be resolved.
  1072. func (r *Repository) Reference(name plumbing.ReferenceName, resolved bool) (
  1073. *plumbing.Reference, error) {
  1074. if resolved {
  1075. return storer.ResolveReference(r.Storer, name)
  1076. }
  1077. return r.Storer.Reference(name)
  1078. }
  1079. // References returns an unsorted ReferenceIter for all references.
  1080. func (r *Repository) References() (storer.ReferenceIter, error) {
  1081. return r.Storer.IterReferences()
  1082. }
  1083. // Worktree returns a worktree based on the given fs, if nil the default
  1084. // worktree will be used.
  1085. func (r *Repository) Worktree() (*Worktree, error) {
  1086. if r.wt == nil {
  1087. return nil, ErrIsBareRepository
  1088. }
  1089. return &Worktree{r: r, Filesystem: r.wt}, nil
  1090. }
  1091. // ResolveRevision resolves revision to corresponding hash. It will always
  1092. // resolve to a commit hash, not a tree or annotated tag.
  1093. //
  1094. // Implemented resolvers : HEAD, branch, tag, heads/branch, refs/heads/branch,
  1095. // refs/tags/tag, refs/remotes/origin/branch, refs/remotes/origin/HEAD, tilde and caret (HEAD~1, master~^, tag~2, ref/heads/master~1, ...), selection by text (HEAD^{/fix nasty bug})
  1096. func (r *Repository) ResolveRevision(rev plumbing.Revision) (*plumbing.Hash, error) {
  1097. p := revision.NewParserFromString(string(rev))
  1098. items, err := p.Parse()
  1099. if err != nil {
  1100. return nil, err
  1101. }
  1102. var commit *object.Commit
  1103. for _, item := range items {
  1104. switch item.(type) {
  1105. case revision.Ref:
  1106. revisionRef := item.(revision.Ref)
  1107. var tryHashes []plumbing.Hash
  1108. maybeHash := plumbing.NewHash(string(revisionRef))
  1109. if !maybeHash.IsZero() {
  1110. tryHashes = append(tryHashes, maybeHash)
  1111. }
  1112. for _, rule := range append([]string{"%s"}, plumbing.RefRevParseRules...) {
  1113. ref, err := storer.ResolveReference(r.Storer, plumbing.ReferenceName(fmt.Sprintf(rule, revisionRef)))
  1114. if err == nil {
  1115. tryHashes = append(tryHashes, ref.Hash())
  1116. break
  1117. }
  1118. }
  1119. // in ambiguous cases, `git rev-parse` will emit a warning, but
  1120. // will always return the oid in preference to a ref; we don't have
  1121. // the ability to emit a warning here, so (for speed purposes)
  1122. // don't bother to detect the ambiguity either, just return in the
  1123. // priority that git would.
  1124. gotOne := false
  1125. for _, hash := range tryHashes {
  1126. commitObj, err := r.CommitObject(hash)
  1127. if err == nil {
  1128. commit = commitObj
  1129. gotOne = true
  1130. break
  1131. }
  1132. tagObj, err := r.TagObject(hash)
  1133. if err == nil {
  1134. // If the tag target lookup fails here, this most likely
  1135. // represents some sort of repo corruption, so let the
  1136. // error bubble up.
  1137. tagCommit, err := tagObj.Commit()
  1138. if err != nil {
  1139. return &plumbing.ZeroHash, err
  1140. }
  1141. commit = tagCommit
  1142. gotOne = true
  1143. break
  1144. }
  1145. }
  1146. if !gotOne {
  1147. return &plumbing.ZeroHash, plumbing.ErrReferenceNotFound
  1148. }
  1149. case revision.CaretPath:
  1150. depth := item.(revision.CaretPath).Depth
  1151. if depth == 0 {
  1152. break
  1153. }
  1154. iter := commit.Parents()
  1155. c, err := iter.Next()
  1156. if err != nil {
  1157. return &plumbing.ZeroHash, err
  1158. }
  1159. if depth == 1 {
  1160. commit = c
  1161. break
  1162. }
  1163. c, err = iter.Next()
  1164. if err != nil {
  1165. return &plumbing.ZeroHash, err
  1166. }
  1167. commit = c
  1168. case revision.TildePath:
  1169. for i := 0; i < item.(revision.TildePath).Depth; i++ {
  1170. c, err := commit.Parents().Next()
  1171. if err != nil {
  1172. return &plumbing.ZeroHash, err
  1173. }
  1174. commit = c
  1175. }
  1176. case revision.CaretReg:
  1177. history := object.NewCommitPreorderIter(commit, nil, nil)
  1178. re := item.(revision.CaretReg).Regexp
  1179. negate := item.(revision.CaretReg).Negate
  1180. var c *object.Commit
  1181. err := history.ForEach(func(hc *object.Commit) error {
  1182. if !negate && re.MatchString(hc.Message) {
  1183. c = hc
  1184. return storer.ErrStop
  1185. }
  1186. if negate && !re.MatchString(hc.Message) {
  1187. c = hc
  1188. return storer.ErrStop
  1189. }
  1190. return nil
  1191. })
  1192. if err != nil {
  1193. return &plumbing.ZeroHash, err
  1194. }
  1195. if c == nil {
  1196. return &plumbing.ZeroHash, fmt.Errorf(`No commit message match regexp : "%s"`, re.String())
  1197. }
  1198. commit = c
  1199. }
  1200. }
  1201. return &commit.Hash, nil
  1202. }
  1203. type RepackConfig struct {
  1204. // UseRefDeltas configures whether packfile encoder will use reference deltas.
  1205. // By default OFSDeltaObject is used.
  1206. UseRefDeltas bool
  1207. // OnlyDeletePacksOlderThan if set to non-zero value
  1208. // selects only objects older than the time provided.
  1209. OnlyDeletePacksOlderThan time.Time
  1210. }
  1211. func (r *Repository) RepackObjects(cfg *RepackConfig) (err error) {
  1212. pos, ok := r.Storer.(storer.PackedObjectStorer)
  1213. if !ok {
  1214. return ErrPackedObjectsNotSupported
  1215. }
  1216. // Get the existing object packs.
  1217. hs, err := pos.ObjectPacks()
  1218. if err != nil {
  1219. return err
  1220. }
  1221. // Create a new pack.
  1222. nh, err := r.createNewObjectPack(cfg)
  1223. if err != nil {
  1224. return err
  1225. }
  1226. // Delete old packs.
  1227. for _, h := range hs {
  1228. // Skip if new hash is the same as an old one.
  1229. if h == nh {
  1230. continue
  1231. }
  1232. err = pos.DeleteOldObjectPackAndIndex(h, cfg.OnlyDeletePacksOlderThan)
  1233. if err != nil {
  1234. return err
  1235. }
  1236. }
  1237. return nil
  1238. }
  1239. // createNewObjectPack is a helper for RepackObjects taking care
  1240. // of creating a new pack. It is used so the the PackfileWriter
  1241. // deferred close has the right scope.
  1242. func (r *Repository) createNewObjectPack(cfg *RepackConfig) (h plumbing.Hash, err error) {
  1243. ow := newObjectWalker(r.Storer)
  1244. err = ow.walkAllRefs()
  1245. if err != nil {
  1246. return h, err
  1247. }
  1248. objs := make([]plumbing.Hash, 0, len(ow.seen))
  1249. for h := range ow.seen {
  1250. objs = append(objs, h)
  1251. }
  1252. pfw, ok := r.Storer.(storer.PackfileWriter)
  1253. if !ok {
  1254. return h, fmt.Errorf("Repository storer is not a storer.PackfileWriter")
  1255. }
  1256. wc, err := pfw.PackfileWriter()
  1257. if err != nil {
  1258. return h, err
  1259. }
  1260. defer ioutil.CheckClose(wc, &err)
  1261. scfg, err := r.Storer.Config()
  1262. if err != nil {
  1263. return h, err
  1264. }
  1265. enc := packfile.NewEncoder(wc, r.Storer, cfg.UseRefDeltas)
  1266. h, err = enc.Encode(objs, scfg.Pack.Window)
  1267. if err != nil {
  1268. return h, err
  1269. }
  1270. // Delete the packed, loose objects.
  1271. if los, ok := r.Storer.(storer.LooseObjectStorer); ok {
  1272. err = los.ForEachObjectHash(func(hash plumbing.Hash) error {
  1273. if ow.isSeen(hash) {
  1274. err = los.DeleteLooseObject(hash)
  1275. if err != nil {
  1276. return err
  1277. }
  1278. }
  1279. return nil
  1280. })
  1281. if err != nil {
  1282. return h, err
  1283. }
  1284. }
  1285. return h, err
  1286. }
上海开阖软件有限公司 沪ICP备12045867号-1