Skip to content

Commit 09b9770

Browse files
committed
Checkout GitLab and GitHub PRs differently to improve upstream tracking.
Prior to this change, GitLab branches were not checked out in such a way that git recorded an upstream ref to track. Since there was no tracking info, `git pull` no longer worked. This commit updates this so the remote branch is first fetched, and then the local branch is explicitly checked out as tracking the remote one. git fetch origin my-mr-branch git checkout -b my-mr-branch origin/my-mr-branch For GitHub, the existing method has been preserved as the namespace refs the service makes available are read-only. Tracking isn't available with this method. git fetch origin pull/1337/head:pr/1337 git checkout pr/1337 I don't think GitHub makes that available (see: https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/checking-out-pull-requests-locally)
1 parent c9d2d66 commit 09b9770

5 files changed

Lines changed: 49 additions & 25 deletions

File tree

src/git.rs

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ pub fn checkout_branch(
119119
remote_name: &str,
120120
remote_branch_name: &str,
121121
local_branch_name: &str,
122+
is_virtual_remote_branch: bool,
122123
) -> Result<bool, String> {
123124
let repo = Repository::open_from_env().expect("Couldn't find repository");
124125
let local_branch_name = match get_default_remote_name() {
@@ -140,32 +141,41 @@ pub fn checkout_branch(
140141
}
141142
};
142143

143-
// Fetch the remote branch if there's no local branch with the correct name
144-
if repo.revparse_single(&local_branch_name).is_err() {
145-
if cmd!(
146-
"git",
147-
"fetch",
148-
remote_name,
149-
&format!("{}:{}", remote_branch_name, local_branch_name)
150-
)
151-
.run()
152-
.is_err()
153-
{
154-
return Err(format!(
155-
"Could not fetch remote branch '{}' to local ref '{}'",
156-
remote_branch_name, local_branch_name
157-
));
144+
let local_branch_exists = repo.revparse_single(&local_branch_name);
145+
match local_branch_exists {
146+
Ok(_) => {
147+
debug!("Checking out branch: {}", local_branch_name);
148+
match cmd!("git", "checkout", &local_branch_name).run() {
149+
Ok(_) => Ok(true),
150+
Err(err) => Err(format!("Could not check out local branch: {}", err)),
151+
}
158152
}
159-
if repo.revparse_single(&local_branch_name).is_err() {
160-
return Err(format!(
161-
"Could not find remote branch: {}",
162-
local_branch_name
163-
));
153+
Err(_) => {
154+
// Fetch the remote branch if there's no local branch with the correct name
155+
let mut fetch_args = vec!["fetch", &remote_name];
156+
let remote_to_local_binding = format!("{}:{}", remote_branch_name, local_branch_name);
157+
fetch_args.push(if is_virtual_remote_branch {
158+
&remote_to_local_binding
159+
} else {
160+
&remote_branch_name
161+
});
162+
if cmd("git", fetch_args).run().is_err() {
163+
return Err(format!(
164+
"Could not fetch remote branch '{}'",
165+
remote_branch_name
166+
));
167+
};
168+
debug!("Checking out branch: {}", local_branch_name);
169+
let origin_with_remote = format!("{}/{}", remote_name, remote_branch_name);
170+
let remote_ref = if is_virtual_remote_branch {
171+
&local_branch_name
172+
} else {
173+
&origin_with_remote
174+
};
175+
match cmd!("git", "checkout", "-b", &local_branch_name, &remote_ref).run() {
176+
Ok(_) => Ok(true),
177+
Err(err) => Err(format!("Could not check out local branch: {}", err)),
178+
}
164179
}
165180
}
166-
debug!("Checking out branch: {}", local_branch_name);
167-
match cmd!("git", "checkout", local_branch_name).run() {
168-
Ok(_) => Ok(true),
169-
Err(err) => Err(format!("Could not check out local branch: {}", err)),
170-
}
171181
}

src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ fn checkout_mr(remote_name: &str, mr_id: i64) {
6363
remote_name,
6464
&remote_branch_name,
6565
&remote.get_local_req_branch(mr_id).unwrap(),
66+
remote.has_virtual_remote_branch_names(),
6667
) {
6768
Ok(_) => {
6869
info!("Done!");

src/remotes/github.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ impl Remote for GitHub {
4747
fn has_useful_branch_names(&mut self) -> bool {
4848
false
4949
}
50+
51+
fn has_virtual_remote_branch_names(&mut self) -> bool {
52+
true
53+
}
5054
}
5155

5256
/// Convert a GitHub PR to a git-req MergeRequest

src/remotes/gitlab.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ impl Remote for GitLab {
7373
fn has_useful_branch_names(&mut self) -> bool {
7474
true
7575
}
76+
77+
fn has_virtual_remote_branch_names(&mut self) -> bool {
78+
false
79+
}
7680
}
7781

7882
/// Query the GitLab API

src/remotes/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ pub trait Remote {
3232
/// Determine if the branch names are useful to display
3333
fn has_useful_branch_names(&mut self) -> bool;
3434

35+
/// If the remote branch is a namespaced ref instead of an actual branch
36+
// This is useful for GitHub's `pull/mr/head` refs, where they're read-only
37+
fn has_virtual_remote_branch_names(&mut self) -> bool;
38+
39+
/// The domain that hosts the remote
3540
fn get_domain(&mut self) -> &str;
3641
}
3742

0 commit comments

Comments
 (0)