fork of go-git with some jj specific features
1package git
2
3import (
4 "bytes"
5 "context"
6 "errors"
7 "io"
8 "io/ioutil"
9 "os"
10 "runtime"
11 "time"
12
13 "github.com/go-git/go-git/v5/config"
14 "github.com/go-git/go-git/v5/plumbing"
15 "github.com/go-git/go-git/v5/plumbing/cache"
16 "github.com/go-git/go-git/v5/plumbing/protocol/packp"
17 "github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"
18 "github.com/go-git/go-git/v5/plumbing/storer"
19 "github.com/go-git/go-git/v5/storage"
20 "github.com/go-git/go-git/v5/storage/filesystem"
21 "github.com/go-git/go-git/v5/storage/memory"
22
23 "github.com/go-git/go-billy/v5/osfs"
24 fixtures "github.com/go-git/go-git-fixtures/v4"
25 . "gopkg.in/check.v1"
26)
27
28type RemoteSuite struct {
29 BaseSuite
30}
31
32var _ = Suite(&RemoteSuite{})
33
34func (s *RemoteSuite) TestFetchInvalidEndpoint(c *C) {
35 r := NewRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"http://\\"}})
36 err := r.Fetch(&FetchOptions{RemoteName: "foo"})
37 c.Assert(err, ErrorMatches, ".*invalid character.*")
38}
39
40func (s *RemoteSuite) TestFetchNonExistentEndpoint(c *C) {
41 r := NewRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"ssh://non-existent/foo.git"}})
42 err := r.Fetch(&FetchOptions{})
43 c.Assert(err, NotNil)
44}
45
46func (s *RemoteSuite) TestFetchInvalidSchemaEndpoint(c *C) {
47 r := NewRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"qux://foo"}})
48 err := r.Fetch(&FetchOptions{})
49 c.Assert(err, ErrorMatches, ".*unsupported scheme.*")
50}
51
52func (s *RemoteSuite) TestFetchInvalidFetchOptions(c *C) {
53 r := NewRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"qux://foo"}})
54 invalid := config.RefSpec("^*$ñ")
55 err := r.Fetch(&FetchOptions{RefSpecs: []config.RefSpec{invalid}})
56 c.Assert(err, Equals, config.ErrRefSpecMalformedSeparator)
57}
58
59func (s *RemoteSuite) TestFetchWildcard(c *C) {
60 r := NewRemote(memory.NewStorage(), &config.RemoteConfig{
61 URLs: []string{s.GetBasicLocalRepositoryURL()},
62 })
63
64 s.testFetch(c, r, &FetchOptions{
65 RefSpecs: []config.RefSpec{
66 config.RefSpec("+refs/heads/*:refs/remotes/origin/*"),
67 },
68 }, []*plumbing.Reference{
69 plumbing.NewReferenceFromStrings("refs/remotes/origin/master", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5"),
70 plumbing.NewReferenceFromStrings("refs/remotes/origin/branch", "e8d3ffab552895c19b9fcf7aa264d277cde33881"),
71 plumbing.NewReferenceFromStrings("refs/tags/v1.0.0", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5"),
72 })
73}
74
75func (s *RemoteSuite) TestFetchExactSHA1(c *C) {
76 r := NewRemote(memory.NewStorage(), &config.RemoteConfig{
77 URLs: []string{"https://github.com/git-fixtures/basic.git"},
78 })
79
80 s.testFetch(c, r, &FetchOptions{
81 RefSpecs: []config.RefSpec{
82 config.RefSpec("35e85108805c84807bc66a02d91535e1e24b38b9:refs/heads/foo"),
83 },
84 }, []*plumbing.Reference{
85 plumbing.NewReferenceFromStrings("refs/heads/foo", "35e85108805c84807bc66a02d91535e1e24b38b9"),
86 })
87}
88
89func (s *RemoteSuite) TestFetchExactSHA1_NotSoported(c *C) {
90 r := NewRemote(memory.NewStorage(), &config.RemoteConfig{
91 URLs: []string{s.GetBasicLocalRepositoryURL()},
92 })
93
94 err := r.Fetch(&FetchOptions{
95 RefSpecs: []config.RefSpec{
96 config.RefSpec("35e85108805c84807bc66a02d91535e1e24b38b9:refs/heads/foo"),
97 },
98 })
99
100 c.Assert(err, Equals, ErrExactSHA1NotSupported)
101
102}
103
104func (s *RemoteSuite) TestFetchWildcardTags(c *C) {
105 r := NewRemote(memory.NewStorage(), &config.RemoteConfig{
106 URLs: []string{s.GetLocalRepositoryURL(fixtures.ByTag("tags").One())},
107 })
108
109 s.testFetch(c, r, &FetchOptions{
110 RefSpecs: []config.RefSpec{
111 config.RefSpec("+refs/heads/*:refs/remotes/origin/*"),
112 },
113 }, []*plumbing.Reference{
114 plumbing.NewReferenceFromStrings("refs/remotes/origin/master", "f7b877701fbf855b44c0a9e86f3fdce2c298b07f"),
115 plumbing.NewReferenceFromStrings("refs/tags/annotated-tag", "b742a2a9fa0afcfa9a6fad080980fbc26b007c69"),
116 plumbing.NewReferenceFromStrings("refs/tags/tree-tag", "152175bf7e5580299fa1f0ba41ef6474cc043b70"),
117 plumbing.NewReferenceFromStrings("refs/tags/commit-tag", "ad7897c0fb8e7d9a9ba41fa66072cf06095a6cfc"),
118 plumbing.NewReferenceFromStrings("refs/tags/blob-tag", "fe6cb94756faa81e5ed9240f9191b833db5f40ae"),
119 plumbing.NewReferenceFromStrings("refs/tags/lightweight-tag", "f7b877701fbf855b44c0a9e86f3fdce2c298b07f"),
120 })
121}
122
123func (s *RemoteSuite) TestFetch(c *C) {
124 r := NewRemote(memory.NewStorage(), &config.RemoteConfig{
125 URLs: []string{s.GetLocalRepositoryURL(fixtures.ByTag("tags").One())},
126 })
127
128 s.testFetch(c, r, &FetchOptions{
129 RefSpecs: []config.RefSpec{
130 config.RefSpec("+refs/heads/master:refs/remotes/origin/master"),
131 },
132 }, []*plumbing.Reference{
133 plumbing.NewReferenceFromStrings("refs/remotes/origin/master", "f7b877701fbf855b44c0a9e86f3fdce2c298b07f"),
134 })
135}
136
137func (s *RemoteSuite) TestFetchNonExistantReference(c *C) {
138 r := NewRemote(memory.NewStorage(), &config.RemoteConfig{
139 URLs: []string{s.GetLocalRepositoryURL(fixtures.ByTag("tags").One())},
140 })
141
142 err := r.Fetch(&FetchOptions{
143 RefSpecs: []config.RefSpec{
144 config.RefSpec("+refs/heads/foo:refs/remotes/origin/foo"),
145 },
146 })
147
148 c.Assert(err, ErrorMatches, "couldn't find remote ref.*")
149 c.Assert(errors.Is(err, NoMatchingRefSpecError{}), Equals, true)
150}
151
152func (s *RemoteSuite) TestFetchContext(c *C) {
153 r := NewRemote(memory.NewStorage(), &config.RemoteConfig{
154 URLs: []string{s.GetLocalRepositoryURL(fixtures.ByTag("tags").One())},
155 })
156
157 ctx, cancel := context.WithCancel(context.Background())
158 cancel()
159
160 err := r.FetchContext(ctx, &FetchOptions{
161 RefSpecs: []config.RefSpec{
162 config.RefSpec("+refs/heads/master:refs/remotes/origin/master"),
163 },
164 })
165 c.Assert(err, NotNil)
166}
167
168func (s *RemoteSuite) TestFetchWithAllTags(c *C) {
169 r := NewRemote(memory.NewStorage(), &config.RemoteConfig{
170 URLs: []string{s.GetLocalRepositoryURL(fixtures.ByTag("tags").One())},
171 })
172
173 s.testFetch(c, r, &FetchOptions{
174 Tags: AllTags,
175 RefSpecs: []config.RefSpec{
176 config.RefSpec("+refs/heads/master:refs/remotes/origin/master"),
177 },
178 }, []*plumbing.Reference{
179 plumbing.NewReferenceFromStrings("refs/remotes/origin/master", "f7b877701fbf855b44c0a9e86f3fdce2c298b07f"),
180 plumbing.NewReferenceFromStrings("refs/tags/annotated-tag", "b742a2a9fa0afcfa9a6fad080980fbc26b007c69"),
181 plumbing.NewReferenceFromStrings("refs/tags/tree-tag", "152175bf7e5580299fa1f0ba41ef6474cc043b70"),
182 plumbing.NewReferenceFromStrings("refs/tags/commit-tag", "ad7897c0fb8e7d9a9ba41fa66072cf06095a6cfc"),
183 plumbing.NewReferenceFromStrings("refs/tags/blob-tag", "fe6cb94756faa81e5ed9240f9191b833db5f40ae"),
184 plumbing.NewReferenceFromStrings("refs/tags/lightweight-tag", "f7b877701fbf855b44c0a9e86f3fdce2c298b07f"),
185 })
186}
187
188func (s *RemoteSuite) TestFetchWithNoTags(c *C) {
189 r := NewRemote(memory.NewStorage(), &config.RemoteConfig{
190 URLs: []string{s.GetLocalRepositoryURL(fixtures.ByTag("tags").One())},
191 })
192
193 s.testFetch(c, r, &FetchOptions{
194 Tags: NoTags,
195 RefSpecs: []config.RefSpec{
196 config.RefSpec("+refs/heads/*:refs/remotes/origin/*"),
197 },
198 }, []*plumbing.Reference{
199 plumbing.NewReferenceFromStrings("refs/remotes/origin/master", "f7b877701fbf855b44c0a9e86f3fdce2c298b07f"),
200 })
201
202}
203
204func (s *RemoteSuite) TestFetchWithDepth(c *C) {
205 r := NewRemote(memory.NewStorage(), &config.RemoteConfig{
206 URLs: []string{s.GetBasicLocalRepositoryURL()},
207 })
208
209 s.testFetch(c, r, &FetchOptions{
210 Depth: 1,
211 RefSpecs: []config.RefSpec{
212 config.RefSpec("+refs/heads/*:refs/remotes/origin/*"),
213 },
214 }, []*plumbing.Reference{
215 plumbing.NewReferenceFromStrings("refs/remotes/origin/master", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5"),
216 plumbing.NewReferenceFromStrings("refs/remotes/origin/branch", "e8d3ffab552895c19b9fcf7aa264d277cde33881"),
217 plumbing.NewReferenceFromStrings("refs/tags/v1.0.0", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5"),
218 })
219
220 c.Assert(r.s.(*memory.Storage).Objects, HasLen, 18)
221}
222
223func (s *RemoteSuite) testFetch(c *C, r *Remote, o *FetchOptions, expected []*plumbing.Reference) {
224 err := r.Fetch(o)
225 c.Assert(err, IsNil)
226
227 var refs int
228 l, err := r.s.IterReferences()
229 c.Assert(err, IsNil)
230 l.ForEach(func(r *plumbing.Reference) error { refs++; return nil })
231
232 c.Assert(refs, Equals, len(expected))
233
234 for _, exp := range expected {
235 r, err := r.s.Reference(exp.Name())
236 c.Assert(err, IsNil)
237 c.Assert(exp.String(), Equals, r.String())
238 }
239}
240
241func (s *RemoteSuite) TestFetchWithProgress(c *C) {
242 url := s.GetBasicLocalRepositoryURL()
243 sto := memory.NewStorage()
244 buf := bytes.NewBuffer(nil)
245
246 r := NewRemote(sto, &config.RemoteConfig{Name: "foo", URLs: []string{url}})
247
248 refspec := config.RefSpec("+refs/heads/*:refs/remotes/origin/*")
249 err := r.Fetch(&FetchOptions{
250 RefSpecs: []config.RefSpec{refspec},
251 Progress: buf,
252 })
253
254 c.Assert(err, IsNil)
255 c.Assert(sto.Objects, HasLen, 31)
256
257 c.Assert(buf.Len(), Not(Equals), 0)
258}
259
260type mockPackfileWriter struct {
261 storage.Storer
262 PackfileWriterCalled bool
263}
264
265func (m *mockPackfileWriter) PackfileWriter() (io.WriteCloser, error) {
266 m.PackfileWriterCalled = true
267 return m.Storer.(storer.PackfileWriter).PackfileWriter()
268}
269
270func (s *RemoteSuite) TestFetchWithPackfileWriter(c *C) {
271 dir, err := ioutil.TempDir("", "fetch")
272 c.Assert(err, IsNil)
273
274 defer os.RemoveAll(dir) // clean up
275
276 fss := filesystem.NewStorage(osfs.New(dir), cache.NewObjectLRUDefault())
277 c.Assert(err, IsNil)
278
279 mock := &mockPackfileWriter{Storer: fss}
280
281 url := s.GetBasicLocalRepositoryURL()
282 r := NewRemote(mock, &config.RemoteConfig{Name: "foo", URLs: []string{url}})
283
284 refspec := config.RefSpec("+refs/heads/*:refs/remotes/origin/*")
285 err = r.Fetch(&FetchOptions{
286 RefSpecs: []config.RefSpec{refspec},
287 })
288
289 c.Assert(err, IsNil)
290
291 var count int
292 iter, err := mock.IterEncodedObjects(plumbing.AnyObject)
293 c.Assert(err, IsNil)
294
295 iter.ForEach(func(plumbing.EncodedObject) error {
296 count++
297 return nil
298 })
299
300 c.Assert(count, Equals, 31)
301 c.Assert(mock.PackfileWriterCalled, Equals, true)
302}
303
304func (s *RemoteSuite) TestFetchNoErrAlreadyUpToDate(c *C) {
305 url := s.GetBasicLocalRepositoryURL()
306 s.doTestFetchNoErrAlreadyUpToDate(c, url)
307}
308
309func (s *RemoteSuite) TestFetchNoErrAlreadyUpToDateButStillUpdateLocalRemoteRefs(c *C) {
310 r := NewRemote(memory.NewStorage(), &config.RemoteConfig{
311 URLs: []string{s.GetBasicLocalRepositoryURL()},
312 })
313
314 o := &FetchOptions{
315 RefSpecs: []config.RefSpec{
316 config.RefSpec("+refs/heads/*:refs/remotes/origin/*"),
317 },
318 }
319
320 err := r.Fetch(o)
321 c.Assert(err, IsNil)
322
323 // Simulate an out of date remote ref even though we have the new commit locally
324 r.s.SetReference(plumbing.NewReferenceFromStrings(
325 "refs/remotes/origin/master", "918c48b83bd081e863dbe1b80f8998f058cd8294",
326 ))
327
328 err = r.Fetch(o)
329 c.Assert(err, IsNil)
330
331 exp := plumbing.NewReferenceFromStrings(
332 "refs/remotes/origin/master", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5",
333 )
334
335 ref, err := r.s.Reference("refs/remotes/origin/master")
336 c.Assert(err, IsNil)
337 c.Assert(exp.String(), Equals, ref.String())
338}
339
340func (s *RemoteSuite) TestFetchNoErrAlreadyUpToDateWithNonCommitObjects(c *C) {
341 fixture := fixtures.ByTag("tags").One()
342 url := s.GetLocalRepositoryURL(fixture)
343 s.doTestFetchNoErrAlreadyUpToDate(c, url)
344}
345
346func (s *RemoteSuite) doTestFetchNoErrAlreadyUpToDate(c *C, url string) {
347 r := NewRemote(memory.NewStorage(), &config.RemoteConfig{URLs: []string{url}})
348
349 o := &FetchOptions{
350 RefSpecs: []config.RefSpec{
351 config.RefSpec("+refs/heads/*:refs/remotes/origin/*"),
352 },
353 }
354
355 err := r.Fetch(o)
356 c.Assert(err, IsNil)
357 err = r.Fetch(o)
358 c.Assert(err, Equals, NoErrAlreadyUpToDate)
359}
360
361func (s *RemoteSuite) testFetchFastForward(c *C, sto storage.Storer) {
362 r := NewRemote(sto, &config.RemoteConfig{
363 URLs: []string{s.GetBasicLocalRepositoryURL()},
364 })
365
366 s.testFetch(c, r, &FetchOptions{
367 RefSpecs: []config.RefSpec{
368 config.RefSpec("+refs/heads/master:refs/heads/master"),
369 },
370 }, []*plumbing.Reference{
371 plumbing.NewReferenceFromStrings("refs/heads/master", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5"),
372 })
373
374 // First make sure that we error correctly when a force is required.
375 err := r.Fetch(&FetchOptions{
376 RefSpecs: []config.RefSpec{
377 config.RefSpec("refs/heads/branch:refs/heads/master"),
378 },
379 })
380 c.Assert(err, Equals, ErrForceNeeded)
381
382 // And that forcing it fixes the problem.
383 err = r.Fetch(&FetchOptions{
384 RefSpecs: []config.RefSpec{
385 config.RefSpec("+refs/heads/branch:refs/heads/master"),
386 },
387 })
388 c.Assert(err, IsNil)
389
390 // Now test that a fast-forward, non-force fetch works.
391 r.s.SetReference(plumbing.NewReferenceFromStrings(
392 "refs/heads/master", "918c48b83bd081e863dbe1b80f8998f058cd8294",
393 ))
394 s.testFetch(c, r, &FetchOptions{
395 RefSpecs: []config.RefSpec{
396 config.RefSpec("refs/heads/master:refs/heads/master"),
397 },
398 }, []*plumbing.Reference{
399 plumbing.NewReferenceFromStrings("refs/heads/master", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5"),
400 })
401}
402
403func (s *RemoteSuite) TestFetchFastForwardMem(c *C) {
404 s.testFetchFastForward(c, memory.NewStorage())
405}
406
407func (s *RemoteSuite) TestFetchFastForwardFS(c *C) {
408 dir, err := ioutil.TempDir("", "fetch")
409 c.Assert(err, IsNil)
410
411 defer os.RemoveAll(dir) // clean up
412
413 fss := filesystem.NewStorage(osfs.New(dir), cache.NewObjectLRUDefault())
414
415 // This exercises `storage.filesystem.Storage.CheckAndSetReference()`.
416 s.testFetchFastForward(c, fss)
417}
418
419func (s *RemoteSuite) TestString(c *C) {
420 r := NewRemote(nil, &config.RemoteConfig{
421 Name: "foo",
422 URLs: []string{"https://github.com/git-fixtures/basic.git"},
423 })
424
425 c.Assert(r.String(), Equals, ""+
426 "foo\thttps://github.com/git-fixtures/basic.git (fetch)\n"+
427 "foo\thttps://github.com/git-fixtures/basic.git (push)",
428 )
429}
430
431func (s *RemoteSuite) TestPushToEmptyRepository(c *C) {
432 url := c.MkDir()
433 server, err := PlainInit(url, true)
434 c.Assert(err, IsNil)
435
436 srcFs := fixtures.Basic().One().DotGit()
437 sto := filesystem.NewStorage(srcFs, cache.NewObjectLRUDefault())
438
439 r := NewRemote(sto, &config.RemoteConfig{
440 Name: DefaultRemoteName,
441 URLs: []string{url},
442 })
443
444 rs := config.RefSpec("refs/heads/*:refs/heads/*")
445 err = r.Push(&PushOptions{
446 RefSpecs: []config.RefSpec{rs},
447 })
448 c.Assert(err, IsNil)
449
450 iter, err := r.s.IterReferences()
451 c.Assert(err, IsNil)
452
453 expected := make(map[string]string)
454 iter.ForEach(func(ref *plumbing.Reference) error {
455 if !ref.Name().IsBranch() {
456 return nil
457 }
458
459 expected[ref.Name().String()] = ref.Hash().String()
460 return nil
461 })
462 c.Assert(err, IsNil)
463
464 AssertReferences(c, server, expected)
465
466}
467
468func (s *RemoteSuite) TestPushContext(c *C) {
469 url := c.MkDir()
470 _, err := PlainInit(url, true)
471 c.Assert(err, IsNil)
472
473 fs := fixtures.ByURL("https://github.com/git-fixtures/tags.git").One().DotGit()
474 sto := filesystem.NewStorage(fs, cache.NewObjectLRUDefault())
475
476 r := NewRemote(sto, &config.RemoteConfig{
477 Name: DefaultRemoteName,
478 URLs: []string{url},
479 })
480
481 ctx, cancel := context.WithCancel(context.Background())
482 cancel()
483
484 numGoroutines := runtime.NumGoroutine()
485
486 err = r.PushContext(ctx, &PushOptions{
487 RefSpecs: []config.RefSpec{"refs/tags/*:refs/tags/*"},
488 })
489 c.Assert(err, NotNil)
490
491 // let the goroutine from pushHashes finish and check that the number of
492 // goroutines is the same as before
493 time.Sleep(100 * time.Millisecond)
494 c.Assert(runtime.NumGoroutine(), Equals, numGoroutines)
495}
496
497func (s *RemoteSuite) TestPushTags(c *C) {
498 url := c.MkDir()
499 server, err := PlainInit(url, true)
500 c.Assert(err, IsNil)
501
502 fs := fixtures.ByURL("https://github.com/git-fixtures/tags.git").One().DotGit()
503 sto := filesystem.NewStorage(fs, cache.NewObjectLRUDefault())
504
505 r := NewRemote(sto, &config.RemoteConfig{
506 Name: DefaultRemoteName,
507 URLs: []string{url},
508 })
509
510 err = r.Push(&PushOptions{
511 RefSpecs: []config.RefSpec{"refs/tags/*:refs/tags/*"},
512 })
513 c.Assert(err, IsNil)
514
515 AssertReferences(c, server, map[string]string{
516 "refs/tags/lightweight-tag": "f7b877701fbf855b44c0a9e86f3fdce2c298b07f",
517 "refs/tags/annotated-tag": "b742a2a9fa0afcfa9a6fad080980fbc26b007c69",
518 "refs/tags/commit-tag": "ad7897c0fb8e7d9a9ba41fa66072cf06095a6cfc",
519 "refs/tags/blob-tag": "fe6cb94756faa81e5ed9240f9191b833db5f40ae",
520 "refs/tags/tree-tag": "152175bf7e5580299fa1f0ba41ef6474cc043b70",
521 })
522}
523
524func (s *RemoteSuite) TestPushNoErrAlreadyUpToDate(c *C) {
525 fs := fixtures.Basic().One().DotGit()
526 sto := filesystem.NewStorage(fs, cache.NewObjectLRUDefault())
527
528 r := NewRemote(sto, &config.RemoteConfig{
529 Name: DefaultRemoteName,
530 URLs: []string{fs.Root()},
531 })
532
533 err := r.Push(&PushOptions{
534 RefSpecs: []config.RefSpec{"refs/heads/*:refs/heads/*"},
535 })
536 c.Assert(err, Equals, NoErrAlreadyUpToDate)
537}
538
539func (s *RemoteSuite) TestPushDeleteReference(c *C) {
540 fs := fixtures.Basic().One().DotGit()
541 sto := filesystem.NewStorage(fs, cache.NewObjectLRUDefault())
542
543 r, err := PlainClone(c.MkDir(), true, &CloneOptions{
544 URL: fs.Root(),
545 })
546 c.Assert(err, IsNil)
547
548 remote, err := r.Remote(DefaultRemoteName)
549 c.Assert(err, IsNil)
550
551 err = remote.Push(&PushOptions{
552 RefSpecs: []config.RefSpec{":refs/heads/branch"},
553 })
554 c.Assert(err, IsNil)
555
556 _, err = sto.Reference(plumbing.ReferenceName("refs/heads/branch"))
557 c.Assert(err, Equals, plumbing.ErrReferenceNotFound)
558
559 _, err = r.Storer.Reference(plumbing.ReferenceName("refs/heads/branch"))
560 c.Assert(err, Equals, plumbing.ErrReferenceNotFound)
561}
562
563func (s *RemoteSuite) TestForcePushDeleteReference(c *C) {
564 fs := fixtures.Basic().One().DotGit()
565 sto := filesystem.NewStorage(fs, cache.NewObjectLRUDefault())
566
567 r, err := PlainClone(c.MkDir(), true, &CloneOptions{
568 URL: fs.Root(),
569 })
570 c.Assert(err, IsNil)
571
572 remote, err := r.Remote(DefaultRemoteName)
573 c.Assert(err, IsNil)
574
575 err = remote.Push(&PushOptions{
576 RefSpecs: []config.RefSpec{":refs/heads/branch"},
577 Force: true,
578 })
579 c.Assert(err, IsNil)
580
581 _, err = sto.Reference(plumbing.ReferenceName("refs/heads/branch"))
582 c.Assert(err, Equals, plumbing.ErrReferenceNotFound)
583
584 _, err = r.Storer.Reference(plumbing.ReferenceName("refs/heads/branch"))
585 c.Assert(err, Equals, plumbing.ErrReferenceNotFound)
586}
587
588func (s *RemoteSuite) TestPushRejectNonFastForward(c *C) {
589 fs := fixtures.Basic().One().DotGit()
590 server := filesystem.NewStorage(fs, cache.NewObjectLRUDefault())
591
592 r, err := PlainClone(c.MkDir(), true, &CloneOptions{
593 URL: fs.Root(),
594 })
595 c.Assert(err, IsNil)
596
597 remote, err := r.Remote(DefaultRemoteName)
598 c.Assert(err, IsNil)
599
600 branch := plumbing.ReferenceName("refs/heads/branch")
601 oldRef, err := server.Reference(branch)
602 c.Assert(err, IsNil)
603 c.Assert(oldRef, NotNil)
604
605 err = remote.Push(&PushOptions{RefSpecs: []config.RefSpec{
606 "refs/heads/master:refs/heads/branch",
607 }})
608 c.Assert(err, ErrorMatches, "non-fast-forward update: refs/heads/branch")
609
610 newRef, err := server.Reference(branch)
611 c.Assert(err, IsNil)
612 c.Assert(newRef, DeepEquals, oldRef)
613}
614
615func (s *RemoteSuite) TestPushForce(c *C) {
616 f := fixtures.Basic().One()
617 sto := filesystem.NewStorage(f.DotGit(), cache.NewObjectLRUDefault())
618
619 dstFs := f.DotGit()
620 dstSto := filesystem.NewStorage(dstFs, cache.NewObjectLRUDefault())
621
622 url := dstFs.Root()
623 r := NewRemote(sto, &config.RemoteConfig{
624 Name: DefaultRemoteName,
625 URLs: []string{url},
626 })
627
628 oldRef, err := dstSto.Reference(plumbing.ReferenceName("refs/heads/branch"))
629 c.Assert(err, IsNil)
630 c.Assert(oldRef, NotNil)
631
632 err = r.Push(&PushOptions{RefSpecs: []config.RefSpec{
633 config.RefSpec("+refs/heads/master:refs/heads/branch"),
634 }})
635 c.Assert(err, IsNil)
636
637 newRef, err := dstSto.Reference(plumbing.ReferenceName("refs/heads/branch"))
638 c.Assert(err, IsNil)
639 c.Assert(newRef, Not(DeepEquals), oldRef)
640}
641
642func (s *RemoteSuite) TestPushForceWithOption(c *C) {
643 f := fixtures.Basic().One()
644 sto := filesystem.NewStorage(f.DotGit(), cache.NewObjectLRUDefault())
645
646 dstFs := f.DotGit()
647 dstSto := filesystem.NewStorage(dstFs, cache.NewObjectLRUDefault())
648
649 url := dstFs.Root()
650 r := NewRemote(sto, &config.RemoteConfig{
651 Name: DefaultRemoteName,
652 URLs: []string{url},
653 })
654
655 oldRef, err := dstSto.Reference(plumbing.ReferenceName("refs/heads/branch"))
656 c.Assert(err, IsNil)
657 c.Assert(oldRef, NotNil)
658
659 err = r.Push(&PushOptions{
660 RefSpecs: []config.RefSpec{"refs/heads/master:refs/heads/branch"},
661 Force: true,
662 })
663 c.Assert(err, IsNil)
664
665 newRef, err := dstSto.Reference(plumbing.ReferenceName("refs/heads/branch"))
666 c.Assert(err, IsNil)
667 c.Assert(newRef, Not(DeepEquals), oldRef)
668}
669
670func (s *RemoteSuite) TestPushPrune(c *C) {
671 fs := fixtures.Basic().One().DotGit()
672 url := c.MkDir()
673 server, err := PlainClone(url, true, &CloneOptions{
674 URL: fs.Root(),
675 })
676 c.Assert(err, IsNil)
677
678 r, err := PlainClone(c.MkDir(), true, &CloneOptions{
679 URL: url,
680 })
681 c.Assert(err, IsNil)
682
683 tag, err := r.Reference(plumbing.ReferenceName("refs/tags/v1.0.0"), true)
684 c.Assert(err, IsNil)
685
686 err = r.DeleteTag("v1.0.0")
687 c.Assert(err, IsNil)
688
689 remote, err := r.Remote(DefaultRemoteName)
690 c.Assert(err, IsNil)
691
692 ref, err := r.Reference(plumbing.ReferenceName("refs/heads/master"), true)
693 c.Assert(err, IsNil)
694
695 err = remote.Push(&PushOptions{
696 RefSpecs: []config.RefSpec{
697 config.RefSpec("refs/heads/*:refs/heads/*"),
698 },
699 Prune: true,
700 })
701 c.Assert(err, Equals, NoErrAlreadyUpToDate)
702
703 AssertReferences(c, server, map[string]string{
704 "refs/tags/v1.0.0": tag.Hash().String(),
705 })
706
707 err = remote.Push(&PushOptions{
708 RefSpecs: []config.RefSpec{
709 config.RefSpec("*:*"),
710 },
711 Prune: true,
712 })
713 c.Assert(err, IsNil)
714
715 AssertReferences(c, server, map[string]string{
716 "refs/remotes/origin/master": ref.Hash().String(),
717 })
718
719 AssertReferences(c, server, map[string]string{
720 "refs/remotes/origin/master": ref.Hash().String(),
721 })
722
723 ref, err = server.Reference(plumbing.ReferenceName("refs/tags/v1.0.0"), true)
724 c.Assert(err, Equals, plumbing.ErrReferenceNotFound)
725}
726
727func (s *RemoteSuite) TestPushNewReference(c *C) {
728 fs := fixtures.Basic().One().DotGit()
729 url := c.MkDir()
730 server, err := PlainClone(url, true, &CloneOptions{
731 URL: fs.Root(),
732 })
733 c.Assert(err, IsNil)
734
735 r, err := PlainClone(c.MkDir(), true, &CloneOptions{
736 URL: url,
737 })
738 c.Assert(err, IsNil)
739
740 remote, err := r.Remote(DefaultRemoteName)
741 c.Assert(err, IsNil)
742
743 ref, err := r.Reference(plumbing.ReferenceName("refs/heads/master"), true)
744 c.Assert(err, IsNil)
745
746 err = remote.Push(&PushOptions{RefSpecs: []config.RefSpec{
747 "refs/heads/master:refs/heads/branch2",
748 }})
749 c.Assert(err, IsNil)
750
751 AssertReferences(c, server, map[string]string{
752 "refs/heads/branch2": ref.Hash().String(),
753 })
754
755 AssertReferences(c, r, map[string]string{
756 "refs/remotes/origin/branch2": ref.Hash().String(),
757 })
758}
759
760func (s *RemoteSuite) TestPushNewReferenceAndDeleteInBatch(c *C) {
761 fs := fixtures.Basic().One().DotGit()
762 url := c.MkDir()
763 server, err := PlainClone(url, true, &CloneOptions{
764 URL: fs.Root(),
765 })
766 c.Assert(err, IsNil)
767
768 r, err := PlainClone(c.MkDir(), true, &CloneOptions{
769 URL: url,
770 })
771 c.Assert(err, IsNil)
772
773 remote, err := r.Remote(DefaultRemoteName)
774 c.Assert(err, IsNil)
775
776 ref, err := r.Reference(plumbing.ReferenceName("refs/heads/master"), true)
777 c.Assert(err, IsNil)
778
779 err = remote.Push(&PushOptions{RefSpecs: []config.RefSpec{
780 "refs/heads/master:refs/heads/branch2",
781 ":refs/heads/branch",
782 }})
783 c.Assert(err, IsNil)
784
785 AssertReferences(c, server, map[string]string{
786 "refs/heads/branch2": ref.Hash().String(),
787 })
788
789 AssertReferences(c, r, map[string]string{
790 "refs/remotes/origin/branch2": ref.Hash().String(),
791 })
792
793 _, err = server.Storer.Reference(plumbing.ReferenceName("refs/heads/branch"))
794 c.Assert(err, Equals, plumbing.ErrReferenceNotFound)
795}
796
797func (s *RemoteSuite) TestPushInvalidEndpoint(c *C) {
798 r := NewRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"http://\\"}})
799 err := r.Push(&PushOptions{RemoteName: "foo"})
800 c.Assert(err, ErrorMatches, ".*invalid character.*")
801}
802
803func (s *RemoteSuite) TestPushNonExistentEndpoint(c *C) {
804 r := NewRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"ssh://non-existent/foo.git"}})
805 err := r.Push(&PushOptions{})
806 c.Assert(err, NotNil)
807}
808
809func (s *RemoteSuite) TestPushInvalidSchemaEndpoint(c *C) {
810 r := NewRemote(nil, &config.RemoteConfig{Name: "origin", URLs: []string{"qux://foo"}})
811 err := r.Push(&PushOptions{})
812 c.Assert(err, ErrorMatches, ".*unsupported scheme.*")
813}
814
815func (s *RemoteSuite) TestPushInvalidFetchOptions(c *C) {
816 r := NewRemote(nil, &config.RemoteConfig{Name: "foo", URLs: []string{"qux://foo"}})
817 invalid := config.RefSpec("^*$ñ")
818 err := r.Push(&PushOptions{RefSpecs: []config.RefSpec{invalid}})
819 c.Assert(err, Equals, config.ErrRefSpecMalformedSeparator)
820}
821
822func (s *RemoteSuite) TestPushInvalidRefSpec(c *C) {
823 r := NewRemote(nil, &config.RemoteConfig{
824 Name: DefaultRemoteName,
825 URLs: []string{"some-url"},
826 })
827
828 rs := config.RefSpec("^*$**")
829 err := r.Push(&PushOptions{
830 RefSpecs: []config.RefSpec{rs},
831 })
832 c.Assert(err, Equals, config.ErrRefSpecMalformedSeparator)
833}
834
835func (s *RemoteSuite) TestPushWrongRemoteName(c *C) {
836 r := NewRemote(nil, &config.RemoteConfig{
837 Name: DefaultRemoteName,
838 URLs: []string{"some-url"},
839 })
840
841 err := r.Push(&PushOptions{
842 RemoteName: "other-remote",
843 })
844 c.Assert(err, ErrorMatches, ".*remote names don't match.*")
845}
846
847func (s *RemoteSuite) TestGetHaves(c *C) {
848 f := fixtures.Basic().One()
849 sto := filesystem.NewStorage(f.DotGit(), cache.NewObjectLRUDefault())
850
851 var localRefs = []*plumbing.Reference{
852 plumbing.NewReferenceFromStrings(
853 "foo",
854 "f7b877701fbf855b44c0a9e86f3fdce2c298b07f",
855 ),
856 plumbing.NewReferenceFromStrings(
857 "bar",
858 "fe6cb94756faa81e5ed9240f9191b833db5f40ae",
859 ),
860 plumbing.NewReferenceFromStrings(
861 "qux",
862 "f7b877701fbf855b44c0a9e86f3fdce2c298b07f",
863 ),
864 }
865
866 l, err := getHaves(localRefs, memory.NewStorage(), sto)
867 c.Assert(err, IsNil)
868 c.Assert(l, HasLen, 2)
869}
870
871func (s *RemoteSuite) TestList(c *C) {
872 repo := fixtures.Basic().One()
873 remote := NewRemote(memory.NewStorage(), &config.RemoteConfig{
874 Name: DefaultRemoteName,
875 URLs: []string{repo.URL},
876 })
877
878 refs, err := remote.List(&ListOptions{})
879 c.Assert(err, IsNil)
880
881 expected := []*plumbing.Reference{
882 plumbing.NewSymbolicReference("HEAD", "refs/heads/master"),
883 plumbing.NewReferenceFromStrings("refs/heads/master", "6ecf0ef2c2dffb796033e5a02219af86ec6584e5"),
884 plumbing.NewReferenceFromStrings("refs/heads/branch", "e8d3ffab552895c19b9fcf7aa264d277cde33881"),
885 plumbing.NewReferenceFromStrings("refs/pull/1/head", "b8e471f58bcbca63b07bda20e428190409c2db47"),
886 plumbing.NewReferenceFromStrings("refs/pull/2/head", "9632f02833b2f9613afb5e75682132b0b22e4a31"),
887 plumbing.NewReferenceFromStrings("refs/pull/2/merge", "c37f58a130ca555e42ff96a071cb9ccb3f437504"),
888 }
889 c.Assert(len(refs), Equals, len(expected))
890 for _, e := range expected {
891 found := false
892 for _, r := range refs {
893 if r.Name() == e.Name() {
894 found = true
895 c.Assert(r, DeepEquals, e)
896 }
897 }
898 c.Assert(found, Equals, true)
899 }
900}
901
902func (s *RemoteSuite) TestUpdateShallows(c *C) {
903 hashes := []plumbing.Hash{
904 plumbing.NewHash("0000000000000000000000000000000000000001"),
905 plumbing.NewHash("0000000000000000000000000000000000000002"),
906 plumbing.NewHash("0000000000000000000000000000000000000003"),
907 plumbing.NewHash("0000000000000000000000000000000000000004"),
908 plumbing.NewHash("0000000000000000000000000000000000000005"),
909 plumbing.NewHash("0000000000000000000000000000000000000006"),
910 }
911
912 tests := []struct {
913 hashes []plumbing.Hash
914 result []plumbing.Hash
915 }{
916 // add to empty shallows
917 {hashes[0:2], hashes[0:2]},
918 // add new hashes
919 {hashes[2:4], hashes[0:4]},
920 // add some hashes already in shallow list
921 {hashes[2:6], hashes[0:6]},
922 // add all hashes
923 {hashes[0:6], hashes[0:6]},
924 // add empty list
925 {nil, hashes[0:6]},
926 }
927
928 remote := NewRemote(memory.NewStorage(), &config.RemoteConfig{
929 Name: DefaultRemoteName,
930 })
931
932 shallows, err := remote.s.Shallow()
933 c.Assert(err, IsNil)
934 c.Assert(len(shallows), Equals, 0)
935
936 resp := new(packp.UploadPackResponse)
937 o := &FetchOptions{
938 Depth: 1,
939 }
940
941 for _, t := range tests {
942 resp.Shallows = t.hashes
943 err = remote.updateShallow(o, resp)
944 c.Assert(err, IsNil)
945
946 shallow, err := remote.s.Shallow()
947 c.Assert(err, IsNil)
948 c.Assert(len(shallow), Equals, len(t.result))
949 c.Assert(shallow, DeepEquals, t.result)
950 }
951}
952
953func (s *RemoteSuite) TestUseRefDeltas(c *C) {
954 url := c.MkDir()
955 _, err := PlainInit(url, true)
956 c.Assert(err, IsNil)
957
958 fs := fixtures.ByURL("https://github.com/git-fixtures/tags.git").One().DotGit()
959 sto := filesystem.NewStorage(fs, cache.NewObjectLRUDefault())
960
961 r := NewRemote(sto, &config.RemoteConfig{
962 Name: DefaultRemoteName,
963 URLs: []string{url},
964 })
965
966 ar := packp.NewAdvRefs()
967
968 ar.Capabilities.Add(capability.OFSDelta)
969 c.Assert(r.useRefDeltas(ar), Equals, false)
970
971 ar.Capabilities.Delete(capability.OFSDelta)
972 c.Assert(r.useRefDeltas(ar), Equals, true)
973}