From d737c4a6856c01a54c3ec1eea9ee4b70d76f67ab Mon Sep 17 00:00:00 2001
From: Eliah Kagan <degeneracypressure@gmail.com>
Date: Sun, 26 Jan 2025 11:30:41 -0500
Subject: [PATCH 1/3] fix!: Rename `Exponential` backoff to `Quadratic`

The `gix_utils::backoff::Exponential` type actually implemented
quadratic, not exponential, backoff. This renames it from
`Exponential` to `Quadratic`.

In exponential backoff, delays are a fixed base, often 2, raised to
a power of a number that increases by one with each attempt. When
the number that increases by one with each attempt is the base,
raised to a fixed power, that is quadratic backoff.

The intended behavior here was quadratic, as implemented. For
example, in the tests, `EXPECTED_TILL_SECOND` lists the values 1,
4, 9, 16, 25, 36, 49, 64, 81, 100, and so on, which are ascending
squares. If they were an exponential sequence, then they would look
like 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, and so on.

Thus, it is only the named that needed to be changed: the
implementation was already as intended.
---
 gix-utils/src/backoff.rs       | 18 +++++++++---------
 gix-utils/tests/backoff/mod.rs | 12 ++++++------
 2 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/gix-utils/src/backoff.rs b/gix-utils/src/backoff.rs
index 66d66302440..adc8f30086c 100644
--- a/gix-utils/src/backoff.rs
+++ b/gix-utils/src/backoff.rs
@@ -9,17 +9,17 @@ fn randomize(backoff_ms: usize) -> usize {
     }
 }
 
