TBR: Revert "jiri: Be consistent about absolute/relative paths."

This reverts commit 64d01b6d0cad826ed8b22b706416f3d3215879d7.

It's breaking postsubmit-poll.  Reverting...

Change-Id: Ied5502665d34b888d241c51206af2afc581dad9b
diff --git a/project/project.go b/project/project.go
index dc972c7..41f5ccb 100644
--- a/project/project.go
+++ b/project/project.go
@@ -66,12 +66,6 @@
 
 // ManifestFromFile returns a manifest parsed from the contents of filename,
 // with defaults filled in.
-//
-// Note that unlike ProjectFromFile, ManifestFromFile does not convert project
-// paths to absolute paths because it's possible to load a manifest with a
-// specific root directory different from jirix.Root.  The usual way to load a
-// manifest is through LoadManifest, which does absolutize the paths, and uses
-// the correct root directory.
 func ManifestFromFile(jirix *jiri.X, filename string) (*Manifest, error) {
 	data, err := jirix.NewSeq().ReadFile(filename)
 	if err != nil {
@@ -146,16 +140,9 @@
 		Done()
 }
 
-// ToFile writes the manifest m to a file with the given filename, with
-// defaults unfilled and all project paths relative to the jiri root.
+// ToFile writes the manifest m to a file with the given filename, with defaults
+// unfilled.
 func (m *Manifest) ToFile(jirix *jiri.X, filename string) error {
-	// Replace absolute paths with relative paths to make it possible to move
-	// the $JIRI_ROOT directory locally.
-	for _, project := range m.Projects {
-		if err := project.relativizePaths(jirix.Root); err != nil {
-			return err
-		}
-	}
 	data, err := m.ToBytes()
 	if err != nil {
 		return err
@@ -401,7 +388,7 @@
 )
 
 // ProjectFromFile returns a project parsed from the contents of filename,
-// with defaults filled in and all paths absolute.
+// with defaults filled in.
 func ProjectFromFile(jirix *jiri.X, filename string) (*Project, error) {
 	data, err := jirix.NewSeq().ReadFile(filename)
 	if err != nil {
@@ -425,21 +412,15 @@
 	if err := p.fillDefaults(); err != nil {
 		return nil, err
 	}
-	p.absolutizePaths(jirix.Root)
 	return p, nil
 }
 
 // ToFile writes the project p to a file with the given filename, with defaults
-// unfilled and all paths relative to the jiri root.
+// unfilled.
 func (p Project) ToFile(jirix *jiri.X, filename string) error {
 	if err := p.unfillDefaults(); err != nil {
 		return err
 	}
-	// Replace absolute paths with relative paths to make it possible to move
-	// the $JIRI_ROOT directory locally.
-	if err := p.relativizePaths(jirix.Root); err != nil {
-		return err
-	}
 	data, err := xml.Marshal(p)
 	if err != nil {
 		return fmt.Errorf("project xml.Marshal failed: %v", err)
@@ -452,45 +433,6 @@
 	return safeWriteFile(jirix, filename, data)
 }
 
-// absolutizePaths makes all relative paths absolute by prepending basepath.
-func (p *Project) absolutizePaths(basepath string) {
-	if p.Path != "" && !filepath.IsAbs(p.Path) {
-		p.Path = filepath.Join(basepath, p.Path)
-	}
-	if p.GitHooks != "" && !filepath.IsAbs(p.GitHooks) {
-		p.GitHooks = filepath.Join(basepath, p.GitHooks)
-	}
-	if p.RunHook != "" && !filepath.IsAbs(p.RunHook) {
-		p.RunHook = filepath.Join(basepath, p.RunHook)
-	}
-}
-
-// relativizePaths makes all absolute paths relative to basepath.
-func (p *Project) relativizePaths(basepath string) error {
-	if filepath.IsAbs(p.Path) {
-		relPath, err := filepath.Rel(basepath, p.Path)
-		if err != nil {
-			return err
-		}
-		p.Path = relPath
-	}
-	if filepath.IsAbs(p.GitHooks) {
-		relGitHooks, err := filepath.Rel(basepath, p.GitHooks)
-		if err != nil {
-			return err
-		}
-		p.GitHooks = relGitHooks
-	}
-	if filepath.IsAbs(p.RunHook) {
-		relRunHook, err := filepath.Rel(basepath, p.RunHook)
-		if err != nil {
-			return err
-		}
-		p.RunHook = relRunHook
-	}
-	return nil
-}
-
 // Key returns the unique ProjectKey for the project.
 func (p Project) Key() ProjectKey {
 	return MakeProjectKey(p.Name, p.Remote)
@@ -677,6 +619,11 @@
 		return err
 	}
 	for _, project := range localProjects {
+		relPath, err := filepath.Rel(jirix.Root, project.Path)
+		if err != nil {
+			return err
+		}
+		project.Path = relPath
 		manifest.Projects = append(manifest.Projects, project)
 	}
 
@@ -854,7 +801,7 @@
 
 	// Compute difference between local and remote.
 	update := Update{}
-	ops := computeOperations(localProjects, remoteProjects, false)
+	ops := computeOperations(localProjects, remoteProjects, false, "")
 	s := jirix.NewSeq()
 	for _, op := range ops {
 		name := op.Project().Name
@@ -1336,6 +1283,7 @@
 	if err != nil {
 		return Project{}, err
 	}
+	project.Path = filepath.Join(jirix.Root, project.Path)
 	return *project, nil
 }
 
@@ -1698,8 +1646,7 @@
 	}
 	// Collect projects.
 	for _, project := range m.Projects {
-		// Make paths absolute by prepending JIRI_ROOT/<root>.
-		project.absolutizePaths(filepath.Join(jirix.Root, root))
+		project.Path = filepath.Join(jirix.Root, root, project.Path)
 		key := project.Key()
 		if dup, ok := ld.Projects[key]; ok && dup != project {
 			// TODO(toddw): Tell the user the other conflicting file.
@@ -1835,7 +1782,7 @@
 	defer jirix.TimerPop()
 
 	getRemoteHeadRevisions(jirix, remoteProjects)
-	ops := computeOperations(localProjects, remoteProjects, gc)
+	ops := computeOperations(localProjects, remoteProjects, gc, "")
 	updates := newFsUpdates()
 	for _, op := range ops {
 		if err := op.Test(jirix, updates); err != nil {
@@ -1870,7 +1817,9 @@
 		}
 		s := jirix.NewSeq()
 		s.Verbose(true).Output([]string{fmt.Sprintf("running hook for project %q", op.Project().Name)})
-		if err := s.Dir(op.Project().Path).Capture(os.Stdout, os.Stderr).Last(op.Project().RunHook, op.Kind()); err != nil {
+		rootDir := filepath.Join(jirix.Root, op.Root())
+		hook := filepath.Join(rootDir, op.Project().RunHook)
+		if err := s.Dir(op.Project().Path).Capture(os.Stdout, os.Stderr).Last(hook, op.Kind()); err != nil {
 			// TODO(nlacasse): Should we delete projectDir or perform some
 			// other cleanup in the event of a hook failure?
 			return fmt.Errorf("error running hook for project %q: %v", op.Project().Name, err)
@@ -1906,6 +1855,7 @@
 		// Apply git hooks, overwriting any existing hooks.  Jiri is in control of
 		// writing all hooks.
 		gitHooksDstDir := filepath.Join(op.Project().Path, ".git", "hooks")
+		gitHooksSrcDir := filepath.Join(jirix.Root, op.Root(), op.Project().GitHooks)
 		// Copy the specified GitHooks directory into the project's git
 		// hook directory.  We walk the file system, creating directories
 		// and copying files as we encounter them.
@@ -1913,7 +1863,7 @@
 			if err != nil {
 				return err
 			}
-			relPath, err := filepath.Rel(op.Project().GitHooks, path)
+			relPath, err := filepath.Rel(gitHooksSrcDir, path)
 			if err != nil {
 				return err
 			}
@@ -1928,7 +1878,7 @@
 			// The file *must* be executable to be picked up by git.
 			return s.WriteFile(dst, src, 0755).Done()
 		}
-		if err := filepath.Walk(op.Project().GitHooks, copyFn); err != nil {
+		if err := filepath.Walk(gitHooksSrcDir, copyFn); err != nil {
 			return err
 		}
 	}
@@ -1950,6 +1900,14 @@
 		Chdir(metadataDir).Done(); err != nil {
 		return err
 	}
+
+	// Replace absolute project paths with relative paths to make it
+	// possible to move the $JIRI_ROOT directory locally.
+	relPath, err := filepath.Rel(jirix.Root, project.Path)
+	if err != nil {
+		return err
+	}
+	project.Path = relPath
 	metadataFile := filepath.Join(metadataDir, jiri.ProjectMetaFile)
 	return project.ToFile(jirix, metadataFile)
 }
@@ -1984,6 +1942,8 @@
 	Project() Project
 	// Kind returns the kind of operation.
 	Kind() string
+	// Root returns the operation's root directory, relative to JIRI_ROOT.
+	Root() string
 	// Run executes the operation.
 	Run(jirix *jiri.X) error
 	// String returns a string representation of the operation.
@@ -2002,12 +1962,18 @@
 	destination string
 	// source is the current project path.
 	source string
+	// root is the directory inside JIRI_ROOT where this operation will run.
+	root string
 }
 
 func (op commonOperation) Project() Project {
 	return op.project
 }
 
+func (op commonOperation) Root() string {
+	return op.root
+}
+
 // createOperation represents the creation of a project.
 type createOperation struct {
 	commonOperation
@@ -2281,7 +2247,7 @@
 // system and manifest file respectively) and outputs a collection of
 // operations that describe the actions needed to update the target
 // projects.
-func computeOperations(localProjects, remoteProjects Projects, gc bool) operations {
+func computeOperations(localProjects, remoteProjects Projects, gc bool, root string) operations {
 	result := operations{}
 	allProjects := map[ProjectKey]bool{}
 	for _, p := range localProjects {
@@ -2298,25 +2264,27 @@
 		if project, ok := remoteProjects[key]; ok {
 			remote = &project
 		}
-		result = append(result, computeOp(local, remote, gc))
+		result = append(result, computeOp(local, remote, gc, root))
 	}
 	sort.Sort(result)
 	return result
 }
 
-func computeOp(local, remote *Project, gc bool) operation {
+func computeOp(local, remote *Project, gc bool, root string) operation {
 	switch {
 	case local == nil && remote != nil:
 		return createOperation{commonOperation{
 			destination: remote.Path,
 			project:     *remote,
 			source:      "",
+			root:        root,
 		}}
 	case local != nil && remote == nil:
 		return deleteOperation{commonOperation{
 			destination: "",
 			project:     *local,
 			source:      local.Path,
+			root:        root,
 		}, gc}
 	case local != nil && remote != nil:
 		switch {
@@ -2327,18 +2295,21 @@
 				destination: remote.Path,
 				project:     *remote,
 				source:      local.Path,
+				root:        root,
 			}}
 		case local.Revision != remote.Revision:
 			return updateOperation{commonOperation{
 				destination: remote.Path,
 				project:     *remote,
 				source:      local.Path,
+				root:        root,
 			}}
 		default:
 			return nullOperation{commonOperation{
 				destination: remote.Path,
 				project:     *remote,
 				source:      local.Path,
+				root:        root,
 			}}
 		}
 	default:
diff --git a/project/project_test.go b/project/project_test.go
index fb12f3e..c61710e 100644
--- a/project/project_test.go
+++ b/project/project_test.go
@@ -1012,7 +1012,7 @@
 			// Default fields are dropped when marshaled, and added when unmarshaled.
 			project.Project{
 				Name:         "project1",
-				Path:         filepath.Join(jirix.Root, "path1"),
+				Path:         "path1",
 				Protocol:     "git",
 				Remote:       "remote1",
 				RemoteBranch: "master",
@@ -1024,15 +1024,13 @@
 		{
 			project.Project{
 				Name:         "project2",
-				Path:         filepath.Join(jirix.Root, "path2"),
-				GitHooks:     filepath.Join(jirix.Root, "git-hooks"),
-				RunHook:      filepath.Join(jirix.Root, "run-hook"),
+				Path:         "path2",
 				Protocol:     "git",
 				Remote:       "remote2",
 				RemoteBranch: "branch2",
 				Revision:     "rev2",
 			},
-			`<project name="project2" path="path2" remote="remote2" remotebranch="branch2" revision="rev2" githooks="git-hooks" runhook="run-hook"/>
+			`<project name="project2" path="path2" remote="remote2" remotebranch="branch2" revision="rev2"/>
 `,
 		},
 	}
@@ -1071,7 +1069,7 @@
 			`<Project name="project" path="path" remote="remote"/>`,
 			project.Project{
 				Name:         "project",
-				Path:         filepath.Join(jirix.Root, "path"),
+				Path:         "path",
 				Protocol:     "git",
 				Remote:       "remote",
 				RemoteBranch: "master",
@@ -1083,7 +1081,7 @@
 			`<Project name="project" path="path" remote="remote"></Project>`,
 			project.Project{
 				Name:         "project",
-				Path:         filepath.Join(jirix.Root, "path"),
+				Path:         "path",
 				Protocol:     "git",
 				Remote:       "remote",
 				RemoteBranch: "master",
@@ -1095,7 +1093,7 @@
 			`<Project this_attribute_should_be_ignored="junk" name="project" path="path" remote="remote" remotebranch="branch" revision="rev"></Project>`,
 			project.Project{
 				Name:         "project",
-				Path:         filepath.Join(jirix.Root, "path"),
+				Path:         "path",
 				Protocol:     "git",
 				Remote:       "remote",
 				RemoteBranch: "branch",