From a03c67568cf15d914cb02476a1cb4a83585ac7d5 Mon Sep 17 00:00:00 2001 From: Jeff Wear Date: Wed, 17 Jul 2019 17:52:13 -0700 Subject: [PATCH] Get permalinks for the local HEAD not the HEAD of the remote branch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On GitHub, permalinks reference the HEAD of the remote branch since that’s what you’re looking at when you request the permalink (https://help.github.com/en/articles/getting-permanent-links-to-files). In Sublime, it makes more sense for permalinks to reference your local HEAD since _that’s_ what you’re looking at when you run a permalink command. Referencing the local HEAD lets the user run permalink commands with a commit checked out rather than a branch; or having checked out a branch that they've just created, if the user has neither added commits to that branch nor pushed the branch to the remote. If the user _has_ added commits to such a branch without pushing them to the remote, then the link will 404 on GitHub. Then again, previously, if the user had pushed the branch to the remote—just not the most recent commits on the branch—then we would have created an out-of-date permalink, and this would arguably have been a worse failure because it would be silent. At some future point, it might be nice to detect, locally, if a permalink was going to 404, and show an error message before the user went to GitHub. Referencing the local HEAD also fixes a bug in the previous implementation where we’d only successfully fetch the remote HEAD if the name of the remote branch was exactly the same as the local branch, since we executed `git rev-parse ` at line 508 without prefixing `remote_branch` with the name of the remote. Tests: - [x] “Open Remote URL in Browser” works if the current branch has been pushed to the remote - [x] “Open Remote URL in Browser” shows an error alert if the current branch has not been pushed to the remote - [x] “Open Remote URL in Browser” shows an error alert if a commit is checked out rather than a branch - [x] “Open Remote URL in Browser (permalink)” works if the current branch has been pushed to the remote - [x] “Open Remote URL in Browser (permalink)” works if a commit is checked out vs. a branch - [x] “Open Remote URL in Browser (permalink)” works if a branch is checked out and the user has not pushed that branch but has also not added commits to that branch - [x] “Open Remote URL in Browser (permalink)” results in a 404 on GitHub if the user has added commits to a branch and not pushed them to GitHub - [x] “Open Remote URL in Browser (master)” works regardless of what’s checked out above --- README.md | 2 +- sublime_github.py | 51 ++++++++++++++++++++++++++++++++++------------- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index c8b2fec..7297713 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ The following commands are available in the Command Palette: **The following commands require the Git plugin, available through the Package Manager. After installing, restart Sublime Text.** -**Note:** These commands use the currently checked out branch to generate GitHub URLs. Each command also has a corresponding version, such as **GitHub: Blame (master)**, that always uses the master branch, regardless of which branch is checked out locally. All commands except **GitHub: Edit** have a corresponding "permalink" version too, like **GitHub: Blame (permalink)**, that uses the most recent commit on the current branch ([more info](https://help.github.com/en/articles/getting-permanent-links-to-files)). +**Note:** These commands use the currently checked-out branch to generate GitHub URLs. Each command also has a corresponding version, such as **GitHub: Blame (master)**, that always uses the master branch, regardless of which branch is checked-out locally. All commands except **GitHub: Edit** have a corresponding "permalink" version too, like **GitHub: Blame (permalink)**, that references the currently checked-out commit ([more info](https://help.github.com/en/articles/getting-permanent-links-to-files)). * **GitHub: Open Remote URL in Browser** diff --git a/sublime_github.py b/sublime_github.py index 49fbf1a..a1599ae 100644 --- a/sublime_github.py +++ b/sublime_github.py @@ -431,19 +431,35 @@ def run(self, edit): if self.branch == "master": branch = "master" else: - # Get the current remote branch--useful whether we want to link directly to that - # branch or to the branch's HEAD. + # Get the current remote branch. Won't be used for permalink commands but is still + # useful to give us the remote name. branch = "" command = "git rev-parse --abbrev-ref --symbolic-full-name %s@{upstream}" % branch - self.run_command(command.split(), self.done_rev_parse) + self.run_command(command.split(), self.done_remote_branch) - def done_rev_parse(self, result): + def done_remote_branch(self, result): if "fatal:" in result: - sublime.error_message(result) - return + if ("no upstream configured" in result or "HEAD does not point to a branch" in result) and not self.branch: + # These results indicate either that the user has not pushed the branch to the remote + # or that the user has a commit checked out rather than a branch. If this is a permalink + # command (`not self.branch`) and the user has only one remote, then we can proceed + # regardless, since we don't calculate the permalink with respect to the remote branch + # but rather the local HEAD--see `generate_url` for why. + self.run_command("git remote".split(), self.done_remote_list) + else: + sublime.error_message(result) + else: + remote, self.remote_branch = result.strip().split("/", 1) + self.done_remote(remote) - remote, self.remote_branch = result.strip().split("/", 1) + def done_remote_list(self, result): + remote_list = result.strip().split('\n') + if len(remote_list) == 1: + self.done_remote(remote_list[0]) + else: + sublime.error_message("Please push this branch to a remote, then re-run this command.") + def done_remote(self, remote): self.settings = sublime.load_settings("GitHub.sublime-settings") self.active_account = self.settings.get("active_account") self.accounts = self.settings.get("accounts") @@ -457,9 +473,9 @@ def done_rev_parse(self, result): command = "git ls-remote --get-url " + remote - self.run_command(command.split(), self.done_remote) + self.run_command(command.split(), self.done_remote_url) - def done_remote(self, result): + def done_remote_url(self, result): remote_loc = result.split()[0] repo_url = re.sub('^git(@|://)', self.protocol + '://', remote_loc) # Replace the "tld:" with "tld/" @@ -505,18 +521,25 @@ def done_toplevel(self, result): if self.branch: self.generate_url() else: - command = "git rev-parse " + self.remote_branch - self.run_command(command.split(), self.done_remote_head) + command = "git rev-parse HEAD" + self.run_command(command.split(), self.done_head) - def done_remote_head(self, result): - self.remote_head = result.strip() + def done_head(self, result): + self.head = result.strip() self.generate_url() def generate_url(self): if self.branch: remote_id = self.remote_branch else: - remote_id = self.remote_head + # Reference the local HEAD for permalinks, rather than the HEAD of the remote branch, because + # this way we'll get a link to what the user is actually looking at in their editor. This also + # lets the user run the permalink command with a commit checked out rather than a branch; or + # having checked out a branch that they've just created, if the user has neither added commits + # to that branch nor pushed the branch to the remote. If the user _has_ added commits to such + # a branch without pushing them to the remote, then the link will 404 on GitHub. At some + # future point, it might be nice to detect this and show an error message locally. + remote_id = self.head self.url = "%s/%s/%s%s%s" % (self.repo_url, self.url_type, remote_id, self.relative_path, self.line_nums) self.on_done() else: