Merge branch 'master' into feature/flatten
diff --git a/cmd/flatten.go b/cmd/flatten.go
new file mode 100644
index 0000000..74aff62
--- /dev/null
+++ b/cmd/flatten.go
@@ -0,0 +1,307 @@
+package cmd
+
+import (
+	"os"
+	"path"
+
+	"github.com/Masterminds/cookoo"
+	"github.com/kylelemons/go-gypsy/yaml"
+)
+
+// Flatten recurses through all dependent packages and flattens to a top level.
+//
+// Flattening involves determining a tree's dependencies and flattening them
+// into a single large list.
+//
+// Params:
+//	- packages ([]string): The packages to read. If this is empty, it reads all
+//		packages.
+//	- force (bool): force git updates.
+//	- conf (*Config): The configuration.
+//
+// Returns:
+//
+func Flatten(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
+	conf := p.Get("conf", &Config{}).(*Config)
+	skip := p.Get("skip", false).(bool)
+	if skip {
+		return conf, nil
+	}
+	packages := p.Get("packages", []string{}).([]string)
+	force := p.Get("force", true).(bool)
+	vend, _ := VendorPath(c)
+
+	// If no packages are supplied, we do them all.
+	if len(packages) == 0 {
+		packages = make([]string, len(conf.Imports))
+		for i, v := range conf.Imports {
+			packages[i] = v.Name
+		}
+	}
+
+	// Build an initial dependency map.
+	deps := make(map[string]*Dependency, len(conf.Imports))
+	for _, imp := range conf.Imports {
+		deps[imp.Name] = imp
+	}
+
+	f := &flattening{conf, vend, vend, deps, packages}
+
+	err := recFlatten(f, force)
+	flattenSetRefs(f)
+	Info("Project relies on %d dependencies.", len(deps))
+	exportFlattenedDeps(conf, deps)
+
+	return conf, err
+}
+
+func exportFlattenedDeps(conf *Config, in map[string]*Dependency) {
+	out := make([]*Dependency, len(in))
+	i := 0
+	for _, v := range in {
+		out[i] = v
+		i++
+	}
+	conf.Imports = out
+}
+
+type flattening struct {
+	conf *Config
+	// Top vendor path, e.g. project/vendor
+	top string
+	// Current path
+	curr string
+	// Built list of dependencies
+	deps map[string]*Dependency
+	// Dependencies that need to be scanned.
+	scan []string
+}
+
+// Hack: Cache record of updates so we don't have to keep doing git pulls.
+var updateCache = map[string]bool{}
+
+// refFlatten recursively flattens the vendor tree.
+func recFlatten(f *flattening, force bool) error {
+	Debug("---> Inspecting %s for changes (%d packages).\n", f.curr, len(f.scan))
+	for _, imp := range f.scan {
+		Debug("----> Scanning %s", imp)
+		base := path.Join(f.top, imp)
+		mod := []string{}
+		if m, ok := mergeGlide(base, imp, f.deps); ok {
+			mod = m
+		} else if m, ok = mergeGodep(base, imp, f.deps); ok {
+			mod = m
+		} else if m, ok = mergeGPM(base, imp, f.deps); ok {
+			mod = m
+		} else if m, ok = mergeGb(base, imp, f.deps); ok {
+			mod = m
+		} else if m, ok = mergeGuess(base, imp, f.deps); ok {
+			mod = m
+		}
+
+		if len(mod) > 0 {
+			Debug("----> Updating all dependencies for %q (%d)", imp, len(mod))
+			flattenGlideUp(f, base, force)
+			f2 := &flattening{
+				conf: f.conf,
+				top:  f.top,
+				curr: base,
+				deps: f.deps,
+				scan: mod}
+			recFlatten(f2, force)
+		}
+	}
+
+	return nil
+}
+
+// flattenGlideUp does a glide update in the middle of a flatten operation.
+//
+// While this is expensive, it is also necessary to make sure we have the
+// correct version of all dependencies. We might be able to simplify by
+// marking packages dirty when they are added.
+func flattenGlideUp(f *flattening, base string, force bool) error {
+	//vdir := path.Join(base, "vendor")
+	for _, imp := range f.deps {
+		wd := path.Join(f.top, imp.Name)
+		if VcsExists(imp, wd) {
+			if updateCache[imp.Name] {
+				Debug("----> Already updated %s", imp.Name)
+				continue
+			}
+			Debug("Updating project %s (%s)\n", imp.Name, wd)
+			if err := VcsUpdate(imp, f.top, force); err != nil {
+				// We can still go on just fine even if this fails.
+				Warn("Skipped update %s: %s\n", imp.Name, err)
+				continue
+			}
+			updateCache[imp.Name] = true
+		} else {
+			Debug("Importing %s to project %s\n", imp.Name, wd)
+			if err := VcsGet(imp, wd); err != nil {
+				Warn("Skipped getting %s: %v\n", imp.Name, err)
+				continue
+			}
+		}
+
+		// If a revision has been set use it.
+		err := VcsVersion(imp, f.top)
+		if err != nil {
+			Warn("Problem setting version on %s: %s\n", imp.Name, err)
+		}
+	}
+
+	return nil
+}
+
+// Set the references for all packages after a flatten is completed.
+func flattenSetRefs(f *flattening) {
+	Debug("Setting final version for %d dependencies.", len(f.deps))
+	for _, imp := range f.deps {
+		if err := VcsVersion(imp, f.top); err != nil {
+			Warn("Problem setting version on %s: %s (flatten)\n", imp.Name, err)
+		}
+	}
+}
+
+func mergeGlide(dir, name string, deps map[string]*Dependency) ([]string, bool) {
+	gp := path.Join(dir, "glide.yaml")
+	if _, err := os.Stat(gp); err != nil {
+		return []string{}, false
+	}
+	f, err := yaml.ReadFile(gp)
+	if err != nil {
+		Warn("Found glide file %q, but can't parse: %s", gp, err)
+		return []string{}, false
+	}
+
+	conf, err := FromYaml(f.Root)
+	if err != nil {
+		Warn("Found glide file %q, but can't use it: %s", gp, err)
+		return []string{}, false
+	}
+
+	Info("Found glide.yaml in %s", gp)
+
+	return mergeDeps(deps, conf.Imports), true
+}
+
+// listGodep appends Godeps entries to the deps.
+//
+// It returns true if any dependencies were found (even if not added because
+// they are duplicates).
+func mergeGodep(dir, name string, deps map[string]*Dependency) ([]string, bool) {
+	Debug("Looking in %s/Godeps/ for a Godeps.json file.\n", dir)
+	d, err := parseGodepGodeps(dir)
+	if err != nil {
+		Warn("Looking for Godeps: %s\n", err)
+		return []string{}, false
+	} else if len(d) == 0 {
+		return []string{}, false
+	}
+
+	Info("Found Godeps.json file for %q", name)
+	return mergeDeps(deps, d), true
+}
+
+// listGb merges GB dependencies into the deps.
+func mergeGb(dir, pkg string, deps map[string]*Dependency) ([]string, bool) {
+	Debug("Looking in %s/vendor/ for a manifest file.\n", dir)
+	d, err := parseGbManifest(dir)
+	if err != nil || len(d) == 0 {
+		return []string{}, false
+	}
+	Info("Found gb manifest file for %q", pkg)
+	return mergeDeps(deps, d), true
+}
+
+// mergeGPM merges GPM Godeps files into deps.
+func mergeGPM(dir, pkg string, deps map[string]*Dependency) ([]string, bool) {
+	d, err := parseGPMGodeps(dir)
+	if err != nil || len(d) == 0 {
+		return []string{}, false
+	}
+	Info("Found GPM file for %q", pkg)
+	return mergeDeps(deps, d), true
+}
+
+// mergeGuess guesses dependencies and merges.
+//
+// This always returns true because it always handles the job of searching
+// for dependencies. So generally it should be the last merge strategy
+// that you try.
+func mergeGuess(dir, pkg string, deps map[string]*Dependency) ([]string, bool) {
+	Info("Scanning %s for dependencies.", pkg)
+	buildContext, err := GetBuildContext()
+	if err != nil {
+		Warn("Could not scan package %q: %s", pkg, err)
+		return []string{}, false
+	}
+
+	res := []string{}
+
+	if _, err := os.Stat(dir); err != nil {
+		Warn("Directory is missing: %s", dir)
+		return res, true
+	}
+
+	d := walkDeps(buildContext, dir, pkg)
+	for _, name := range d {
+		name, _ := NormalizeName(name)
+		repo := getRepoRootFromPackage(name)
+		if _, ok := deps[name]; ok {
+			Debug("====> Seen %s already. Skipping", name)
+			continue
+		}
+
+		found := findPkg(buildContext, name, dir)
+		switch found.PType {
+		case ptypeUnknown:
+			Debug("✨☆ Undownloaded dependency: %s", name)
+			nd := &Dependency{
+				Name:       name,
+				Repository: "https://" + repo,
+			}
+			deps[name] = nd
+			res = append(res, name)
+		case ptypeGoroot, ptypeCgo:
+			break
+		default:
+			// We're looking for dependencies that might exist in $GOPATH
+			// but not be on vendor. We add any that are on $GOPATH.
+			if _, ok := deps[name]; !ok {
+				Debug("✨☆ GOPATH dependency: %s", name)
+				nd := &Dependency{Name: name}
+				deps[name] = nd
+				res = append(res, name)
+			}
+		}
+	}
+
+	return res, true
+}
+
+// mergeDeps merges any dependency array into deps.
+func mergeDeps(orig map[string]*Dependency, add []*Dependency) []string {
+	mod := []string{}
+	for _, dd := range add {
+		// Add it unless it's already there.
+		if existing, ok := orig[dd.Name]; !ok {
+			orig[dd.Name] = dd
+			Debug("Adding %s to the scan list", dd.Name)
+			mod = append(mod, dd.Name)
+		} else if existing.Reference == "" && dd.Reference != "" {
+			// If a nested dep has finer dependency references than outside,
+			// set the reference.
+			existing.Reference = dd.Reference
+			mod = append(mod, dd.Name)
+		} else if dd.Reference != "" && existing.Reference != "" && dd.Reference != existing.Reference {
+			// We can detect version conflicts, but we can't really do
+			// anything to correct, since we don't know the intentions of the
+			// authors.
+			Warn("Conflict: %s ref is %s, but also asked for %s", existing.Name, existing.Reference, dd.Reference)
+			Info("Keeping %s %s", existing.Name, existing.Reference)
+		}
+	}
+	return mod
+}
diff --git a/cmd/get_imports.go b/cmd/get_imports.go
index d988cdf..fdc53de 100644
--- a/cmd/get_imports.go
+++ b/cmd/get_imports.go
@@ -30,6 +30,9 @@
 
 // GetAll gets zero or more repos.
 //
