51 lines · 1.9 KB · Go
RawDownload
// Copyright 2025 The Forgejo Authors. All rights reserved.// SPDX-License-Identifier: GPL-3.0-or-laterpackage git import (	"errors"	"fmt"	"strings") var ErrRemoteRefNotFound = errors.New("unable to find remote ref") // Fetch executes git-fetch(1) for the repository, it will fetch the refspec// from the remote into the repository.//// Valid remote URLs are denoted in section GIT URLS of git-fetch(1).//// The return value, on success, is the object ID of the remote reference. If// no local reference is given in `refspec` then do not assume it's available in// the `FETCH_HEAD` reference, it might have been overwritten by the time you// read or use it.func (repo *Repository) Fetch(remoteURL, refspec string) (string, error) {	objectFormat, err := repo.GetObjectFormat()	if err != nil {		return "", err	} 	stdout, stderr, err := NewCommand(repo.Ctx, "fetch", "--porcelain", "--end-of-options").AddDynamicArguments(remoteURL, refspec).RunStdString(&RunOpts{Dir: repo.Path})	if err != nil {		if strings.HasPrefix(stderr, "fatal: couldn't find remote ref ") {			return "", ErrRemoteRefNotFound		}		return "", err	} 	localReference, _, ok := strings.Cut(refspec, ":")	if !ok {		localReference = "FETCH_HEAD"	} 	// The porcelain format, per section OUTPUT of git-fetch(1), is expected to be:	// <flag><space><old-object-id><space><new-object-id><space><local-reference>\n	// flag is one character.	if expectedLen := 1 + 1 + objectFormat.FullLength() + 1 + objectFormat.FullLength() + 1 + len(localReference) + 1; len(stdout) != expectedLen {		return "", fmt.Errorf("output of git-fetch(1) is unexpected, we expected it to be %d bytes but it is %d bytes. stdout: %s", expectedLen, len(stdout), stdout)	} 	// Extract the new objectID.	newObjectID := stdout[1+1+objectFormat.FullLength()+1 : 1+1+objectFormat.FullLength()+1+objectFormat.FullLength()]	return newObjectID, nil}