2017-06-19 19:52:45 +00:00
|
|
|
package githistory
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/hex"
|
|
|
|
"fmt"
|
2017-09-22 22:16:13 +00:00
|
|
|
"strings"
|
2017-06-19 19:52:45 +00:00
|
|
|
|
|
|
|
"github.com/git-lfs/git-lfs/errors"
|
|
|
|
"github.com/git-lfs/git-lfs/git"
|
2017-11-22 22:07:24 +00:00
|
|
|
"github.com/git-lfs/git-lfs/tasklog"
|
2017-09-22 22:16:13 +00:00
|
|
|
"github.com/git-lfs/git-lfs/tools"
|
2018-07-05 16:49:10 +00:00
|
|
|
"github.com/git-lfs/gitobj"
|
2017-06-19 19:52:45 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// refUpdater is a type responsible for moving references from one point in the
|
|
|
|
// Git object graph to another.
|
|
|
|
type refUpdater struct {
|
|
|
|
// CacheFn is a function that returns the SHA1 transformation from an
|
|
|
|
// original hash to a new one. It specifies a "bool" return value
|
|
|
|
// signaling whether or not that given "old" SHA1 was migrated.
|
|
|
|
CacheFn func(old []byte) ([]byte, bool)
|
|
|
|
// Logger logs the progress of reference updating.
|
2017-11-22 22:07:24 +00:00
|
|
|
Logger *tasklog.Logger
|
2017-06-19 19:52:45 +00:00
|
|
|
// Refs is a set of *git.Ref's to migrate.
|
|
|
|
Refs []*git.Ref
|
|
|
|
// Root is the given directory on disk in which the repository is
|
|
|
|
// located.
|
|
|
|
Root string
|
2017-12-11 23:22:23 +00:00
|
|
|
|
2018-07-05 16:49:10 +00:00
|
|
|
db *gitobj.ObjectDatabase
|
2017-06-19 19:52:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateRefs performs the reference update(s) from existing locations (see:
|
|
|
|
// Refs) to their respective new locations in the graph (see CacheFn).
|
|
|
|
//
|
|
|
|
// It creates reflog entries as well as stderr log entries as it progresses
|
|
|
|
// through the reference updates.
|
|
|
|
//
|
|
|
|
// It returns any error encountered, or nil if the reference update(s) was/were
|
|
|
|
// successful.
|
|
|
|
func (r *refUpdater) UpdateRefs() error {
|
|
|
|
list := r.Logger.List("migrate: Updating refs")
|
|
|
|
defer list.Complete()
|
|
|
|
|
2017-09-22 22:16:13 +00:00
|
|
|
var maxNameLen int
|
|
|
|
for _, ref := range r.Refs {
|
|
|
|
maxNameLen = tools.MaxInt(maxNameLen, len(ref.Name))
|
|
|
|
}
|
|
|
|
|
2019-06-21 17:12:04 +00:00
|
|
|
seen := make(map[string]struct{})
|
2017-06-19 19:52:45 +00:00
|
|
|
for _, ref := range r.Refs {
|
2019-06-21 17:12:04 +00:00
|
|
|
if err := r.updateOneRef(list, maxNameLen, seen, ref); err != nil {
|
2019-06-14 19:19:52 +00:00
|
|
|
return err
|
2017-06-19 19:52:45 +00:00
|
|
|
}
|
2019-06-14 19:19:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2017-06-19 19:52:45 +00:00
|
|
|
|
2019-06-24 15:55:17 +00:00
|
|
|
func (r *refUpdater) updateOneTag(tag *gitobj.Tag, toObj []byte) ([]byte, error) {
|
2019-06-21 17:27:40 +00:00
|
|
|
newTag, err := r.db.WriteTag(&gitobj.Tag{
|
|
|
|
Object: toObj,
|
|
|
|
ObjectType: tag.ObjectType,
|
|
|
|
Name: tag.Name,
|
|
|
|
Tagger: tag.Tagger,
|
|
|
|
|
|
|
|
Message: tag.Message,
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "could not rewrite tag: %s", tag.Name)
|
|
|
|
}
|
|
|
|
return newTag, nil
|
|
|
|
}
|
|
|
|
|
2019-06-21 17:12:04 +00:00
|
|
|
func (r *refUpdater) updateOneRef(list *tasklog.ListTask, maxNameLen int, seen map[string]struct{}, ref *git.Ref) error {
|
2019-06-14 19:19:52 +00:00
|
|
|
sha1, err := hex.DecodeString(ref.Sha)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrapf(err, "could not decode: %q", ref.Sha)
|
|
|
|
}
|
2017-12-11 23:23:01 +00:00
|
|
|
|
2020-05-13 21:48:32 +00:00
|
|
|
refspec := ref.Refspec()
|
|
|
|
if _, ok := seen[refspec]; ok {
|
2019-06-21 17:12:04 +00:00
|
|
|
return nil
|
|
|
|
}
|
2020-05-13 21:48:32 +00:00
|
|
|
seen[refspec] = struct{}{}
|
2019-06-21 17:12:04 +00:00
|
|
|
|
2019-06-14 19:19:52 +00:00
|
|
|
to, ok := r.CacheFn(sha1)
|
2017-12-11 23:23:01 +00:00
|
|
|
|
2019-06-14 19:19:52 +00:00
|
|
|
if ref.Type == git.RefTypeLocalTag {
|
|
|
|
tag, _ := r.db.Tag(sha1)
|
2019-06-24 15:55:17 +00:00
|
|
|
if tag != nil && tag.ObjectType == gitobj.TagObjectType {
|
|
|
|
innerTag, _ := r.db.Tag(tag.Object)
|
|
|
|
name := fmt.Sprintf("refs/tags/%s", innerTag.Name)
|
|
|
|
if _, ok := seen[name]; !ok {
|
|
|
|
old, err := git.ResolveRef(name)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = r.updateOneRef(list, maxNameLen, seen, old)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
updated, err := git.ResolveRef(name)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
updatedSha, err := hex.DecodeString(updated.Sha)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrapf(err, "could not decode: %q", ref.Sha)
|
|
|
|
}
|
|
|
|
|
|
|
|
newTag, err := r.updateOneTag(tag, updatedSha)
|
|
|
|
if newTag == nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
to = newTag
|
|
|
|
ok = true
|
|
|
|
} else if tag != nil && tag.ObjectType == gitobj.CommitObjectType {
|
|
|
|
toObj, okObj := r.CacheFn(tag.Object)
|
|
|
|
if !okObj {
|
|
|
|
return nil
|
|
|
|
}
|
2017-12-11 23:23:01 +00:00
|
|
|
|
2019-06-24 15:55:17 +00:00
|
|
|
newTag, err := r.updateOneTag(tag, toObj)
|
2019-06-21 17:27:40 +00:00
|
|
|
if newTag == nil {
|
|
|
|
return err
|
2017-12-11 23:23:01 +00:00
|
|
|
}
|
2019-06-14 19:19:52 +00:00
|
|
|
to = newTag
|
|
|
|
ok = true
|
2017-06-19 19:52:45 +00:00
|
|
|
}
|
2019-06-14 19:19:52 +00:00
|
|
|
}
|
2017-06-19 19:52:45 +00:00
|
|
|
|
2019-06-14 19:19:52 +00:00
|
|
|
if !ok {
|
|
|
|
return nil
|
|
|
|
}
|
2017-06-19 19:52:45 +00:00
|
|
|
|
2019-06-14 19:19:52 +00:00
|
|
|
if err := git.UpdateRefIn(r.Root, ref, to, ""); err != nil {
|
|
|
|
return err
|
2017-06-19 19:52:45 +00:00
|
|
|
}
|
|
|
|
|
2019-06-14 19:19:52 +00:00
|
|
|
namePadding := tools.MaxInt(maxNameLen-len(ref.Name), 0)
|
|
|
|
list.Entry(fmt.Sprintf(" %s%s\t%s -> %x", ref.Name, strings.Repeat(" ", namePadding), ref.Sha, to))
|
2017-06-19 19:52:45 +00:00
|
|
|
return nil
|
|
|
|
}
|