Repository: VirtusLab/git-machete
Pull request #1367 (2024-11-04 12:07:24)

Allow disabling fetch for certain remotes in traverse
This PR adds a new configuration option, `machete.traverse.fetch.<remote>`, that allows disabling the fetch on certain remotes when running `git machete traverse --fetch` (or `git machete traverse -W`, which includes `--fetch`). 

The motivation: we might have remotes that are temporarily offline, or take a long time to respond, but are not the main remote we use to synchronize. This config allows continued usage of `git machete traverse -W` for the remotes that are working.

PawelLipski: Nice! I'm gonna take a closer look tmrw
PawelLipski: Ready after a few patches... to be release soon as v3.31.0

Patch:
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index f074121..74ab82d 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -4,6 +4,7 @@
 
 - added: `git machete git{hub,lab} update-{pr,mr}-descriptions` subcommands
 - added: `git machete gith{hub,lab} create-{pr,mr} --update-related-descriptions` flags
+- added: `machete.traverse.fetch.<remote>` git config key to selectively exclude remotes from `git machete traverse --fetch` (contributed by @gjulianm)
 
 ## New in git-machete 3.30.0
 
diff --git a/docs/man/git-machete.1 b/docs/man/git-machete.1
index f910fd1..ef28221 100644
--- a/docs/man/git-machete.1
+++ b/docs/man/git-machete.1
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
 .in \\n[rst2man-indent\\n[rst2man-indent-level]]u
 ..
-.TH "GIT-MACHETE" "1" "Nov 01, 2024" "" "git-machete"
+.TH "GIT-MACHETE" "1" "Nov 05, 2024" "" "git-machete"
 .SH NAME
 git-machete \- git-machete 3.31.0
 .sp
@@ -483,6 +483,12 @@ develop
 .UNINDENT
 .UNINDENT
 .TP
