mirror of
https://github.com/golang/go
synced 2024-11-05 22:36:10 -07:00
149 lines
3.3 KiB
Go
149 lines
3.3 KiB
Go
|
// Copyright 2013 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.
|
||
|
|
||
|
package main
|
||
|
|
||
|
import (
|
||
|
"encoding/xml"
|
||
|
"fmt"
|
||
|
"log"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
"sync"
|
||
|
)
|
||
|
|
||
|
// Repo represents a mercurial repository.
|
||
|
type Repo struct {
|
||
|
Path string
|
||
|
sync.Mutex
|
||
|
}
|
||
|
|
||
|
// RemoteRepo constructs a *Repo representing a remote repository.
|
||
|
func RemoteRepo(url string) *Repo {
|
||
|
return &Repo{
|
||
|
Path: url,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Clone clones the current Repo to a new destination
|
||
|
// returning a new *Repo if successful.
|
||
|
func (r *Repo) Clone(path, rev string) (*Repo, error) {
|
||
|
r.Lock()
|
||
|
defer r.Unlock()
|
||
|
if err := run(*cmdTimeout, nil, *buildroot, r.hgCmd("clone", "-r", rev, r.Path, path)...); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return &Repo{
|
||
|
Path: path,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
// UpdateTo updates the working copy of this Repo to the
|
||
|
// supplied revision.
|
||
|
func (r *Repo) UpdateTo(hash string) error {
|
||
|
r.Lock()
|
||
|
defer r.Unlock()
|
||
|
return run(*cmdTimeout, nil, r.Path, r.hgCmd("update", hash)...)
|
||
|
}
|
||
|
|
||
|
// Exists reports whether this Repo represents a valid Mecurial repository.
|
||
|
func (r *Repo) Exists() bool {
|
||
|
fi, err := os.Stat(filepath.Join(r.Path, ".hg"))
|
||
|
if err != nil {
|
||
|
return false
|
||
|
}
|
||
|
return fi.IsDir()
|
||
|
}
|
||
|
|
||
|
// Pull pulls changes from the default path, that is, the path
|
||
|
// this Repo was cloned from.
|
||
|
func (r *Repo) Pull() error {
|
||
|
r.Lock()
|
||
|
defer r.Unlock()
|
||
|
return run(*cmdTimeout, nil, r.Path, r.hgCmd("pull")...)
|
||
|
}
|
||
|
|
||
|
// Log returns the changelog for this repository.
|
||
|
func (r *Repo) Log() ([]HgLog, error) {
|
||
|
if err := r.Pull(); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
const N = 50 // how many revisions to grab
|
||
|
|
||
|
r.Lock()
|
||
|
defer r.Unlock()
|
||
|
data, _, err := runLog(*cmdTimeout, nil, r.Path, r.hgCmd("log",
|
||
|
"--encoding=utf-8",
|
||
|
"--limit="+strconv.Itoa(N),
|
||
|
"--template="+xmlLogTemplate)...,
|
||
|
)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
var logStruct struct {
|
||
|
Log []HgLog
|
||
|
}
|
||
|
err = xml.Unmarshal([]byte("<Top>"+data+"</Top>"), &logStruct)
|
||
|
if err != nil {
|
||
|
log.Printf("unmarshal hg log: %v", err)
|
||
|
return nil, err
|
||
|
}
|
||
|
return logStruct.Log, nil
|
||
|
}
|
||
|
|
||
|
// FullHash returns the full hash for the given Mercurial revision.
|
||
|
func (r *Repo) FullHash(rev string) (string, error) {
|
||
|
r.Lock()
|
||
|
defer r.Unlock()
|
||
|
s, _, err := runLog(*cmdTimeout, nil, r.Path,
|
||
|
r.hgCmd("log",
|
||
|
"--encoding=utf-8",
|
||
|
"--rev="+rev,
|
||
|
"--limit=1",
|
||
|
"--template={node}")...,
|
||
|
)
|
||
|
if err != nil {
|
||
|
return "", nil
|
||
|
}
|
||
|
s = strings.TrimSpace(s)
|
||
|
if s == "" {
|
||
|
return "", fmt.Errorf("cannot find revision")
|
||
|
}
|
||
|
if len(s) != 40 {
|
||
|
return "", fmt.Errorf("hg returned invalid hash " + s)
|
||
|
}
|
||
|
return s, nil
|
||
|
}
|
||
|
|
||
|
func (r *Repo) hgCmd(args ...string) []string {
|
||
|
return append([]string{"hg", "--config", "extensions.codereview=!"}, args...)
|
||
|
}
|
||
|
|
||
|
// HgLog represents a single Mercurial revision.
|
||
|
type HgLog struct {
|
||
|
Hash string
|
||
|
Author string
|
||
|
Date string
|
||
|
Desc string
|
||
|
Parent string
|
||
|
|
||
|
// Internal metadata
|
||
|
added bool
|
||
|
}
|
||
|
|
||
|
// xmlLogTemplate is a template to pass to Mercurial to make
|
||
|
// hg log print the log in valid XML for parsing with xml.Unmarshal.
|
||
|
const xmlLogTemplate = `
|
||
|
<Log>
|
||
|
<Hash>{node|escape}</Hash>
|
||
|
<Parent>{parent|escape}</Parent>
|
||
|
<Author>{author|escape}</Author>
|
||
|
<Date>{date|rfc3339date}</Date>
|
||
|
<Desc>{desc|escape}</Desc>
|
||
|
</Log>
|
||
|
`
|