forked from tangled.org/core
this repo has no description
at file-tree 4.0 kB view raw
1package git 2 3import ( 4 "bytes" 5 "fmt" 6 "log" 7 "os" 8 "os/exec" 9 "strings" 10 11 "github.com/bluekeyes/go-gitdiff/gitdiff" 12 "github.com/go-git/go-git/v5/plumbing" 13 "github.com/go-git/go-git/v5/plumbing/object" 14 "tangled.sh/tangled.sh/core/patchutil" 15 "tangled.sh/tangled.sh/core/types" 16) 17 18func (g *GitRepo) Diff() (*types.NiceDiff, error) { 19 c, err := g.r.CommitObject(g.h) 20 if err != nil { 21 return nil, fmt.Errorf("commit object: %w", err) 22 } 23 24 patch := &object.Patch{} 25 commitTree, err := c.Tree() 26 parent := &object.Commit{} 27 if err == nil { 28 parentTree := &object.Tree{} 29 if c.NumParents() != 0 { 30 parent, err = c.Parents().Next() 31 if err == nil { 32 parentTree, err = parent.Tree() 33 if err == nil { 34 patch, err = parentTree.Patch(commitTree) 35 if err != nil { 36 return nil, fmt.Errorf("patch: %w", err) 37 } 38 } 39 } 40 } else { 41 patch, err = parentTree.Patch(commitTree) 42 if err != nil { 43 return nil, fmt.Errorf("patch: %w", err) 44 } 45 } 46 } 47 48 diffs, _, err := gitdiff.Parse(strings.NewReader(patch.String())) 49 if err != nil { 50 log.Println(err) 51 } 52 53 nd := types.NiceDiff{} 54 for _, d := range diffs { 55 ndiff := types.Diff{} 56 ndiff.Name.New = d.NewName 57 ndiff.Name.Old = d.OldName 58 ndiff.IsBinary = d.IsBinary 59 ndiff.IsNew = d.IsNew 60 ndiff.IsDelete = d.IsDelete 61 ndiff.IsCopy = d.IsCopy 62 ndiff.IsRename = d.IsRename 63 64 for _, tf := range d.TextFragments { 65 ndiff.TextFragments = append(ndiff.TextFragments, *tf) 66 for _, l := range tf.Lines { 67 switch l.Op { 68 case gitdiff.OpAdd: 69 nd.Stat.Insertions += 1 70 case gitdiff.OpDelete: 71 nd.Stat.Deletions += 1 72 } 73 } 74 } 75 76 nd.Diff = append(nd.Diff, ndiff) 77 } 78 79 nd.Stat.FilesChanged = len(diffs) 80 nd.Commit.This = c.Hash.String() 81 82 if parent.Hash.IsZero() { 83 nd.Commit.Parent = "" 84 } else { 85 nd.Commit.Parent = parent.Hash.String() 86 } 87 nd.Commit.Author = c.Author 88 nd.Commit.Message = c.Message 89 90 return &nd, nil 91} 92 93func (g *GitRepo) DiffTree(commit1, commit2 *object.Commit) (*types.DiffTree, error) { 94 tree1, err := commit1.Tree() 95 if err != nil { 96 return nil, err 97 } 98 99 tree2, err := commit2.Tree() 100 if err != nil { 101 return nil, err 102 } 103 104 diff, err := object.DiffTree(tree1, tree2) 105 if err != nil { 106 return nil, err 107 } 108 109 patch, err := diff.Patch() 110 if err != nil { 111 return nil, err 112 } 113 114 diffs, _, err := gitdiff.Parse(strings.NewReader(patch.String())) 115 if err != nil { 116 return nil, err 117 } 118 119 return &types.DiffTree{ 120 Rev1: commit1.Hash.String(), 121 Rev2: commit2.Hash.String(), 122 Patch: patch.String(), 123 Diff: diffs, 124 }, nil 125} 126 127// FormatPatch generates a git-format-patch output between two commits, 128// and returns the raw format-patch series, a parsed FormatPatch and an error. 129func (g *GitRepo) FormatPatch(base, commit2 *object.Commit) (string, []patchutil.FormatPatch, error) { 130 var stdout bytes.Buffer 131 cmd := exec.Command( 132 "git", 133 "-C", 134 g.path, 135 "format-patch", 136 fmt.Sprintf("%s..%s", base.Hash.String(), commit2.Hash.String()), 137 "--stdout", 138 ) 139 cmd.Stdout = &stdout 140 cmd.Stderr = os.Stderr 141 err := cmd.Run() 142 if err != nil { 143 return "", nil, err 144 } 145 146 formatPatch, err := patchutil.ExtractPatches(stdout.String()) 147 if err != nil { 148 return "", nil, err 149 } 150 151 return stdout.String(), formatPatch, nil 152} 153 154func (g *GitRepo) MergeBase(commit1, commit2 *object.Commit) (*object.Commit, error) { 155 isAncestor, err := commit1.IsAncestor(commit2) 156 if err != nil { 157 return nil, err 158 } 159 160 if isAncestor { 161 return commit1, nil 162 } 163 164 mergeBase, err := commit1.MergeBase(commit2) 165 if err != nil { 166 return nil, err 167 } 168 169 if len(mergeBase) == 0 { 170 return nil, fmt.Errorf("failed to find a merge-base") 171 } 172 173 return mergeBase[0], nil 174} 175 176func (g *GitRepo) ResolveRevision(revStr string) (*object.Commit, error) { 177 rev, err := g.r.ResolveRevision(plumbing.Revision(revStr)) 178 if err != nil { 179 return nil, fmt.Errorf("resolving revision %s: %w", revStr, err) 180 } 181 182 commit, err := g.r.CommitObject(*rev) 183 if err != nil { 184 185 return nil, fmt.Errorf("getting commit for %s: %w", revStr, err) 186 } 187 188 return commit, nil 189}