Chromium Code Reviews| Index: go/src/infra/libs/git/diff_tree.go |
| diff --git a/go/src/infra/libs/git/diff_tree.go b/go/src/infra/libs/git/diff_tree.go |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..8d39194447573c28519e1b3343a54c84912a6f8e |
| --- /dev/null |
| +++ b/go/src/infra/libs/git/diff_tree.go |
| @@ -0,0 +1,97 @@ |
| +package git |
| + |
| +import "fmt" |
| +import "strconv" |
| +import "strings" |
| + |
| +// TreeDiff represents the difference between two Treeish objects |
| +type TreeDiff []TreeDiffEntry |
| + |
| +// TreeDiffEntry represents the before and after of one path in the repo. |
| +// Note that the Old.Name and New.Name may be different if this item was |
| +// Moved or Copied. |
| +type TreeDiffEntry struct { |
| + // Actino is one of "ACDMRTUX" |
| + // U is for unmerged... if you're just comparing trees you should never see this |
| + // X is probably a bug in git... you should also never see this. |
| + // T is a type change, so if a tree turned into a blob, for example |
| + Action string |
| + |
| + // For Action types 'R' or 'C', what percentage (from 0-100) are the old and |
| + // new blobs similar. |
| + Similarity int |
| + |
| + Old TreeDiffEntryHalf |
| + New TreeDiffEntryHalf |
| +} |
| + |
| +// TreeDiffEntryHalf is one entry in a TreeDiffEntry, either the Old or New half. |
| +type TreeDiffEntryHalf struct { |
| + Child |
| + Name string |
| +} |
| + |
| +func (t TreeDiffEntryHalf) String() string { |
|
M-A Ruel
2014/10/18 00:47:05
Use a pointer, otherwise is copy the struct
iannucci
2014/10/20 21:11:57
Done.
|
| + return fmt.Sprintf("%s: %s", t.Name, t.Child) |
| +} |
| + |
| +// DiffTree computes the 2-tree diff (with copy/rename detection) and returns |
| +// a parsed TreeDiff of what it found. |
| +// |
| +// This diff-tree invocation is done with -t, which implies that it is recursive, |
| +// and that the actual intermediate tree objects will also be contianed in the |
| +// return value. |
| +func (r *Repo) DiffTree(left, right string) (ret TreeDiff) { |
| + atoi := func(s string, base int) int { |
| + ret, err := strconv.ParseInt(s, base, 0) |
| + if err != nil { |
| + panic(err) |
| + } |
| + return int(ret) |
| + } |
| + |
| + lines := strings.Split(strings.TrimRight( |
| + r.RunOutput("diff-tree", "-t", "-z", "-M", "-M", "-C", left, right), "\000"), |
| + "\000") |
| + |
| + infoStream := make(chan string, len(lines)) |
| + for _, line := range lines { |
| + infoStream <- line |
| + } |
| + close(infoStream) |
| + for header := range infoStream { |
| + if len(header) == 0 { |
| + break |
| + } |
| + if header[0] != ':' { |
| + panic("git.DiffTree: desynchronized parsing error") |
|
M-A Ruel
2014/10/18 00:47:05
[BTW I know it's usual to use panic() all around w
|
| + } |
| + info := strings.Fields(strings.TrimLeft(header, ":")) |
| + // old_mode new_mode old_id new_id action |
| + // oldPath (if action[0] in "RC") |
| + // newPath |
| + action := info[4] |
| + similarity := 0 |
| + oldPath := <-infoStream |
| + newPath := oldPath |
| + if action[0] == 'R' || action[0] == 'C' { |
| + newPath = <-infoStream |
| + similarity = atoi(action[1:], 10) |
| + } |
| + |
| + ret = append(ret, (TreeDiffEntry{ |
| + Action: action, |
| + Similarity: similarity, |
| + Old: TreeDiffEntryHalf{ |
| + *NewEmptyChild(Mode(atoi(info[0], 8)), MakeObjectID(info[2])), |
| + oldPath, |
| + }, |
| + New: TreeDiffEntryHalf{ |
| + *NewEmptyChild(Mode(atoi(info[1], 8)), MakeObjectID(info[3])), |
| + newPath, |
| + }, |
| + })) |
| + } |
| + |
| + return |
| +} |