github.com/zxy12/go_duplicate_112_new@v0.0.0-20200807091221-747231827200/src/cmd/go/internal/modfetch/pseudo.go (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Pseudo-versions 6 // 7 // Code authors are expected to tag the revisions they want users to use, 8 // including prereleases. However, not all authors tag versions at all, 9 // and not all commits a user might want to try will have tags. 10 // A pseudo-version is a version with a special form that allows us to 11 // address an untagged commit and order that version with respect to 12 // other versions we might encounter. 13 // 14 // A pseudo-version takes one of the general forms: 15 // 16 // (1) vX.0.0-yyyymmddhhmmss-abcdef123456 17 // (2) vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456 18 // (3) vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456+incompatible 19 // (4) vX.Y.Z-pre.0.yyyymmddhhmmss-abcdef123456 20 // (5) vX.Y.Z-pre.0.yyyymmddhhmmss-abcdef123456+incompatible 21 // 22 // If there is no recently tagged version with the right major version vX, 23 // then form (1) is used, creating a space of pseudo-versions at the bottom 24 // of the vX version range, less than any tagged version, including the unlikely v0.0.0. 25 // 26 // If the most recent tagged version before the target commit is vX.Y.Z or vX.Y.Z+incompatible, 27 // then the pseudo-version uses form (2) or (3), making it a prerelease for the next 28 // possible semantic version after vX.Y.Z. The leading 0 segment in the prerelease string 29 // ensures that the pseudo-version compares less than possible future explicit prereleases 30 // like vX.Y.(Z+1)-rc1 or vX.Y.(Z+1)-1. 31 // 32 // If the most recent tagged version before the target commit is vX.Y.Z-pre or vX.Y.Z-pre+incompatible, 33 // then the pseudo-version uses form (4) or (5), making it a slightly later prerelease. 34 35 package modfetch 36 37 import ( 38 "cmd/go/internal/semver" 39 "fmt" 40 "regexp" 41 "strings" 42 "time" 43 ) 44 45 // PseudoVersion returns a pseudo-version for the given major version ("v1") 46 // preexisting older tagged version ("" or "v1.2.3" or "v1.2.3-pre"), revision time, 47 // and revision identifier (usually a 12-byte commit hash prefix). 48 func PseudoVersion(major, older string, t time.Time, rev string) string { 49 if major == "" { 50 major = "v0" 51 } 52 major = strings.TrimSuffix(major, "-unstable") // make gopkg.in/macaroon-bakery.v2-unstable use "v2" 53 segment := fmt.Sprintf("%s-%s", t.UTC().Format("20060102150405"), rev) 54 build := semver.Build(older) 55 older = semver.Canonical(older) 56 if older == "" { 57 return major + ".0.0-" + segment // form (1) 58 } 59 if semver.Prerelease(older) != "" { 60 return older + ".0." + segment + build // form (4), (5) 61 } 62 63 // Form (2), (3). 64 // Extract patch from vMAJOR.MINOR.PATCH 65 v := older[:len(older)] 66 i := strings.LastIndex(v, ".") + 1 67 v, patch := v[:i], v[i:] 68 69 // Increment PATCH by adding 1 to decimal: 70 // scan right to left turning 9s to 0s until you find a digit to increment. 71 // (Number might exceed int64, but math/big is overkill.) 72 digits := []byte(patch) 73 for i = len(digits) - 1; i >= 0 && digits[i] == '9'; i-- { 74 digits[i] = '0' 75 } 76 if i >= 0 { 77 digits[i]++ 78 } else { 79 // digits is all zeros 80 digits[0] = '1' 81 digits = append(digits, '0') 82 } 83 patch = string(digits) 84 85 // Reassemble. 86 return v + patch + "-0." + segment + build 87 } 88 89 var pseudoVersionRE = regexp.MustCompile(`^v[0-9]+\.(0\.0-|\d+\.\d+-([^+]*\.)?0\.)\d{14}-[A-Za-z0-9]+(\+incompatible)?$`) 90 91 // IsPseudoVersion reports whether v is a pseudo-version. 92 func IsPseudoVersion(v string) bool { 93 return strings.Count(v, "-") >= 2 && semver.IsValid(v) && pseudoVersionRE.MatchString(v) 94 } 95 96 // PseudoVersionTime returns the time stamp of the pseudo-version v. 97 // It returns an error if v is not a pseudo-version or if the time stamp 98 // embedded in the pseudo-version is not a valid time. 99 func PseudoVersionTime(v string) (time.Time, error) { 100 timestamp, _, err := parsePseudoVersion(v) 101 t, err := time.Parse("20060102150405", timestamp) 102 if err != nil { 103 return time.Time{}, fmt.Errorf("pseudo-version with malformed time %s: %q", timestamp, v) 104 } 105 return t, nil 106 } 107 108 // PseudoVersionRev returns the revision identifier of the pseudo-version v. 109 // It returns an error if v is not a pseudo-version. 110 func PseudoVersionRev(v string) (rev string, err error) { 111 _, rev, err = parsePseudoVersion(v) 112 return 113 } 114 115 func parsePseudoVersion(v string) (timestamp, rev string, err error) { 116 if !IsPseudoVersion(v) { 117 return "", "", fmt.Errorf("malformed pseudo-version %q", v) 118 } 119 v = strings.TrimSuffix(v, "+incompatible") 120 j := strings.LastIndex(v, "-") 121 v, rev = v[:j], v[j+1:] 122 i := strings.LastIndex(v, "-") 123 if j := strings.LastIndex(v, "."); j > i { 124 timestamp = v[j+1:] 125 } else { 126 timestamp = v[i+1:] 127 } 128 return timestamp, rev, nil 129 }