From 022540d75f6f80c154d429ad5d47fc66f5089a56 Mon Sep 17 00:00:00 2001
From: Sebastian Thiel <sebastian.thiel@icloud.com>
Date: Sun, 12 Jan 2025 19:49:50 +0100
Subject: [PATCH] feat: add
 `Repository::upstream_branch_and_remote_name_for_tracking_branch()`

It's a way to learn about the Remote and upstream branch which would
match the given local tracking branch.
---
 gix/src/repository/config/branch.rs | 37 ++++++++++++++++++++++-------
 gix/src/repository/mod.rs           | 11 +++++++++
 2 files changed, 40 insertions(+), 8 deletions(-)

diff --git a/gix/src/repository/config/branch.rs b/gix/src/repository/config/branch.rs
index 661dbb264d0..e24a15ed54f 100644
--- a/gix/src/repository/config/branch.rs
+++ b/gix/src/repository/config/branch.rs
@@ -5,7 +5,9 @@ use gix_ref::{FullName, FullNameRef};
 use crate::bstr::BStr;
 use crate::config::cache::util::ApplyLeniencyDefault;
 use crate::config::tree::{Branch, Push};
-use crate::repository::{branch_remote_ref_name, branch_remote_tracking_ref_name};
+use crate::repository::{
+    branch_remote_ref_name, branch_remote_tracking_ref_name, upstream_branch_and_remote_name_for_tracking_branch,
+};
 use crate::{push, remote};
 
 /// Query configuration related to branches.
@@ -20,19 +22,18 @@ impl crate::Repository {
         self.subsection_str_names_of("branch")
     }
 
-    /// Returns the validated reference on the remote associated with the given `name`,
+    /// Returns the validated reference name of the upstream branch on the remote associated with the given `name`,
     /// which will be used when *merging*.
-    /// The returned value corresponds to the `branch.<short_branch_name>.merge` configuration key.
+    /// The returned value corresponds to the `branch.<short_branch_name>.merge` configuration key for [`remote::Direction::Fetch`].
+    /// For the [push direction](`remote::Direction::Push`) the Git configuration is used for a variety of different outcomes,
+    /// similar to what would happen when running `git push <name>`.
     ///
-    /// Returns `None` if there is no value at the given key, or if no remote or remote ref is configured.
-    /// May return an error if the reference name to be returned is invalid.
+    /// Returns `None` if there is nothing configured, or if no remote or remote ref is configured.
     ///
     /// ### Note
     ///
-    /// This name refers to what Git calls upstream branch (as opposed to upstream *tracking* branch).
+    /// The returned name refers to what Git calls upstream branch (as opposed to upstream *tracking* branch).
     /// The value is also fast to retrieve compared to its tracking branch.
-    /// Also note that a [remote::Direction] isn't used here as Git only supports (and requires) configuring
-    /// the remote to fetch from, not the one to push to.
     ///
     /// See also [`Reference::remote_ref_name()`](crate::Reference::remote_ref_name()).
     #[doc(alias = "branch_upstream_name", alias = "git2")]
@@ -125,6 +126,26 @@ impl crate::Repository {
             .map(|res| res.map_err(Into::into))
     }
 
+    /// Given a local `tracking_branch` name, find the remote that maps to it along with the name of the branch on
+    /// the side of the remote, also called upstream branch.
+    ///
+    /// Return `Ok(None)` if there is no remote with fetch-refspecs that would match `tracking_branch` on the right-hand side,
+    /// or `Err` if the matches were ambiguous.
+    ///
+    /// ### Limitations
+    ///
+    /// A single valid mapping is required as fine-grained matching isn't implemented yet. This means that
+    pub fn upstream_branch_and_remote_name_for_tracking_branch(
+        &self,
+        tracking_branch: &FullNameRef,
+    ) -> Result<
+        Option<(Cow<'_, FullNameRef>, remote::Name<'_>)>,
+        upstream_branch_and_remote_name_for_tracking_branch::Error,
+    > {
+        for remote_name in self.remote_names() {}
+        todo!()
+    }
+
     /// Returns the unvalidated name of the remote associated with the given `short_branch_name`,
     /// typically `main` instead of `refs/heads/main`.
     /// In some cases, the returned name will be an URL.
diff --git a/gix/src/repository/mod.rs b/gix/src/repository/mod.rs
index 4d5ca4093fa..eabf8588968 100644
--- a/gix/src/repository/mod.rs
+++ b/gix/src/repository/mod.rs
@@ -360,6 +360,17 @@ pub mod branch_remote_tracking_ref_name {
     }
 }
 
+///
+pub mod upstream_branch_and_remote_name_for_tracking_branch {
+    /// The error returned by [Repository::upstream_branch_and_remote_name_for_tracking_branch()](crate::Repository::upstream_branch_and_remote_name_for_tracking_branch()).
+    #[derive(Debug, thiserror::Error)]
+    #[allow(missing_docs)]
+    pub enum Error {
+        #[error("The name of the tracking reference was invalid")]
+        ValidateTrackingRef(#[from] gix_validate::reference::name::Error),
+    }
+}
+
 ///
 #[cfg(feature = "attributes")]
 pub mod pathspec_defaults_ignore_case {