Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
- fixed: `git machete discover` no longer produces a different branch tree depending on which worktree it is run from;
the fresh-branch recency ranking now aggregates HEAD reflogs across all worktrees rather than only the current one (reported by @jasonoura, contributed by @earfman)
- fixed: when run from a branch being slid out, `git machete slide-out` no longer checks out that branch's new parent if a child branch is going to be checked out right afterwards anyway for the rebase/merge
- fixed: pull/merge request-reading commands (`anno-prs`, `checkout-prs`, `retarget-pr`, `restack-pr`, `update-pr-descriptions` and their GitLab counterparts)
now address the base/target repository - the one that actually hosts the PR/MR - when the `machete.{github,gitlab}.base*` git config keys are set, rather than the head/source repository

## New in git-machete 3.44.1

Expand Down
28 changes: 24 additions & 4 deletions docs/man/git-machete.1

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion docs/source/git-config-keys/github.access.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
The name of the git remote (as in ``git remote``) that git-machete pushes the head branch to.
Unless both ``machete.github.organization`` and ``machete.github.repository`` are set, this remote's URL is also inspected
to derive the GitHub organization and repository that the pull request resides in.
The pull request is operated on through the GitHub API, which addresses that organization/repository rather than a git remote.
Unless the ``machete.github.base*`` keys below point elsewhere, the pull request is operated on through the GitHub API,
which addresses that organization/repository rather than a git remote.
By default (when this key is unset), if exactly one remote's URL corresponds to GitHub, that remote is selected automatically;
set this key to disambiguate when more than one remote points to GitHub.
For example, ``git config machete.github.remote origin``
Expand All @@ -22,6 +23,10 @@
``machete.github.baseRemote``
Like ``machete.github.remote``, but used to locate the base repository that the pull request targets,
which may differ from the head repository (for example, the base in an upstream repository and the head in a fork).
Setting this key is what makes the PR-reading/-modifying commands (``anno-prs``, ``checkout-prs``, ``retarget-pr``,
``restack-pr``, ``update-pr-descriptions``) address that base repository rather than the head one.
``create-pr`` does not need it: it infers the base repository from the base branch's tracking remote,
so it already targets the correct base (even one in a separate fork/upstream repository) even when this key is unset.
Defaults to ``machete.github.remote`` when unset.
For example, ``git config machete.github.baseRemote upstream``

Expand Down
7 changes: 6 additions & 1 deletion docs/source/git-config-keys/gitlab.access.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
The name of the git remote (as in ``git remote``) that git-machete pushes the source branch to.
Unless both ``machete.gitlab.namespace`` and ``machete.gitlab.project`` are set, this remote's URL is also inspected
to derive the GitLab namespace and project that the merge request resides in.
The merge request is operated on through the GitLab API, which addresses that namespace/project rather than a git remote.
Unless the ``machete.gitlab.base*`` keys below point elsewhere, the merge request is operated on through the GitLab API,
which addresses that namespace/project rather than a git remote.
By default (when this key is unset), if exactly one remote's URL corresponds to GitLab, that remote is selected automatically;
set this key to disambiguate when more than one remote points to GitLab.
For example, ``git config machete.gitlab.remote origin``
Expand All @@ -22,6 +23,10 @@
``machete.gitlab.baseRemote``
Like ``machete.gitlab.remote``, but used to locate the target project that the merge request targets,
which may differ from the source project (for example, the target in an upstream project and the source in a fork).
Setting this key is what makes the MR-reading/-modifying commands (``anno-mrs``, ``checkout-mrs``, ``retarget-mr``,
``restack-mr``, ``update-mr-descriptions``) address that target project rather than the source one.
``create-mr`` does not need it: it infers the target project from the target branch's tracking remote,
so it already targets the correct project (even one in a separate fork/upstream project) even when this key is unset.
Defaults to ``machete.gitlab.remote`` when unset.
For example, ``git config machete.gitlab.baseRemote upstream``

