github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/misc/dashboard/builder/vcs.go (about)

     1  // Copyright 2013 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  package main
     6  
     7  import (
     8  	"encoding/xml"
     9  	"fmt"
    10  	"log"
    11  	"os"
    12  	"path/filepath"
    13  	"strconv"
    14  	"strings"
    15  	"sync"
    16  )
    17  
    18  // Repo represents a mercurial repository.
    19  type Repo struct {
    20  	Path string
    21  	sync.Mutex
    22  }
    23  
    24  // RemoteRepo constructs a *Repo representing a remote repository.
    25  func RemoteRepo(url string) *Repo {
    26  	return &Repo{
    27  		Path: url,
    28  	}
    29  }
    30  
    31  // Clone clones the current Repo to a new destination
    32  // returning a new *Repo if successful.
    33  func (r *Repo) Clone(path, rev string) (*Repo, error) {
    34  	r.Lock()
    35  	defer r.Unlock()
    36  	if err := run(*cmdTimeout, nil, *buildroot, r.hgCmd("clone", "-r", rev, r.Path, path)...); err != nil {
    37  		return nil, err
    38  	}
    39  	return &Repo{
    40  		Path: path,
    41  	}, nil
    42  }
    43  
    44  // UpdateTo updates the working copy of this Repo to the
    45  // supplied revision.
    46  func (r *Repo) UpdateTo(hash string) error {
    47  	r.Lock()
    48  	defer r.Unlock()
    49  	return run(*cmdTimeout, nil, r.Path, r.hgCmd("update", hash)...)
    50  }
    51  
    52  // Exists reports whether this Repo represents a valid Mecurial repository.
    53  func (r *Repo) Exists() bool {
    54  	fi, err := os.Stat(filepath.Join(r.Path, ".hg"))
    55  	if err != nil {
    56  		return false
    57  	}
    58  	return fi.IsDir()
    59  }
    60  
    61  // Pull pulls changes from the default path, that is, the path
    62  // this Repo was cloned from.
    63  func (r *Repo) Pull() error {
    64  	r.Lock()
    65  	defer r.Unlock()
    66  	return run(*cmdTimeout, nil, r.Path, r.hgCmd("pull")...)
    67  }
    68  
    69  // Log returns the changelog for this repository.
    70  func (r *Repo) Log() ([]HgLog, error) {
    71  	if err := r.Pull(); err != nil {
    72  		return nil, err
    73  	}
    74  	const N = 50 // how many revisions to grab
    75  
    76  	r.Lock()
    77  	defer r.Unlock()
    78  	data, _, err := runLog(*cmdTimeout, nil, r.Path, r.hgCmd("log",
    79  		"--encoding=utf-8",
    80  		"--limit="+strconv.Itoa(N),
    81  		"--template="+xmlLogTemplate)...,
    82  	)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  
    87  	var logStruct struct {
    88  		Log []HgLog
    89  	}
    90  	err = xml.Unmarshal([]byte("<Top>"+data+"</Top>"), &logStruct)
    91  	if err != nil {
    92  		log.Printf("unmarshal hg log: %v", err)
    93  		return nil, err
    94  	}
    95  	return logStruct.Log, nil
    96  }
    97  
    98  // FullHash returns the full hash for the given Mercurial revision.
    99  func (r *Repo) FullHash(rev string) (string, error) {
   100  	r.Lock()
   101  	defer r.Unlock()
   102  	s, _, err := runLog(*cmdTimeout, nil, r.Path,
   103  		r.hgCmd("log",
   104  			"--encoding=utf-8",
   105  			"--rev="+rev,
   106  			"--limit=1",
   107  			"--template={node}")...,
   108  	)
   109  	if err != nil {
   110  		return "", nil
   111  	}
   112  	s = strings.TrimSpace(s)
   113  	if s == "" {
   114  		return "", fmt.Errorf("cannot find revision")
   115  	}
   116  	if len(s) != 40 {
   117  		return "", fmt.Errorf("hg returned invalid hash " + s)
   118  	}
   119  	return s, nil
   120  }
   121  
   122  func (r *Repo) hgCmd(args ...string) []string {
   123  	return append([]string{"hg", "--config", "extensions.codereview=!"}, args...)
   124  }
   125  
   126  // HgLog represents a single Mercurial revision.
   127  type HgLog struct {
   128  	Hash   string
   129  	Author string
   130  	Date   string
   131  	Desc   string
   132  	Parent string
   133  
   134  	// Internal metadata
   135  	added bool
   136  }
   137  
   138  // xmlLogTemplate is a template to pass to Mercurial to make
   139  // hg log print the log in valid XML for parsing with xml.Unmarshal.
   140  const xmlLogTemplate = `
   141          <Log>
   142          <Hash>{node|escape}</Hash>
   143          <Parent>{parent|escape}</Parent>
   144          <Author>{author|escape}</Author>
   145          <Date>{date|rfc3339date}</Date>
   146          <Desc>{desc|escape}</Desc>
   147          </Log>
   148  `