2014-09-16 18:30:31 +02:00
|
|
|
package module
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net/url"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
2015-02-02 19:49:53 +01:00
|
|
|
"runtime"
|
2014-09-16 18:30:31 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// HgGetter is a Getter implementation that will download a module from
|
|
|
|
// a Mercurial repository.
|
|
|
|
type HgGetter struct{}
|
|
|
|
|
|
|
|
func (g *HgGetter) Get(dst string, u *url.URL) error {
|
|
|
|
if _, err := exec.LookPath("hg"); err != nil {
|
|
|
|
return fmt.Errorf("hg must be available and on the PATH")
|
|
|
|
}
|
|
|
|
|
2015-02-02 19:49:53 +01:00
|
|
|
newURL, err := urlParse(u.String())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if fixWindowsDrivePath(newURL) {
|
|
|
|
// See valid file path form on http://www.selenic.com/hg/help/urls
|
|
|
|
newURL.Path = fmt.Sprintf("/%s", newURL.Path)
|
|
|
|
}
|
|
|
|
|
2014-09-16 19:30:55 +02:00
|
|
|
// Extract some query parameters we use
|
|
|
|
var rev string
|
2015-02-02 19:49:53 +01:00
|
|
|
q := newURL.Query()
|
2014-09-16 19:30:55 +02:00
|
|
|
if len(q) > 0 {
|
|
|
|
rev = q.Get("rev")
|
|
|
|
q.Del("rev")
|
|
|
|
|
2015-02-02 19:49:53 +01:00
|
|
|
newURL.RawQuery = q.Encode()
|
2014-09-16 19:30:55 +02:00
|
|
|
}
|
|
|
|
|
2015-02-02 19:49:53 +01:00
|
|
|
_, err = os.Stat(dst)
|
2014-09-16 18:30:31 +02:00
|
|
|
if err != nil && !os.IsNotExist(err) {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err != nil {
|
2015-02-02 19:49:53 +01:00
|
|
|
if err := g.clone(dst, newURL); err != nil {
|
2014-09-16 18:30:31 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-02 19:49:53 +01:00
|
|
|
if err := g.pull(dst, newURL); err != nil {
|
2014-09-16 18:30:31 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-02-02 19:49:53 +01:00
|
|
|
return g.update(dst, newURL, rev)
|
2014-09-16 18:30:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g *HgGetter) clone(dst string, u *url.URL) error {
|
|
|
|
cmd := exec.Command("hg", "clone", "-U", u.String(), dst)
|
|
|
|
return getRunCommand(cmd)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *HgGetter) pull(dst string, u *url.URL) error {
|
|
|
|
cmd := exec.Command("hg", "pull")
|
|
|
|
cmd.Dir = dst
|
|
|
|
return getRunCommand(cmd)
|
|
|
|
}
|
|
|
|
|
2014-09-16 19:30:55 +02:00
|
|
|
func (g *HgGetter) update(dst string, u *url.URL, rev string) error {
|
|
|
|
args := []string{"update"}
|
|
|
|
if rev != "" {
|
|
|
|
args = append(args, rev)
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd := exec.Command("hg", args...)
|
2014-09-16 18:30:31 +02:00
|
|
|
cmd.Dir = dst
|
|
|
|
return getRunCommand(cmd)
|
|
|
|
}
|
2015-02-02 19:49:53 +01:00
|
|
|
|
|
|
|
func fixWindowsDrivePath(u *url.URL) bool {
|
|
|
|
// hg assumes a file:/// prefix for Windows drive letter file paths.
|
|
|
|
// (e.g. file:///c:/foo/bar)
|
|
|
|
// If the URL Path does not begin with a '/' character, the resulting URL
|
|
|
|
// path will have a file:// prefix. (e.g. file://c:/foo/bar)
|
|
|
|
// See http://www.selenic.com/hg/help/urls and the examples listed in
|
|
|
|
// http://selenic.com/repo/hg-stable/file/1265a3a71d75/mercurial/util.py#l1936
|
|
|
|
return runtime.GOOS == "windows" && u.Scheme == "file" &&
|
|
|
|
len(u.Path) > 1 && u.Path[0] != '/' && u.Path[1] == ':'
|
|
|
|
}
|