@@ -43,12 +43,49 @@ type Publisher struct { |
| 43 |
Git gitutil.Git |
43 |
Git gitutil.Git |
| 44 |
Progress func(format string, args ...any) |
44 |
Progress func(format string, args ...any) |
| 45 |
AfterPublish func(context.Context, Result) |
45 |
AfterPublish func(context.Context, Result) |
|
|
46 |
prepared *preparedPush |
|
|
47 |
} |
|
|
48 |
|
|
|
49 |
type preparedPush struct { |
|
|
50 |
lock *s3store.Lock |
|
|
51 |
metadata Metadata |
| 46 |
} |
52 |
} |
| 47 |
|
53 |
|
| 48 |
func (p *Publisher) List(ctx context.Context) (Metadata, error) { |
54 |
func (p *Publisher) List(ctx context.Context) (Metadata, error) { |
| 49 |
return LoadMetadata(ctx, p.Store) |
55 |
return LoadMetadata(ctx, p.Store) |
| 50 |
} |
56 |
} |
| 51 |
|
57 |
|
|
|
58 |
// Prepare serializes a push before refs are advertised. Holding the lock from |
|
|
59 |
// advertisement through publication removes a redundant metadata read without |
|
|
60 |
// weakening the concurrent-update check. |
|
|
61 |
func (p *Publisher) Prepare(ctx context.Context) (Metadata, error) { |
|
|
62 |
if p.prepared != nil { |
|
|
63 |
return p.prepared.metadata, nil |
|
|
64 |
} |
|
|
65 |
lock, err := p.Store.AcquireLock(ctx) |
|
|
66 |
if err != nil { |
|
|
67 |
return Metadata{}, err |
|
|
68 |
} |
|
|
69 |
metadata, err := LoadMetadata(ctx, p.Store) |
|
|
70 |
if err != nil { |
|
|
71 |
_ = lock.Release(context.WithoutCancel(ctx)) |
|
|
72 |
return Metadata{}, err |
|
|
73 |
} |
|
|
74 |
p.prepared = &preparedPush{lock: lock, metadata: metadata} |
|
|
75 |
return metadata, nil |
|
|
76 |
} |
|
|
77 |
|
|
|
78 |
// Abort releases a prepared push if the remote-helper session ends before it |
|
|
79 |
// sends a push command. |
|
|
80 |
func (p *Publisher) Abort(ctx context.Context) error { |
|
|
81 |
if p.prepared == nil { |
|
|
82 |
return nil |
|
|
83 |
} |
|
|
84 |
lock := p.prepared.lock |
|
|
85 |
p.prepared = nil |
|
|
86 |
return lock.Release(ctx) |
|
|
87 |
} |
|
|
88 |
|
| 52 |
// Push applies a batch as one publication. expected contains the refs advertised |
89 |
// Push applies a batch as one publication. expected contains the refs advertised |
| 53 |
// to Git before it sent the push commands and prevents a concurrent update from |
90 |
// to Git before it sent the push commands and prevents a concurrent update from |
| 54 |
// being silently overwritten. |
91 |
// being silently overwritten. |
@@ -63,9 +100,22 @@ func (p *Publisher) Push(ctx context.Context, updates []Update, expected map[str |
| 63 |
if shallow { |
100 |
if shallow { |
| 64 |
return Result{}, fmt.Errorf("cannot publish from a shallow repository; fetch the complete history first") |
101 |
return Result{}, fmt.Errorf("cannot publish from a shallow repository; fetch the complete history first") |
| 65 |
} |
102 |
} |
| 66 |
lock, err := p.Store.AcquireLock(ctx) |
103 |
var lock *s3store.Lock |
| 67 |
if err != nil { |
104 |
var metadata Metadata |
| 68 |
return Result{}, err |
105 |
if p.prepared != nil { |
|
|
106 |
lock = p.prepared.lock |
|
|
107 |
metadata = p.prepared.metadata |
|
|
108 |
p.prepared = nil |
|
|
109 |
} else { |
|
|
110 |
lock, err = p.Store.AcquireLock(ctx) |
|
|
111 |
if err != nil { |
|
|
112 |
return Result{}, err |
|
|
113 |
} |
|
|
114 |
metadata, err = LoadMetadata(ctx, p.Store) |
|
|
115 |
if err != nil { |
|
|
116 |
_ = lock.Release(context.WithoutCancel(ctx)) |
|
|
117 |
return Result{}, err |
|
|
118 |
} |
| 69 |
} |
119 |
} |
| 70 |
defer func() { |
120 |
defer func() { |
| 71 |
if releaseErr := lock.Release(context.WithoutCancel(ctx)); err == nil && releaseErr != nil { |
121 |
if releaseErr := lock.Release(context.WithoutCancel(ctx)); err == nil && releaseErr != nil { |
@@ -73,10 +123,6 @@ func (p *Publisher) Push(ctx context.Context, updates []Update, expected map[str |
| 73 |
} |
123 |
} |
| 74 |
}() |
124 |
}() |
| 75 |
|
125 |
|
| 76 |
metadata, err := LoadMetadata(ctx, p.Store) |
|
|
| 77 |
if err != nil { |
|
|
| 78 |
return Result{}, err |
|
|
| 79 |
} |
|
|
| 80 |
if err := checkExpected(metadata, updates, expected); err != nil { |
126 |
if err := checkExpected(metadata, updates, expected); err != nil { |
| 81 |
return Result{}, err |
127 |
return Result{}, err |
| 82 |
} |
128 |
} |