+// This takes a package name, normalizes it, finds the repo, and installs it.
+// It's the workhorse behind `glide get`.
+//
 // Params:
 //	- packages ([]string): Package names to get.
 // 	- verbose (bool): default false
@@ -153,6 +156,12 @@
 			Debug("===> Skipping %q", dep.Name)
 			continue
 		}
+
+		// Hack: The updateCache global keeps us from re-updating the same
+		// dependencies when we're recursing. We cache here to prevent
+		// flattening from causing unnecessary updates.
+		updateCache[dep.Name] = true
+
 		if err := VcsUpdate(dep, cwd, force); err != nil {
 			Warn("Update failed for %s: %s\n", dep.Name, err)
 		}
diff --git a/cmd/msg.go b/cmd/msg.go
index 5f7e555..56b6942 100644
--- a/cmd/msg.go
+++ b/cmd/msg.go
@@ -33,16 +33,16 @@
 	if Quiet {
 		return
 	}
-	fmt.Print(Color(Green, "[INFO] "))
+	fmt.Fprint(os.Stderr, Color(Green, "[INFO] "))
 	Msg(msg, args...)
 }
 
 // Debug logs debug information
 func Debug(msg string, args ...interface{}) {
-	if Quiet {
+	if Quiet || !IsDebugging {
 		return
 	}
-	fmt.Print("[DEBUG] ")
+	fmt.Fprint(os.Stderr, "[DEBUG] ")
 	Msg(msg, args...)
 }
 
@@ -77,14 +77,14 @@
 // varying types.
 func Msg(msg string, args ...interface{}) {
 	if len(args) == 0 {
-		fmt.Print(msg)
+		fmt.Fprint(os.Stderr, msg)
 		return
 	}
-	fmt.Printf(msg, args...)
+	fmt.Fprintf(os.Stderr, msg, args...)
 
 	// Get rid of the annoying fact that messages need \n at the end, but do
 	// it in a backward compatible way.
 	if !strings.HasSuffix(msg, "\n") {
-		fmt.Println()
+		fmt.Fprintln(os.Stderr)
 	}
 }
diff --git a/cmd/msg_windows.go b/cmd/msg_windows.go
index 604dba6..6774ebd 100644
--- a/cmd/msg_windows.go
+++ b/cmd/msg_windows.go
@@ -19,7 +19,7 @@
 
 // Debug logs debug information
 func Debug(msg string, args ...interface{}) {
-	if Quiet {
+	if Quiet || !IsDebugging {
 		return
 	}
 	fmt.Print("[DEBUG] ")
diff --git a/cmd/recursive_glide.go b/cmd/recursive_glide.go
deleted file mode 100644
index 717249e..0000000
--- a/cmd/recursive_glide.go
+++ /dev/null
@@ -1,246 +0,0 @@
-package cmd
-
-import (
-	"github.com/Masterminds/cookoo"
-	"github.com/kylelemons/go-gypsy/yaml"
-	"io/ioutil"
-	"os"
-	"path"
-	"strings"
-)
-
-// Recurse does glide installs on dependent packages.
-//
-// Recurse looks in all known packages for a glide.yaml files and installs for
-// each one it finds.
-//
-// The packages scanned can be restricted (at the top level) by providing
-// a list of packages to scan in the `packages` param.
-//
-// Params:
-// 	- enable (bool)
-// 	- importGodeps (bool)
-// 	- importGPM (bool)
-// 	- importGb (bool)
-// 	- deleteFlatten (bool)
-// 	- force (bool)
-// 	- packages ([]string): Packages to recurse through. If empty, does all of them.
-func Recurse(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
-	if !p.Get("enable", true).(bool) {
-		return nil, nil
-	}
-	force := p.Get("force", true).(bool)
-	plist := p.Get("packages", []string{}).([]string)
-	pkgs := list2map(plist)
-
-	godeps, gpm, gb, deleteFlatten := false, false, false, false
-	if g, ok := p.Has("importGodeps"); ok {
-		godeps = g.(bool)
-	}
-	if g, ok := p.Has("importGPM"); ok {
-		gpm = g.(bool)
-	}
-	if g, ok := p.Has("importGb"); ok {
-		gb = g.(bool)
-	}
-
-	if g, ok := p.Has("deleteFlatten"); ok {
-		deleteFlatten = g.(bool)
-	}
-
-	Info("Checking dependencies for updates. Godeps: %v, GPM: %v, gb: %v\n", godeps, gpm, gb)
-	if deleteFlatten == true {
-		Info("Deleting flattened dependencies enabled\n")
-	}
-	conf := p.Get("conf", &Config{}).(*Config)
-	vend, _ := VendorPath(c)
-
-	return recDepResolve(conf, pkgs, vend, godeps, gpm, gb, force, deleteFlatten)
-}
-
-func recDepResolve(conf *Config, filter map[string]bool, vend string, godeps, gpm, gb, force, deleteFlatten bool) (interface{}, error) {
-
-	Info("Inspecting %s.\n", vend)
-
-	if len(conf.Imports) == 0 {
-		Info("No imports.\n")
-	}
-
-	restrict := len(filter) > 0
-
-	// Look in each package to see whether it has a glide.yaml, and no vendor/
-	for _, imp := range conf.Imports {
-		if restrict && !filter[imp.Name] {
-			Debug("===> Skipping %q", imp.Name)
-			continue
-		}
-		if imp.Flattened == true {
-			continue
-		}
-		base := path.Join(vend, imp.Name)
-		Info("Looking in %s for a glide.yaml file.\n", base)
-		if !needsGlideUp(base) {
-			if godeps {
-				importGodep(base, imp.Name)
-			}
-			if gpm {
-				importGPM(base, imp.Name)
-			}
-			if gb {
-				importGb(base, imp.Name)
-			}
-			if !needsGlideUp(base) {
-				Info("Package %s manages its own dependencies.\n", imp.Name)
-				continue
-			}
-		}
-
-		if err := dependencyGlideUp(conf, base, godeps, gpm, gb, force, deleteFlatten); err != nil {
-			Warn("Failed to update dependency %s: %s", imp.Name, err)
-		}
-	}
-
-	return nil, nil
-}
-
-func dependencyGlideUp(parentConf *Config, base string, godep, gpm, gb, force, deleteFlatten bool) error {
-	Info("Doing a glide in %s\n", base)
-	fname := path.Join(base, "glide.yaml")
-	f, err := yaml.ReadFile(fname)
-	if err != nil {
-		return err
-	}
-
-	conf, err := FromYaml(f.Root)
-	conf.Parent = parentConf
-	if err != nil {
-		return err
-	}
-	for _, imp := range conf.Imports {
-		vdir := path.Join(base, "vendor")
-		wd := path.Join(vdir, imp.Name)
-		// if our root glide.yaml says to flatten this, we skip it
-		if dep := conf.GetRoot().Imports.Get(imp.Name); dep != nil {
-			flatten := conf.GetRoot().Flatten
-			if flatten == true && dep.Flatten == false ||
-				flatten == false && dep.Flatten == true {
-				flatten = dep.Flatten
-			}
-			if flatten == true {
-				Info("Skipping importing %s due to flatten being set in root import glide.yaml\n", imp.Name)
-				imp.Flattened = true
-			}
-
-			if flatten == true && imp.Reference != dep.Reference {
-				Warn("Flattened package %s ref (%s) is diferent from sub vendored package ref (%s)\n", imp.Name, imp.Reference, dep.Reference)
-			}
-
-			if imp.Flattened == true && deleteFlatten == true {
-				if exists, _ := fileExist(wd); exists == true || true {
-					remove := wd + string(os.PathSeparator)
-					Warn("Removing flattened sub vendored package: %s\n", strings.TrimPrefix(remove, base))
-					rerr := os.RemoveAll(remove)
-					if rerr != nil {
-						return rerr
-					}
-				}
-			}
-			if imp.Flattened == true {
-				continue
-			}
-		}
-
-		// We don't use the global var to find vendor dir name because the
-		// user may mis-use that var to modify the local vendor dir, and
-		// we don't want that to break the embedded vendor dirs.
-
-		if err := ensureDir(wd); err != nil {
-			Warn("Skipped getting %s (vendor/ error): %s\n", imp.Name, err)
-			continue
-		}
-
-		if VcsExists(imp, wd) {
-			Info("Updating project %s (%s)\n", imp.Name, wd)
-			if err := VcsUpdate(imp, vdir, force); err != nil {
-				// We can still go on just fine even if this fails.
-				Warn("Skipped update %s: %s\n", imp.Name, err)
-				continue
-			}
-		} else {
-			Info("Importing %s to project %s\n", imp.Name, base)
-			if err := VcsGet(imp, wd); err != nil {
-				Warn("Skipped getting %s: %v\n", imp.Name, err)
-				continue
-			}
-		}
-
-		// If a revision has been set use it.
-		err = VcsVersion(imp, vdir)
-		if err != nil {
-			Warn("Problem setting version on %s: %s\n", imp.Name, err)
-		}
-
-		//recDepResolve(conf, path.Join(wd, "vendor"))
-	}
-	// We only filter at the top level.
-	e := map[string]bool{}
-	recDepResolve(conf, e, path.Join(base, "vendor"), godep, gpm, gb, force, deleteFlatten)
-	return nil
-}
-
-func ensureDir(dirpath string) error {
-	if fi, err := os.Stat(dirpath); err == nil && fi.IsDir() {
-		return nil
-	}
-	return os.MkdirAll(dirpath, 0755)
-}
-
-func needsGlideUp(dir string) bool {
-	stat, err := os.Stat(path.Join(dir, "glide.yaml"))
-	if err != nil || stat.IsDir() {
-		return false
-	}
-
-	// Should probably see if vendor is there and non-empty.
-
-	return true
-}
-
-func importGodep(dir, pkg string) error {
-	Info("Looking in %s/Godeps/ for a Godeps.json file.\n", dir)
-	d, err := parseGodepGodeps(dir)
-	if err != nil {
-		Warn("Looking for Godeps: %s\n", err)
-		return err
-	}
-	return quickDirtyYAMLWrite(dir, d, pkg)
-}
-
-func importGPM(dir, pkg string) error {
-	d, err := parseGPMGodeps(dir)
-	if err != nil {
-		return err
-	}
-	return quickDirtyYAMLWrite(dir, d, pkg)
-}
-
-func importGb(dir, pkg string) error {
-	Info("Looking in %s/vendor/ for a manifest file.\n", dir)
-	d, err := parseGbManifest(dir)
-	if err != nil {
-		return err
-	}
-	return quickDirtyYAMLWrite(dir, d, pkg)
-}
-
-func quickDirtyYAMLWrite(dir string, d []*Dependency, pkg string) error {
-	if len(d) == 0 {
-		return nil
-	}
-	c := &Config{Name: pkg, Imports: d}
-	node := c.ToYaml()
-	data := yaml.Render(node)
-	f := path.Join(dir, "glide.yaml")
-	Info("Writing new glide.yaml file in %s\n", dir)
-	return ioutil.WriteFile(f, []byte(data), 0755)
-}
diff --git a/cmd/util.go b/cmd/util.go
index 9169f54..5527153 100644
--- a/cmd/util.go
+++ b/cmd/util.go
@@ -15,10 +15,12 @@
 
 // Quiet, when set to true, can suppress Info and Debug messages.
 var Quiet = false
+var IsDebugging = false
 
 // BeQuiet supresses Info and Debug messages.
 func BeQuiet(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
 	Quiet = p.Get("quiet", false).(bool)
+	IsDebugging = p.Get("debug", false).(bool)
 	return Quiet, nil
 }
 
diff --git a/cmd/yaml.go b/cmd/yaml.go
index f0fbbdf..b1a6e2c 100644
--- a/cmd/yaml.go
+++ b/cmd/yaml.go
@@ -63,6 +63,7 @@
 // 	- filename (string): If set, the file will be opened and the content will be written to it.
 func WriteYaml(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
 	top := p.Get("yaml.Node", yaml.Scalar("nothing to print")).(yaml.Node)
+	toStdout := p.Get("toStdout", true).(bool)
 	var out io.Writer
 	if nn, ok := p.Has("filename"); ok && len(nn.(string)) > 0 {
 		file, err := os.Create(nn.(string))
@@ -70,11 +71,12 @@
 		}
 		defer file.Close()
 		out = io.Writer(file)
-	} else {
+		fmt.Fprint(out, yaml.Render(top))
+	} else if toStdout {
 		out = p.Get("out", os.Stdout).(io.Writer)
+		fmt.Fprint(out, yaml.Render(top))
 	}
-
-	fmt.Fprint(out, yaml.Render(top))
+	// Otherwise we supress output.
 
 	return true, nil
 }
diff --git a/glide.go b/glide.go
index 82a16b5..deb76fe 100644
--- a/glide.go
+++ b/glide.go
@@ -90,6 +90,10 @@
 			Name:  "quiet, q",
 			Usage: "Quiet (no info or debug messages)",
 		},
+		cli.BoolFlag{
+			Name:  "debug",
+			Usage: "Print Debug messages (verbose)",
+		},
 	}
 	app.CommandNotFound = func(c *cli.Context, command string) {
 		cxt.Put("os.Args", os.Args)
@@ -171,8 +175,9 @@
 					os.Exit(1)
 				}
 				cxt.Put("packages", []string(c.Args()))
