diff --git a/cmd/zeta-serve/shutdown_other.go b/cmd/zeta-serve/shutdown_other.go index f0a2798..97a6a0c 100644 --- a/cmd/zeta-serve/shutdown_other.go +++ b/cmd/zeta-serve/shutdown_other.go @@ -1,7 +1,7 @@ // Copyright ©️ Ant Group. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -//go:build darwin || linux || freebsd || netbsd || openbsd +//go:build darwin || linux || freebsd || netbsd || openbsd || dragonfly package main diff --git a/modules/bitmap/bitmap_test.go b/modules/bitmap/bitmap_test.go index ddf947c..24898a9 100644 --- a/modules/bitmap/bitmap_test.go +++ b/modules/bitmap/bitmap_test.go @@ -1,3 +1,5 @@ +//go:build !386 + package bitmap import ( diff --git a/modules/merkletrie/filesystem/node.go b/modules/merkletrie/filesystem/node.go index c330051..692725f 100644 --- a/modules/merkletrie/filesystem/node.go +++ b/modules/merkletrie/filesystem/node.go @@ -67,6 +67,10 @@ func (n *Node) Mode() filemode.FileMode { return m } +func (n *Node) HijackMode(mode filemode.FileMode) { + n.mode, _ = mode.ToOSFileMode() +} + func (n *Node) ModifiedAt() time.Time { return n.modifiedAt } diff --git a/modules/zeta/backend/pack/encode.go b/modules/zeta/backend/pack/encode.go index 669dd1b..c706d2b 100644 --- a/modules/zeta/backend/pack/encode.go +++ b/modules/zeta/backend/pack/encode.go @@ -134,6 +134,10 @@ func (e *Encoder) Name() string { return e.sum.String() } +const ( + offset64PosMask = uint64(1) << 31 +) + // https://codewords.recurse.com/issues/three/unpacking-git-packfiles func (e *Encoder) WriteIndex(fd *os.File) error { sort.Sort(e.objects) @@ -169,12 +173,12 @@ func (e *Encoder) WriteIndex(fd *os.File) error { } } offset64Set := make([]uint64, 0, 20) - var offset64Pos int + var offset64Pos uint64 for _, o := range e.objects { offset := o.Offset if offset > math.MaxInt32 { offset64Set = append(offset64Set, offset) - offset = uint64(offset64Pos | (1 << 31)) + offset = uint64(offset64Pos | offset64PosMask) offset64Pos++ } if err := binary.WriteUint32(w, uint32(offset)); err != nil { diff --git a/pkg/zeta/worktree_bsd.go b/pkg/zeta/worktree_bsd.go index 286b0ef..cec7852 100644 --- a/pkg/zeta/worktree_bsd.go +++ b/pkg/zeta/worktree_bsd.go @@ -1,8 +1,8 @@ // Copyright 2018 Sourced Technologies, S.L. // SPDX-License-Identifier: Apache-2.0 -//go:build darwin || freebsd || netbsd -// +build darwin freebsd netbsd +//go:build freebsd || netbsd +// +build freebsd netbsd package zeta @@ -10,6 +10,7 @@ import ( "syscall" "time" + "github.com/antgroup/hugescm/modules/merkletrie" "github.com/antgroup/hugescm/modules/plumbing/format/index" ) @@ -32,3 +33,36 @@ func init() { func isSymlinkWindowsNonAdmin(error) bool { return false } + +func (w *Worktree) excludeIgnoredChanges(changes merkletrie.Changes) merkletrie.Changes { + if len(changes) == 0 { + return changes + } + m, err := w.ignoreMatcher() + if err != nil { + return changes + } + + var res merkletrie.Changes + for _, ch := range changes { + var path []string + for _, n := range ch.To { + path = append(path, n.Name()) + } + if len(path) == 0 { + for _, n := range ch.From { + path = append(path, n.Name()) + } + } + if len(path) != 0 { + isDir := (len(ch.To) > 0 && ch.To.IsDir()) || (len(ch.From) > 0 && ch.From.IsDir()) + if m.Match(path, isDir) { + if len(ch.From) == 0 { + continue + } + } + } + res = append(res, ch) + } + return res +} diff --git a/pkg/zeta/worktree_checkout.go b/pkg/zeta/worktree_checkout.go index 617ef1d..cd6014f 100644 --- a/pkg/zeta/worktree_checkout.go +++ b/pkg/zeta/worktree_checkout.go @@ -13,7 +13,6 @@ import ( "github.com/antgroup/hugescm/modules/merkletrie" "github.com/antgroup/hugescm/modules/plumbing" - "github.com/antgroup/hugescm/modules/plumbing/filemode" "github.com/antgroup/hugescm/modules/plumbing/format/index" "github.com/antgroup/hugescm/modules/zeta/object" "github.com/antgroup/hugescm/pkg/progress" @@ -423,7 +422,7 @@ func (cg *checkoutGroup) coco(ctx context.Context, w *Worktree, bar ProgressBar) fmt.Fprintf(os.Stderr, "\x1b[2K\rcheckout file %s error: %v\n", e.name, err) return err } - if err := w.addIndex(e.name, e.entry.Hash, cg.recv, e.entry.IsFragments()); err != nil { + if err := w.addIndex(e.name, e.entry, cg.recv); err != nil { fmt.Fprintf(os.Stderr, "\x1b[2K\rreset file %s index error: %v\n", e.name, err) return err } @@ -440,23 +439,15 @@ func (cg *checkoutGroup) run(ctx context.Context, w *Worktree, bar ProgressBar) }() } -func (w *Worktree) addIndex(name string, h plumbing.Hash, recv indexRecv, isFragments bool) error { +func (w *Worktree) addIndex(name string, entry *object.TreeEntry, recv indexRecv) error { fi, err := w.fs.Lstat(name) if err != nil { return err } - - mode, err := filemode.NewFromOS(fi.Mode()) - if err != nil { - return err - } - if isFragments { - mode |= filemode.Fragments - } e := &index.Entry{ - Hash: h, + Hash: entry.Hash, Name: name, - Mode: mode, + Mode: entry.Mode, ModifiedAt: fi.ModTime(), Size: uint64(fi.Size()), } @@ -470,15 +461,15 @@ func (w *Worktree) addIndex(name string, h plumbing.Hash, recv indexRecv, isFrag return nil } -func (w *Worktree) addPseudoIndexRecv(name string, oe *object.TreeEntry, recv indexRecv) { +func (w *Worktree) addPseudoIndexRecv(name string, entry *object.TreeEntry, recv indexRecv) { now := time.Now() e := &index.Entry{ - Hash: oe.Hash, + Hash: entry.Hash, Name: name, - Mode: oe.Mode, + Mode: entry.Mode, ModifiedAt: now, CreatedAt: now, - Size: uint64(oe.Size), + Size: uint64(entry.Size), } recv(e) } diff --git a/pkg/zeta/worktree_drawin.go b/pkg/zeta/worktree_drawin.go new file mode 100644 index 0000000..b72d012 --- /dev/null +++ b/pkg/zeta/worktree_drawin.go @@ -0,0 +1,96 @@ +// Copyright 2018 Sourced Technologies, S.L. +// SPDX-License-Identifier: Apache-2.0 + +//go:build darwin +// +build darwin + +package zeta + +import ( + "bytes" + "strings" + "syscall" + "time" + + "github.com/antgroup/hugescm/modules/merkletrie" + "github.com/antgroup/hugescm/modules/plumbing/format/index" +) + +const ( + escapeChars = "*?[]\\" +) + +func init() { + fillSystemInfo = func(e *index.Entry, sys any) { + if os, ok := sys.(*syscall.Stat_t); ok { + e.CreatedAt = time.Unix(os.Atimespec.Unix()) + e.Dev = uint32(os.Dev) + e.Inode = uint32(os.Ino) + e.GID = os.Gid + e.UID = os.Uid + } + } +} + +func isSymlinkWindowsNonAdmin(error) bool { + return false +} + +func (w *Worktree) excludeIgnoredChanges(changes merkletrie.Changes) merkletrie.Changes { + if len(changes) == 0 { + return changes + } + m, err := w.ignoreMatcher() + if err != nil { + return changes + } + var newItems merkletrie.Changes + var res merkletrie.Changes + rmItems := make(map[string]merkletrie.Change) + for _, ch := range changes { + var path []string + for _, n := range ch.To { + path = append(path, n.Name()) + } + if len(path) == 0 { + for _, n := range ch.From { + path = append(path, n.Name()) + } + } + if len(path) != 0 { + isDir := (len(ch.To) > 0 && ch.To.IsDir()) || (len(ch.From) > 0 && ch.From.IsDir()) + if m.Match(path, isDir) { + if len(ch.From) == 0 { + continue + } + } + } + // Add + if ch.From == nil { + newItems = append(newItems, ch) + continue + } + // Del + if ch.To == nil { + rmItems[strings.ToLower(ch.From.String())] = ch + continue + } + res = append(res, ch) + } + for _, ch := range newItems { + name := strings.ToLower(ch.To.String()) + if c, ok := rmItems[name]; ok { + if !bytes.Equal(c.From.Hash(), ch.To.Hash()) { + ch.From = c.From + res = append(res, ch) // rename and modify + } + delete(rmItems, name) + continue + } + res = append(res, ch) + } + for _, ch := range rmItems { + res = append(res, ch) + } + return res +} diff --git a/pkg/zeta/worktree_linux.go b/pkg/zeta/worktree_linux.go index 2d7b9e5..8d5977b 100644 --- a/pkg/zeta/worktree_linux.go +++ b/pkg/zeta/worktree_linux.go @@ -10,6 +10,7 @@ import ( "syscall" "time" + "github.com/antgroup/hugescm/modules/merkletrie" "github.com/antgroup/hugescm/modules/plumbing/format/index" ) @@ -32,3 +33,36 @@ func init() { func isSymlinkWindowsNonAdmin(error) bool { return false } + +func (w *Worktree) excludeIgnoredChanges(changes merkletrie.Changes) merkletrie.Changes { + if len(changes) == 0 { + return changes + } + m, err := w.ignoreMatcher() + if err != nil { + return changes + } + + var res merkletrie.Changes + for _, ch := range changes { + var path []string + for _, n := range ch.To { + path = append(path, n.Name()) + } + if len(path) == 0 { + for _, n := range ch.From { + path = append(path, n.Name()) + } + } + if len(path) != 0 { + isDir := (len(ch.To) > 0 && ch.To.IsDir()) || (len(ch.From) > 0 && ch.From.IsDir()) + if m.Match(path, isDir) { + if len(ch.From) == 0 { + continue + } + } + } + res = append(res, ch) + } + return res +} diff --git a/pkg/zeta/worktree_status.go b/pkg/zeta/worktree_status.go index 9ee9fcf..7de154d 100644 --- a/pkg/zeta/worktree_status.go +++ b/pkg/zeta/worktree_status.go @@ -319,36 +319,6 @@ func (w *Worktree) ignoredChanges(changes merkletrie.Changes) []string { return ignored } -func (w *Worktree) excludeIgnoredChanges(changes merkletrie.Changes) merkletrie.Changes { - m, err := w.ignoreMatcher() - if err != nil { - return changes - } - - var res merkletrie.Changes - for _, ch := range changes { - var path []string - for _, n := range ch.To { - path = append(path, n.Name()) - } - if len(path) == 0 { - for _, n := range ch.From { - path = append(path, n.Name()) - } - } - if len(path) != 0 { - isDir := (len(ch.To) > 0 && ch.To.IsDir()) || (len(ch.From) > 0 && ch.From.IsDir()) - if m.Match(path, isDir) { - if len(ch.From) == 0 { - continue - } - } - } - res = append(res, ch) - } - return res -} - func (w *Worktree) doAddDirectory(ctx context.Context, idx *index.Index, s Status, directory string, ignorePattern []ignore.Pattern, dryRun bool) (added bool, err error) { if len(ignorePattern) > 0 { m := ignore.NewMatcher(ignorePattern) diff --git a/pkg/zeta/worktree_test.go b/pkg/zeta/worktree_test.go index 2e84db4..f8e450f 100644 --- a/pkg/zeta/worktree_test.go +++ b/pkg/zeta/worktree_test.go @@ -9,6 +9,7 @@ import ( "sort" "testing" + "github.com/BurntSushi/toml" "github.com/antgroup/hugescm/modules/merkletrie/noder" "github.com/antgroup/hugescm/modules/plumbing" "github.com/antgroup/hugescm/modules/plumbing/filemode" @@ -16,7 +17,6 @@ import ( "github.com/antgroup/hugescm/modules/zeta/config" "github.com/antgroup/hugescm/modules/zeta/object" "github.com/antgroup/hugescm/pkg/zeta/odb" - "github.com/BurntSushi/toml" ) func TestWorktree(t *testing.T) { @@ -753,3 +753,7 @@ func TestEncode(t *testing.T) { } _ = toml.NewEncoder(os.Stderr).Encode(a) } + +func TestMode(t *testing.T) { + fmt.Fprintf(os.Stderr, "%o\n", filemode.Regular&filemode.Executable) +} diff --git a/pkg/zeta/worktree_unix_other.go b/pkg/zeta/worktree_unix_other.go index e2719f4..f40666a 100644 --- a/pkg/zeta/worktree_unix_other.go +++ b/pkg/zeta/worktree_unix_other.go @@ -10,6 +10,7 @@ import ( "syscall" "time" + "github.com/antgroup/hugescm/modules/merkletrie" "github.com/antgroup/hugescm/modules/plumbing/format/index" ) @@ -32,3 +33,36 @@ func init() { func isSymlinkWindowsNonAdmin(error) bool { return false } + +func (w *Worktree) excludeIgnoredChanges(changes merkletrie.Changes) merkletrie.Changes { + if len(changes) == 0 { + return changes + } + m, err := w.ignoreMatcher() + if err != nil { + return changes + } + + var res merkletrie.Changes + for _, ch := range changes { + var path []string + for _, n := range ch.To { + path = append(path, n.Name()) + } + if len(path) == 0 { + for _, n := range ch.From { + path = append(path, n.Name()) + } + } + if len(path) != 0 { + isDir := (len(ch.To) > 0 && ch.To.IsDir()) || (len(ch.From) > 0 && ch.From.IsDir()) + if m.Match(path, isDir) { + if len(ch.From) == 0 { + continue + } + } + } + res = append(res, ch) + } + return res +} diff --git a/pkg/zeta/worktree_windows.go b/pkg/zeta/worktree_windows.go index 4dc461f..cf5fb66 100644 --- a/pkg/zeta/worktree_windows.go +++ b/pkg/zeta/worktree_windows.go @@ -7,10 +7,16 @@ package zeta import ( + "bytes" "os" + "strings" "syscall" "time" + "github.com/antgroup/hugescm/modules/merkletrie" + "github.com/antgroup/hugescm/modules/merkletrie/filesystem" + "github.com/antgroup/hugescm/modules/plumbing" + "github.com/antgroup/hugescm/modules/plumbing/filemode" "github.com/antgroup/hugescm/modules/plumbing/format/index" ) @@ -41,3 +47,102 @@ func isSymlinkWindowsNonAdmin(err error) bool { return false } + +type hasher interface { + HashRaw() plumbing.Hash + Mode() filemode.FileMode +} + +func (w *Worktree) hijackChangeFileMode(ch *merkletrie.Change) bool { + if ch.From == nil || ch.To == nil { + return false + } + from := ch.From.Last() + to := ch.To.Last() + a, ok := from.(hasher) + if !ok { + return false + } + b, ok := to.(hasher) + if !ok { + return false + } + modeA := a.Mode() + modeB := b.Mode() + if a.HashRaw() == b.HashRaw() { + return modeA&filemode.Regular == modeB&filemode.Regular + } + if modeA&filemode.Regular == modeB&filemode.Regular { + // rewrite change + if fa, ok := from.(*filesystem.Node); ok { + fa.HijackMode(modeB) + return false + } + if fb, ok := to.(*filesystem.Node); ok { + fb.HijackMode(modeA) + } + } + return false +} + +func (w *Worktree) excludeIgnoredChanges(changes merkletrie.Changes) merkletrie.Changes { + if len(changes) == 0 { + return changes + } + m, err := w.ignoreMatcher() + if err != nil { + return changes + } + var newItems merkletrie.Changes + var res merkletrie.Changes + rmItems := make(map[string]merkletrie.Change) + for _, ch := range changes { + var path []string + for _, n := range ch.To { + path = append(path, n.Name()) + } + if len(path) == 0 { + for _, n := range ch.From { + path = append(path, n.Name()) + } + } + if len(path) != 0 { + isDir := (len(ch.To) > 0 && ch.To.IsDir()) || (len(ch.From) > 0 && ch.From.IsDir()) + if m.Match(path, isDir) { + if len(ch.From) == 0 { + continue + } + } + } + if w.hijackChangeFileMode(&ch) { + continue + } + // Add + if ch.From == nil { + newItems = append(newItems, ch) + continue + } + // Del + if ch.To == nil { + rmItems[strings.ToLower(ch.From.String())] = ch + continue + } + res = append(res, ch) + } + for _, ch := range newItems { + name := strings.ToLower(ch.To.String()) + if c, ok := rmItems[name]; ok { + if !bytes.Equal(c.From.Hash(), ch.To.Hash()) { + ch.From = c.From + res = append(res, ch) // rename and modify + } + delete(rmItems, name) + continue + } + res = append(res, ch) + } + for _, ch := range rmItems { + res = append(res, ch) + } + return res +}