Repository: cli/cli
File: git/client.go (Go)

Code before:
	}

	return "", err
}

// RemoteTrackingRef is the structured form of the string "refs/remotes/<remote>/<branch>".
// For example, the @{push} revision syntax could report "refs/remotes/origin/main" which would
// be parsed into RemoteTrackingRef{Remote: "origin", Branch: "main"}.
type RemoteTrackingRef struct {
	Remote string
	Branch string
}

func (r RemoteTrackingRef) String() string {
	return fmt.Sprintf("refs/remotes/%s/%s", r.Remote, r.Branch)
}

// ParseRemoteTrackingRef parses a string of the form "refs/remotes/<remote>/<branch>" into
// a RemoteTrackingBranch struct. If the string does not match this format, an error is returned.
func ParseRemoteTrackingRef(s string) (RemoteTrackingRef, error) {
	prefix := "refs/remotes/"
	if !strings.HasPrefix(s, prefix) {
		return RemoteTrackingRef{}, fmt.Errorf("remote tracking branch must have format refs/remotes/<remote>/<branch> but was: %s", s)
	}

	// For now, we assume that refnames are of the format "<remote>/<branch>", where
	// the remote is a single path component, and branch may have many path components e.g.
	// "origin/my/branch" is valid as: {Remote: "origin", Branch: "my/branch"}
	// but "my/origin/branch" would parse incorrectly as: {Remote: "my", Branch: "origin/branch"}
	refName := strings.TrimPrefix(s, prefix)
	refNameParts := strings.SplitN(refName, "/", 2)
	if len(refNameParts) != 2 {
		return RemoteTrackingRef{}, fmt.Errorf("remote tracking branch must have format refs/remotes/<remote>/<branch> but was: %s", s)
	}

	return RemoteTrackingRef{
		Remote: refNameParts[0],
		Branch: refNameParts[1],
	}, nil
}

// PushRevision gets the value of the @{push} revision syntax
// An error here doesn't necessarily mean something is broken, but may mean that the @{push}
// revision syntax couldn't be resolved, such as in non-centralized workflows with
// push.default = simple. Downstream consumers should consider how to handle this error.
func (c *Client) PushRevision(ctx context.Context, branch string) (RemoteTrackingRef, error) {
	revParseOut, err := c.revParse(ctx, "--symbolic-full-name", branch+"@{push}")
	if err != nil {
		return RemoteTrackingRef{}, err
	}


Review comment (suggestion):
Can expand this comment to explain that git has a bunch of resolution rules around this, and doesn't seem to offer a good way to get `remote` and `branch` out of a ref.

It becomes particularly confusing if you have something like:

```
[remote "foo"]
	url = https://github.com/williammartin/test-repo.git
	fetch = +refs/heads/*:refs/remotes/foo/*
[remote "foo/bar"]
	url = https://github.com/williammartin/test-repo.git
	fetch = +refs/heads/*:refs/remotes/foo/bar/*
[branch "bar/baz"]
	remote = foo
	merge = refs/heads/bar/baz
[branch "baz"]
	remote = foo/bar
	merge = refs/heads/baz
```

This resolves to remote refs of:

```
➜ git rev-parse --symbolic-full-name baz@{push}
refs/remotes/foo/bar/baz

➜ git rev-parse --symbolic-full-name bar/baz@{push}
refs/remotes/foo/bar/baz
```

When using this ref, git assumes it means `remote: foo` `branch: bar/baz`





Code after:
	}

	return "", err
}

// RemoteTrackingRef is the structured form of the string "refs/remotes/<remote>/<branch>".
// For example, the @{push} revision syntax could report "refs/remotes/origin/main" which would
// be parsed into RemoteTrackingRef{Remote: "origin", Branch: "main"}.
type RemoteTrackingRef struct {
	Remote string
	Branch string
}

func (r RemoteTrackingRef) String() string {
	return fmt.Sprintf("refs/remotes/%s/%s", r.Remote, r.Branch)
}

// ParseRemoteTrackingRef parses a string of the form "refs/remotes/<remote>/<branch>" into
// a RemoteTrackingBranch struct. If the string does not match this format, an error is returned.
//
// For now, we assume that refnames are of the format "<remote>/<branch>", where
// the remote is a single path component, and branch may have many path components e.g.
// "origin/my/branch" is valid as: {Remote: "origin", Branch: "my/branch"}
// but "my/origin/branch" would parse incorrectly as: {Remote: "my", Branch: "origin/branch"}
// I don't believe there is a way to fix this without providing the list of remotes to this function.
//
// It becomes particularly confusing if you have something like:
//
// ```
// [remote "foo"]
// 	url = https://github.com/williammartin/test-repo.git
// 	fetch = +refs/heads/*:refs/remotes/foo/*
// [remote "foo/bar"]
// 	url = https://github.com/williammartin/test-repo.git
// 	fetch = +refs/heads/*:refs/remotes/foo/bar/*
// [branch "bar/baz"]
// 	remote = foo
// 	merge = refs/heads/bar/baz
// [branch "baz"]
// 	remote = foo/bar
// 	merge = refs/heads/baz
// ```
//
// These @{push} refs would resolve identically:
//
// ```
// ➜ git rev-parse --symbolic-full-name baz@{push}
// refs/remotes/foo/bar/baz

// ➜ git rev-parse --symbolic-full-name bar/baz@{push}
// refs/remotes/foo/bar/baz