diff options
Diffstat (limited to 'src/cmd/hgpatch/main.go')
-rw-r--r-- | src/cmd/hgpatch/main.go | 361 |
1 files changed, 0 insertions, 361 deletions
diff --git a/src/cmd/hgpatch/main.go b/src/cmd/hgpatch/main.go deleted file mode 100644 index 4f7aec22b..000000000 --- a/src/cmd/hgpatch/main.go +++ /dev/null @@ -1,361 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "bytes" - "container/vector" - "exec" - "flag" - "fmt" - "io/ioutil" - "os" - "patch" - "path/filepath" - "sort" - "strings" -) - -var checkSync = flag.Bool("checksync", true, "check whether repository is out of sync") - -func usage() { - fmt.Fprintf(os.Stderr, "usage: hgpatch [options] [patchfile]\n") - flag.PrintDefaults() - os.Exit(2) -} - -func main() { - flag.Usage = usage - flag.Parse() - - args := flag.Args() - var data []byte - var err os.Error - switch len(args) { - case 0: - data, err = ioutil.ReadAll(os.Stdin) - case 1: - data, err = ioutil.ReadFile(args[0]) - default: - usage() - } - chk(err) - - pset, err := patch.Parse(data) - chk(err) - - // Change to hg root directory, because - // patch paths are relative to root. - root, err := hgRoot() - chk(err) - chk(os.Chdir(root)) - - // Make sure there are no pending changes on the server. - if *checkSync && hgIncoming() { - fmt.Fprintf(os.Stderr, "incoming changes waiting; run hg sync first\n") - os.Exit(2) - } - - // Make sure we won't be editing files with local pending changes. - dirtylist, err := hgModified() - chk(err) - dirty := make(map[string]bool) - for _, f := range dirtylist { - dirty[f] = true - } - conflict := make(map[string]bool) - for _, f := range pset.File { - if f.Verb == patch.Delete || f.Verb == patch.Rename { - if dirty[f.Src] { - conflict[f.Src] = true - } - } - if f.Verb != patch.Delete { - if dirty[f.Dst] { - conflict[f.Dst] = true - } - } - } - if len(conflict) > 0 { - fmt.Fprintf(os.Stderr, "cannot apply patch to locally modified files:\n") - for name := range conflict { - fmt.Fprintf(os.Stderr, "\t%s\n", name) - } - os.Exit(2) - } - - // Apply changes in memory. - op, err := pset.Apply(ioutil.ReadFile) - chk(err) - - // Write changes to disk copy: order of commands matters. - // Accumulate undo log as we go, in case there is an error. - // Also accumulate list of modified files to print at end. - changed := make(map[string]int) - - // Copy, Rename create the destination file, so they - // must happen before we write the data out. - // A single patch may have a Copy and a Rename - // with the same source, so we have to run all the - // Copy in one pass, then all the Rename. - for i := range op { - o := &op[i] - if o.Verb == patch.Copy { - makeParent(o.Dst) - chk(hgCopy(o.Dst, o.Src)) - undoRevert(o.Dst) - changed[o.Dst] = 1 - } - } - for i := range op { - o := &op[i] - if o.Verb == patch.Rename { - makeParent(o.Dst) - chk(hgRename(o.Dst, o.Src)) - undoRevert(o.Dst) - undoRevert(o.Src) - changed[o.Src] = 1 - changed[o.Dst] = 1 - } - } - - // Run Delete before writing to files in case one of the - // deleted paths is becoming a directory. - for i := range op { - o := &op[i] - if o.Verb == patch.Delete { - chk(hgRemove(o.Src)) - undoRevert(o.Src) - changed[o.Src] = 1 - } - } - - // Write files. - for i := range op { - o := &op[i] - if o.Verb == patch.Delete { - continue - } - if o.Verb == patch.Add { - makeParent(o.Dst) - changed[o.Dst] = 1 - } - if o.Data != nil { - chk(ioutil.WriteFile(o.Dst, o.Data, 0644)) - if o.Verb == patch.Add { - undoRm(o.Dst) - } else { - undoRevert(o.Dst) - } - changed[o.Dst] = 1 - } - if o.Mode != 0 { - chk(os.Chmod(o.Dst, uint32(o.Mode&0755))) - undoRevert(o.Dst) - changed[o.Dst] = 1 - } - } - - // hg add looks at the destination file, so it must happen - // after we write the data out. - for i := range op { - o := &op[i] - if o.Verb == patch.Add { - chk(hgAdd(o.Dst)) - undoRevert(o.Dst) - changed[o.Dst] = 1 - } - } - - // Finished editing files. Write the list of changed files to stdout. - list := make([]string, len(changed)) - i := 0 - for f := range changed { - list[i] = f - i++ - } - sort.Strings(list) - for _, f := range list { - fmt.Printf("%s\n", f) - } -} - - -// make parent directory for name, if necessary -func makeParent(name string) { - parent, _ := filepath.Split(name) - chk(mkdirAll(parent, 0755)) -} - -// Copy of os.MkdirAll but adds to undo log after -// creating a directory. -func mkdirAll(path string, perm uint32) os.Error { - dir, err := os.Lstat(path) - if err == nil { - if dir.IsDirectory() { - return nil - } - return &os.PathError{"mkdir", path, os.ENOTDIR} - } - - i := len(path) - for i > 0 && path[i-1] == '/' { // Skip trailing slashes. - i-- - } - - j := i - for j > 0 && path[j-1] != '/' { // Scan backward over element. - j-- - } - - if j > 0 { - err = mkdirAll(path[0:j-1], perm) - if err != nil { - return err - } - } - - err = os.Mkdir(path, perm) - if err != nil { - // Handle arguments like "foo/." by - // double-checking that directory doesn't exist. - dir, err1 := os.Lstat(path) - if err1 == nil && dir.IsDirectory() { - return nil - } - return err - } - undoRm(path) - return nil -} - -// If err != nil, process the undo log and exit. -func chk(err os.Error) { - if err != nil { - fmt.Fprintf(os.Stderr, "%s\n", err) - runUndo() - os.Exit(2) - } -} - - -// Undo log -type undo func() os.Error - -var undoLog vector.Vector // vector of undo - -func undoRevert(name string) { undoLog.Push(undo(func() os.Error { return hgRevert(name) })) } - -func undoRm(name string) { undoLog.Push(undo(func() os.Error { return os.Remove(name) })) } - -func runUndo() { - for i := undoLog.Len() - 1; i >= 0; i-- { - if err := undoLog.At(i).(undo)(); err != nil { - fmt.Fprintf(os.Stderr, "%s\n", err) - } - } -} - - -// hgRoot returns the root directory of the repository. -func hgRoot() (string, os.Error) { - out, err := run([]string{"hg", "root"}, nil) - if err != nil { - return "", err - } - return strings.TrimSpace(out), nil -} - -// hgIncoming returns true if hg sync will pull in changes. -func hgIncoming() bool { - // hg -q incoming exits 0 when there is nothing incoming, 1 otherwise. - _, err := run([]string{"hg", "-q", "incoming"}, nil) - return err == nil -} - -// hgModified returns a list of the modified files in the -// repository. -func hgModified() ([]string, os.Error) { - out, err := run([]string{"hg", "status", "-n"}, nil) - if err != nil { - return nil, err - } - return strings.Split(strings.TrimSpace(out), "\n"), nil -} - -// hgAdd adds name to the repository. -func hgAdd(name string) os.Error { - _, err := run([]string{"hg", "add", name}, nil) - return err -} - -// hgRemove removes name from the repository. -func hgRemove(name string) os.Error { - _, err := run([]string{"hg", "rm", name}, nil) - return err -} - -// hgRevert reverts name. -func hgRevert(name string) os.Error { - _, err := run([]string{"hg", "revert", name}, nil) - return err -} - -// hgCopy copies src to dst in the repository. -// Note that the argument order matches io.Copy, not "hg cp". -func hgCopy(dst, src string) os.Error { - _, err := run([]string{"hg", "cp", src, dst}, nil) - return err -} - -// hgRename renames src to dst in the repository. -// Note that the argument order matches io.Copy, not "hg mv". -func hgRename(dst, src string) os.Error { - _, err := run([]string{"hg", "mv", src, dst}, nil) - return err -} - -func dup(a []string) []string { - b := make([]string, len(a)) - copy(b, a) - return b -} - -var lookPathCache = make(map[string]string) - -// run runs the command argv, resolving argv[0] if necessary by searching $PATH. -// It provides input on standard input to the command. -func run(argv []string, input []byte) (out string, err os.Error) { - if len(argv) < 1 { - return "", &runError{dup(argv), os.EINVAL} - } - - prog, ok := lookPathCache[argv[0]] - if !ok { - prog, err = exec.LookPath(argv[0]) - if err != nil { - return "", &runError{dup(argv), err} - } - lookPathCache[argv[0]] = prog - } - - cmd := exec.Command(prog, argv[1:]...) - if len(input) > 0 { - cmd.Stdin = bytes.NewBuffer(input) - } - bs, err := cmd.CombinedOutput() - if err != nil { - return "", &runError{dup(argv), err} - } - return string(bs), nil -} - -// A runError represents an error that occurred while running a command. -type runError struct { - cmd []string - err os.Error -} - -func (e *runError) String() string { return strings.Join(e.cmd, " ") + ": " + e.err.String() } |