@@ -263,47 +263,51 @@ func (h *GitopiaHandler) Push(remote *core.Remote, refsToPush []core.RefToPush)
263263 remoteURL := fmt .Sprintf ("%v/%v.git" , gitServerHost , h .remoteRepository .Id )
264264 lfsURL := remoteURL // Use same URL for LFS
265265
266- var newRemoteRefSha string
266+ var pushRefspecs []string
267+ var deleteBranches , deleteTags []string
267268 var setBranches []gitopiatypes.MsgMultiSetBranch_Branch
268269 var setTags []gitopiatypes.MsgMultiSetTag_Tag
269- var deleteBranches , deleteTags []string
270- var res []string
271-
270+ isForce := false
271+ var res []string // To return the refs that were processed
272272 var packfileCid string
273+
273274 packfileRes , err := h .storageClient .RepositoryPackfile (context .Background (), & storagetypes.QueryRepositoryPackfileRequest {
274275 RepositoryId : h .remoteRepository .Id ,
275276 })
276277 if err == nil {
277278 packfileCid = packfileRes .Packfile .Cid
278279 }
279280
281+ // --- First Pass: Collect refspecs and deletions ---
280282 for _ , ref := range refsToPush {
281- if ref .Local == "" {
283+ if ref .Local == "" { // This is a delete operation
282284 if strings .HasPrefix (ref .Remote , branchPrefix ) {
283285 remoteBranchName := strings .TrimPrefix (ref .Remote , branchPrefix )
284-
285- // Check if it's the default branch
286286 if remoteBranchName == h .remoteRepository .DefaultBranch {
287287 return nil , fmt .Errorf ("fatal: cannot delete default branch, %v" , remoteBranchName )
288288 }
289-
290289 deleteBranches = append (deleteBranches , remoteBranchName )
291290 res = append (res , ref .Remote )
292- } else if strings .HasPrefix (refsToPush [ 0 ] .Remote , tagPrefix ) {
293- remoteTagName := strings .TrimPrefix (refsToPush [ 0 ] .Remote , tagPrefix )
291+ } else if strings .HasPrefix (ref .Remote , tagPrefix ) {
292+ remoteTagName := strings .TrimPrefix (ref .Remote , tagPrefix )
294293 deleteTags = append (deleteTags , remoteTagName )
295294 res = append (res , ref .Remote )
296295 }
297-
298296 continue
299297 }
300298
301- force := false
302- if strings .HasPrefix (ref .Local , "+" ) {
303- ref .Local = strings .TrimPrefix (ref .Local , "+" )
304- force = true
299+ // This is a create/update operation
300+ localRef := ref .Local
301+ if strings .HasPrefix (localRef , "+" ) {
302+ localRef = strings .TrimPrefix (localRef , "+" )
303+ isForce = true
305304 }
306305
306+ pushRefspecs = append (pushRefspecs , fmt .Sprintf ("%s:%s" , ref .Local , ref .Remote ))
307+ }
308+
309+ // --- Execute a single Git Push if there's anything to push ---
310+ if len (pushRefspecs ) > 0 {
307311 if h .wallet .Type () == wallet .LEDGER {
308312 remote .Logger .Println ("Please sign the git server request on your ledger device." )
309313 }
@@ -313,63 +317,82 @@ func (h *GitopiaHandler) Push(remote *core.Remote, refsToPush []core.RefToPush)
313317 if err != nil {
314318 return nil , errors .Wrap (err , "error signing data" )
315319 }
316-
317320 credential := fmt .Sprintf ("%s:%s" , h .wallet .Address (), signature )
321+
318322 args := []string {
319- "-c" ,
320- fmt .Sprintf ("http.extraheader=Authorization: Basic %s" , base64 .StdEncoding .EncodeToString ([]byte (credential ))),
321- "-c" ,
322- "credential.helper=" ,
323- "-c" ,
324- "credential.helper=gitopia" ,
325- "-c" ,
326- fmt .Sprintf ("lfs.url=%s" , lfsURL ),
323+ "-c" , fmt .Sprintf ("http.extraheader=Authorization: Basic %s" , base64 .StdEncoding .EncodeToString ([]byte (credential ))),
324+ "-c" , "credential.helper=" ,
325+ "-c" , "credential.helper=gitopia" ,
326+ "-c" , fmt .Sprintf ("lfs.url=%s" , lfsURL ),
327327 "push" ,
328+ "--no-verify" , // Keep this to prevent the double-hook call
328329 remoteURL ,
329- fmt .Sprintf ("%s:%s" , ref .Local , ref .Remote ),
330330 }
331- if force {
331+
332+ // Add all refspecs to the command
333+ args = append (args , pushRefspecs ... )
334+
335+ if isForce {
332336 args = append (args , "--force" )
333337 }
338+
334339 cmd := core .GitCommand ("git" , args ... )
335340 if err := cmd .Run (); err != nil {
336341 return nil , errors .Wrap (err , "error pushing to remote repository" )
337342 }
343+ }
344+
345+ // --- Second Pass: Collect metadata for Gitopia transaction ---
346+ for _ , ref := range refsToPush {
347+ if ref .Local == "" {
348+ continue // Deletes already handled
349+ }
350+
351+ localRef := ref .Local
352+ if strings .HasPrefix (localRef , "+" ) {
353+ localRef = strings .TrimPrefix (localRef , "+" )
354+ }
338355
339356 // Update ref on gitopia
340- if strings .HasPrefix (ref . Local , branchPrefix ) {
341- localCommitHash , err := remote .Repo .ResolveRevision (plumbing .Revision (ref . Local ))
357+ if strings .HasPrefix (localRef , branchPrefix ) {
358+ localCommitHash , err := remote .Repo .ResolveRevision (plumbing .Revision (localRef ))
342359 if err != nil {
343- return nil , fmt .Errorf ("fatal: local branch %s doesn't exist" , ref . Local )
360+ return nil , fmt .Errorf ("fatal: local branch %s doesn't exist" , localRef )
344361 }
345362
346- newRemoteRefSha = localCommitHash .String ()
347363 remoteBranchName := strings .TrimPrefix (ref .Remote , branchPrefix )
348364 branch := gitopiatypes.MsgMultiSetBranch_Branch {
349365 Name : remoteBranchName ,
350- Sha : newRemoteRefSha ,
366+ Sha : localCommitHash . String () ,
351367 }
352-
353368 setBranches = append (setBranches , branch )
354369 res = append (res , ref .Remote )
355- } else if strings .HasPrefix (ref . Local , tagPrefix ) {
356- localTagName := strings .TrimPrefix (ref . Local , tagPrefix )
370+ } else if strings .HasPrefix (localRef , tagPrefix ) {
371+ localTagName := strings .TrimPrefix (localRef , tagPrefix )
357372 tagRef , err := remote .Repo .Tag (localTagName )
358373 if err != nil {
359- return nil , fmt .Errorf ("fatal: invalid tag name, %v" , localTagName )
360- }
361-
362- newRemoteRefSha = tagRef .Hash ().String ()
363- remoteTagName := strings .TrimPrefix (ref .Remote , tagPrefix )
364- tag := gitopiatypes.MsgMultiSetTag_Tag {
365- Name : remoteTagName ,
366- Sha : newRemoteRefSha ,
374+ // Could be a lightweight tag, resolve revision instead
375+ commitHash , err := remote .Repo .ResolveRevision (plumbing .Revision (localRef ))
376+ if err != nil {
377+ return nil , fmt .Errorf ("fatal: invalid tag name or ref, %v" , localTagName )
378+ }
379+ tag := gitopiatypes.MsgMultiSetTag_Tag {
380+ Name : strings .TrimPrefix (ref .Remote , tagPrefix ),
381+ Sha : commitHash .String (),
382+ }
383+ setTags = append (setTags , tag )
384+ } else {
385+ // Annotated tag
386+ tag := gitopiatypes.MsgMultiSetTag_Tag {
387+ Name : strings .TrimPrefix (ref .Remote , tagPrefix ),
388+ Sha : tagRef .Hash ().String (),
389+ }
390+ setTags = append (setTags , tag )
367391 }
368392
369- setTags = append (setTags , tag )
370393 res = append (res , ref .Remote )
371394 } else {
372- return nil , fmt .Errorf ("fatal: not a valid branch/tag , %v" , ref . Local )
395+ return nil , fmt .Errorf ("fatal: invalid refspec , %v" , ref )
373396 }
374397 }
375398
@@ -444,7 +467,7 @@ func (h *GitopiaHandler) havePushPermission(walletAddress string) (havePermissio
444467 DaoId : h .remoteRepository .Owner .Id ,
445468 })
446469 if err != nil {
447- return havePermission , err
470+ return havePermission , errors . Wrap ( err , "error querying DAO members" )
448471 }
449472
450473 for _ , member := range resp .Members {
0 commit comments