+.B \fBmachete.traverse.fetch.<remote>\fP:
+Configures the behavior of \fBgit machete traverse\fP command for the given remote when \fB\-\-fetch\fP flag is used.
+If set to \fBfalse\fP, this remote will not be fetched before the traversal.
+The default value of this configuration is \fBtrue\fP\&.
+This is useful for excluding remotes that are temporarily offline, or take a long time to respond.
+.TP
 .B \fBmachete.traverse.push\fP:
 To change the behavior of \fBgit machete traverse\fP command so that it doesn\(aqt push branches by default,
 you need to set config key \fBgit config machete.traverse.push false\fP\&.
@@ -2190,6 +2196,12 @@ whether a grey edge is displayed in \fBstatus\fP,
 whether \fBtraverse\fP suggests to slide out the branch.
 .UNINDENT
 .TP
+.B \fBmachete.traverse.fetch.<remote>\fP:
+Configures the behavior of \fBgit machete traverse\fP command for the given remote when \fB\-\-fetch\fP flag is used.
+If set to \fBfalse\fP, this remote will not be fetched before the traversal.
+The default value of this configuration is \fBtrue\fP\&.
+This is useful for excluding remotes that are temporarily offline, or take a long time to respond.
+.TP
 .B \fBmachete.traverse.push\fP
 To change the behavior of \fBgit machete traverse\fP command so that it doesn\(aqt push branches by default,
 you need to set config key \fBgit config machete.traverse.push false\fP\&.
diff --git a/docs/source/cli/config.rst b/docs/source/cli/config.rst
index 879f181..e29a613 100644
--- a/docs/source/cli/config.rst
+++ b/docs/source/cli/config.rst
@@ -48,6 +48,9 @@ Note: ``config`` is not a command as such, just a help topic (there is no ``git
 ``machete.status.extraSpaceBeforeBranchName``:
     .. include:: git-config-keys/status_extraSpaceBeforeBranchName.rst
 
+``machete.traverse.fetch.<remote>``:
+    .. include:: git-config-keys/traverse_fetch_remote.rst
+
 ``machete.traverse.push``:
     .. include:: git-config-keys/traverse_push.rst
 
diff --git a/docs/source/cli/traverse.rst b/docs/source/cli/traverse.rst
index 331f6bd..9d350ff 100644
--- a/docs/source/cli/traverse.rst
+++ b/docs/source/cli/traverse.rst
@@ -147,5 +147,8 @@ when the current user is NOT the author of the PR/MR associated with that branch
 ``machete.squashMergeDetection``:
     .. include:: git-config-keys/squashMergeDetection.rst
 
+``machete.traverse.fetch.<remote>``:
+    .. include:: git-config-keys/traverse_fetch_remote.rst
+
 ``machete.traverse.push``
     .. include:: git-config-keys/traverse_push.rst
diff --git a/docs/source/git-config-keys/traverse_fetch_remote.rst b/docs/source/git-config-keys/traverse_fetch_remote.rst
new file mode 100644
index 0000000..083a3a9
--- /dev/null
+++ b/docs/source/git-config-keys/traverse_fetch_remote.rst
@@ -0,0 +1,4 @@
+Configures the behavior of ``git machete traverse`` command for the given remote when ``--fetch`` flag is used.
+If set to ``false``, this remote will not be fetched before the traversal.
+The default value of this configuration is ``true``.
+This is useful for excluding remotes that are temporarily offline, or take a long time to respond.
diff --git a/git_machete/client.py b/git_machete/client.py
index 0786bbc..db54483 100644
--- a/git_machete/client.py
+++ b/git_machete/client.py
@@ -773,8 +773,9 @@ class MacheteClient:
 
         if opt_fetch:
             for rem in self.__git.get_remotes():
-                print(f"Fetching {bold(rem)}...")
-                self.__git.fetch_remote(rem)
+                if self.remote_enabled_for_traverse_fetch(rem):
+                    print(f"Fetching {bold(rem)}...")
+                    self.__git.fetch_remote(rem)
             if self.__git.get_remotes():
                 print("")
 
@@ -1869,6 +1870,9 @@ class MacheteClient:
                     debug(f"upstream candidate {candidate} rejected ({reject_reason_message})")
         return None
 
+    def remote_enabled_for_traverse_fetch(self, remote: str) -> bool:
+        return self.__git.get_boolean_config_attr(git_config_keys.traverse_remote_fetch(remote), True)
+
     # Also includes config that is invalid (corresponding to a non-existent/GCed commit etc.).
     def has_any_fork_point_override_config(self, branch: LocalBranchShortName) -> bool:
         return (self.__git.get_config_attr_or_none(git_config_keys.override_fork_point_to(branch)) or
diff --git a/git_machete/generated_docs.py b/git_machete/generated_docs.py
index dacbd40..fbbd66f 100644
--- a/git_machete/generated_docs.py
+++ b/git_machete/generated_docs.py
@@ -346,6 +346,12 @@ long_docs: Dict[str, str] = {
               │
               └─ feature_branch2
 
+           `machete.traverse.fetch.<remote>`:
+              Configures the behavior of `git machete traverse` command for the given remote when `--fetch` flag is used.
+              If set to `false`, this remote will not be fetched before the traversal.
+              The default value of this configuration is `true`.
+              This is useful for excluding remotes that are temporarily offline, or take a long time to respond.
+
            `machete.traverse.push`:
  
               To change the behavior of `git machete traverse` command so that it doesn't push branches by default,
@@ -1532,6 +1538,12 @@ long_docs: Dict[str, str] = {
 
                  * whether `traverse` suggests to slide out the branch.
 
+           `machete.traverse.fetch.<remote>`:
+              Configures the behavior of `git machete traverse` command for the given remote when `--fetch` flag is used.
+              If set to `false`, this remote will not be fetched before the traversal.
+              The default value of this configuration is `true`.
+              This is useful for excluding remotes that are temporarily offline, or take a long time to respond.
+
            `machete.traverse.push`
  
               To change the behavior of `git machete traverse` command so that it doesn't push branches by default,
diff --git a/git_machete/git_config_keys.py b/git_machete/git_config_keys.py
index 88dc170..a44e004 100644
--- a/git_machete/git_config_keys.py
+++ b/git_machete/git_config_keys.py
@@ -10,3 +10,7 @@ def override_fork_point_to(branch: str) -> str:
 
 def override_fork_point_while_descendant_of(branch: str) -> str:
     return f'machete.overrideForkPoint.{branch}.whileDescendantOf'
+
+
+def traverse_remote_fetch(remote: str) -> str:
+    return f'machete.traverse.fetch.{remote}'


Test patch:
diff --git a/tests/test_traverse.py b/tests/test_traverse.py
index 0f984f8..16718cc 100644
--- a/tests/test_traverse.py
+++ b/tests/test_traverse.py
@@ -1167,8 +1167,8 @@ class TestTraverse(BaseTest):
         (
             self.repo_sandbox
             .remove_remote("origin")
-            .add_remote("origin_1", origin_1_remote_path)
-            .add_remote("origin_2", origin_2_remote_path)
+            .add_remote("origin-1", origin_1_remote_path)
+            .add_remote("origin-2", origin_2_remote_path)
         )
 
         self.repo_sandbox.new_branch("master").commit()
@@ -1176,11 +1176,14 @@ class TestTraverse(BaseTest):
 
         self.patch_symbol(mocker, 'builtins.input', mock_input_returning("xd"))
         assert_success(
-            ["traverse"],
+            ["traverse", "--fetch"],
             """
+            Fetching origin-1...
+            Fetching origin-2...
+
             Branch master is untracked and there's no origin remote.
-            [1] origin_1
-            [2] origin_2
+            [1] origin-1
+            [2] origin-2
             Select number 1..2 to specify the destination remote repository, or 'n' to skip this branch, or 'q' to quit the traverse:
 
               master * (untracked)
@@ -1190,18 +1193,21 @@ class TestTraverse(BaseTest):
         )
 
         self.patch_symbol(mocker, 'builtins.input', mock_input_returning("1", "o", "2", "yq"))
+        self.repo_sandbox.set_git_config_key("machete.traverse.fetch.origin-2", "false")
         assert_success(
-            ["traverse"],
+            ["traverse", "--fetch"],
             """
+            Fetching origin-1...
+
             Branch master is untracked and there's no origin remote.
-            [1] origin_1
-            [2] origin_2
+            [1] origin-1
+            [2] origin-2
             Select number 1..2 to specify the destination remote repository, or 'n' to skip this branch, or 'q' to quit the traverse:
-            Push untracked branch master to origin_1? (y, N, q, yq, o[ther-remote])
-            [1] origin_1
-            [2] origin_2
+            Push untracked branch master to origin-1? (y, N, q, yq, o[ther-remote])
+            [1] origin-1
+            [2] origin-2
             Select number 1..2 to specify the destination remote repository, or 'n' to skip this branch, or 'q' to quit the traverse:
-            Push untracked branch master to origin_2? (y, N, q, yq, o[ther-remote])
+            Push untracked branch master to origin-2? (y, N, q, yq, o[ther-remote])
             """
         )