mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
godoc/redirect: support loading hg->git change map from a file
This isn't exposed through the godoc binary, as it will only be used by the Google-specific deployment of godoc. Change-Id: Id5808f3adcb7eb36a7ccd6e4960ce3f01179fe51 Reviewed-on: https://go-review.googlesource.com/1567 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
parent
88d370fb93
commit
55402a2b46
138
godoc/redirect/hash.go
Normal file
138
godoc/redirect/hash.go
Normal file
@ -0,0 +1,138 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file provides a compact encoding of
|
||||
// a map of Mercurial hashes to Git hashes.
|
||||
|
||||
package redirect
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// hashMap is a map of Mercurial hashes to Git hashes.
|
||||
type hashMap struct {
|
||||
file *os.File
|
||||
entries int
|
||||
}
|
||||
|
||||
// newHashMap takes a file handle that contains a map of Mercurial to Git
|
||||
// hashes. The file should be a sequence of pairs of little-endian encoded
|
||||
// uint32s, representing a hgHash and a gitHash respectively.
|
||||
// The sequence must be sorted by hgHash.
|
||||
// The file must remain open for as long as the returned hashMap is used.
|
||||
func newHashMap(f *os.File) (*hashMap, error) {
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &hashMap{file: f, entries: int(fi.Size() / 8)}, nil
|
||||
}
|
||||
|
||||
// Lookup finds an hgHash in the map that matches the given prefix, and returns
|
||||
// its corresponding gitHash. The prefix must be at least 8 characters long.
|
||||
func (m *hashMap) Lookup(s string) gitHash {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
hg, err := hgHashFromString(s)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
var git gitHash
|
||||
b := make([]byte, 8)
|
||||
sort.Search(m.entries, func(i int) bool {
|
||||
n, err := m.file.ReadAt(b, int64(i*8))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if n != 8 {
|
||||
panic(io.ErrUnexpectedEOF)
|
||||
}
|
||||
v := hgHash(binary.LittleEndian.Uint32(b[:4]))
|
||||
if v == hg {
|
||||
git = gitHash(binary.LittleEndian.Uint32(b[4:]))
|
||||
}
|
||||
return v >= hg
|
||||
})
|
||||
return git
|
||||
}
|
||||
|
||||
// hgHash represents the lower (leftmost) 32 bits of a Mercurial hash.
|
||||
type hgHash uint32
|
||||
|
||||
func (h hgHash) String() string {
|
||||
return intToHash(int64(h))
|
||||
}
|
||||
|
||||
func hgHashFromString(s string) (hgHash, error) {
|
||||
if len(s) < 8 {
|
||||
return 0, fmt.Errorf("string too small: len(s) = %d", len(s))
|
||||
}
|
||||
hash := s[:8]
|
||||
i, err := strconv.ParseInt(hash, 16, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return hgHash(i), nil
|
||||
}
|
||||
|
||||
// gitHash represents the leftmost 28 bits of a Git hash in its upper 28 bits,
|
||||
// and it encodes hash's repository in the lower 4 bits.
|
||||
type gitHash uint32
|
||||
|
||||
func (h gitHash) Hash() string {
|
||||
return intToHash(int64(h))[:7]
|
||||
}
|
||||
|
||||
func (h gitHash) Repo() string {
|
||||
return repo(h & 0xF).String()
|
||||
}
|
||||
|
||||
func intToHash(i int64) string {
|
||||
s := strconv.FormatInt(i, 16)
|
||||
if len(s) < 8 {
|
||||
s = strings.Repeat("0", 8-len(s)) + s
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// repo represents a Go Git repository.
|
||||
type repo byte
|
||||
|
||||
const (
|
||||
repoGo repo = iota
|
||||
repoBlog
|
||||
repoCrypto
|
||||
repoExp
|
||||
repoImage
|
||||
repoMobile
|
||||
repoNet
|
||||
repoSys
|
||||
repoTalks
|
||||
repoText
|
||||
repoTools
|
||||
)
|
||||
|
||||
func (r repo) String() string {
|
||||
return map[repo]string{
|
||||
repoGo: "go",
|
||||
repoBlog: "blog",
|
||||
repoCrypto: "crypto",
|
||||
repoExp: "exp",
|
||||
repoImage: "image",
|
||||
repoMobile: "mobile",
|
||||
repoNet: "net",
|
||||
repoSys: "sys",
|
||||
repoTalks: "talks",
|
||||
repoText: "text",
|
||||
repoTools: "tools",
|
||||
}[r]
|
||||
}
|
@ -8,7 +8,9 @@
|
||||
package redirect // import "golang.org/x/tools/godoc/redirect"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -33,6 +35,7 @@ func Register(mux *http.ServeMux) {
|
||||
// NB: /src/pkg (sans trailing slash) is the index of packages.
|
||||
mux.HandleFunc("/src/pkg/", srcPkgHandler)
|
||||
mux.HandleFunc("/cl/", clHandler)
|
||||
mux.HandleFunc("/change/", changeHandler)
|
||||
}
|
||||
|
||||
func handlePathRedirects(mux *http.ServeMux, redirects map[string]string, prefix string) {
|
||||
@ -128,11 +131,6 @@ var redirects = map[string]string{
|
||||
}
|
||||
|
||||
var prefixHelpers = map[string]string{
|
||||
// TODO(adg): add redirects from known hg hashes to the git equivalents
|
||||
// and switch this to point to "https://go.googlesource.com/go/+/".
|
||||
// (We can only change this once we know all the new git hashes.)
|
||||
"change": "https://code.google.com/p/go/source/detail?r=",
|
||||
|
||||
"issue": "https://github.com/golang/go/issues/",
|
||||
"play": "http://play.golang.org/",
|
||||
"talks": "http://talks.golang.org/",
|
||||
@ -193,3 +191,39 @@ func clHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
http.Redirect(w, r, target, http.StatusFound)
|
||||
}
|
||||
|
||||
var changeMap *hashMap
|
||||
|
||||
// LoadChangeMap loads the specified map of Mercurial to Git revisions,
|
||||
// which is used by the /change/ handler to intelligently map old hg
|
||||
// revisions to their new git equivalents.
|
||||
// It should be called before calling Register.
|
||||
// The file should remain open as long as the process is running.
|
||||
// See the implementation of this package for details.
|
||||
func LoadChangeMap(filename string) error {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m, err := newHashMap(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
changeMap = m
|
||||
return nil
|
||||
}
|
||||
|
||||
func changeHandler(w http.ResponseWriter, r *http.Request) {
|
||||
const prefix = "/change/"
|
||||
if p := r.URL.Path; p == prefix {
|
||||
// redirect /prefix/ to /prefix
|
||||
http.Redirect(w, r, p[:len(p)-1], http.StatusFound)
|
||||
return
|
||||
}
|
||||
hash := r.URL.Path[len(prefix):]
|
||||
target := "https://go.googlesource.com/go/+/" + hash
|
||||
if git := changeMap.Lookup(hash); git > 0 {
|
||||
target = fmt.Sprintf("https://go.googlesource.com/%v/+/%v", git.Repo(), git.Hash())
|
||||
}
|
||||
http.Redirect(w, r, target, http.StatusFound)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user