diff --git a/.gitignore b/.gitignore index 529f7a6..da16254 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ bin/* local/* .DS_Store *.gop1 +*.tomlp1 *.rej /out/ /.vscode/ diff --git a/cmd/zeta/main.go b/cmd/zeta/main.go index a698aa5..3b74974 100644 --- a/cmd/zeta/main.go +++ b/cmd/zeta/main.go @@ -52,6 +52,7 @@ type App struct { MergeBase command.MergeBase `cmd:"merge-base" help:"Find optimal common ancestors for merge"` LsFiles command.LsFiles `cmd:"ls-files" help:"Show information about files in the index and the working tree"` HashObject command.HashObject `cmd:"hash-object" help:"Compute hash or create object"` + MergeFile command.MergeFile `cmd:"merge-file" help:"Run a three-way file merge"` Version command.Version `cmd:"version" help:"Display version information"` Debug bool `name:"debug" help:"Enable debug mode; analyze timing"` } diff --git a/modules/diferenco/diferenco.go b/modules/diferenco/diferenco.go index 5f133df..36babad 100644 --- a/modules/diferenco/diferenco.go +++ b/modules/diferenco/diferenco.go @@ -38,40 +38,38 @@ const ( Patience ) -func (a Algorithm) String() string { - switch a { - case Unspecified: - return "Unspecified" - case Histogram: - return "Histogram" - case Myers: - return "Myers" - case Minimal: - return "Minimal" - case ONP: - return "O(NP)" - case Patience: - return "Patience" - } - return "Unknown" -} - var ( ErrUnsupportedAlgorithm = errors.New("unsupport algorithm") ) var ( - diffAlgorithms = map[string]Algorithm{ + algorithmValueMap = map[string]Algorithm{ "histogram": Histogram, "onp": ONP, "myers": Myers, "patience": Patience, "minimal": Minimal, } + algorithmNameMap = map[Algorithm]string{ + Unspecified: "unspecified", + Histogram: "histogram", + ONP: "onp", + Myers: "myers", + Minimal: "minimal", + Patience: "patience", + } ) -func ParseAlgorithm(s string) (Algorithm, error) { - if a, ok := diffAlgorithms[strings.ToLower(s)]; ok { +func (a Algorithm) String() string { + n, ok := algorithmNameMap[a] + if ok { + return n + } + return "unspecified" +} + +func AlgorithmFromName(s string) (Algorithm, error) { + if a, ok := algorithmValueMap[strings.ToLower(s)]; ok { return a, nil } return Unspecified, fmt.Errorf("unsupport algoritm '%s' %w", s, ErrUnsupportedAlgorithm) diff --git a/modules/diferenco/merge.go b/modules/diferenco/merge.go index b1b8175..f5b932a 100644 --- a/modules/diferenco/merge.go +++ b/modules/diferenco/merge.go @@ -328,6 +328,7 @@ func ParseConflictStyle(s string) int { type MergeOptions struct { TextO, TextA, TextB string + RO, R1, R2 io.Reader // when if set LabelO, LabelA, LabelB string A Algorithm Style int // Conflict Style @@ -399,9 +400,18 @@ func Merge(ctx context.Context, opts *MergeOptions) (string, bool, error) { default: } s := NewSink(NEWLINE_RAW) - slicesO := s.SplitLines(opts.TextO) - slicesA := s.SplitLines(opts.TextA) - slicesB := s.SplitLines(opts.TextB) + slicesO, err := s.parseLines(opts.RO, opts.TextO) + if err != nil { + return "", false, err + } + slicesA, err := s.parseLines(opts.R1, opts.TextA) + if err != nil { + return "", false, err + } + slicesB, err := s.parseLines(opts.R2, opts.TextB) + if err != nil { + return "", false, err + } regions, err := Diff3Merge(ctx, slicesO, slicesA, slicesB, opts.A, true) if err != nil { return "", false, err diff --git a/modules/zeta/object/text.go b/modules/diferenco/text.go similarity index 63% rename from modules/zeta/object/text.go rename to modules/diferenco/text.go index 1fdba8e..4a804a9 100644 --- a/modules/zeta/object/text.go +++ b/modules/diferenco/text.go @@ -1,7 +1,4 @@ -// Copyright ©️ Ant Group. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package object +package diferenco import ( "bytes" @@ -23,43 +20,41 @@ import ( // #define MAX_XDIFF_SIZE (1024UL * 1024 * 1023) const ( - // MAX_DIFF_SIZE 100MiB - MAX_DIFF_SIZE = 100 * 1024 * 1024 + MAX_DIFF_SIZE = 100 << 20 // MAX_DIFF_SIZE 100MiB BINARY = "binary" - sniffLen = 8000 UTF8 = "UTF-8" + sniffLen = 8000 ) var ( - ErrNotTextContent = errors.New("not a text content") + ErrNonTextContent = errors.New("non-text content") ) -func textCharset(s string) string { +func checkCharset(s string) string { if _, charset, ok := strings.Cut(s, ";"); ok { return strings.TrimPrefix(strings.TrimSpace(charset), "charset=") } - return "UTF-8" + return UTF8 } -func resolveCharset(payload []byte) string { +func detectCharset(payload []byte) string { result := mime.DetectAny(payload) for p := result; p != nil; p = p.Parent() { if p.Is("text/plain") { - return textCharset(p.String()) + return checkCharset(p.String()) } } return BINARY } -// readText: Read all text content: automatically detect text encoding and convert to UTF-8, binary will return ErrNotTextContent -func readText(r io.Reader) (string, string, error) { +func readUnifiedText(r io.Reader) (string, string, error) { sniffBytes, err := streamio.ReadMax(r, sniffLen) if err != nil { return "", "", err } - charset := resolveCharset(sniffBytes) + charset := detectCharset(sniffBytes) if charset == BINARY { - return "", "", ErrNotTextContent + return "", "", ErrNonTextContent } reader := io.MultiReader(bytes.NewReader(sniffBytes), r) if strings.EqualFold(charset, UTF8) { @@ -75,7 +70,7 @@ func readText(r io.Reader) (string, string, error) { } buf, err := chardet.DecodeFromCharset(b.Bytes(), charset) if err != nil { - return "", "", ErrNotTextContent + return "", "", ErrNonTextContent } if len(buf) == 0 { return "", "", nil @@ -83,30 +78,31 @@ func readText(r io.Reader) (string, string, error) { return unsafe.String(unsafe.SliceData(buf), len(buf)), charset, nil } -func readTextUTF8(r io.Reader) (string, error) { +func readRawText(r io.Reader, size int) (string, error) { var b bytes.Buffer if _, err := b.ReadFrom(io.LimitReader(r, sniffLen)); err != nil { return "", err } if bytes.IndexByte(b.Bytes(), 0) != -1 { - return "", ErrNotTextContent + return "", ErrNonTextContent } + b.Grow(size) if _, err := b.ReadFrom(r); err != nil { return "", err } - return b.String(), nil + content := b.Bytes() + return unsafe.String(unsafe.SliceData(content), len(content)), nil } -// GetUnifiedText: Read all text content. -func GetUnifiedText(r io.Reader, size int64, codecvt bool) (string, string, error) { +func ReadUnifiedText(r io.Reader, size int64, textConv bool) (content string, charset string, err error) { if size > MAX_DIFF_SIZE { - return "", "", ErrNotTextContent + return "", "", ErrNonTextContent } - if codecvt { - return readText(r) + if textConv { + return readUnifiedText(r) } - s, err := readTextUTF8(r) - return s, UTF8, err + content, err = readRawText(r, int(size)) + return content, UTF8, err } func NewUnifiedReader(r io.Reader) (io.Reader, error) { @@ -114,7 +110,7 @@ func NewUnifiedReader(r io.Reader) (io.Reader, error) { if err != nil { return nil, err } - charset := resolveCharset(sniffBytes) + charset := detectCharset(sniffBytes) reader := io.MultiReader(bytes.NewReader(sniffBytes), r) // binary or UTF-8 not need convert if charset == BINARY || strings.EqualFold(charset, UTF8) { @@ -122,3 +118,14 @@ func NewUnifiedReader(r io.Reader) (io.Reader, error) { } return chardet.NewReader(reader, charset), nil } + +func NewTextReader(r io.Reader) (io.Reader, error) { + sniffBytes, err := streamio.ReadMax(r, sniffLen) + if err != nil { + return nil, err + } + if bytes.IndexByte(sniffBytes, 0) != -1 { + return nil, ErrNonTextContent + } + return io.MultiReader(bytes.NewReader(sniffBytes), r), nil +} diff --git a/modules/zeta/object/file.go b/modules/zeta/object/file.go index 6856461..6bd5937 100644 --- a/modules/zeta/object/file.go +++ b/modules/zeta/object/file.go @@ -62,6 +62,10 @@ func (f *File) OriginReader(ctx context.Context) (io.ReadCloser, int64, error) { return &readCloser{Reader: br.Contents, Closer: br}, br.Size, nil } +const ( + sniffLen = 8000 +) + func (f *File) Reader(ctx context.Context) (io.ReadCloser, bool, error) { if f.b == nil { return nil, false, io.ErrUnexpectedEOF @@ -89,7 +93,7 @@ func (f *File) UnifiedText(ctx context.Context, codecvt bool) (content string, e return "", err } defer r.Close() - content, _, err = GetUnifiedText(r, f.Size, codecvt) + content, _, err = diferenco.ReadUnifiedText(r, f.Size, codecvt) return content, err } diff --git a/modules/zeta/object/patch.go b/modules/zeta/object/patch.go index e512be5..d4349cb 100644 --- a/modules/zeta/object/patch.go +++ b/modules/zeta/object/patch.go @@ -26,7 +26,7 @@ type PatchOptions struct { } func sizeOverflow(f *File) bool { - return f != nil && f.Size > MAX_DIFF_SIZE + return f != nil && f.Size > diferenco.MAX_DIFF_SIZE } func fileStatName(from, to *File) string { @@ -64,14 +64,14 @@ func fileStatWithContext(ctx context.Context, opts *PatchOptions, c *Change) (*F return s, nil } fromContent, err := from.UnifiedText(ctx, opts.Textconv) - if plumbing.IsNoSuchObject(err) || err == ErrNotTextContent { + if plumbing.IsNoSuchObject(err) || err == diferenco.ErrNonTextContent { return s, nil } if err != nil { return nil, err } toContent, err := to.UnifiedText(ctx, opts.Textconv) - if plumbing.IsNoSuchObject(err) || err == ErrNotTextContent { + if plumbing.IsNoSuchObject(err) || err == diferenco.ErrNonTextContent { return s, nil } if err != nil { @@ -122,14 +122,14 @@ func filePatchWithContext(ctx context.Context, opts *PatchOptions, c *Change) (* return &diferenco.Unified{From: from.asFile(), To: to.asFile(), IsBinary: true}, nil } fromContent, err := from.UnifiedText(ctx, opts.Textconv) - if plumbing.IsNoSuchObject(err) || err == ErrNotTextContent { + if plumbing.IsNoSuchObject(err) || err == diferenco.ErrNonTextContent { return &diferenco.Unified{From: from.asFile(), To: to.asFile(), IsBinary: true}, nil } if err != nil { return nil, err } toContent, err := to.UnifiedText(ctx, opts.Textconv) - if plumbing.IsNoSuchObject(err) || err == ErrNotTextContent { + if plumbing.IsNoSuchObject(err) || err == diferenco.ErrNonTextContent { return &diferenco.Unified{From: from.asFile(), To: to.asFile(), IsBinary: true}, nil } if err != nil { diff --git a/pkg/command/command_diff.go b/pkg/command/command_diff.go index 3bc64d9..0b5ad8b 100644 --- a/pkg/command/command_diff.go +++ b/pkg/command/command_diff.go @@ -63,7 +63,7 @@ func (c *Diff) Passthrough(paths []string) { func (c *Diff) checkAlgorithm() (diferenco.Algorithm, error) { if len(c.DiffAlgorithm) != 0 { - return diferenco.ParseAlgorithm(c.DiffAlgorithm) + return diferenco.AlgorithmFromName(c.DiffAlgorithm) } if c.Histogram { return diferenco.Histogram, nil diff --git a/pkg/command/command_merge_file.go b/pkg/command/command_merge_file.go new file mode 100644 index 0000000..8a25f67 --- /dev/null +++ b/pkg/command/command_merge_file.go @@ -0,0 +1,127 @@ +package command + +import ( + "context" + "fmt" + "io" + "os" + + "github.com/antgroup/hugescm/modules/diferenco" + "github.com/antgroup/hugescm/pkg/zeta" +) + +type MergeFile struct { + Stdout bool `name:"stdout" short:"p" negatable:"" help:"Send results to standard output"` + ObjectID bool `name:"object-id" negatable:"" help:"Use object IDs instead of filenames"` + Diff3 bool `name:"diff3" negatable:"" help:"Use a diff3 based merge"` + ZDiff3 bool `name:"zdiff3" negatable:"" help:"Use a zealous diff3 based merge"` + DiffAlgorithm string `name:"diff-algorithm" help:"Choose a diff algorithm, supported: histogram|onp|myers|patience|minimal"` + L []string `name:":L" short:"L" help:"Set labels for file1/orig-file/file2"` + F1 string `arg:"" name:"0" help:"file1"` + O string `arg:"" name:"1" help:"orig-file"` + F2 string `arg:"" name:"2" help:"file2"` +} + +const ( + mergeFileSummaryFormat = `%szeta merge-file [] [-L [-L [-L ]]] ` +) + +func (c *MergeFile) Summary() string { + return fmt.Sprintf(mergeFileSummaryFormat, W("Usage: ")) +} + +func readText(p string, textConv bool) (string, error) { + fd, err := os.Open(p) + if err != nil { + return "", err + } + defer fd.Close() + si, err := fd.Stat() + if err != nil { + return "", err + } + content, _, err := diferenco.ReadUnifiedText(fd, si.Size(), textConv) + return content, err +} + +func (c *MergeFile) mergeExtra(g *Globals) error { + var a diferenco.Algorithm + var err error + if len(c.DiffAlgorithm) != 0 { + if a, err = diferenco.AlgorithmFromName(c.DiffAlgorithm); err != nil { + fmt.Fprintf(os.Stderr, "parse diff.algorithm error: %v\n", err) + return err + } + } + var style int + switch { + case c.Diff3: + style = diferenco.STYLE_DIFF3 + case c.ZDiff3: + style = diferenco.STYLE_ZEALOUS_DIFF3 + } + g.DbgPrint("algorithm: %s conflict style: %v", a, style) + textO, err := readText(c.O, false) + if err != nil { + fmt.Fprintf(os.Stderr, "merge-file: open error: %v\n", err) + return err + } + textA, err := readText(c.F1, false) + if err != nil { + fmt.Fprintf(os.Stderr, "merge-file: open error: %v\n", err) + return err + } + textB, err := readText(c.F2, false) + if err != nil { + fmt.Fprintf(os.Stderr, "merge-file: open error: %v\n", err) + return err + } + mergedText, conflict, err := diferenco.Merge(context.Background(), &diferenco.MergeOptions{ + TextO: textO, + TextA: textA, + TextB: textB, + LabelO: c.O, + LabelA: c.F1, + LabelB: c.F1, + A: a, + Style: style, + }) + if err != nil { + fmt.Fprintf(os.Stderr, "merge-file: merge error: %v\n", err) + return err + } + _, _ = io.WriteString(os.Stdout, mergedText) + if conflict { + return &zeta.ErrExitCode{ExitCode: 1, Message: "conflict"} + } + return nil +} + +func (c *MergeFile) Run(g *Globals) error { + if !c.ObjectID { + return c.mergeExtra(g) + } + r, err := zeta.Open(context.Background(), &zeta.OpenOptions{ + Worktree: g.CWD, + Values: g.Values, + Verbose: g.Verbose, + }) + if err != nil { + return err + } + defer r.Close() + var style int + switch { + case c.Diff3: + style = diferenco.STYLE_DIFF3 + case c.ZDiff3: + style = diferenco.STYLE_ZEALOUS_DIFF3 + } + if err := r.MergeFile(context.Background(), &zeta.MergeFileOptions{O: c.O, A: c.F1, B: c.F2, Style: style, DiffAlgorithm: c.DiffAlgorithm, Stdout: c.Stdout}); err != nil { + if !zeta.IsExitCode(err, 1) { + diev("merge-file: error: %v", err) + } + return err + } + return nil +} diff --git a/pkg/tr/languages/zh-CN.toml b/pkg/tr/languages/zh-CN.toml index d5009d0..b69d02a 100644 --- a/pkg/tr/languages/zh-CN.toml +++ b/pkg/tr/languages/zh-CN.toml @@ -380,6 +380,14 @@ "Write the object into the object database" = "将对象写入对象数据库" "Read the object from stdin" = "从标准输入读取对象" "Process file as it were from this path" = "处理文件并假设其来自于此路径" +# merge-file +"Run a three-way file merge" = "运行三向文件合并" +"Send results to standard output" = "将结果发送到标准输出" +"Use object IDs instead of filenames" = "使用对象 ID 替换文件名" +"Use a diff3 based merge" = "使用基于 diff3 的合并" +"Use a zealous diff3 based merge" = "使用基于狂热 diff3(zealous diff3)的合并" +"Set labels for file1/orig-file/file2" = "为 文件1/初始文件/文件2 设置标签" + # Others "WARNING" = "警告" "not zeta repository" = "不是 zeta 存储库" diff --git a/pkg/zeta/cat.go b/pkg/zeta/cat.go index 0a2231a..7e9d563 100644 --- a/pkg/zeta/cat.go +++ b/pkg/zeta/cat.go @@ -13,6 +13,7 @@ import ( "os" "strings" + "github.com/antgroup/hugescm/modules/diferenco" "github.com/antgroup/hugescm/modules/plumbing" "github.com/antgroup/hugescm/modules/zeta/backend" "github.com/antgroup/hugescm/modules/zeta/object" @@ -108,7 +109,7 @@ func (r *Repository) catBlob(ctx context.Context, w io.Writer, oid plumbing.Hash } reader := b.Contents if textconv { - if reader, err = object.NewUnifiedReader(b.Contents); err != nil { + if reader, err = diferenco.NewUnifiedReader(b.Contents); err != nil { return err } } diff --git a/pkg/zeta/merge_file.go b/pkg/zeta/merge_file.go new file mode 100644 index 0000000..76454f6 --- /dev/null +++ b/pkg/zeta/merge_file.go @@ -0,0 +1,131 @@ +package zeta + +import ( + "context" + "fmt" + "io" + "os" + "os/exec" + "strings" + + "github.com/antgroup/hugescm/modules/diferenco" + "github.com/antgroup/hugescm/pkg/zeta/odb" +) + +func (r *Repository) resolveMergeDriver() odb.MergeDriver { + if driverName, ok := os.LookupEnv(ENV_ZETA_MERGE_TEXT_DRIVER); ok { + switch driverName { + case "git": + if _, err := exec.LookPath("git"); err == nil { + r.DbgPrint("Use git merge-file as text merge driver") + return r.odb.ExternalMerge + } + case "diff3": + if _, err := exec.LookPath("diff3"); err == nil { + r.DbgPrint("Use diff3 as text merge driver") + return r.odb.Diff3Merge + } + default: + r.DbgPrint("Unsupport merge driver '%s'", driverName) + } + } + var diffAlgorithm diferenco.Algorithm + var err error + if len(r.Diff.Algorithm) != 0 { + if diffAlgorithm, err = diferenco.AlgorithmFromName(r.Diff.Algorithm); err != nil { + warn("diff: bad config: diff.algorithm value: %s", r.Diff.Algorithm) + } + } + mergeConflictStyle := diferenco.ParseConflictStyle(r.Merge.ConflictStyle) + return func(ctx context.Context, o, a, b, labelO, labelA, labelB string) (string, bool, error) { + return diferenco.Merge(ctx, &diferenco.MergeOptions{ + TextO: o, + TextA: a, + TextB: b, + LabelO: labelO, + LabelA: labelA, + LabelB: labelB, + A: diffAlgorithm, + Style: mergeConflictStyle, + }) + } +} + +type MergeFileOptions struct { + O, A, B string + Style int + DiffAlgorithm string + Stdout bool + TextConv bool +} + +func (opts *MergeFileOptions) diffAlgorithmFromName(defaultDiffAlgorithm string) diferenco.Algorithm { + if len(opts.DiffAlgorithm) != 0 { + if diffAlgorithm, err := diferenco.AlgorithmFromName(opts.DiffAlgorithm); err == nil { + return diffAlgorithm + } + warn("diff: bad --diff-algorithm value: %s", opts.DiffAlgorithm) + } + if len(defaultDiffAlgorithm) != 0 { + if diffAlgorithm, err := diferenco.AlgorithmFromName(defaultDiffAlgorithm); err == nil { + return diffAlgorithm + } + warn("diff: bad config: diff.algorithm value: %s", defaultDiffAlgorithm) + } + return diferenco.Unspecified +} + +func (r *Repository) MergeFile(ctx context.Context, opts *MergeFileOptions) error { + diffAlgorithm := opts.diffAlgorithmFromName(r.Diff.Algorithm) + r.DbgPrint("algorithm: %s conflict style: %v", diffAlgorithm, opts.Style) + o, err := r.Revision(ctx, opts.O) + if err != nil { + return err + } + textO, _, err := r.readMissingText(ctx, o, false) + if err != nil { + return err + } + a, err := r.Revision(ctx, opts.A) + if err != nil { + return err + } + textA, _, err := r.readMissingText(ctx, a, false) + if err != nil { + return err + } + b, err := r.Revision(ctx, opts.B) + if err != nil { + return err + } + textB, _, err := r.readMissingText(ctx, b, false) + if err != nil { + return err + } + merged, conflict, err := diferenco.Merge(ctx, &diferenco.MergeOptions{ + TextO: textO, + TextA: textA, + TextB: textB, + LabelO: o.String()[0:8], + LabelA: a.String()[0:8], + LabelB: b.String()[0:8], + A: diffAlgorithm, + Style: opts.Style, + }) + if err != nil { + return err + } + if opts.Stdout { + _, _ = io.WriteString(os.Stdout, merged) + } else { + oid, err := r.odb.HashTo(ctx, strings.NewReader(merged), int64(len(merged))) + if err != nil { + return err + } + _, _ = fmt.Fprintln(os.Stdout, oid.String()) + } + if conflict { + return &ErrExitCode{ExitCode: 1, Message: "conflict"} + } + return nil +} diff --git a/pkg/zeta/merge_tree.go b/pkg/zeta/merge_tree.go index 89bfe40..8765303 100644 --- a/pkg/zeta/merge_tree.go +++ b/pkg/zeta/merge_tree.go @@ -9,7 +9,6 @@ import ( "errors" "fmt" "os" - "os/exec" "github.com/antgroup/hugescm/modules/diferenco" "github.com/antgroup/hugescm/modules/plumbing" @@ -44,46 +43,7 @@ func (r *Repository) readMissingText(ctx context.Context, oid plumbing.Hash, tex return "", "", err } defer br.Close() - return object.GetUnifiedText(br.Contents, br.Size, textConv) -} - -func (r *Repository) resolveMergeDriver() odb.MergeDriver { - if driverName, ok := os.LookupEnv(ENV_ZETA_MERGE_TEXT_DRIVER); ok { - switch driverName { - case "git": - if _, err := exec.LookPath("git"); err == nil { - r.DbgPrint("Use git merge-file as text merge driver") - return r.odb.ExternalMerge - } - case "diff3": - if _, err := exec.LookPath("diff3"); err == nil { - r.DbgPrint("Use diff3 as text merge driver") - return r.odb.Diff3Merge - } - default: - r.DbgPrint("Unsupport merge driver '%s'", driverName) - } - } - var diffAlgorithm diferenco.Algorithm - var err error - if len(r.Diff.Algorithm) != 0 { - if diffAlgorithm, err = diferenco.ParseAlgorithm(r.Diff.Algorithm); err != nil { - warn("diff: bad config, key: diff.algorithm value: %s", r.Diff.Algorithm) - } - } - mergeConflictStyle := diferenco.ParseConflictStyle(r.Merge.ConflictStyle) - return func(ctx context.Context, o, a, b, labelO, labelA, labelB string) (string, bool, error) { - return diferenco.Merge(ctx, &diferenco.MergeOptions{ - TextO: o, - TextA: a, - TextB: b, - LabelO: labelO, - LabelA: labelA, - LabelB: labelB, - A: diffAlgorithm, - Style: mergeConflictStyle, - }) - } + return diferenco.ReadUnifiedText(br.Contents, br.Size, textConv) } func (o *MergeTreeOptions) formatJson(result *odb.MergeResult) { diff --git a/pkg/zeta/misc.go b/pkg/zeta/misc.go index 5475bc2..8c1752d 100644 --- a/pkg/zeta/misc.go +++ b/pkg/zeta/misc.go @@ -287,6 +287,16 @@ type ErrExitCode struct { Message string } +func IsExitCode(err error, i int) bool { + if err == nil { + return false + } + if e, ok := err.(*ErrExitCode); ok { + return e.ExitCode == i + } + return false +} + func (e *ErrExitCode) Error() string { return e.Message } diff --git a/pkg/zeta/odb/merge.go b/pkg/zeta/odb/merge.go index 381bf8c..d131b42 100644 --- a/pkg/zeta/odb/merge.go +++ b/pkg/zeta/odb/merge.go @@ -380,7 +380,7 @@ func (d *ODB) mergeEntry(ctx context.Context, ch *ChangeEntry, opts *MergeOption M: opts.MergeDriver, G: opts.TextGetter, }) - if err == object.ErrNotTextContent { + if err == diferenco.ErrNonTextContent { result.Messages = append(result.Messages, tr.Sprintf("warning: Cannot merge binary files: %s (%s vs. %s)", ch.Path, opts.Branch1, opts.Branch2)) result.Conflicts = append(result.Conflicts, ch.makeConflict(CONFLICT_BINARY)) return &TreeEntry{Path: ch.Path, TreeEntry: ch.Our}, nil @@ -428,7 +428,7 @@ func (d *ODB) mergeEntry(ctx context.Context, ch *ChangeEntry, opts *MergeOption M: opts.MergeDriver, G: opts.TextGetter, }) - if err == object.ErrNotTextContent { + if err == diferenco.ErrNonTextContent { result.Messages = append(result.Messages, tr.Sprintf("warning: Cannot merge binary files: %s (%s vs. %s)", ch.Path, opts.Branch1, opts.Branch2)) result.Conflicts = append(result.Conflicts, ch.makeConflict(CONFLICT_BINARY)) return &TreeEntry{Path: ch.Path, TreeEntry: ch.Our}, nil @@ -487,7 +487,7 @@ func (d *ODB) unifiedText(ctx context.Context, oid plumbing.Hash, textConv bool) return "", "", err } defer br.Close() - return object.GetUnifiedText(br.Contents, br.Size, textConv) + return diferenco.ReadUnifiedText(br.Contents, br.Size, textConv) } // MergeTree: three way merge tree diff --git a/pkg/zeta/odb/merge_driver.go b/pkg/zeta/odb/merge_driver.go index 895ccb3..34dbb2c 100644 --- a/pkg/zeta/odb/merge_driver.go +++ b/pkg/zeta/odb/merge_driver.go @@ -9,8 +9,8 @@ import ( "strings" "github.com/antgroup/hugescm/modules/chardet" + "github.com/antgroup/hugescm/modules/diferenco" "github.com/antgroup/hugescm/modules/plumbing" - "github.com/antgroup/hugescm/modules/zeta/object" ) type MergeDriver func(ctx context.Context, o, a, b string, labelO, labelA, labelB string) (string, bool, error) @@ -47,7 +47,7 @@ func (d *ODB) mergeText(ctx context.Context, opts *mergeOptions) (*mergeTextResu if err != nil { return nil, err } - if !opts.Textconv || strings.EqualFold(charset, object.UTF8) { + if !opts.Textconv || strings.EqualFold(charset, diferenco.UTF8) { size := int64(len(mergedText)) oid, err := d.HashTo(ctx, strings.NewReader(mergedText), size) if err != nil { diff --git a/pkg/zeta/worktree_diff.go b/pkg/zeta/worktree_diff.go index 9343360..eca083c 100644 --- a/pkg/zeta/worktree_diff.go +++ b/pkg/zeta/worktree_diff.go @@ -24,7 +24,7 @@ func (w *Worktree) openText(p string, size int64, textConv bool) (string, error) return "", err } defer fd.Close() - content, _, err := object.GetUnifiedText(fd, size, textConv) + content, _, err := diferenco.ReadUnifiedText(fd, size, textConv) return content, err } @@ -34,7 +34,7 @@ func (w *Worktree) openBlobText(ctx context.Context, oid plumbing.Hash, textConv return "", err } defer br.Close() - content, _, err := object.GetUnifiedText(br.Contents, br.Size, textConv) + content, _, err := diferenco.ReadUnifiedText(br.Contents, br.Size, textConv) return content, err } @@ -46,11 +46,11 @@ func (w *Worktree) readContent(ctx context.Context, p noder.Path, textConv bool) switch a := p.Last().(type) { case *filesystem.Node: f = &diferenco.File{Name: name, Hash: a.HashRaw().String(), Mode: uint32(a.Mode())} - if a.Size() > object.MAX_DIFF_SIZE { + if a.Size() > diferenco.MAX_DIFF_SIZE { return f, "", false, true, nil } content, err = w.openText(name, a.Size(), textConv) - if err == object.ErrNotTextContent { + if err == diferenco.ErrNonTextContent { return f, "", false, true, nil } return f, content, false, false, nil @@ -59,12 +59,12 @@ func (w *Worktree) readContent(ctx context.Context, p noder.Path, textConv bool) if a.IsFragments() { return f, "", true, false, err } - if a.Size() > object.MAX_DIFF_SIZE { + if a.Size() > diferenco.MAX_DIFF_SIZE { return f, "", false, true, nil } content, err = w.openBlobText(ctx, a.HashRaw(), textConv) // When the current repository uses an incomplete checkout mechanism, we treat these files as binary files, i.e. no differences can be calculated. - if err == object.ErrNotTextContent || plumbing.IsNoSuchObject(err) { + if err == diferenco.ErrNonTextContent || plumbing.IsNoSuchObject(err) { return f, "", false, true, nil } return f, content, false, false, nil @@ -73,11 +73,11 @@ func (w *Worktree) readContent(ctx context.Context, p noder.Path, textConv bool) if a.IsFragments() { return f, "", true, false, err } - if a.Size() > object.MAX_DIFF_SIZE { + if a.Size() > diferenco.MAX_DIFF_SIZE { return f, "", false, true, nil } content, err = w.openBlobText(ctx, a.HashRaw(), textConv) - if err == object.ErrNotTextContent || plumbing.IsNoSuchObject(err) { + if err == diferenco.ErrNonTextContent || plumbing.IsNoSuchObject(err) { return f, "", false, true, nil } return f, content, a.IsFragments(), false, nil @@ -337,7 +337,7 @@ func (w *Worktree) between(ctx context.Context, opts *DiffOptions) error { func (w *Worktree) DiffContext(ctx context.Context, opts *DiffOptions) error { if opts.Algorithm == diferenco.Unspecified && len(w.Diff.Algorithm) != 0 { - if a, err := diferenco.ParseAlgorithm(w.Diff.Algorithm); err != nil { + if a, err := diferenco.AlgorithmFromName(w.Diff.Algorithm); err != nil { warn("diff: bad config, key: diff.algorithm value: %s", w.Diff.Algorithm) } else { opts.Algorithm = a