1
2
3
4
5 package codehost
6
7 import (
8 "archive/zip"
9 "context"
10 "encoding/xml"
11 "fmt"
12 "io"
13 "os"
14 "path"
15 "path/filepath"
16 "strconv"
17 "time"
18
19 "cmd/go/internal/base"
20 )
21
22 func svnParseStat(rev, out string) (*RevInfo, error) {
23 var log struct {
24 Logentry struct {
25 Revision int64 `xml:"revision,attr"`
26 Date string `xml:"date"`
27 } `xml:"logentry"`
28 }
29 if err := xml.Unmarshal([]byte(out), &log); err != nil {
30 return nil, vcsErrorf("unexpected response from svn log --xml: %v\n%s", err, out)
31 }
32
33 t, err := time.Parse(time.RFC3339, log.Logentry.Date)
34 if err != nil {
35 return nil, vcsErrorf("unexpected response from svn log --xml: %v\n%s", err, out)
36 }
37
38 info := &RevInfo{
39 Name: strconv.FormatInt(log.Logentry.Revision, 10),
40 Short: fmt.Sprintf("%012d", log.Logentry.Revision),
41 Time: t.UTC(),
42 Version: rev,
43 }
44 return info, nil
45 }
46
47 func svnReadZip(ctx context.Context, dst io.Writer, workDir, rev, subdir, remote string) (err error) {
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 remotePath := remote
67 if subdir != "" {
68 remotePath += "/" + subdir
69 }
70
71 release, err := base.AcquireNet()
72 if err != nil {
73 return err
74 }
75 out, err := Run(ctx, workDir, []string{
76 "svn", "list",
77 "--non-interactive",
78 "--xml",
79 "--incremental",
80 "--recursive",
81 "--revision", rev,
82 "--", remotePath,
83 })
84 release()
85 if err != nil {
86 return err
87 }
88
89 type listEntry struct {
90 Kind string `xml:"kind,attr"`
91 Name string `xml:"name"`
92 Size int64 `xml:"size"`
93 }
94 var list struct {
95 Entries []listEntry `xml:"entry"`
96 }
97 if err := xml.Unmarshal(out, &list); err != nil {
98 return vcsErrorf("unexpected response from svn list --xml: %v\n%s", err, out)
99 }
100
101 exportDir := filepath.Join(workDir, "export")
102
103 if err := os.RemoveAll(exportDir); err != nil {
104 return err
105 }
106 defer os.RemoveAll(exportDir)
107
108 release, err = base.AcquireNet()
109 if err != nil {
110 return err
111 }
112 _, err = Run(ctx, workDir, []string{
113 "svn", "export",
114 "--non-interactive",
115 "--quiet",
116
117
118 "--native-eol", "LF",
119 "--ignore-externals",
120 "--ignore-keywords",
121
122 "--revision", rev,
123 "--", remotePath,
124 exportDir,
125 })
126 release()
127 if err != nil {
128 return err
129 }
130
131
132
133
134
135
136 basePath := path.Join(path.Base(remote), subdir)
137
138 zw := zip.NewWriter(dst)
139 for _, e := range list.Entries {
140 if e.Kind != "file" {
141 continue
142 }
143
144 zf, err := zw.Create(path.Join(basePath, e.Name))
145 if err != nil {
146 return err
147 }
148
149 f, err := os.Open(filepath.Join(exportDir, e.Name))
150 if err != nil {
151 if os.IsNotExist(err) {
152 return vcsErrorf("file reported by 'svn list', but not written by 'svn export': %s", e.Name)
153 }
154 return fmt.Errorf("error opening file created by 'svn export': %v", err)
155 }
156
157 n, err := io.Copy(zf, f)
158 f.Close()
159 if err != nil {
160 return err
161 }
162 if n != e.Size {
163 return vcsErrorf("file size differs between 'svn list' and 'svn export': file %s listed as %v bytes, but exported as %v bytes", e.Name, e.Size, n)
164 }
165 }
166
167 return zw.Close()
168 }
169
View as plain text