Skip to content

Commit 7c0f9e2

Browse files
committed
progress: make ProgressMeter implement log.Task
1 parent 957575f commit 7c0f9e2

File tree

1 file changed

+47
-88
lines changed

1 file changed

+47
-88
lines changed

progress/meter.go

Lines changed: 47 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import (
44
"fmt"
55
"os"
66
"path/filepath"
7-
"strings"
87
"sync"
98
"sync/atomic"
109
"time"
1110

12-
"github.com/olekukonko/ts"
11+
"github.com/git-lfs/git-lfs/git/githistory/log"
12+
"github.com/git-lfs/git-lfs/tools/humanize"
1313
)
1414

1515
// ProgressMeter provides a progress bar type output for the TransferQueue. It
@@ -23,14 +23,13 @@ type ProgressMeter struct {
2323
estimatedBytes int64
2424
currentBytes int64
2525
skippedBytes int64
26-
started int32
2726
estimatedFiles int32
28-
startTime time.Time
29-
finished chan interface{}
27+
paused uint32
3028
logger *progressLogger
3129
fileIndex map[string]int64 // Maps a file name to its transfer number
3230
fileIndexMutex *sync.Mutex
3331
dryRun bool
32+
updates chan *log.Update
3433
}
3534

3635
type env interface {
@@ -91,10 +90,9 @@ func WithOSEnv(os env) meterOption {
9190
func NewMeter(options ...meterOption) *ProgressMeter {
9291
m := &ProgressMeter{
9392
logger: &progressLogger{},
94-
startTime: time.Now(),
9593
fileIndex: make(map[string]int64),
9694
fileIndexMutex: &sync.Mutex{},
97-
finished: make(chan interface{}),
95+
updates: make(chan *log.Update),
9896
}
9997

10098
for _, opt := range options {
@@ -106,29 +104,27 @@ func NewMeter(options ...meterOption) *ProgressMeter {
106104

107105
// Start begins sending status updates to the optional log file, and stdout.
108106
func (p *ProgressMeter) Start() {
109-
if atomic.CompareAndSwapInt32(&p.started, 0, 1) {
110-
go p.writer()
111-
}
107+
atomic.StoreUint32(&p.paused, 0)
112108
}
113109

114110
// Pause stops sending status updates temporarily, until Start() is called again.
115111
func (p *ProgressMeter) Pause() {
116-
if atomic.CompareAndSwapInt32(&p.started, 1, 0) {
117-
p.finished <- true
118-
}
112+
atomic.StoreUint32(&p.paused, 1)
119113
}
120114

121115
// Add tells the progress meter that a single file of the given size will
122116
// possibly be transferred. If a file doesn't need to be transferred for some
123117
// reason, be sure to call Skip(int64) with the same size.
124118
func (p *ProgressMeter) Add(size int64) {
119+
defer p.update()
125120
atomic.AddInt32(&p.estimatedFiles, 1)
126121
atomic.AddInt64(&p.estimatedBytes, size)
127122
}
128123

129124
// Skip tells the progress meter that a file of size `size` is being skipped
130125
// because the transfer is unnecessary.
131126
func (p *ProgressMeter) Skip(size int64) {
127+
defer p.update()
132128
atomic.AddInt64(&p.skippedFiles, 1)
133129
atomic.AddInt64(&p.skippedBytes, size)
134130
// Reduce bytes and files so progress easier to parse
@@ -139,6 +135,7 @@ func (p *ProgressMeter) Skip(size int64) {
139135
// StartTransfer tells the progress meter that a transferring file is being
140136
// added to the TransferQueue.
141137
func (p *ProgressMeter) StartTransfer(name string) {
138+
defer p.update()
142139
idx := atomic.AddInt64(&p.transferringFiles, 1)
143140
p.fileIndexMutex.Lock()
144141
p.fileIndex[name] = idx
@@ -147,12 +144,14 @@ func (p *ProgressMeter) StartTransfer(name string) {
147144

148145
// TransferBytes increments the number of bytes transferred
149146
func (p *ProgressMeter) TransferBytes(direction, name string, read, total int64, current int) {
147+
defer p.update()
150148
atomic.AddInt64(&p.currentBytes, int64(current))
151149
p.logBytes(direction, name, read, total)
152150
}
153151

154152
// FinishTransfer increments the finished transfer count
155153
func (p *ProgressMeter) FinishTransfer(name string) {
154+
defer p.update()
156155
atomic.AddInt64(&p.finishedFiles, 1)
157156
p.fileIndexMutex.Lock()
158157
delete(p.fileIndex, name)
@@ -161,102 +160,62 @@ func (p *ProgressMeter) FinishTransfer(name string) {
161160

162161
// Finish shuts down the ProgressMeter
163162
func (p *ProgressMeter) Finish() {
164-
close(p.finished)
165163
p.update()
166-
p.logger.Close()
167-
if !p.dryRun && p.estimatedBytes > 0 {
168-
fmt.Fprintf(os.Stdout, "\n")
169-
}
164+
close(p.updates)
170165
}
171166

172-
func (p *ProgressMeter) logBytes(direction, name string, read, total int64) {
173-
p.fileIndexMutex.Lock()
174-
idx := p.fileIndex[name]
175-
p.fileIndexMutex.Unlock()
176-
line := fmt.Sprintf("%s %d/%d %d/%d %s\n", direction, idx, p.estimatedFiles, read, total, name)
177-
if err := p.logger.Write([]byte(line)); err != nil {
178-
p.logger.Shutdown()
179-
}
167+
func (p *ProgressMeter) Updates() <-chan *log.Update {
168+
return p.updates
180169
}
181170

182-
func (p *ProgressMeter) writer() {
183-
p.update()
184-
for {
185-
select {
186-
case <-p.finished:
187-
return
188-
case <-time.After(time.Millisecond * 200):
189-
p.update()
190-
}
191-
}
171+
func (p *ProgressMeter) Throttled() bool {
172+
return true
192173
}
193174

194175
func (p *ProgressMeter) update() {
195-
if p.dryRun || (p.estimatedFiles == 0 && p.skippedFiles == 0) {
176+
if p.skipUpdate() {
196177
return
197178
}
198179

180+
p.updates <- &log.Update{
181+
S: p.str(),
182+
At: time.Now(),
183+
}
184+
}
185+
186+
func (p *ProgressMeter) skipUpdate() bool {
187+
return p.dryRun ||
188+
(p.estimatedFiles == 0 && p.skippedFiles == 0) ||
189+
atomic.LoadUint32(&p.paused) == 1
190+
}
191+
192+
func (p *ProgressMeter) str() string {
199193
// (%d of %d files, %d skipped) %f B / %f B, %f B skipped
200194
// skipped counts only show when > 0
201195

202-
out := fmt.Sprintf("\rGit LFS: (%d of %d files", p.finishedFiles, p.estimatedFiles)
196+
out := fmt.Sprintf("\rGit LFS: (%d of %d files",
197+
p.finishedFiles,
198+
p.estimatedFiles)
203199
if p.skippedFiles > 0 {
204200
out += fmt.Sprintf(", %d skipped", p.skippedFiles)
205201
}
206-
out += fmt.Sprintf(") %s / %s", formatBytes(p.currentBytes), formatBytes(p.estimatedBytes))
202+
out += fmt.Sprintf(") %s / %s",
203+
humanize.FormatBytes(uint64(p.currentBytes)),
204+
humanize.FormatBytes(uint64(p.estimatedBytes)))
207205
if p.skippedBytes > 0 {
208-
out += fmt.Sprintf(", %s skipped", formatBytes(p.skippedBytes))
209-
}
210-
211-
fmt.Fprintf(os.Stdout, pad(out))
212-
}
213-
214-
func formatBytes(i int64) string {
215-
switch {
216-
case i > 1099511627776:
217-
return fmt.Sprintf("%#0.2f TB", float64(i)/1099511627776)
218-
case i > 1073741824:
219-
return fmt.Sprintf("%#0.2f GB", float64(i)/1073741824)
220-
case i > 1048576:
221-
return fmt.Sprintf("%#0.2f MB", float64(i)/1048576)
222-
case i > 1024:
223-
return fmt.Sprintf("%#0.2f KB", float64(i)/1024)
224-
}
225-
226-
return fmt.Sprintf("%d B", i)
227-
}
228-
229-
const defaultWidth = 80
230-
231-
// pad pads the given message to occupy the entire maximum width of the terminal
232-
// LFS is attached to. In doing so, this safeguards subsequent prints of shorter
233-
// messages from leaving stray characters from the previous message on the
234-
// screen by writing over them with whitespace padding.
235-
func pad(msg string) string {
236-
width := defaultWidth
237-
size, err := ts.GetSize()
238-
if err == nil {
239-
// If `ts.GetSize()` was successful, set the width to the number
240-
// of columns present in the terminal LFS is attached to.
241-
// Otherwise, fall-back to `defaultWidth`.
242-
width = size.Col()
206+
out += fmt.Sprintf(", %s skipped",
207+
humanize.FormatBytes(uint64(p.skippedBytes)))
243208
}
244209

245-
// Pad the string with whitespace so that printing at the start of the
246-
// line removes all traces from the last print.removes all traces from
247-
// the last print.
248-
padding := strings.Repeat(" ", maxInt(0, width-len(msg)))
249-
250-
return msg + padding
210+
return out
251211
}
252212

253-
// maxInt returns the greater of two `int`s, "a", or "b". This function
254-
// originally comes from `github.com/git-lfs/git-lfs/tools#MaxInt`, but would
255-
// introduce an import cycle if depended on directly.
256-
func maxInt(a, b int) int {
257-
if a > b {
258-
return a
213+
func (p *ProgressMeter) logBytes(direction, name string, read, total int64) {
214+
p.fileIndexMutex.Lock()
215+
idx := p.fileIndex[name]
216+
p.fileIndexMutex.Unlock()
217+
line := fmt.Sprintf("%s %d/%d %d/%d %s\n", direction, idx, p.estimatedFiles, read, total, name)
218+
if err := p.logger.Write([]byte(line)); err != nil {
219+
p.logger.Shutdown()
259220
}
260-
261-
return b
262221
}

0 commit comments

Comments
 (0)