-				cxt.Put("recursiveDependencies", !c.Bool("no-recursive"))
+				cxt.Put("skipFlatten", !c.Bool("no-recursive"))
 				cxt.Put("insecure", c.Bool("insecure"))
+				// FIXME: Are these used anywhere?
 				if c.Bool("import") {
 					cxt.Put("importGodeps", true)
 					cxt.Put("importGPM", true)
@@ -188,21 +193,42 @@
 				{
 					Name:  "godep",
 					Usage: "Import Godep's Godeps.json files and display the would-be yaml file",
+					Flags: []cli.Flag{
+						cli.StringFlag{
+							Name:  "file, f",
+							Usage: "Save all of the discovered dependencies to a Glide YAML file.",
+						},
+					},
 					Action: func(c *cli.Context) {
+						cxt.Put("toPath", c.String("file"))
 						setupHandler(c, "import godep", cxt, router)
 					},
 				},
 				{
 					Name:  "gpm",
 					Usage: "Import GPM's Godeps and Godeps-Git files and display the would-be yaml file",
+					Flags: []cli.Flag{
+						cli.StringFlag{
+							Name:  "file, f",
+							Usage: "Save all of the discovered dependencies to a Glide YAML file.",
+						},
+					},
 					Action: func(c *cli.Context) {
+						cxt.Put("toPath", c.String("file"))
 						setupHandler(c, "import gpm", cxt, router)
 					},
 				},
 				{
 					Name:  "gb",
 					Usage: "Import gb's manifest file and display the would-be yaml file",
+					Flags: []cli.Flag{
+						cli.StringFlag{
+							Name:  "file, f",
+							Usage: "Save all of the discovered dependencies to a Glide YAML file.",
+						},
+					},
 					Action: func(c *cli.Context) {
+						cxt.Put("toPath", c.String("file"))
 						setupHandler(c, "import gb", cxt, router)
 					},
 				},
@@ -278,18 +304,18 @@
 	stored in 'vendor/foo/bar/vendor'. This behavior can be disabled with
 	'--no-recursive'.
 
-	If the '--import' flag is specified, Glide will also import Godep and GPM
-	files as it finds them in dependencies. It will create a glide.yaml file
-	from the Godeps data, and then update. This has no effect if '--no-recursive'
-	is set.
+	Glide will also import Godep, GB, and GPM files as it finds them in dependencies.
+	It will create a glide.yaml file from the Godeps data, and then update. This
+	has no effect if '--no-recursive' is set.
 
 	If the '--update-vendored' flag (aliased to '-u') is present vendored
 	dependencies, stored in your projects VCS repository, will be updated. This
 	works by removing the old package, checking out an the repo and setting the
 	version, and removing the VCS directory.
 
-	If the '--delete-flatten' flag is present, Glide will remove any dependencies
-	marked flatten within dependencies.
+	By default, packages that are discovered are considered transient, and are
+	not stored in the glide.yaml file. The --file=NAME.yaml flag allows you
+	to save the discovered dependencies to a YAML file.
 	`,
 			Flags: []cli.Flag{
 				cli.BoolFlag{
@@ -297,12 +323,8 @@
 					Usage: "Delete vendor packages not specified in config.",
 				},
 				cli.BoolFlag{
-					Name:  "no-recursive",
-					Usage: "Disable updating dependencies' dependencies.",
-				},
-				cli.BoolFlag{
-					Name:  "import",
-					Usage: "When updating dependencies, convert Godeps (GPM, Godep) to glide.yaml and pull dependencies",
+					Name:  "no-recursive, quick",
+					Usage: "Disable updating dependencies' dependencies. Only update things in glide.yaml.",
 				},
 				cli.BoolFlag{
 					Name:  "force",
@@ -312,16 +334,18 @@
 					Name:  "update-vendored, u",
 					Usage: "Update vendored packages (without local VCS repo). Warning, changes will be lost.",
 				},
-				cli.BoolFlag{
-					Name:  "delete-flatten",
-					Usage: "Delete flattened vendor packages.",
+				cli.StringFlag{
+					Name:  "file, f",
+					Usage: "Save all of the discovered dependencies to a Glide YAML file.",
 				},
 			},
 			Action: func(c *cli.Context) {
 				cxt.Put("deleteOptIn", c.Bool("delete"))
 				cxt.Put("forceUpdate", c.Bool("force"))
-				cxt.Put("recursiveDependencies", !c.Bool("no-recursive"))
+				cxt.Put("skipFlatten", c.Bool("no-recursive"))
 				cxt.Put("deleteFlatten", c.Bool("delete-flatten"))
+				cxt.Put("toPath", c.String("file"))
+				cxt.Put("toStdout", false)
 				if c.Bool("import") {
 					cxt.Put("importGodeps", true)
 					cxt.Put("importGPM", true)
@@ -394,6 +418,7 @@
 
 func setupHandler(c *cli.Context, route string, cxt cookoo.Context, router *cookoo.Router) {
 	cxt.Put("q", c.GlobalBool("quiet"))
+	cxt.Put("debug", c.GlobalBool("debug"))
 	cxt.Put("yaml", c.GlobalString("yaml"))
 	cxt.Put("cliArgs", c.Args())
 	if err := router.HandleRequest(route, cxt, false); err != nil {
@@ -407,6 +432,7 @@
 		// TODO: Add setup for debug in addition to quiet.
 		Does(cmd.BeQuiet, "quiet").
 		Using("quiet").From("cxt:q").
+		Using("debug").From("cxt:debug").
 		Does(cmd.VersionGuard, "v")
 
 	reg.Route("@ready", "Prepare for glide commands.").
@@ -422,13 +448,9 @@
 		Using("conf").From("cxt:cfg").
 		Using("insecure").From("cxt:insecure").
 		Does(cmd.MergeToYaml, "merged").Using("conf").From("cxt:cfg").
-		Does(cmd.Recurse, "recurse").Using("conf").From("cxt:cfg").
-		Using("enable").From("cxt:recursiveDependencies").
-		Using("importGodeps").From("cxt:importGodeps").
-		Using("importGPM").From("cxt:importGPM").
-		Using("importGb").From("cxt:importGb").
-		Using("force").From("cxt:forceUpdate").WithDefault(false).
+		Does(cmd.Flatten, "flatten").Using("conf").From("cxt:cfg").
 		Using("packages").From("cxt:packages").
+		Using("force").From("cxt:forceUpdate").
 		Does(cmd.WriteYaml, "out").
 		Using("yaml.Node").From("cxt:merged").
 		Using("filename").WithDefault("glide.yaml").From("cxt:yaml")
@@ -456,17 +478,18 @@
 		Using("force").From("cxt:forceUpdate").
 		Using("packages").From("cxt:packages").
 		Does(cmd.SetReference, "version").Using("conf").From("cxt:cfg").
-		Does(cmd.Recurse, "recurse").Using("conf").From("cxt:cfg").
-		Using("deleteFlatten").From("cxt:deleteFlatten").
-		Using("importGodeps").From("cxt:importGodeps").
-		Using("importGPM").From("cxt:importGPM").
-		Using("importGb").From("cxt:importGb").
-		Using("enable").From("cxt:recursiveDependencies").
-		Using("force").From("cxt:forceUpdate").
+		Does(cmd.Flatten, "flattened").Using("conf").From("cxt:cfg").
 		Using("packages").From("cxt:packages").
+		Using("force").From("cxt:forceUpdate").
+		Using("skip").From("cxt:skipFlatten").
 		Does(cmd.VendoredCleanUp, "_").
 		Using("conf").From("cxt:cfg").
-		Using("update").From("cxt:updateVendoredDeps")
+		Using("update").From("cxt:updateVendoredDeps").
+		Does(cmd.MergeToYaml, "merged").Using("conf").From("cxt:cfg").
+		Does(cmd.WriteYaml, "out").
+		Using("yaml.Node").From("cxt:merged").
+		Using("filename").From("cxt:toPath").
+		Using("toStdout").From("cxt:toStdout")
 
 	//Does(cmd.Rebuild, "rebuild").Using("conf").From("cxt:cfg")
 
@@ -498,7 +521,8 @@
 		Using("conf").From("cxt:cfg").
 		// Does(cmd.UpdateReferences, "refs").Using("conf").From("cxt:cfg").
 		Does(cmd.MergeToYaml, "merged").Using("conf").From("cxt:cfg").
-		Does(cmd.WriteYaml, "out").Using("yaml.Node").From("cxt:merged")
+		Does(cmd.WriteYaml, "out").Using("yaml.Node").From("cxt:merged").
+		Using("filename").From("cxt:toPath")
 
 	reg.Route("import godep", "Read a Godeps.json file").
 		Includes("@startup").
@@ -509,7 +533,8 @@
 		Using("conf").From("cxt:cfg").
 		// Does(cmd.UpdateReferences, "refs").Using("conf").From("cxt:cfg").
 		Does(cmd.MergeToYaml, "merged").Using("conf").From("cxt:cfg").
-		Does(cmd.WriteYaml, "out").Using("yaml.Node").From("cxt:merged")
+		Does(cmd.WriteYaml, "out").Using("yaml.Node").From("cxt:merged").
+		Using("filename").From("cxt:toPath")
 
 	reg.Route("import gb", "Read a vendor/manifest file").
 		Includes("@startup").
@@ -519,7 +544,8 @@
 		Using("dependencies").From("cxt:manifest").
 		Using("conf").From("cxt:cfg").
 		Does(cmd.MergeToYaml, "merged").Using("conf").From("cxt:cfg").
-		Does(cmd.WriteYaml, "out").Using("yaml.Node").From("cxt:merged")
+		Does(cmd.WriteYaml, "out").Using("yaml.Node").From("cxt:merged").
+		Using("filename").From("cxt:toPath")
 
 	reg.Route("guess", "Guess dependencies").
 		Includes("@ready").