MergeInto also names programs a commit of the branch added and a later one removed: the merge commit would keep the blob in history (issue 1577)
This commit is contained in:
@@ -13,6 +13,9 @@ import (
|
||||
type Program struct {
|
||||
Path string `json:"path"`
|
||||
Kind string `json:"kind"` // ELF, Mach-O or PE
|
||||
// Commit is set for a program a commit of the branch added and a later one removed (or replaced): the merge's tree is clean,
|
||||
// but the merge commit carries the blob into the trunk's history for good. Empty for a program the merge's tree holds.
|
||||
Commit string `json:"commit,omitempty"`
|
||||
}
|
||||
|
||||
// ExecutableKind says what executable format the first bytes of a file are, or "" when they are none. A PE needs its header
|
||||
@@ -40,6 +43,46 @@ func ExecutableKind(head []byte) string {
|
||||
// programHeadSize is how many bytes of a file are read to tell its format: enough for a PE header at its usual offsets.
|
||||
const programHeadSize = 0x100
|
||||
|
||||
// historyPrograms names the compiled programs that a commit of base..head added or modified and the merge's tree no longer
|
||||
// holds at that path (those it holds are in final already): each with the commit that added it. git is the merge's git runner.
|
||||
func historyPrograms(git func(args ...string) (string, error), base, head string, final []Program) ([]Program, error) {
|
||||
revs, err := git("rev-list", "--no-merges", "--reverse", base+".."+head)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
held := map[string]bool{}
|
||||
for _, p := range final {
|
||||
held[p.Path] = true
|
||||
}
|
||||
out, seen := []Program{}, map[string]bool{}
|
||||
for _, c := range strings.Fields(revs) {
|
||||
raw, err := git("diff-tree", "-r", "-z", "--no-commit-id", "--no-renames", "--root", "--diff-filter=AM", c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f := strings.Split(raw, "\x00")
|
||||
for i := 0; i+1 < len(f); i += 2 {
|
||||
meta := strings.Fields(strings.TrimPrefix(f[i], ":"))
|
||||
if len(meta) < 4 || (meta[1] != "100644" && meta[1] != "100755") || held[f[i+1]] || seen[meta[3]] {
|
||||
continue
|
||||
}
|
||||
seen[meta[3]] = true
|
||||
blob, err := git("cat-file", "blob", meta[3])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
head := []byte(blob)
|
||||
if len(head) > programHeadSize {
|
||||
head = head[:programHeadSize]
|
||||
}
|
||||
if k := ExecutableKind(head); k != "" {
|
||||
out = append(out, Program{Path: f[i+1], Kind: k, Commit: c})
|
||||
}
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// programsIn reads the output of `git diff --raw -z --no-renames --diff-filter=AM` (":<old mode> <new mode> <old sha> <new
|
||||
// sha> <status>\0<path>\0" per file) and names each added or modified regular file of the tree dir that is a compiled
|
||||
// program, from its first bytes read locally. A symlink (mode 120000) and a submodule (160000) are not read. It never
|
||||
|
||||
Reference in New Issue
Block a user