Merge pull request #557 from Masterminds/fix/550

Fixed #550: There was opportunity for nil pointers in lock
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6b80ea1..7e757f7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,7 @@
   others are deprecated and no longer needed.
 
 ## Fixed
+- #553: Export was failing with different physical devices
 - #542: Glide failed to detect some test dependencies (thanks @sdboyer)
 - #517: Fixed failure to install testImport from lock when no imports present
   or when same dependency on both import and testImport
diff --git a/glide.go b/glide.go
index ce39ede..77c39a7 100644
--- a/glide.go
+++ b/glide.go
@@ -105,6 +105,12 @@
 			Usage:  "The location of Glide files",
 			EnvVar: "GLIDE_HOME",
 		},
+		cli.StringFlag{
+			Name:   "tmp",
+			Value:  "",
+			Usage:  "The temp directory to use. Defaults to systems temp",
+			EnvVar: "GLIDE_TMP",
+		},
 		cli.BoolFlag{
 			Name:  "no-color",
 			Usage: "Turn off colored output for log messages",
@@ -858,6 +864,7 @@
 	action.Quiet(c.Bool("quiet"))
 	action.Init(c.String("yaml"), c.String("home"))
 	action.EnsureGoVendor()
+	gpath.Tmp = c.String("tmp")
 	return nil
 }
 
diff --git a/path/path.go b/path/path.go
index ecc8a10..48e95fb 100644
--- a/path/path.go
+++ b/path/path.go
@@ -21,6 +21,10 @@
 // As of Go 1.5, this is always vendor.
 var VendorDir = "vendor"
 
+// Tmp is the temporary directory Glide should use. Defaults to "" which
+// signals using the system default.
+var Tmp = ""
+
 // Cache the location of the homedirectory.
 var homeDir = ""
 
@@ -211,3 +215,85 @@
 
 	return false, err
 }
+
+// CopyDir copies an entire source directory to the dest directory.
+//
+// This is akin to `cp -a src/* dest/`
+//
+// We copy the directory here rather than jumping out to a shell so we can
+// support multiple operating systems.
+func CopyDir(source string, dest string) error {
+
+	// get properties of source dir
+	si, err := os.Stat(source)
+	if err != nil {
+		return err
+	}
+
+	err = os.MkdirAll(dest, si.Mode())
+	if err != nil {
+		return err
+	}
+
+	d, _ := os.Open(source)
+
+	objects, err := d.Readdir(-1)
+
+	for _, obj := range objects {
+
+		sp := filepath.Join(source, "/", obj.Name())
+
+		dp := filepath.Join(dest, "/", obj.Name())
+
+		if obj.IsDir() {
+			err = CopyDir(sp, dp)
+			if err != nil {
+				return err
+			}
+		} else {
+			// perform copy
+			err = CopyFile(sp, dp)
+			if err != nil {
+				return err
+			}
+		}
+
+	}
+	return nil
+}
+
+// CopyFile copies a source file to a destination.
+//
+// It follows symbolic links and retains modes.
+func CopyFile(source string, dest string) error {
+	ln, err := os.Readlink(source)
+	if err == nil {
+		return os.Symlink(ln, dest)
+	}
+	s, err := os.Open(source)
+	if err != nil {
+		return err
+	}
+
+	defer s.Close()
+
+	d, err := os.Create(dest)
+	if err != nil {
+		return err
+	}
+
+	defer d.Close()
+
+	_, err = io.Copy(d, s)
+	if err != nil {
+		return err
+	}
+
+	si, err := os.Stat(source)
+	if err != nil {
+		return err
+	}
+	err = os.Chmod(dest, si.Mode())
+
+	return err
+}
diff --git a/repo/installer.go b/repo/installer.go
index a5d6aee..018f8fc 100644
--- a/repo/installer.go
+++ b/repo/installer.go
@@ -244,7 +244,7 @@
 
 // Export from the cache to the vendor directory
 func (i *Installer) Export(conf *cfg.Config) error {
-	tempDir, err := ioutil.TempDir("", "glide-vendor")
+	tempDir, err := ioutil.TempDir(gpath.Tmp, "glide-vendor")
 	if err != nil {
 		return err
 	}
@@ -358,6 +358,15 @@
 	}
 
 	err = os.Rename(vp, i.VendorPath())
+
+	// When there are different physical devices we cannot rename cross device.
+	// Fall back to manual copy.
+	if err != nil && strings.Contains(err.Error(), "invalid cross-device link") {
+		msg.Debug("Cross link err, trying manual copy: %s", err)
+
+		err = gpath.CopyDir(vp, i.VendorPath())
+	}
+
 	return err
 
 }