diff --git a/cmd/add.go b/cmd/add.go index 377ff1d..f06b94c 100644 --- a/cmd/add.go +++ b/cmd/add.go @@ -89,13 +89,7 @@ var addCmd = &cobra.Command{ // check if the arg is the target of excluding path cleanedArg := filepath.Clean(arg) cleanedArg = strings.ReplaceAll(cleanedArg, `\`, "/") - isExcluded := false - for _, excludePath := range client.ExcludePaths { - if excludePath == cleanedArg { - isExcluded = true - } - } - if isExcluded { + if client.Ignore.IsIncluded(cleanedArg) { continue } diff --git a/cmd/root.go b/cmd/root.go index 982aff7..4343005 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -72,12 +72,17 @@ func init() { fmt.Println(err) os.Exit(1) } - r, err := store.NewRefs(rootGoitPath) + refs, err := store.NewRefs(rootGoitPath) if err != nil { fmt.Println(err) os.Exit(1) } - client = store.NewClient(config, index, head, r, rootGoitPath) + ignore, err := store.NewIgnore(rootGoitPath) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + client = store.NewClient(config, index, head, refs, ignore, rootGoitPath) gLogger = log.NewGoitLogger(client.RootGoitPath) diff --git a/internal/store/client.go b/internal/store/client.go index 1fffe19..1973bfa 100644 --- a/internal/store/client.go +++ b/internal/store/client.go @@ -5,17 +5,17 @@ type Client struct { Idx *Index Head *Head Refs *Refs + Ignore *Ignore RootGoitPath string - ExcludePaths []string } -func NewClient(config *Config, index *Index, head *Head, refs *Refs, rootGoitPath string) *Client { +func NewClient(config *Config, index *Index, head *Head, refs *Refs, ignore *Ignore, rootGoitPath string) *Client { return &Client{ Conf: config, Idx: index, Head: head, Refs: refs, + Ignore: ignore, RootGoitPath: rootGoitPath, - ExcludePaths: []string{".goit"}, } } diff --git a/internal/store/ignore.go b/internal/store/ignore.go new file mode 100644 index 0000000..4ca4951 --- /dev/null +++ b/internal/store/ignore.go @@ -0,0 +1,75 @@ +package store + +import ( + "bufio" + "fmt" + "os" + "path/filepath" + "regexp" + "strings" +) + +var ( + directoryRegexp = regexp.MustCompile(`.*\/`) +) + +type Ignore struct { + paths []string +} + +func NewIgnore(rootGoitPath string) (*Ignore, error) { + i := newIgnore() + if err := i.load(rootGoitPath); err != nil { + return nil, err + } + return i, nil +} + +func newIgnore() *Ignore { + return &Ignore{ + paths: []string{`\.goit/.*`}, + } +} + +func (i *Ignore) load(rootGoitPath string) error { + goitignorePath := filepath.Join(filepath.Dir(rootGoitPath), ".goitignore") + if _, err := os.Stat(goitignorePath); os.IsNotExist(err) { + return nil + } + f, err := os.Open(goitignorePath) + if err != nil { + return fmt.Errorf("fail to open %s: %w", goitignorePath, err) + } + defer f.Close() + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + text := scanner.Text() + var replacedText string + if directoryRegexp.MatchString(text) { + replacedText = fmt.Sprintf("%s.*", text) + } else { + replacedText = strings.ReplaceAll(text, ".", `\.`) + replacedText = strings.ReplaceAll(replacedText, "*", ".*") + } + i.paths = append(i.paths, replacedText) + } + + return nil +} + +// return true if the parameter is included in ignore list +func (i *Ignore) IsIncluded(path string) bool { + target := path + info, _ := os.Stat(path) + if info.IsDir() && !directoryRegexp.MatchString(path) { + target = fmt.Sprintf("%s/", path) + } + for _, exFile := range i.paths { + exRegexp := regexp.MustCompile(exFile) + if exRegexp.MatchString(target) { + return true + } + } + return false +} diff --git a/internal/store/ignore_test.go b/internal/store/ignore_test.go new file mode 100644 index 0000000..4fd5b54 --- /dev/null +++ b/internal/store/ignore_test.go @@ -0,0 +1,66 @@ +package store + +import ( + "os" + "path/filepath" + "reflect" + "testing" +) + +func TestNewIgnore(t *testing.T) { + type fields struct { + content string + } + tests := []struct { + name string + fields fields + want *Ignore + wantErr bool + }{ + { + name: "success: empty", + fields: fields{ + content: "", + }, + want: &Ignore{ + paths: []string{`\.goit/.*`}, + }, + wantErr: false, + }, + { + name: "success: some ignore list", + fields: fields{ + content: "*.exe\ndir/", + }, + want: &Ignore{ + paths: []string{`\.goit/.*`, `.*\.exe`, `dir/.*`}, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tmpDir := t.TempDir() + + goitignorePath := filepath.Join(tmpDir, ".goitignore") + f, err := os.Create(goitignorePath) + if err != nil { + t.Log(err) + } + _, err = f.WriteString(tt.fields.content) + if err != nil { + t.Log(err) + } + f.Close() + + goitPath := filepath.Join(tmpDir, ".goit") + i, err := NewIgnore(goitPath) + if (err != nil) != tt.wantErr { + t.Errorf("got = %v, want = %v", err, tt.wantErr) + } + if !reflect.DeepEqual(i, tt.want) { + t.Errorf("got = %v, want = %v", i, tt.want) + } + }) + } +} diff --git a/testdata/.goitignore b/testdata/.goitignore new file mode 100644 index 0000000..6105d9f --- /dev/null +++ b/testdata/.goitignore @@ -0,0 +1,2 @@ +*.exe +dir/dir2/ \ No newline at end of file