git-module

git clone git://git.lin.moe/fork/git-module.git

  1package git
  2
  3import (
  4	"context"
  5	"fmt"
  6	"io"
  7)
  8
  9// DiffOptions contains optional arguments for parsing diff.
 10//
 11// Docs: https://git-scm.com/docs/git-diff#Documentation/git-diff.txt---full-index
 12type DiffOptions struct {
 13	// The commit ID to used for computing diff between a range of commits (base,
 14	// revision]. When not set, only computes diff for a single commit at revision.
 15	Base string
 16	CommandOptions
 17}
 18
 19// Diff returns a parsed diff object between given commits of the repository.
 20func (r *Repository) Diff(ctx context.Context, rev string, maxFiles, maxFileLines, maxLineChars int, opts ...DiffOptions) (*Diff, error) {
 21	var opt DiffOptions
 22	if len(opts) > 0 {
 23		opt = opts[0]
 24	}
 25
 26	commit, err := r.CatFileCommit(ctx, rev)
 27	if err != nil {
 28		return nil, err
 29	}
 30
 31	var args []string
 32	if opt.Base == "" {
 33		// First commit of repository
 34		if commit.ParentsCount() == 0 {
 35			args = []string{"show", "--full-index", "--end-of-options", rev}
 36		} else {
 37			c, err := commit.Parent(ctx, 0)
 38			if err != nil {
 39				return nil, err
 40			}
 41			args = []string{"diff", "--full-index", "-M", c.ID.String(), "--end-of-options", rev}
 42		}
 43	} else {
 44		args = []string{"diff", "--full-index", "-M", opt.Base, "--end-of-options", rev}
 45	}
 46
 47	stdout, w := io.Pipe()
 48	done := make(chan SteamParseDiffResult)
 49	go StreamParseDiff(stdout, done, maxFiles, maxFileLines, maxLineChars)
 50
 51	err = pipe(ctx, r.path, args, opt.Envs, w)
 52	_ = w.Close() // Close writer to exit parsing goroutine
 53	if err != nil {
 54		return nil, err
 55	}
 56
 57	result := <-done
 58	return result.Diff, result.Err
 59}
 60
 61// RawDiffFormat is the format of a raw diff.
 62type RawDiffFormat string
 63
 64const (
 65	RawDiffNormal RawDiffFormat = "diff"
 66	RawDiffPatch  RawDiffFormat = "patch"
 67)
 68
 69// RawDiffOptions contains optional arguments for dumping a raw diff.
 70//
 71// Docs: https://git-scm.com/docs/git-format-patch
 72type RawDiffOptions struct {
 73	CommandOptions
 74}
 75
 76// RawDiff dumps diff of repository in given revision directly to given
 77// io.Writer.
 78func (r *Repository) RawDiff(ctx context.Context, rev string, diffType RawDiffFormat, w io.Writer, opts ...RawDiffOptions) error {
 79	var opt RawDiffOptions
 80	if len(opts) > 0 {
 81		opt = opts[0]
 82	}
 83
 84	commit, err := r.CatFileCommit(ctx, rev) //nolint
 85	if err != nil {
 86		return err
 87	}
 88
 89	var args []string
 90	switch diffType {
 91	case RawDiffNormal:
 92		if commit.ParentsCount() == 0 {
 93			args = []string{"show", "--full-index", "--end-of-options", rev}
 94		} else {
 95			c, err := commit.Parent(ctx, 0)
 96			if err != nil {
 97				return err
 98			}
 99			args = []string{"diff", "--full-index", "-M", c.ID.String(), "--end-of-options", rev}
100		}
101	case RawDiffPatch:
102		if commit.ParentsCount() == 0 {
103			args = []string{"format-patch", "--full-index", "--no-signoff", "--no-signature", "--stdout", "--root", "--end-of-options", rev}
104		} else {
105			c, err := commit.Parent(ctx, 0)
106			if err != nil {
107				return err
108			}
109			args = []string{"format-patch", "--full-index", "--no-signoff", "--no-signature", "--stdout", "--end-of-options", rev + "..." + c.ID.String()}
110		}
111	default:
112		return fmt.Errorf("invalid diffType: %s", diffType)
113	}
114
115	if err = pipe(ctx, r.path, args, opt.Envs, w); err != nil {
116		return err
117	}
118	return nil
119}
120
121// DiffBinaryOptions contains optional arguments for producing binary patch.
122type DiffBinaryOptions struct {
123	CommandOptions
124}
125
126// DiffBinary returns binary patch between base and head revisions that could be
127// used for git-apply.
128func (r *Repository) DiffBinary(ctx context.Context, base, head string, opts ...DiffBinaryOptions) ([]byte, error) {
129	var opt DiffBinaryOptions
130	if len(opts) > 0 {
131		opt = opts[0]
132	}
133
134	args := []string{"diff", "--full-index", "--binary", "--end-of-options", base, head}
135	return exec(ctx, r.path, args, opt.Envs)
136}