Merge branch 'Release-0.9'
diff --git a/README.md b/README.md
index 6ab8cde..6b1a900 100644
--- a/README.md
+++ b/README.md
@@ -408,13 +408,13 @@
 work and lots of extra space in your VCS. There may also be unforeseen errors
 ([see an example](https://github.com/mattfarina/golang-broken-vendor)).
 
-#### Q: How do I import settings from GPM, Godep, or gb?
+#### Q: How do I import settings from GPM, Godep, gom or gb?
 
 There are two parts to importing.
 
-1. If a package you import has configuration for GPM, Godep, or gb Glide will
+1. If a package you import has configuration for GPM, Godep, gom or gb Glide will
    recursively install the dependencies automatically.
-2. If you would like to import configuration from GPM, Godep, or gb to Glide see
+2. If you would like to import configuration from GPM, Godep, gom or gb to Glide see
    the `glide import` command. For example, you can run `glide import godep` for
    Glide to detect the projects Godep configuration and generate a `glide.yaml`
    file for you.
diff --git a/action/import_gom.go b/action/import_gom.go
new file mode 100644
index 0000000..87ab616
--- /dev/null
+++ b/action/import_gom.go
@@ -0,0 +1,21 @@
+package action
+
+import (
+	"github.com/Masterminds/glide/gom"
+	"github.com/Masterminds/glide/msg"
+)
+
+// ImportGom imports a Gomfile.
+func ImportGom(dest string) {
+	base := "."
+	config := EnsureConfig()
+	if !gom.Has(base) {
+		msg.Die("No gom data found.")
+	}
+	deps, err := gom.Parse(base)
+	if err != nil {
+		msg.Die("Failed to extract Gomfile: %s", err)
+	}
+	appendImports(deps, config)
+	writeConfigToFileOrStdout(config, dest)
+}
diff --git a/glide.go b/glide.go
index 26dc1b5..6d4bed5 100644
--- a/glide.go
+++ b/glide.go
@@ -307,6 +307,19 @@
 						action.ImportGB(c.String("file"))
 					},
 				},
