Skip to content

Commit 25230d0

Browse files
committed
[annex] sync: Check stderr if even no failure
git annex sync commands can fail without returning an error code. Previously we checked for merge errors that exhibited this behaviour, but it can also happen in other cases (permission denied). For all functions that use annex sync internally (AnnexPull, AnnexPush, AnnexSync), first check if stderr printed any known error messages, then check for merge conflicts and finally check if the return code was not 0.
1 parent 3f999ff commit 25230d0

File tree

1 file changed

+73
-37
lines changed

1 file changed

+73
-37
lines changed

git/annex.go

Lines changed: 73 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -146,47 +146,61 @@ func AnnexInit(description string) error {
146146
return nil
147147
}
148148

149+
// parseSyncErrors should be used by all annex sync commands to check the
150+
// output for common error messages and return the appropriate gin message.
151+
func parseSyncErrors(messages string) error {
152+
if strings.Contains(messages, "Permission denied") {
153+
return fmt.Errorf("permission denied")
154+
} else if strings.Contains(messages, "Host key verification failed") {
155+
// Bad host key configured
156+
return fmt.Errorf("server key does not match known host key")
157+
}
158+
return nil
159+
}
160+
149161
// AnnexPull downloads all annexed files. Optionally also downloads all file content.
150162
// (git annex sync --no-push [--content])
151163
func AnnexPull(remote string) error {
152164
args := []string{"sync", "--verbose", "--no-push", "--no-commit", remote}
153165
cmd := AnnexCommand(args...)
154166
stdout, stderr, err := cmd.OutputError()
155-
cmd.Wait()
156167
sstdout := string(stdout)
157168
sstderr := string(stderr)
158-
if err != nil {
159-
log.Write("Error during AnnexPull.")
169+
170+
// some errors don't return with an error status, so we need to check
171+
// stderr for common error strings
172+
if err := parseSyncErrors(sstderr); err != nil {
173+
return fmt.Errorf("download failed: %v", err)
174+
}
175+
176+
// some conflicts are resolved automatically and don't produce an error in some combinations
177+
if err := checkMergeErrors(sstdout, sstderr); err != nil {
178+
mergeAbort() // abort a potential failed merge attempt
179+
return fmt.Errorf("download failed: %v", err)
180+
}
181+
182+
if err != nil { // command actually failed
183+
log.Write("Error during AnnexPull")
160184
log.Write("[Error]: %v", err)
161185
logstd(stdout, stderr)
162-
mergeAbort() // abort a potential failed merge attempt
163-
// TODO: Use giterror
164-
if strings.Contains(sstderr, "Permission denied") {
165-
return fmt.Errorf("download failed: permission denied")
166-
} else if strings.Contains(sstderr, "Host key verification failed") {
167-
// Bad host key configured
168-
return fmt.Errorf("download failed: server key does not match known host key")
169-
} else {
170-
err = checkMergeErrors(sstdout, sstderr)
171-
if err == nil {
172-
err = fmt.Errorf("failed")
173-
}
174-
return err
175-
}
186+
mergeAbort() // abort a potential failed merge attempt (that wasn't caught earlier)
187+
188+
// since we don't know what the error was, show the internal annex sync
189+
// error to the user
190+
return fmt.Errorf("download failed: %s", sstderr)
176191
}
177192

178-
// some conflicts are resolved automatically and don't produce an error in some combinations
179-
return checkMergeErrors(sstdout, sstderr)
193+
return nil
180194
}
181195

182196
func checkMergeErrors(stdout, stderr string) error {
183197
messages := strings.ToLower(stdout + stderr)
184198
if strings.Contains(messages, "would be overwritten by merge") {
185199
// Untracked local file conflicts with file being pulled
186-
return fmt.Errorf("download failed: local modified or untracked files would be overwritten by download:\n %s", strings.Join(parseFilesOverwrite(messages), ", "))
200+
return fmt.Errorf("local modified or untracked files would be overwritten by download:\n %s", strings.Join(parseFilesOverwrite(messages), ", "))
187201
} else if strings.Contains(messages, "unresolved conflict") {
188202
// Merge conflict in git files
189-
return fmt.Errorf("download failed: files changed locally and remotely and cannot be automatically merged (merge conflict):\n %s", strings.Join(parseFilesConflict(messages), ", "))
203+
return fmt.Errorf("files changed locally and remotely and cannot be automatically merged (merge conflict):\n %s", strings.Join(parseFilesConflict(messages), ", "))
190204
// abort merge
191205
} else if strings.Contains(messages, "merge conflict was automatically resolved") {
192206
// Merge conflict in annex files (automatically resolved by keeping both copies)
@@ -206,12 +220,30 @@ func AnnexSync(content bool) error {
206220
}
207221
cmd := AnnexCommand(cmdargs...)
208222
stdout, stderr, err := cmd.OutputError()
209-
cmd.Wait()
210-
if err != nil {
211-
log.Write("Error during AnnexSync.")
223+
sstdout := string(stdout)
224+
sstderr := string(stderr)
225+
226+
// some errors don't return with an error status, so we need to check
227+
// stderr for common error strings
228+
if err := parseSyncErrors(sstderr); err != nil {
229+
return fmt.Errorf("sync failed: %v", err)
230+
}
231+
232+
// some conflicts are resolved automatically and don't produce an error in some combinations
233+
if err := checkMergeErrors(sstdout, sstderr); err != nil {
234+
mergeAbort() // abort a potential failed merge attempt
235+
return fmt.Errorf("sync failed: %v", err)
236+
}
237+
238+
if err != nil { // command actually failed
239+
log.Write("Error during AnnexSync")
212240
log.Write("[Error]: %v", err)
213241
logstd(stdout, stderr)
214-
return fmt.Errorf(string(stderr))
242+
mergeAbort() // abort a potential failed merge attempt (that wasn't caught earlier)
243+
244+
// since we don't know what the error was, show the internal annex sync
245+
// error to the user
246+
return fmt.Errorf("sync failed: %s", sstderr)
215247
}
216248
return nil
217249
}
@@ -267,20 +299,24 @@ func AnnexPush(paths []string, remote string, pushchan chan<- RepoFileStatus) {
267299
defer close(pushchan)
268300
cmd := AnnexCommand("sync", "--verbose", "--no-pull", "--no-commit", remote) // NEVER commit changes when doing annex-sync
269301
stdout, stderr, err := cmd.OutputError()
270-
if err != nil {
271-
log.Write("Error during AnnexPush (sync --no-pull)")
302+
sstderr := string(stderr)
303+
304+
// some errors don't return with an error status, so we need to check
305+
// stderr for common error strings
306+
if err := parseSyncErrors(sstderr); err != nil {
307+
pushchan <- RepoFileStatus{Err: fmt.Errorf("upload failed: %v", err)}
308+
return
309+
}
310+
311+
if err != nil { // command actually failed
312+
log.Write("Error during AnnexPush")
272313
log.Write("[Error]: %v", err)
273314
logstd(stdout, stderr)
274-
errmsg := "failed"
275-
sstderr := string(stderr)
276-
if strings.Contains(sstderr, "Permission denied") {
277-
errmsg = "upload failed: permission denied"
278-
} else if strings.Contains(sstderr, "Host key verification failed") {
279-
errmsg = "upload failed: server key does not match known host key"
280-
} else if strings.Contains(sstderr, "rejected") {
281-
errmsg = "upload failed: changes were made on the server that have not been downloaded; run 'gin download' to update local copies"
282-
}
283-
pushchan <- RepoFileStatus{Err: fmt.Errorf(errmsg)}
315+
mergeAbort() // abort a potential failed merge attempt (that wasn't caught earlier)
316+
317+
// since we don't know what the error was, show the internal annex sync
318+
// error to the user
319+
pushchan <- RepoFileStatus{Err: fmt.Errorf(sstderr)}
284320
return
285321
}
286322

0 commit comments

Comments
 (0)