/
status.go
130 lines (108 loc) · 3.14 KB
/
status.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package main
import (
"flag"
"fmt"
"os"
"path/filepath"
base58 "github.com/jbenet/go-base58"
attrs "github.com/mildred/doc/attrs"
ignore "github.com/mildred/doc/ignore"
repo "github.com/mildred/doc/repo"
)
const usageStatus string = `doc status [OPTIONS...] [DIR]
Scan DIR or the current directory and display a list of new and modified files
with a symbol describing its status:
? Untracked file
+ Modified file (mtime changed since lash hash)
* Unsaved file (missing PAR2 information)
C Conflict (main filename)
c Conflict (alternate file)
Additionally, (ro) can appear if the file is read only, to notify that doc add
will probably fail to set the extended attributes
Options:
`
func mainStatus(args []string) int {
f := flag.NewFlagSet("status", flag.ExitOnError)
opt_no_par2 := f.Bool("n", false, "Do not show files missing PAR2 redundency data")
opt_show_only_hash := f.Bool("c", false, "Show only unchanged committed files with their hash")
opt_no_docignore := f.Bool("no-docignore", false, "Don't treat .docignore files specially")
f.Usage = func() {
fmt.Print(usageStatus)
f.PrintDefaults()
}
f.Parse(args)
dir := f.Arg(0)
if dir == "" {
dir = "."
}
rep := repo.GetRepo(dir)
status := 0
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
fmt.Fprintf(os.Stderr, "%s: %v\n", path, err.Error())
status = 1
return err
}
// Skip directories containing an empty .docignore file
if !*opt_no_docignore && ignore.IsIgnored(path) {
return filepath.SkipDir
}
// Skip .dirstore/ at root
if filepath.Base(path) == attrs.DirStoreName && filepath.Dir(path) == dir && info.IsDir() {
return filepath.SkipDir
} else if !info.Mode().IsRegular() {
return nil
}
if *opt_show_only_hash {
hash, err := repo.GetHash(path, info, false)
if err != nil {
fmt.Fprintf(os.Stderr, "%s: %v\n", path, err.Error())
return nil
}
if hash != nil {
fmt.Printf("%s\t%s\n", base58.Encode(hash), path)
}
} else {
var conflict string = ""
if repo.ConflictFile(path) != "" {
conflict = " c"
} else if len(repo.ConflictFileAlternatives(path)) > 0 {
conflict = " C"
}
hashTime, err := repo.GetHashTime(path)
if repo.IsNoData(err) {
if info.Mode()&os.FileMode(0200) == 0 {
fmt.Printf("?%s (ro)\t%s\n", conflict, path)
} else {
fmt.Printf("?%s\t%s\n", conflict, path)
}
return nil
} else if err != nil {
fmt.Fprintf(os.Stderr, "%s: %v\n", path, err.Error())
return nil
}
var redundency string = "*"
if rep != nil {
digest, err := repo.GetHash(path, info, true)
if err != nil {
fmt.Fprintf(os.Stderr, "%s: %v\n", path, err.Error())
return nil
}
if par2exists, _ := rep.Par2Exists(digest); par2exists {
redundency = ""
}
}
if hashTime != info.ModTime() {
fmt.Printf("+%s%s\t%s\n", conflict, redundency, path)
} else if conflict != "" || (redundency != "" && !*opt_no_par2) {
fmt.Printf("%s%s\t%s\n", conflict, redundency, path)
}
}
return nil
})
if err != nil {
fmt.Fprintf(os.Stderr, "%v", err)
os.Exit(1)
}
return status
}