+				{
+					Name:  "gom",
+					Usage: "Import Gomfile 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) {
+						action.ImportGom(c.String("file"))
+					},
+				},
 			},
 		},
 		{
diff --git a/gom/gom.go b/gom/gom.go
new file mode 100644
index 0000000..fae091a
--- /dev/null
+++ b/gom/gom.go
@@ -0,0 +1,110 @@
+package gom
+
+import (
+	"errors"
+	"os"
+	"path/filepath"
+
+	"github.com/Masterminds/glide/cfg"
+	"github.com/Masterminds/glide/msg"
+	"github.com/Masterminds/glide/util"
+)
+
+// Has returns true if this dir has a Gomfile.
+func Has(dir string) bool {
+	path := filepath.Join(dir, "Gomfile")
+	fi, err := os.Stat(path)
+	return err == nil && !fi.IsDir()
+}
+
+// Parse parses a Gomfile.
+func Parse(dir string) ([]*cfg.Dependency, error) {
+	path := filepath.Join(dir, "Gomfile")
+	if fi, err := os.Stat(path); err != nil || fi.IsDir() {
+		return []*cfg.Dependency{}, nil
+	}
+
+	msg.Info("Found Gomfile.\n")
+	buf := []*cfg.Dependency{}
+
+	goms, err := parseGomfile(path)
+	if err != nil {
+		return []*cfg.Dependency{}, err
+	}
+
+	for _, gom := range goms {
+		// Do we need to skip this dependency?
+		if val, ok := gom.options["skipdep"]; ok && val.(string) == "true" {
+			continue
+		}
+
+		// Check for custom cloning command
+		if _, ok := gom.options["command"]; ok {
+			return []*cfg.Dependency{}, errors.New("Glide does not support custom Gomfile commands")
+		}
+
+		// Check for groups/environments
+		if val, ok := gom.options["group"]; ok {
+			groups := toStringSlice(val)
+			if !stringsContain(groups, "development") && !stringsContain(groups, "production") {
+				// right now we only support development and production
+				msg.Info("Skipping dependency '%s' because it isn't in the development or production group", gom.name)
+				continue
+			}
+		}
+
+		pkg, sub := util.NormalizeName(gom.name)
+
+		dep := &cfg.Dependency{
+			Name: pkg,
+		}
+
+		if len(sub) > 0 {
+			dep.Subpackages = []string{sub}
+		}
+
+		// Check for a specific revision
+		if val, ok := gom.options["commit"]; ok {
+			dep.Reference = val.(string)
+		}
+		if val, ok := gom.options["tag"]; ok {
+			dep.Reference = val.(string)
+		}
+		if val, ok := gom.options["branch"]; ok {
+			dep.Reference = val.(string)
+		}
+
+		// Parse goos and goarch
+		if val, ok := gom.options["goos"]; ok {
+			dep.Os = toStringSlice(val)
+		}
+		if val, ok := gom.options["goarch"]; ok {
+			dep.Arch = toStringSlice(val)
+		}
+
+		buf = append(buf, dep)
+	}
+
+	return buf, nil
+}
+
+func stringsContain(v []string, key string) bool {
+	for _, s := range v {
+		if s == key {
+			return true
+		}
+	}
+	return false
+}
+
+func toStringSlice(v interface{}) []string {
+	if v, ok := v.(string); ok {
+		return []string{v}
+	}
+
+	if v, ok := v.([]string); ok {
+		return v
+	}
+
+	return []string{}
+}
\ No newline at end of file
diff --git a/gom/parser.go b/gom/parser.go
new file mode 100644
index 0000000..bf54f61
--- /dev/null
+++ b/gom/parser.go
@@ -0,0 +1,130 @@
+package gom
+
+// This is copied + slightly adapted from gom's `gomfile.go` file.
+//
+// gom's license is MIT-style.
+
+import (
+	"bufio"
+	"fmt"
+	"io"
+	"os"
+	"regexp"
+	"strings"
+)
+
+var qx = `'[^']*'|"[^"]*"`
+var kx = `:[a-z][a-z0-9_]*`
+var ax = `(?:\s*` + kx + `\s*|,\s*` + kx + `\s*)`
+var re_group = regexp.MustCompile(`\s*group\s+((?:` + kx + `\s*|,\s*` + kx + `\s*)*)\s*do\s*$`)
+var re_end = regexp.MustCompile(`\s*end\s*$`)
+var re_gom = regexp.MustCompile(`^\s*gom\s+(` + qx + `)\s*((?:,\s*` + kx + `\s*=>\s*(?:` + qx + `|\s*\[\s*` + ax + `*\s*\]\s*))*)$`)
+var re_options = regexp.MustCompile(`(,\s*` + kx + `\s*=>\s*(?:` + qx + `|\s*\[\s*` + ax + `*\s*\]\s*)\s*)`)
+
+func unquote(name string) string {
+	name = strings.TrimSpace(name)
+	if len(name) > 2 {
+		if (name[0] == '\'' && name[len(name)-1] == '\'') || (name[0] == '"' && name[len(name)-1] == '"') {
+			return name[1 : len(name)-1]
+		}
+	}
+	return name
+}
+
+func parseOptions(line string, options map[string]interface{}) {
+	ss := re_options.FindAllStringSubmatch(line, -1)
+	re_a := regexp.MustCompile(ax)
+	for _, s := range ss {
+		kvs := strings.SplitN(strings.TrimSpace(s[0])[1:], "=>", 2)
+		kvs[0], kvs[1] = strings.TrimSpace(kvs[0]), strings.TrimSpace(kvs[1])
+		if kvs[1][0] == '[' {
+			as := re_a.FindAllStringSubmatch(kvs[1][1:len(kvs[1])-1], -1)
+			a := []string{}
+			for i := range as {
+				it := strings.TrimSpace(as[i][0])
+				if strings.HasPrefix(it, ",") {
+					it = strings.TrimSpace(it[1:])
+				}
+				if strings.HasPrefix(it, ":") {
+					it = strings.TrimSpace(it[1:])
+				}
+				a = append(a, it)
+			}
+			options[kvs[0][1:]] = a
+		} else {
+			options[kvs[0][1:]] = unquote(kvs[1])
+		}
+	}
+}
+
+type Gom struct {
+	name    string
+	options map[string]interface{}
+}
+
+func parseGomfile(filename string) ([]Gom, error) {
+	f, err := os.Open(filename + ".lock")
+	if err != nil {
+		f, err = os.Open(filename)
+		if err != nil {
+			return nil, err
+		}
+	}
+	br := bufio.NewReader(f)
+
+	goms := make([]Gom, 0)
+
+	n := 0
+	skip := 0
+	valid := true
+	var envs []string
+	for {
+		n++
+		lb, _, err := br.ReadLine()
+		if err != nil {
+			if err == io.EOF {
+				return goms, nil
+			}
+			return nil, err
+		}
+		line := strings.TrimSpace(string(lb))
+		if line == "" || strings.HasPrefix(line, "#") {
+			continue
+		}
+
+		name := ""
+		options := make(map[string]interface{})
+		var items []string
+		if re_group.MatchString(line) {
+			envs = strings.Split(re_group.FindStringSubmatch(line)[1], ",")
+			for i := range envs {
+				envs[i] = strings.TrimSpace(envs[i])[1:]
+			}
+			valid = true
+			continue
+		} else if re_end.MatchString(line) {
+			if !valid {
+				skip--
+				if skip < 0 {
+					return nil, fmt.Errorf("Syntax Error at line %d", n)
+				}
+			}
+			valid = false
+			envs = nil
+			continue
+		} else if skip > 0 {
+			continue
+		} else if re_gom.MatchString(line) {
+			items = re_gom.FindStringSubmatch(line)[1:]
+			name = unquote(items[0])
+			parseOptions(items[1], options)
+		} else {
+			return nil, fmt.Errorf("Syntax Error at line %d", n)
+		}
+		if envs != nil {
+			options["group"] = envs
+		}
+		goms = append(goms, Gom{name, options})
+	}
+	return goms, nil
+}
\ No newline at end of file
diff --git a/importer/importer.go b/importer/importer.go
index 51a9ba5..9d5d2ac 100644
--- a/importer/importer.go
+++ b/importer/importer.go
@@ -1,4 +1,4 @@
-// Package importer imports dependency configuration from Glide, Godep, GPM, and GB
+// Package importer imports dependency configuration from Glide, Godep, GPM, GB and gom
 package importer
 
 import (
@@ -9,12 +9,13 @@
 	"github.com/Masterminds/glide/cfg"
 	"github.com/Masterminds/glide/gb"
 	"github.com/Masterminds/glide/godep"
+	"github.com/Masterminds/glide/gom"
 	"github.com/Masterminds/glide/gpm"
 )
 
 var i = &DefaultImporter{}
 
-// Import uses the DefaultImporter to import from Glide, Godep, GPM, and GB.
+// Import uses the DefaultImporter to import from Glide, Godep, GPM, GB and gom.
 func Import(path string) (bool, []*cfg.Dependency, error) {
 	return i.Import(path)
 }
@@ -29,10 +30,10 @@
 	Import(path string) (bool, []*cfg.Dependency, error)
 }
 
-// DefaultImporter imports from Glide, Godep, GPM, and GB.
+// DefaultImporter imports from Glide, Godep, GPM, GB and gom.
 type DefaultImporter struct{}
 
-// Import tries to import configuration from Glide, Godep, GPM, and GB.
+// Import tries to import configuration from Glide, Godep, GPM, GB and gom.
 func (d *DefaultImporter) Import(path string) (bool, []*cfg.Dependency, error) {
 
 	// Try importing from Glide first.
@@ -77,6 +78,15 @@
 		return true, deps, nil
 	}
 
+	// Try importing from gom
+	if gom.Has(path) {
+		deps, err := gom.Parse(path)
+		if err != nil {
+			return false, []*cfg.Dependency{}, err
+		}
+		return true, deps, nil
+	}
+
 	// When none are found.
 	return false, []*cfg.Dependency{}, nil
 }