Skip to content

Commit 9b405a6

Browse files
committed
mirror_pull: parse 'git fetch --porcelain' instead of human readable output
Signed-off-by: Steven Noonan <[email protected]>
1 parent 0f66814 commit 9b405a6

File tree

1 file changed

+41
-97
lines changed

1 file changed

+41
-97
lines changed

services/mirror/mirror_pull.go

Lines changed: 41 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -81,113 +81,57 @@ type mirrorSyncResult struct {
8181
newCommitID string
8282
}
8383

84-
// parseRemoteUpdateOutput detects create, update and delete operations of references from upstream.
85-
// possible output example:
86-
/*
87-
// * [new tag] v0.1.8 -> v0.1.8
88-
// * [new branch] master -> origin/master
89-
// * [new ref] refs/pull/2/head -> refs/pull/2/head"
90-
// - [deleted] (none) -> origin/test // delete a branch
91-
// - [deleted] (none) -> 1 // delete a tag
92-
// 957a993..a87ba5f test -> origin/test
93-
// + f895a1e...957a993 test -> origin/test (forced update)
94-
*/
95-
// TODO: return whether it's a force update
96-
func parseRemoteUpdateOutput(output, remoteName string) []*mirrorSyncResult {
97-
results := make([]*mirrorSyncResult, 0, 3)
98-
lines := strings.Split(output, "\n")
99-
for i := range lines {
100-
// Make sure reference name is presented before continue
101-
idx := strings.Index(lines[i], "-> ")
102-
if idx == -1 {
84+
// parseFetchPorcelain parses `git fetch --porcelain` lines into mirrorSyncResult.
85+
// Lines look like: "<flag> <old> <new> <local-ref>"
86+
func parseFetchPorcelain(output string) []*mirrorSyncResult {
87+
results := make([]*mirrorSyncResult, 0, 8)
88+
89+
for _, line := range strings.Split(output, "\n") {
90+
if line == "" {
91+
continue
92+
}
93+
flag := line[0]
94+
fields := strings.Fields(line[1:]) // trim flag, split rest
95+
if len(fields) < 3 {
96+
log.Warn("parseFetchPorcelain: unexpected line %q", line)
10397
continue
10498
}
99+
oldOID, newOID, ref := fields[0], fields[1], fields[2]
105100

106-
refName := strings.TrimSpace(lines[i][idx+3:])
101+
// Normalize ref name
102+
var refFull git.RefName
103+
if strings.HasPrefix(ref, "refs/") {
104+
refFull = git.RefName(ref)
105+
} else {
106+
// fetch porcelain prints local tracking ref; treat non-refs/* as branches
107+
refFull = git.RefNameFromBranch(ref)
108+
}
107109

108-
switch {
109-
case strings.HasPrefix(lines[i], " * [new tag]"): // new tag
110-
results = append(results, &mirrorSyncResult{
111-
refName: git.RefNameFromTag(refName),
112-
oldCommitID: gitShortEmptySha,
113-
})
114-
case strings.HasPrefix(lines[i], " * [new branch]"): // new branch
115-
refName = strings.TrimPrefix(refName, remoteName+"/")
110+
switch flag {
111+
case '*': // new ref
116112
results = append(results, &mirrorSyncResult{
117-
refName: git.RefNameFromBranch(refName),
113+
refName: refFull,
118114
oldCommitID: gitShortEmptySha,
115+
newCommitID: newOID,
119116
})
120-
case strings.HasPrefix(lines[i], " * [new ref]"): // new reference
117+
case '-': // pruned (deleted)
121118
results = append(results, &mirrorSyncResult{
122-
refName: git.RefName(refName),
123-
oldCommitID: gitShortEmptySha,
124-
})
125-
case strings.HasPrefix(lines[i], " - "): // Delete reference
126-
isTag := !strings.HasPrefix(refName, remoteName+"/")
127-
var refFullName git.RefName
128-
if strings.HasPrefix(refName, "refs/") {
129-
refFullName = git.RefName(refName)
130-
} else if isTag {
131-
refFullName = git.RefNameFromTag(refName)
132-
} else {
133-
refFullName = git.RefNameFromBranch(strings.TrimPrefix(refName, remoteName+"/"))
134-
}
135-
results = append(results, &mirrorSyncResult{
136-
refName: refFullName,
119+
refName: refFull,
120+
oldCommitID: oldOID,
137121
newCommitID: gitShortEmptySha,
138122
})
139-
case strings.HasPrefix(lines[i], " + "): // Force update
140-
if idx := strings.Index(refName, " "); idx > -1 {
141-
refName = refName[:idx]
142-
}
143-
delimIdx := strings.Index(lines[i][3:], " ")
144-
if delimIdx == -1 {
145-
log.Error("SHA delimiter not found: %q", lines[i])
146-
continue
147-
}
148-
shas := strings.Split(lines[i][3:delimIdx+3], "...")
149-
if len(shas) != 2 {
150-
log.Error("Expect two SHAs but not what found: %q", lines[i])
151-
continue
152-
}
153-
var refFullName git.RefName
154-
if strings.HasPrefix(refName, "refs/") {
155-
refFullName = git.RefName(refName)
156-
} else {
157-
refFullName = git.RefNameFromBranch(strings.TrimPrefix(refName, remoteName+"/"))
158-
}
159-
160-
results = append(results, &mirrorSyncResult{
161-
refName: refFullName,
162-
oldCommitID: shas[0],
163-
newCommitID: shas[1],
164-
})
165-
case strings.HasPrefix(lines[i], " "): // New commits of a reference
166-
delimIdx := strings.Index(lines[i][3:], " ")
167-
if delimIdx == -1 {
168-
log.Error("SHA delimiter not found: %q", lines[i])
169-
continue
170-
}
171-
shas := strings.Split(lines[i][3:delimIdx+3], "..")
172-
if len(shas) != 2 {
173-
log.Error("Expect two SHAs but not what found: %q", lines[i])
174-
continue
175-
}
176-
var refFullName git.RefName
177-
if strings.HasPrefix(refName, "refs/") {
178-
refFullName = git.RefName(refName)
179-
} else {
180-
refFullName = git.RefNameFromBranch(strings.TrimPrefix(refName, remoteName+"/"))
181-
}
182-
123+
case '+', ' ', 't': // force, fast-forward, tag update
183124
results = append(results, &mirrorSyncResult{
184-
refName: refFullName,
185-
oldCommitID: shas[0],
186-
newCommitID: shas[1],
125+
refName: refFull,
126+
oldCommitID: oldOID,
127+
newCommitID: newOID,
187128
})
188-
129+
case '!': // failed fetch for this ref
130+
log.Error("fetch failed for %s (%s -> %s)", ref, oldOID, newOID)
131+
case '=': // up-to-date (only if --verbose); ignore
132+
continue
189133
default:
190-
log.Warn("parseRemoteUpdateOutput: unexpected update line %q", lines[i])
134+
log.Warn("parseFetchPorcelain: unknown flag %q on %q", flag, line)
191135
}
192136
}
193137
return results
@@ -260,7 +204,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
260204
if m.EnablePrune {
261205
cmd.AddArguments("--prune")
262206
}
263-
cmd.AddArguments("--tags").AddDynamicArguments(m.GetRemoteName())
207+
cmd.AddArguments("--tags", "--porcelain", "--no-progress").AddDynamicArguments(m.GetRemoteName())
264208

265209
remoteURL, remoteErr := gitrepo.GitRemoteGetURL(ctx, m.Repo, m.GetRemoteName())
266210
if remoteErr != nil {
@@ -324,7 +268,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
324268
return nil, false
325269
}
326270
}
327-
output := stderrBuilder.String()
271+
output := stdoutBuilder.String()
328272

329273
if err := git.WriteCommitGraph(ctx, repoPath); err != nil {
330274
log.Error("SyncMirrors [repo: %-v]: %v", m.Repo, err)
@@ -426,7 +370,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
426370
}
427371

428372
m.UpdatedUnix = timeutil.TimeStampNow()
429-
return parseRemoteUpdateOutput(output, m.GetRemoteName()), true
373+
return parseFetchPorcelain(output), true
430374
}
431375

432376
func getRepoPullMirrorLockKey(repoID int64) string {

0 commit comments

Comments
 (0)