-/// A utility to calculate steps for exponential backoff similar to how it's done in `git`.
-pub struct Exponential<Fn> {
+/// A utility to calculate steps for quadratic backoff similar to how it's done in `git`.
+pub struct Quadratic<Fn> {
     multiplier: usize,
     max_multiplier: usize,
     exponent: usize,
     transform: Fn,
 }
 
-impl Default for Exponential<fn(usize) -> usize> {
+impl Default for Quadratic<fn(usize) -> usize> {
     fn default() -> Self {
-        Exponential {
+        Quadratic {
             multiplier: 1,
             max_multiplier: 1000,
             exponent: 1,
@@ -28,10 +28,10 @@ impl Default for Exponential<fn(usize) -> usize> {
     }
 }
 
-impl Exponential<fn(usize) -> usize> {
-    /// Create a new exponential backoff iterator that backs off in randomized, ever increasing steps.
+impl Quadratic<fn(usize) -> usize> {
+    /// Create a new quadratic backoff iterator that backs off in randomized, ever increasing steps.
     pub fn default_with_random() -> Self {
-        Exponential {
+        Quadratic {
             multiplier: 1,
             max_multiplier: 1000,
             exponent: 1,
@@ -40,7 +40,7 @@ impl Exponential<fn(usize) -> usize> {
     }
 }
 
-impl<Transform> Exponential<Transform>
+impl<Transform> Quadratic<Transform>
 where
     Transform: Fn(usize) -> usize,
 {
@@ -62,7 +62,7 @@ where
     }
 }
 
-impl<Transform> Iterator for Exponential<Transform>
+impl<Transform> Iterator for Quadratic<Transform>
 where
     Transform: Fn(usize) -> usize,
 {
diff --git a/gix-utils/tests/backoff/mod.rs b/gix-utils/tests/backoff/mod.rs
index ca9077841ca..769ed916691 100644
--- a/gix-utils/tests/backoff/mod.rs
+++ b/gix-utils/tests/backoff/mod.rs
@@ -1,6 +1,6 @@
 use std::time::Duration;
 
-use gix_utils::backoff::Exponential;
+use gix_utils::backoff::Quadratic;
 
 const EXPECTED_TILL_SECOND: &[usize] = &[
     1usize, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400, 441, 484, 529, 576,
@@ -8,9 +8,9 @@ const EXPECTED_TILL_SECOND: &[usize] = &[
 ];
 
 #[test]
-fn random_exponential_produces_values_in_the_correct_range() {
+fn random_quadratic_produces_values_in_the_correct_range() {
     let mut num_identities = 0;
-    for (actual, expected) in Exponential::default_with_random().zip(EXPECTED_TILL_SECOND) {
+    for (actual, expected) in Quadratic::default_with_random().zip(EXPECTED_TILL_SECOND) {
         let actual: usize = actual.as_millis().try_into().unwrap();
         if actual == *expected {
             num_identities += 1;
@@ -33,9 +33,9 @@ fn random_exponential_produces_values_in_the_correct_range() {
 #[test]
 fn how_many_iterations_for_a_second_of_waittime() {
     let max = Duration::from_millis(1000);
-    assert_eq!(Exponential::default().until_no_remaining(max).count(), 14);
+    assert_eq!(Quadratic::default().until_no_remaining(max).count(), 14);
     assert_eq!(
-        Exponential::default()
+        Quadratic::default()
             .until_no_remaining(max)
             .reduce(|acc, n| acc + n)
             .unwrap(),
@@ -47,7 +47,7 @@ fn how_many_iterations_for_a_second_of_waittime() {
 #[test]
 fn output_with_default_settings() {
     assert_eq!(
-        Exponential::default().take(33).collect::<Vec<_>>(),
+        Quadratic::default().take(33).collect::<Vec<_>>(),
         EXPECTED_TILL_SECOND
             .iter()
             .map(|n| Duration::from_millis(*n as u64))

From bf4fa1b5e4a59399284dce4da51943f034e8023c Mon Sep 17 00:00:00 2001
From: Eliah Kagan <degeneracypressure@gmail.com>
Date: Mon, 27 Jan 2025 03:48:35 -0500
Subject: [PATCH 2/3] Adapt to changes in `gix-utils`

`Exponential` was renamed to `Quadratic` in `gix_utils::backoff`.
This updates the use (and comment) in `gix-lock` accordingly.
---
 gix-lock/src/acquire.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/gix-lock/src/acquire.rs b/gix-lock/src/acquire.rs
index 750e5b76af4..2c993dbc4ed 100644
--- a/gix-lock/src/acquire.rs
+++ b/gix-lock/src/acquire.rs
@@ -14,7 +14,7 @@ pub enum Fail {
     /// Fail after the first unsuccessful attempt of obtaining a lock.
     #[default]
     Immediately,
-    /// Retry after failure with exponentially longer sleep times to block the current thread.
+    /// Retry after failure with quadratically longer sleep times to block the current thread.
     /// Fail once the given duration is exceeded, similar to [Fail::Immediately]
     AfterDurationWithBackoff(Duration),
 }
@@ -176,7 +176,7 @@ fn lock_with_mode<T>(
     match mode {
         Fail::Immediately => try_lock(&lock_path, directory, cleanup),
         Fail::AfterDurationWithBackoff(time) => {
-            for wait in backoff::Exponential::default_with_random().until_no_remaining(time) {
+            for wait in backoff::Quadratic::default_with_random().until_no_remaining(time) {
                 attempts += 1;
                 match try_lock(&lock_path, directory, cleanup.clone()) {
                     Ok(v) => return Ok((lock_path, v)),

From 6990f76a88b0a629a1bd578979218f5e791a862e Mon Sep 17 00:00:00 2001
From: Eliah Kagan <degeneracypressure@gmail.com>
Date: Sun, 26 Jan 2025 12:31:16 -0500
Subject: [PATCH 3/3] Comment forthcoming dependency changes for gix-testtools

- Add a TODO comment about changing `Exponential` to `Quadratic`.
  This cannot be done yet, but when upgrading dependencies, it will
  need to be done (since the rename is a breaking change).

- Replace the TODO comment about `login_shell()` with one about how
  to use the value of `shell()` on Windows, and modify the last
  component, since #1758 replaced `login_shell()` with `shell()`.

  `shell()` returns a path to `sh`, and gix-testtools currently
  needs `bash` for fixtures. But the specific approach for finding
  `sh.exe` relative to the Git for Windows `git-core` directory
  also works for `bash.exe`.
---
 tests/tools/src/lib.rs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/tests/tools/src/lib.rs b/tests/tools/src/lib.rs
index cb91c0b87c6..82510d627de 100644
--- a/tests/tools/src/lib.rs
+++ b/tests/tools/src/lib.rs
@@ -219,6 +219,7 @@ pub fn spawn_git_daemon(working_dir: impl AsRef<Path>) -> std::io::Result<GitDae
             .spawn()?;
 
     let server_addr = addr_at(free_port);
+    // TODO(deps): Upgrading dependencies will require changing `Exponential` to `Quadratic`.
     for time in gix_lock::backoff::Exponential::default_with_random() {
         std::thread::sleep(time);
         if std::net::TcpStream::connect(server_addr).is_ok() {
@@ -652,8 +653,8 @@ fn configure_command<'a, I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
 }
 
 fn bash_program() -> &'static Path {
-    // TODO: use `gix_path::env::login_shell()` when available.
     if cfg!(windows) {
+        // TODO(deps): Once `gix_path::env::shell()` is available, maybe do `shell().parent()?.join("bash.exe")`
         static GIT_BASH: Lazy<Option<PathBuf>> = Lazy::new(|| {
             GIT_CORE_DIR
                 .parent()?