diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 736a55665..61c0a6887 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -2,6 +2,7 @@
## New in git-machete 3.45.1
+- fixed: `git machete traverse --sync-github-prs`/`--sync-gitlab-mrs` no longer offers to create a PR/MR for a branch annotated with `push=no`
- fixed: `git machete discover` preserves full annotations on rediscovered branches, rather than only their traversal qualifiers (contributed by @be-student)
- fixed: branch layout files are now always read and written as UTF-8 rather than the process locale encoding, so annotations with non-ASCII characters no longer break on Windows
diff --git a/docs/man/git-machete.1 b/docs/man/git-machete.1
index 30613a578..ad77e3d3b 100644
--- a/docs/man/git-machete.1
+++ b/docs/man/git-machete.1
@@ -2349,7 +2349,7 @@ asks the user whether to \fBpull\fP the branch
if \fB\-H\fP/\fB\-\-sync\-github\-prs\fP or \fB\-L\fP/\fB\-\-sync\-gitlab\-mrs\fP option is present:
.INDENT 2.0
.IP \(bu 2
-asks the user whether to \fBcreate\fP a PR/MR for the given branch if it doesn\(aqt exist yet,
+asks the user whether to \fBcreate\fP a PR/MR for the given branch if it doesn\(aqt exist yet (unless the branch is annotated with \fBpush=no\fP),
.IP \(bu 2
asks the user whether to \fBretarget\fP the PR/MR if it exists for the given branch,
and its base/target branch in GitHub/GitLab is different than the upstream in machete file
@@ -2374,6 +2374,7 @@ Unlike with \fBgit rebase\fP or \fBgit cherry\-pick\fP, there is no special \fB\
.sp
The rebase, push and slide\-out behaviors of \fBtraverse\fP can also be customized for each branch separately using \fIbranch qualifiers\fP\&.
There are \fBpush=no\fP, \fBrebase=no\fP and \fBslide\-out=no\fP qualifiers that can be used to opt out of default behavior (rebasing, pushing and sliding the branch out).
+\fBpush=no\fP also skips creating a PR/MR under \fB\-\-sync\-github\-prs\fP/\fB\-\-sync\-gitlab\-mrs\fP\&.
The qualifier can appear anywhere in the annotation, but needs to be separated by a whitespace from any other character, as in: \fBsome_annotation_text rebase=no push=no slide\-out=no\fP\&.
Qualifiers can only be overwritten by manually editing \fB\&.git/machete\fP file or modifying it with \fBgit machete e[dit]\fP, or by updating annotations with \fBgit machete anno\fP\&.
Example machete file with branch qualifiers:
diff --git a/docs/source/cli/traverse.rst b/docs/source/cli/traverse.rst
index 57005e217..43a340b9f 100644
--- a/docs/source/cli/traverse.rst
+++ b/docs/source/cli/traverse.rst
@@ -50,7 +50,7 @@ For each branch, the command performs the following actions:
* if ``-H``/``--sync-github-prs`` or ``-L``/``--sync-gitlab-mrs`` option is present:
- - asks the user whether to **create** a PR/MR for the given branch if it doesn't exist yet,
+ - asks the user whether to **create** a PR/MR for the given branch if it doesn't exist yet (unless the branch is annotated with ``push=no``),
- asks the user whether to **retarget** the PR/MR if it exists for the given branch,
and its base/target branch in GitHub/GitLab is different than the upstream in machete file
@@ -69,6 +69,7 @@ Unlike with ``git rebase`` or ``git cherry-pick``, there is no special ``--conti
The rebase, push and slide-out behaviors of ``traverse`` can also be customized for each branch separately using *branch qualifiers*.
There are ``push=no``, ``rebase=no`` and ``slide-out=no`` qualifiers that can be used to opt out of default behavior (rebasing, pushing and sliding the branch out).
+``push=no`` also skips creating a PR/MR under ``--sync-github-prs``/``--sync-gitlab-mrs``.
The qualifier can appear anywhere in the annotation, but needs to be separated by a whitespace from any other character, as in: ``some_annotation_text rebase=no push=no slide-out=no``.
Qualifiers can only be overwritten by manually editing ``.git/machete`` file or modifying it with ``git machete e[dit]``, or by updating annotations with ``git machete anno``.
Example machete file with branch qualifiers:
diff --git a/git_machete/client/traverse.py b/git_machete/client/traverse.py
index 885277fa5..10ff13124 100644
--- a/git_machete/client/traverse.py
+++ b/git_machete/client/traverse.py
@@ -276,7 +276,8 @@ def traverse(
if parent:
prs = [_pr for _pr in self._get_all_open_prs() if _pr.head == branch]
if not prs:
- needs_create_pr = True
+ if branch_anno is None or branch_anno.qualifiers.push:
+ needs_create_pr = True
use_merge = opt_merge or (branch_anno is not None and branch_anno.qualifiers.update_with_merge)
diff --git a/git_machete/generated_docs.py b/git_machete/generated_docs.py
index 3d57a076e..c5afe251f 100644
--- a/git_machete/generated_docs.py
+++ b/git_machete/generated_docs.py
@@ -1686,7 +1686,7 @@
* if `-H`/`--sync-github-prs` or `-L`/`--sync-gitlab-mrs` option is present:
- - asks the user whether to create a PR/MR for the given branch if it doesn't exist yet,
+ - asks the user whether to create a PR/MR for the given branch if it doesn't exist yet (unless the branch is annotated with `push=no`),
- asks the user whether to retarget the PR/MR if it exists for the given branch,
and its base/target branch in GitHub/GitLab is different than the upstream in machete file
@@ -1705,6 +1705,7 @@
The rebase, push and slide-out behaviors of `traverse` can also be customized for each branch separately using branch qualifiers.
There are `push=no`, `rebase=no` and `slide-out=no` qualifiers that can be used to opt out of default behavior (rebasing, pushing and sliding the branch out).
+ `push=no` also skips creating a PR/MR under `--sync-github-prs`/`--sync-gitlab-mrs`.
The qualifier can appear anywhere in the annotation, but needs to be separated by a whitespace from any other character, as in: `some_annotation_text rebase=no push=no slide-out=no`.
Qualifiers can only be overwritten by manually editing `.git/machete` file or modifying it with `git machete e[dit]`, or by updating annotations with `git machete anno`.
Example machete file with branch qualifiers:
diff --git a/tests/test_traverse_github.py b/tests/test_traverse_github.py
index 1c2e21cb3..5210a7142 100644
--- a/tests/test_traverse_github.py
+++ b/tests/test_traverse_github.py
@@ -368,3 +368,67 @@ def test_traverse_sync_create_github_prs(self, mocker: MockerFixture) -> None:
Reached branch drop-constraint which has no successor; nothing left to update
"""
)
+
+ def test_traverse_sync_create_github_prs_skips_push_no(self, mocker: MockerFixture) -> None:
+ self.patch_symbol(mocker, 'git_machete.code_hosting.OrganizationAndRepository.from_url', mock_from_url)
+ self.patch_symbol(mocker, 'git_machete.github.GitHubToken.for_domain', mock_github_token_for_domain_fake)
+ self.patch_symbol(mocker, 'urllib.request.urlopen', mock_urlopen(MockGitHubAPIState.with_prs()))
+
+ create_repo_with_remote()
+ new_branch("develop")
+ commit()
+ push()
+ new_branch("someone-elses-branch")
+ commit()
+ push()
+ check_out("develop")
+ new_branch("annotated-ok")
+ commit()
+ push()
+ check_out("develop")
+ new_branch("my-branch")
+ commit()
+ push()
+
+ body: str = \
+ """
+ develop
+ someone-elses-branch rebase=no push=no
+ annotated-ok WIP
+ my-branch
+ """
+ rewrite_branch_layout_file(body)
+ check_out("develop")
+
+ self.patch_symbol(mocker, 'builtins.input', mock_input_returning("n", "q"))
+ assert_success(
+ ["traverse", "--sync-github-prs"],
+ """
+ Checking for open GitHub PRs... OK
+ Checking out annotated-ok... OK
+
+ develop
+ |
+ o-someone-elses-branch rebase=no push=no
+ |
+ o-annotated-ok * WIP
+ |
+ o-my-branch
+
+ Branch annotated-ok does not have a PR in GitHub.
+ Create a PR from annotated-ok to develop? (y, d[raft], N, q, yq)
+
+ Checking out my-branch... OK
+
+ develop
+ |
+ o-someone-elses-branch rebase=no push=no
+ |
+ o-annotated-ok WIP
+ |
+ o-my-branch *
+
+ Branch my-branch does not have a PR in GitHub.
+ Create a PR from my-branch to develop? (y, d[raft], N, q, yq)
+ """
+ )
diff --git a/tests/test_traverse_gitlab.py b/tests/test_traverse_gitlab.py
index 480b54d41..b6c812bc1 100644
--- a/tests/test_traverse_gitlab.py
+++ b/tests/test_traverse_gitlab.py
@@ -217,3 +217,67 @@ def test_traverse_sync_gitlab_mrs(self, mocker: MockerFixture) -> None:
Retarget MR !2 to allow-ownership-link? (y, N, q, yq)
Switching target branch of MR !2 to allow-ownership-link... OK
""")
+
+ def test_traverse_sync_create_gitlab_mrs_skips_push_no(self, mocker: MockerFixture) -> None:
+ self.patch_symbol(mocker, 'git_machete.code_hosting.OrganizationAndRepository.from_url', mock_from_url)
+ self.patch_symbol(mocker, 'git_machete.gitlab.GitLabToken.for_domain', mock_gitlab_token_for_domain_fake)
+ self.patch_symbol(mocker, 'urllib.request.urlopen', mock_urlopen(MockGitLabAPIState.with_mrs()))
+
+ create_repo_with_remote()
+ new_branch("develop")
+ commit()
+ push()
+ new_branch("someone-elses-branch")
+ commit()
+ push()
+ check_out("develop")
+ new_branch("annotated-ok")
+ commit()
+ push()
+ check_out("develop")
+ new_branch("my-branch")
+ commit()
+ push()
+
+ body: str = \
+ """
+ develop
+ someone-elses-branch rebase=no push=no
+ annotated-ok WIP
+ my-branch
+ """
+ rewrite_branch_layout_file(body)
+ check_out("develop")
+
+ self.patch_symbol(mocker, 'builtins.input', mock_input_returning("n", "q"))
+ assert_success(
+ ["traverse", "--sync-gitlab-mrs"],
+ """
+ Checking for open GitLab MRs... OK
+ Checking out annotated-ok... OK
+
+ develop
+ |
+ o-someone-elses-branch rebase=no push=no
+ |
+ o-annotated-ok * WIP
+ |
+ o-my-branch
+
+ Branch annotated-ok does not have an MR in GitLab.
+ Create an MR from annotated-ok to develop? (y, d[raft], N, q, yq)
+
+ Checking out my-branch... OK
+
+ develop
+ |
+ o-someone-elses-branch rebase=no push=no
+ |
+ o-annotated-ok WIP
+ |
+ o-my-branch *
+
+ Branch my-branch does not have an MR in GitLab.
+ Create an MR from my-branch to develop? (y, d[raft], N, q, yq)
+ """
+ )