Add `glide tree` command.
diff --git a/cmd/tree.go b/cmd/tree.go
new file mode 100644
index 0000000..5061bb9
--- /dev/null
+++ b/cmd/tree.go
@@ -0,0 +1,187 @@
+package cmd
+
+import (
+	"errors"
+	"fmt"
+	"go/build"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"github.com/Masterminds/cookoo"
+)
+
+var ErrPkgNotFound = errors.New("package not found")
+
+// Tree prints a tree representing dependencies.
+func Tree(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
+	showcore := p.Get("showcore", false).(bool)
+	basedir := p.Get("dir", ".").(string)
+	myName := guessPackageName(basedir)
+
+	if basedir == "." {
+		var err error
+		basedir, err = os.Getwd()
+		if err != nil {
+			Error("Could not get working directory")
+			return nil, err
+		}
+	}
+
+	fmt.Println(myName)
+
+	// Start with *.go
+	// Do a breadth-first search of subdirectories, excluding _*, .*, testdata,
+	// and vendor.
+	/*
+		deps := walkDeps(basedir, myName)
+		for _, name := range deps {
+			found := findPkg(name, basedir)
+			msg := found.Path
+			if found.PType == ptypeUnknown {
+				msg = "glide get " + found.Name
+			}
+			if !showcore && found.PType == ptypeGoroot {
+				continue
+			}
+			fmt.Printf("\t%s\t(%s)\n", found.Name, msg)
+		}
+	*/
+	displayTree(basedir, myName, 1, showcore)
+
+	// Now look up all of the external dependencies.
+
+	// Make it pretty.
+	return nil, nil
+}
+
+func displayTree(basedir, myName string, level int, core bool) {
+	deps := walkDeps(basedir, myName)
+	for _, name := range deps {
+		found := findPkg(name, basedir)
+		if found.PType == ptypeUnknown {
+			msg := "glide get " + found.Name
+			fmt.Printf("\t%s\t(%s)\n", found.Name, msg)
+			continue
+		}
+		if !core && found.PType == ptypeGoroot {
+			continue
+		}
+		fmt.Print(strings.Repeat("\t", level))
+		fmt.Printf("%s   (%s)\n", found.Name, found.Path)
+		displayTree(found.Path, found.Name, level+1, core)
+	}
+}
+
+type ptype int8
+
+const (
+	ptypeUnknown ptype = iota
+	ptypeLocal
+	ptypeVendor
+	ptypeGopath
+	ptypeGoroot
+)
+
+type pinfo struct {
+	Name, Path string
+	PType      ptype
+}
+
+func findPkg(name, cwd string) *pinfo {
+	var fi os.FileInfo
+	var err error
+	var p string
+
+	info := &pinfo{
+		Name: name,
+	}
+
+	// Recurse backward to scan other vendor/ directories
+	for wd := cwd; wd != "/"; wd = filepath.Dir(wd) {
+		p = filepath.Join(wd, "vendor", name)
+		if fi, err = os.Stat(p); err == nil && (fi.IsDir() || isLink(fi)) {
+			info.Path = p
+			info.PType = ptypeVendor
+			return info
+		}
+	}
+	// Check $GOPATH
+	for _, r := range strings.Split(os.Getenv("GOPATH"), ":") {
+		p = filepath.Join(r, "src", name)
+		if fi, err = os.Stat(p); err == nil && (fi.IsDir() || isLink(fi)) {
+			info.Path = p
+			info.PType = ptypeGopath
+			return info
+		}
+	}
+
+	// Check $GOROOT
+	for _, r := range strings.Split(os.Getenv("GOROOT"), ":") {
+		p = filepath.Join(r, "src", name)
+		if fi, err = os.Stat(p); err == nil && (fi.IsDir() || isLink(fi)) {
+			info.Path = p
+			info.PType = ptypeGoroot
+			return info
+		}
+	}
+	return info
+}
+
+func isLink(fi os.FileInfo) bool {
+	return fi.Mode()&os.ModeSymlink == os.ModeSymlink
+}
+
+func walkDeps(base, myName string) []string {
+	externalDeps := []string{}
+	filepath.Walk(base, func(path string, fi os.FileInfo, err error) error {
+		if excludeSubtree(path, fi) {
+			if fi.IsDir() {
+				return filepath.SkipDir
+			}
+			return nil
+		}
+		pkg, err := build.ImportDir(path, 0)
+		if err != nil {
+			return err
+		}
+
+		if pkg.Goroot {
+			return nil
+		}
+
+		for _, imp := range pkg.Imports {
+			//if strings.HasPrefix(imp, myName) {
+			////Info("Skipping %s because it is a subpackage of %s", imp, myName)
+			//continue
+			//}
+			if imp == myName {
+				continue
+			}
+			externalDeps = append(externalDeps, imp)
+		}
+
+		return nil
+	})
+	return externalDeps
+}
+
+func excludeSubtree(path string, fi os.FileInfo) bool {
+	top := filepath.Base(path)
+
+	if !fi.IsDir() && !isLink(fi) {
+		return true
+	}
+
+	// Provisionally, we'll skip vendor. We definitely
+	// should skip testdata.
+	if top == "vendor" || top == "testdata" {
+		return true
+	}
+
+	// Skip anything that starts with _
+	if strings.HasPrefix(top, "_") || (strings.HasPrefix(top, ".") && top != ".") {
+		return true
+	}
+	return false
+}
diff --git a/glide.go b/glide.go
index 59c9a38..22de28d 100644
--- a/glide.go
+++ b/glide.go
@@ -318,6 +318,19 @@
 			},
 		},
 		{
+			Name:  "tree",
+			Usage: "Tree prints the dependencies of this project as a tree.",
+			Description: `This scans a project's source files and builds a tree
+	representation of the import graph.
+
+	It ignores testdata/ and directories that begin with _. Packages in
+	vendor/ are only included if they are referenced by the main project or
+	one of its dependencies.`,
+			Action: func(c *cli.Context) {
+				setupHandler(c, "tree", cxt, router)
+			},
+		},
+		{
 			Name:  "guess",
 			Usage: "Guess dependencies for existing source.",
 			Description: `This looks through existing source and dependencies,
@@ -481,6 +494,12 @@
 		Does(cmd.PrintName, "status").
 		Using("conf").From("cxt:cfg")
 
+	reg.Route("tree", "Print a dependency graph.").
+		Includes("@startup").
+		Includes("@ready").
+		Does(cmd.Tree, "tree").
+		Using("conf").From("cxt:cfg")
+
 	reg.Route("nv", "No Vendor").
 		Includes("@startup").
 		Does(cmd.NoVendor, "paths").