diff --git a/src/BUILD_ b/src/BUILD_
index ced68310f2..87b0630549 100644
--- a/src/BUILD_
+++ b/src/BUILD_
@@ -7,6 +7,7 @@ go_binary(
         '//src/clean',
         '//src/cli',
         '//src/core',
+        '//src/export',
         '//src/gc',
         '//src/help',
         '//src/metrics',
diff --git a/src/core/utils.go b/src/core/utils.go
index 3d4dc5888a..efb834b20f 100644
--- a/src/core/utils.go
+++ b/src/core/utils.go
@@ -173,6 +173,10 @@ func RecursiveCopyFile(from string, to string, mode os.FileMode, link, fallback
 				if fi.IsDir() {
 					return RecursiveCopyFile(name+"/", dest+"/", mode, link, fallback)
 				} else {
+					// 0 indicates inheriting the existing mode bits.
+					if mode == 0 {
+						mode = info.Mode()
+					}
 					return copyOrLinkFile(name, dest, mode, link, fallback)
 				}
 			} else {
diff --git a/src/export/BUILD b/src/export/BUILD
new file mode 100644
index 0000000000..d6a66f5b58
--- /dev/null
+++ b/src/export/BUILD
@@ -0,0 +1,10 @@
+go_library(
+    name = 'export',
+    srcs = ['export.go'],
+    deps = [
+        '//src/core',
+        '//src/gc',
+        '//third_party/go:logging',
+    ],
+    visibility = ['PUBLIC'],
+)
diff --git a/src/export/export.go b/src/export/export.go
new file mode 100644
index 0000000000..c28ab440e8
--- /dev/null
+++ b/src/export/export.go
@@ -0,0 +1,75 @@
+// Package export handles exporting parts of the repo to other directories.
+// This is useful if, for example, one wanted to separate out part of
+// their repo with all dependencies.
+package export
+
+import (
+	"path"
+	"strings"
+
+	"gopkg.in/op/go-logging.v1"
+
+	"core"
+	"gc"
+)
+
+var log = logging.MustGetLogger("export")
+
+// ToDir exports a set of targets to the given directory.
+// It dies on any errors.
+func ToDir(state *core.BuildState, dir string, targets []core.BuildLabel) {
+	done := map[*core.BuildTarget]bool{}
+	for _, target := range targets {
+		export(state.Graph, dir, state.Graph.TargetOrDie(target), done)
+	}
+	// Now write all the build files
+	packages := map[*core.Package]bool{}
+	for target := range done {
+		packages[state.Graph.PackageOrDie(target.Label.PackageName)] = true
+	}
+	for pkg := range packages {
+		dest := path.Join(dir, pkg.Filename)
+		if err := core.RecursiveCopyFile(pkg.Filename, dest, 0, false, false); err != nil {
+			log.Fatalf("Failed to copy BUILD file: %s\n", pkg.Filename)
+		}
+		// Now rewrite the unused targets out of it
+		victims := []string{}
+		for name, target := range pkg.Targets {
+			if !done[target] {
+				victims = append(victims, name)
+			}
+		}
+		if err := gc.RewriteFile(state, dest, victims); err != nil {
+			log.Fatalf("Failed to rewrite BUILD file: %s\n", err)
+		}
+	}
+}
+
+// export implements the logic of ToDir, but prevents repeating targets.
+func export(graph *core.BuildGraph, dir string, target *core.BuildTarget, done map[*core.BuildTarget]bool) {
+	if done[target] {
+		return
+	}
+	for _, src := range target.AllSources() {
+		if src.Label() == nil { // We'll handle these dependencies later
+			for _, p := range src.FullPaths(graph) {
+				if !strings.HasPrefix(p, "/") { // Don't copy system file deps.
+					if err := core.RecursiveCopyFile(p, path.Join(dir, p), 0, false, false); err != nil {
+						log.Fatalf("Error copying file: %s\n", err)
+					}
+				}
+			}
+		}
+	}
+	done[target] = true
+	for _, dep := range target.Dependencies() {
+		if parent := dep.Parent(graph); parent != nil && parent != target.Parent(graph) && parent != target {
+			export(graph, dir, parent, done)
+		} else {
+			export(graph, dir, dep, done)
+		}
+	}
+	for _, subinclude := range graph.PackageOrDie(target.Label.PackageName).Subincludes {
+		export(graph, dir, graph.TargetOrDie(subinclude), done)
+	}
+}
diff --git a/src/gc/gc.go b/src/gc/gc.go
index 65d8da29c9..de34d09702 100644
--- a/src/gc/gc.go
+++ b/src/gc/gc.go
@@ -201,8 +201,11 @@ func publicDependencies(graph *core.BuildGraph, target *core.BuildTarget) []*cor
 	return ret
 }
 
-// rewriteFile rewrites a BUILD file to exclude a set of targets.
-func rewriteFile(state *core.BuildState, filename string, targets []string) error {
+// RewriteFile rewrites a BUILD file to exclude a set of targets.
+func RewriteFile(state *core.BuildState, filename string, targets []string) error {
+	for i, t := range targets {
+		targets[i] = fmt.Sprintf(`"%s"`, t)
+	}
 	data := string(MustAsset("rewrite.py"))
 	// Template in the variables we want.
 	data = strings.Replace(data, "__FILENAME__", filename, 1)
@@ -214,12 +217,12 @@ func rewriteFile(state *core.BuildState, filename string, targets []string) erro
 func removeTargets(state *core.BuildState, labels core.BuildLabels) error {
 	byPackage := map[string][]string{}
 	for _, l := range labels {
-		byPackage[l.PackageName] = append(byPackage[l.PackageName], `"`+l.Name+`"`)
+		byPackage[l.PackageName] = append(byPackage[l.PackageName], l.Name)
 	}
 	for pkgName, victims := range byPackage {
 		filename := state.Graph.PackageOrDie(pkgName).Filename
-		log.Notice("Rewriting %s to remove %s...\n", filename, strings.Replace(strings.Join(victims, ", "), `"`, "", -1))
-		if err := rewriteFile(state, filename, victims); err != nil {
+		log.Notice("Rewriting %s to remove %s...\n", filename, strings.Join(victims, ", "))
+		if err := RewriteFile(state, filename, victims); err != nil {
 			return err
 		}
 	}
diff --git a/src/gc/rewrite_test.go b/src/gc/rewrite_test.go
index c33c9fd341..6e7b7912c7 100644
--- a/src/gc/rewrite_test.go
+++ b/src/gc/rewrite_test.go
@@ -31,7 +31,7 @@ func TestRewriteFile(t *testing.T) {
 	wd, _ := os.Getwd()
 	err := core.CopyFile("src/gc/test_data/before.build", path.Join(wd, "test.build"), 0644)
 	assert.NoError(t, err)
-	assert.NoError(t, rewriteFile(state, "test.build", []string{`"prometheus"`, `"cover"`}))
+	assert.NoError(t, RewriteFile(state, "test.build", []string{"prometheus", "cover"}))
 	rewritten, err := ioutil.ReadFile("test.build")
 	assert.NoError(t, err)
 	after, err := ioutil.ReadFile("src/gc/test_data/after.build")
diff --git a/src/gc/stub.go b/src/gc/stub.go
index 6e9f79000d..644a95333b 100644
--- a/src/gc/stub.go
+++ b/src/gc/stub.go
@@ -5,3 +5,6 @@ import "core"
 // GarbageCollect is a stub used at initial bootstrap time to avoid requiring us to run go-bindata yet again.
 func GarbageCollect(state *core.BuildState, filter, targets []core.BuildLabel, keepLabels []string, conservative, targetsOnly, srcsOnly, noPrompt, dryRun, git bool) {
 }
+
+// RewriteFile is also a stub used at boostrap time that does nothing.
+func RewriteFile(state *core.BuildState, filename string, targets []string) error { return nil }
diff --git a/src/please.go b/src/please.go
index a8c025cd40..31e1106ebe 100644
--- a/src/please.go
+++ b/src/please.go
@@ -18,6 +18,7 @@ import (
 	"clean"
 	"cli"
 	"core"
+	"export"
 	"gc"
 	"help"
 	"metrics"
@@ -167,6 +168,13 @@ var opts struct {
 		} `positional-args:"true"`
 	} `command:"gc" description:"Analyzes the repo to determine unneeded targets."`
 
+	Export struct {
+		Output string `short:"o" long:"output" required:"true" description:"Directory to export into"`
+		Args   struct {
+			Targets []core.BuildLabel `positional-arg-name:"targets" description:"Targets to export."`
+		} `positional-args:"true"`
+	} `command:"export" description:"Exports a set of targets and files from the repo."`
+
 	Help struct {
 		Args struct {
 			Topic string `positional-arg-name:"topic" description:"Topic to display help on"`
@@ -346,6 +354,13 @@ var buildFunctions = map[string]func() bool{
 		}
 		return success
 	},
+	"export": func() bool {
+		success, state := runBuild(opts.Export.Args.Targets, false, false)
+		if success {
+			export.ToDir(state, opts.Export.Output, state.ExpandOriginalTargets())
+		}
+		return success
+	},
 	"help": func() bool {
 		return help.Help(opts.Help.Args.Topic)
 	},