Expand Down
46 changes: 39 additions & 7 deletions git_machete/client/with_code_hosting.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,8 @@ def create_pull_request(
def restack_pull_request(self, *, opt_update_related_descriptions: bool) -> None:
spec = self.code_hosting_spec
head = self._git.get_current_branch()
_, org_repo_remote = self._init_code_hosting_client(branch_used_for_tracking_data=head)
_, org_repo_remote = self._init_code_hosting_client(
branch_used_for_tracking_data=head, base_branch_used_for_tracking_data=self.parent_of(head))

pr: Optional[PullRequest] = self.__get_sole_pull_request_for_head(head, ignore_if_missing=False)
assert pr is not None
Expand Down Expand Up @@ -538,7 +539,8 @@ def retarget_pull_request(self, *, opt_branch: Optional[LocalBranchShortName],
head: ManagedBranchName = self.expect_in_managed_branches(opt_branch or self._git.get_current_branch())
spec = self.code_hosting_spec
if self.__code_hosting_client is None:
self._init_code_hosting_client(branch_used_for_tracking_data=head)
self._init_code_hosting_client(
branch_used_for_tracking_data=head, base_branch_used_for_tracking_data=self.parent_of(head))

pr: Optional[PullRequest] = self.__get_sole_pull_request_for_head(
head, ignore_if_missing=opt_ignore_if_missing)
Expand Down Expand Up @@ -605,7 +607,8 @@ def __derive_org_repo_and_remote(
keys = spec.git_config_keys
if is_base:
remote_key, org_key, repo_key = keys.base_remote, keys.base_organization, keys.base_repository
# The base remote falls back to the (non-base) remote; the base organization/repository have no such fallback.
# The base remote falls back to the (non-base) remote; the base organization/repository have no such fallback
# (`create_pull_request` relies on that to detect a fork base).
remote_from_config = self._config.code_hosting_base_remote(keys) or self._config.code_hosting_remote(keys)
org_from_config = self._config.code_hosting_base_organization(keys)
repo_from_config = self._config.code_hosting_base_repository(keys)
Expand Down Expand Up @@ -691,16 +694,45 @@ def __derive_org_repo_and_remote(
f'{spec.git_config_keys.for_locating_repo_message()}\n')

def _init_code_hosting_client(self,
branch_used_for_tracking_data: Optional[LocalBranchShortName] = None
branch_used_for_tracking_data: Optional[LocalBranchShortName] = None,
base_branch_used_for_tracking_data: Optional[LocalBranchShortName] = None
) -> Tuple[str, OrganizationAndRepositoryAndRemote]:
if self.__code_hosting_client is not None:
raise UnexpectedMacheteException("Code hosting client has already been initialized.")
domain = self.__derive_code_hosting_domain()
org_repo_remote = self.__derive_org_repo_and_remote(
# PR-reading/-modifying commands must talk to the repository that *hosts* the PRs, i.e. the base repository.
# In a fork workflow the base (upstream) repository differs from the head (fork) repository that holds the branches,
# so the code hosting client is created against the base repository, while the returned head remote is still what
# callers use to fetch/push branches.
# The base repository is located in two ways: explicit machete.<spec>.base* config keys take precedence (honored for
# every PR-reading/-modifying command); otherwise, when a base branch is given (retarget/restack), the base repository
# is inferred from that branch's tracking remote, exactly like create_pull_request does. Inference is best-effort: if it
# cannot be resolved unambiguously (e.g. the base branch has no tracking data among several candidate remotes), we fall
# back to the head repository, preserving the pre-inference behavior. When neither applies, the base repository resolves
# to the head one, so this is a no-op for the common (non-fork) case.
head_org_repo_remote = self.__derive_org_repo_and_remote(
domain=domain, branch_used_for_tracking_data=branch_used_for_tracking_data)
keys = self.code_hosting_spec.git_config_keys
base_config_present = (
self._config.code_hosting_base_remote(keys) is not None or
self._config.code_hosting_base_organization(keys) is not None or
self._config.code_hosting_base_repository(keys) is not None)
base_org_repo_remote = head_org_repo_remote
if base_config_present:
base_org_repo_remote = self.__derive_org_repo_and_remote(
domain=domain,
branch_used_for_tracking_data=base_branch_used_for_tracking_data or branch_used_for_tracking_data,
is_base=True)
elif base_branch_used_for_tracking_data is not None:
try:
base_org_repo_remote = self.__derive_org_repo_and_remote(
domain=domain, branch_used_for_tracking_data=base_branch_used_for_tracking_data, is_base=True)
except MacheteException:
# Ambiguous inference with no explicit base* config to honor -> fall back to the head repository.
pass
self.code_hosting_client = self.code_hosting_spec.create_client(
domain=domain, organization=org_repo_remote.organization, repository=org_repo_remote.repository)
return domain, org_repo_remote
domain=domain, organization=base_org_repo_remote.organization, repository=base_org_repo_remote.repository)
return domain, head_org_repo_remote

START_GIT_MACHETE_GENERATED_COMMENT = '<!-- start git-machete generated -->'
END_GIT_MACHETE_GENERATED_COMMENT = '<!-- end git-machete generated -->'
Expand Down
Loading