forked from git-lfs/git-lfs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcommand_dedup.go
147 lines (123 loc) · 4.29 KB
/
command_dedup.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package commands
import (
"os"
"path/filepath"
"sync/atomic"
"github.com/git-lfs/git-lfs/v3/config"
"github.com/git-lfs/git-lfs/v3/errors"
"github.com/git-lfs/git-lfs/v3/git"
"github.com/git-lfs/git-lfs/v3/lfs"
"github.com/git-lfs/git-lfs/v3/tools"
"github.com/git-lfs/git-lfs/v3/tr"
"github.com/spf13/cobra"
)
var (
dedupFlags = struct {
test bool
}{}
dedupStats = &struct {
totalProcessedCount int64
totalProcessedSize int64
}{}
)
func dedupTestCommand(*cobra.Command, []string) {
setupRepository()
if supported, err := tools.CheckCloneFileSupported(cfg.TempDir()); err != nil || !supported {
if err == nil {
err = errors.New(tr.Tr.Get("Unknown reason"))
}
Exit(tr.Tr.Get("This system does not support de-duplication: %s", err))
}
if len(cfg.Extensions()) > 0 {
Exit(tr.Tr.Get("This platform supports file de-duplication, however, Git LFS extensions are configured and therefore de-duplication can not be used."))
}
Print(tr.Tr.Get("OK: This platform and repository support file de-duplication."))
}
func dedupCommand(cmd *cobra.Command, args []string) {
if dedupFlags.test {
dedupTestCommand(cmd, args)
return
}
setupRepository()
if gitDir, err := git.GitDir(); err != nil {
ExitWithError(err)
} else if supported, err := tools.CheckCloneFileSupported(gitDir); err != nil || !supported {
Exit(tr.Tr.Get("This system does not support de-duplication."))
}
if len(cfg.Extensions()) > 0 {
Exit(tr.Tr.Get("This platform supports file de-duplication, however, Git LFS extensions are configured and therefore de-duplication can not be used."))
}
if dirty, err := git.IsWorkingCopyDirty(); err != nil {
ExitWithError(err)
} else if dirty {
Exit(tr.Tr.Get("Working tree is dirty. Please commit or reset your change."))
}
// We assume working tree is clean.
gitScanner := lfs.NewGitScanner(config.New(), func(p *lfs.WrappedPointer, err error) {
if err != nil {
Exit(tr.Tr.Get("Could not scan for Git LFS tree: %s", err))
return
}
if success, err := dedup(p); err != nil {
// TRANSLATORS: Leading spaces should be included on
// the second line so the format specifier aligns with
// with the first format specifier on the first line.
Error(tr.Tr.Get("Skipped: %s (Size: %d)\n %s", p.Name, p.Size, err))
} else if !success {
Error(tr.Tr.Get("Skipped: %s (Size: %d)", p.Name, p.Size))
} else if success {
Print(tr.Tr.Get("Success: %s (Size: %d)", p.Name, p.Size))
atomic.AddInt64(&dedupStats.totalProcessedCount, 1)
atomic.AddInt64(&dedupStats.totalProcessedSize, p.Size)
}
})
if err := gitScanner.ScanTree("HEAD", nil); err != nil {
ExitWithError(err)
}
// TRANSLATORS: The second and third strings should have the colons
// aligned in a column.
Print("\n\n%s\n %s\n %s", tr.Tr.Get("Finished successfully."),
tr.Tr.GetN(
"De-duplicated size: %d byte",
"De-duplicated size: %d bytes",
int(dedupStats.totalProcessedSize),
dedupStats.totalProcessedSize),
tr.Tr.Get(" count: %d", dedupStats.totalProcessedCount))
}
// dedup executes
// Precondition: working tree MUST clean. We can replace working tree files from mediafile safely.
func dedup(p *lfs.WrappedPointer) (success bool, err error) {
// PRECONDITION, check ofs object exists or skip this file.
if !cfg.LFSObjectExists(p.Oid, p.Size) { // Not exists,
// Basically, this is not happens because executing 'git status' in `git.IsWorkingCopyDirty()` recover it.
return false, errors.New(tr.Tr.Get("Git LFS object file does not exist"))
}
// DO de-dup
// Gather original state
originalStat, err := os.Stat(p.Name)
if err != nil {
return false, err
}
// Do clone
srcFile := cfg.Filesystem().ObjectPathname(p.Oid)
if srcFile == os.DevNull {
return true, nil
}
dstFile := filepath.Join(cfg.LocalWorkingDir(), p.Name)
// Clone the file. This overwrites the destination if it exists.
if ok, err := tools.CloneFileByPath(dstFile, srcFile); err != nil {
return false, err
} else if !ok {
return false, errors.Errorf(tr.Tr.Get("unknown clone file error"))
}
// Recover original state
if err := os.Chmod(dstFile, originalStat.Mode()); err != nil {
return false, err
}
return true, nil
}
func init() {
RegisterCommand("dedup", dedupCommand, func(cmd *cobra.Command) {
cmd.Flags().BoolVarP(&dedupFlags.test, "test", "t", false, "test")
})
}