push.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. package main
  2. import (
  3. "bytes"
  4. "fmt"
  5. "os/exec"
  6. "path/filepath"
  7. "strings"
  8. "github.com/pkg/errors"
  9. )
  10. func push(src, dst string) error {
  11. var force = strings.HasPrefix(src, "+")
  12. if force {
  13. src = src[1:]
  14. }
  15. var present []string
  16. for _, h := range ref2hash {
  17. present = append(present, h)
  18. }
  19. // also: track previously pushed branches in 2nd map and extend present with it
  20. need2push, err := gitListObjects(src, present)
  21. if err != nil {
  22. return errors.Wrapf(err, "push: git list objects failed %q %v", src, present)
  23. }
  24. n := len(need2push)
  25. type pair struct {
  26. Sha1 string
  27. MHash string
  28. Err error
  29. }
  30. added := make(chan pair)
  31. objHash2multi := make(map[string]string, n)
  32. for _, sha1 := range need2push {
  33. go func(sha1 string) {
  34. r, err := gitFlattenObject(sha1)
  35. if err != nil {
  36. added <- pair{Err: errors.Wrapf(err, "gitFlattenObject failed")}
  37. return
  38. }
  39. mhash, err := ipfsShell.Add(r)
  40. if err != nil {
  41. added <- pair{Err: errors.Wrapf(err, "shell.Add(%s) failed", sha1)}
  42. return
  43. }
  44. added <- pair{Sha1: sha1, MHash: mhash}
  45. }(sha1)
  46. }
  47. for n > 0 {
  48. select {
  49. // add timeout?
  50. case p := <-added:
  51. if p.Err != nil {
  52. return p.Err
  53. }
  54. log.Log("sha1", p.Sha1, "mhash", p.MHash, "msg", "added")
  55. objHash2multi[p.Sha1] = p.MHash
  56. n--
  57. }
  58. }
  59. root, err := ipfsShell.ResolvePath(ipfsRepoPath)
  60. if err != nil {
  61. return errors.Wrapf(err, "resolvePath(%s) failed", ipfsRepoPath)
  62. }
  63. for sha1, mhash := range objHash2multi {
  64. newRoot, err := ipfsShell.PatchLink(root, filepath.Join("objects", sha1[:2], sha1[2:]), mhash, true)
  65. if err != nil {
  66. return errors.Wrapf(err, "patchLink failed")
  67. }
  68. root = newRoot
  69. log.Log("newRoot", newRoot, "sha1", sha1, "msg", "updated object")
  70. }
  71. srcSha1, err := gitRefHash(src)
  72. if err != nil {
  73. return errors.Wrapf(err, "gitRefHash(%s) failed", src)
  74. }
  75. h, ok := ref2hash[dst]
  76. if !ok {
  77. return errors.Errorf("writeRef: ref2hash entry missing: %s %+v", dst, ref2hash)
  78. }
  79. isFF := gitIsAncestor(h, srcSha1)
  80. if isFF != nil && !force {
  81. // TODO: print "non-fast-forward" to git
  82. return errors.Errorf("non-fast-forward")
  83. }
  84. mhash, err := ipfsShell.Add(bytes.NewBufferString(fmt.Sprintf("%s\n", srcSha1)))
  85. if err != nil {
  86. return errors.Wrapf(err, "shell.Add(%s) failed", srcSha1)
  87. }
  88. root, err = ipfsShell.PatchLink(root, dst, mhash, true)
  89. if err != nil {
  90. // TODO:print "fetch first" to git
  91. err = errors.Wrapf(err, "patchLink(%s) failed", ipfsRepoPath)
  92. log.Log("err", err, "msg", "shell.PatchLink failed")
  93. return errors.Errorf("fetch first")
  94. }
  95. log.Log("newRoot", root, "dst", dst, "hash", srcSha1, "msg", "updated ref")
  96. // invalidate info/refs and HEAD(?)
  97. // TODO: unclean: need to put other revs, too make a soft git update-server-info maybe
  98. noInfoRefsHash, err := ipfsShell.Patch(root, "rm-link", "info/refs")
  99. if err == nil {
  100. log.Log("newRoot", noInfoRefsHash, "msg", "rm-link'ed info/refs")
  101. root = noInfoRefsHash
  102. } else {
  103. // todo shell.IsNotExists() ?
  104. log.Log("err", err, "msg", "shell.Patch rm-link info/refs failed - might be okay... TODO")
  105. }
  106. newRemoteURL := fmt.Sprintf("ipfs:///ipfs/%s", root)
  107. updateRepoCMD := exec.Command("git", "remote", "set-url", thisGitRemote, newRemoteURL)
  108. out, err := updateRepoCMD.CombinedOutput()
  109. if err != nil {
  110. return errors.Wrapf(err, "updating remote url failed\nOut:%s", string(out))
  111. }
  112. log.Log("msg", "remote updated", "address", newRemoteURL)
  113. return nil
  114. }