From 150947f8e34889c38a57ac40a8de1909e92ddb92 Mon Sep 17 00:00:00 2001 From: Danny Sepler Date: Sun, 20 Sep 2020 13:31:25 -0400 Subject: [PATCH 1/6] Minor performance improvements to the prompt git functions --- news/prompt-speed.rst | 23 +++++++++++++++ xonsh/prompt/vc.py | 67 +++++++++++++++++++++---------------------- 2 files changed, 55 insertions(+), 35 deletions(-) create mode 100644 news/prompt-speed.rst diff --git a/news/prompt-speed.rst b/news/prompt-speed.rst new file mode 100644 index 000000000..5ac871e51 --- /dev/null +++ b/news/prompt-speed.rst @@ -0,0 +1,23 @@ +**Added:** + +* + +**Changed:** + +* Minor improvements to the get prompt speed. (Mostly in git.) + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* + +**Security:** + +* diff --git a/xonsh/prompt/vc.py b/xonsh/prompt/vc.py index f4bcbd127..6da679021 100644 --- a/xonsh/prompt/vc.py +++ b/xonsh/prompt/vc.py @@ -24,26 +24,15 @@ RE_REMOVE_ANSI = LazyObject( def _get_git_branch(q): denv = builtins.__xonsh__.env.detype() try: - branches = xt.decode_bytes( - subprocess.check_output( - ["git", "branch"], env=denv, stderr=subprocess.DEVNULL - ) - ).splitlines() + cmd = ["git", "rev-parse", "--abbrev-ref", "HEAD"] + branch = xt.decode_bytes( + subprocess.check_output(cmd, env=denv, stderr=subprocess.DEVNULL) + ) + branch = branch.splitlines()[0] or None except (subprocess.CalledProcessError, OSError, FileNotFoundError): q.put(None) else: - for branch in branches: - if not branch.startswith("* "): - continue - elif branch.endswith(")"): - branch = branch.split()[-1][:-1] - else: - branch = branch.split()[-1] - - q.put(branch) - break - else: - q.put(None) + q.put(branch) def get_git_branch(): @@ -59,7 +48,6 @@ def get_git_branch(): t.join(timeout=timeout) try: branch = q.get_nowait() - # branch = RE_REMOVE_ANSI.sub("", branch or "") if branch: branch = RE_REMOVE_ANSI.sub("", branch) except queue.Empty: @@ -157,16 +145,17 @@ def current_branch(): """ branch = None cmds = builtins.__xonsh__.commands_cache - # check for binary only once - if cmds.is_empty(): - has_git = bool(cmds.locate_binary("git", ignore_alias=True)) - has_hg = bool(cmds.locate_binary("hg", ignore_alias=True)) - else: - has_git = bool(cmds.lazy_locate_binary("git", ignore_alias=True)) - has_hg = bool(cmds.lazy_locate_binary("hg", ignore_alias=True)) - if has_git: + + # This allows us to locate binaries after git only if necessary + def has(vc): + if cmds.is_empty(): + return bool(cmds.locate_binary(vc, ignore_alias=True)) + else: + return bool(cmds.lazy_locate_binary(vc, ignore_alias=True)) + + if has("git"): branch = get_git_branch() - if not branch and has_hg: + if not branch and has("hg"): branch = get_hg_branch() if isinstance(branch, subprocess.TimeoutExpired): branch = "" @@ -175,19 +164,27 @@ def current_branch(): def _git_dirty_working_directory(q, include_untracked): - status = None denv = builtins.__xonsh__.env.detype() try: - cmd = ["git", "status", "--porcelain"] + # Borrowing this conversation + # https://github.com/sindresorhus/pure/issues/115 if include_untracked: - cmd.append("--untracked-files=normal") + cmd = [ + "git", + "status", + "--porcelain", + "--quiet", + "--untracked-files=normal", + ] else: - cmd.append("--untracked-files=no") - status = subprocess.check_output(cmd, stderr=subprocess.DEVNULL, env=denv) + cmd = ["git", "diff", "--no-ext-diff", "--quiet"] + child = subprocess.Popen(cmd, stderr=subprocess.DEVNULL, env=denv) + child.communicate() + dwd = bool(child.returncode) except (subprocess.CalledProcessError, OSError, FileNotFoundError): q.put(None) - if status is not None: - return q.put(bool(status)) + else: + q.put(dwd) def git_dirty_working_directory(include_untracked=False): @@ -244,7 +241,7 @@ def dirty_working_directory(): cmds = builtins.__xonsh__.commands_cache if cmds.lazy_locate_binary("git", ignore_alias=True): dwd = git_dirty_working_directory() - if cmds.lazy_locate_binary("hg", ignore_alias=True) and dwd is None: + if dwd is None and cmds.lazy_locate_binary("hg", ignore_alias=True): dwd = hg_dirty_working_directory() return dwd From bb30e46f1e84bbdc3fb98b1f8393c4e3b7473895 Mon Sep 17 00:00:00 2001 From: Danny Sepler Date: Mon, 21 Sep 2020 20:56:25 -0400 Subject: [PATCH 2/6] Feedback --- xonsh/prompt/vc.py | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/xonsh/prompt/vc.py b/xonsh/prompt/vc.py index 6da679021..4499c354d 100644 --- a/xonsh/prompt/vc.py +++ b/xonsh/prompt/vc.py @@ -137,6 +137,15 @@ def _first_branch_timeout_message(): ) +def _has(binary): + """ This allows us to locate binaries after git only if necessary """ + cmds = builtins.__xonsh__.commands_cache + if cmds.is_empty(): + return bool(cmds.locate_binary(binary, ignore_alias=True)) + else: + return bool(cmds.lazy_locate_binary(binary, ignore_alias=True)) + + def current_branch(): """Gets the branch for a current working directory. Returns an empty string if the cwd is not a repository. This currently only works for git and hg @@ -144,18 +153,9 @@ def current_branch(): '' is returned. """ branch = None - cmds = builtins.__xonsh__.commands_cache - - # This allows us to locate binaries after git only if necessary - def has(vc): - if cmds.is_empty(): - return bool(cmds.locate_binary(vc, ignore_alias=True)) - else: - return bool(cmds.lazy_locate_binary(vc, ignore_alias=True)) - - if has("git"): + if _has("git"): branch = get_git_branch() - if not branch and has("hg"): + if not branch and _has("hg"): branch = get_hg_branch() if isinstance(branch, subprocess.TimeoutExpired): branch = "" @@ -178,8 +178,7 @@ def _git_dirty_working_directory(q, include_untracked): ] else: cmd = ["git", "diff", "--no-ext-diff", "--quiet"] - child = subprocess.Popen(cmd, stderr=subprocess.DEVNULL, env=denv) - child.communicate() + child = subprocess.run(cmd, stderr=subprocess.DEVNULL, env=denv) dwd = bool(child.returncode) except (subprocess.CalledProcessError, OSError, FileNotFoundError): q.put(None) @@ -238,10 +237,9 @@ def dirty_working_directory(): None. Currently supports git and hg. """ dwd = None - cmds = builtins.__xonsh__.commands_cache - if cmds.lazy_locate_binary("git", ignore_alias=True): + if _has("git"): dwd = git_dirty_working_directory() - if dwd is None and cmds.lazy_locate_binary("hg", ignore_alias=True): + if dwd is None and _has("hg"): dwd = hg_dirty_working_directory() return dwd From 50420249ae768420f539f46491bed46e450d325c Mon Sep 17 00:00:00 2001 From: Danny Sepler Date: Mon, 21 Sep 2020 21:12:38 -0400 Subject: [PATCH 3/6] more feedback --- xonsh/prompt/vc.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/xonsh/prompt/vc.py b/xonsh/prompt/vc.py index 4499c354d..5b708070f 100644 --- a/xonsh/prompt/vc.py +++ b/xonsh/prompt/vc.py @@ -137,7 +137,7 @@ def _first_branch_timeout_message(): ) -def _has(binary): +def _vc_has(binary): """ This allows us to locate binaries after git only if necessary """ cmds = builtins.__xonsh__.commands_cache if cmds.is_empty(): @@ -153,9 +153,9 @@ def current_branch(): '' is returned. """ branch = None - if _has("git"): + if _vc_has("git"): branch = get_git_branch() - if not branch and _has("hg"): + if not branch and _vc_has("hg"): branch = get_hg_branch() if isinstance(branch, subprocess.TimeoutExpired): branch = "" @@ -237,9 +237,9 @@ def dirty_working_directory(): None. Currently supports git and hg. """ dwd = None - if _has("git"): + if _vc_has("git"): dwd = git_dirty_working_directory() - if dwd is None and _has("hg"): + if dwd is None and _vc_has("hg"): dwd = hg_dirty_working_directory() return dwd From fdf5622ee8679311a16fc7a7b81ab731ffd842b3 Mon Sep 17 00:00:00 2001 From: Danny Sepler Date: Mon, 21 Sep 2020 22:31:44 -0400 Subject: [PATCH 4/6] Explaining --exit-code return values --- xonsh/prompt/vc.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xonsh/prompt/vc.py b/xonsh/prompt/vc.py index 5b708070f..7d0432777 100644 --- a/xonsh/prompt/vc.py +++ b/xonsh/prompt/vc.py @@ -166,7 +166,7 @@ def current_branch(): def _git_dirty_working_directory(q, include_untracked): denv = builtins.__xonsh__.env.detype() try: - # Borrowing this conversation + # Borrowed from this conversation # https://github.com/sindresorhus/pure/issues/115 if include_untracked: cmd = [ @@ -179,6 +179,9 @@ def _git_dirty_working_directory(q, include_untracked): else: cmd = ["git", "diff", "--no-ext-diff", "--quiet"] child = subprocess.run(cmd, stderr=subprocess.DEVNULL, env=denv) + # "--quiet" git commands imply "--exit-code", which returns: + # 1 if there are differences + # 0 if there are no differences dwd = bool(child.returncode) except (subprocess.CalledProcessError, OSError, FileNotFoundError): q.put(None) From 2ac84228f705d3204f594d135497eb77777220c5 Mon Sep 17 00:00:00 2001 From: Danny Sepler Date: Mon, 21 Sep 2020 23:30:54 -0400 Subject: [PATCH 5/6] Also include indexed files in working directory --- xonsh/prompt/vc.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/xonsh/prompt/vc.py b/xonsh/prompt/vc.py index 7d0432777..c89ecfa11 100644 --- a/xonsh/prompt/vc.py +++ b/xonsh/prompt/vc.py @@ -177,7 +177,9 @@ def _git_dirty_working_directory(q, include_untracked): "--untracked-files=normal", ] else: - cmd = ["git", "diff", "--no-ext-diff", "--quiet"] + unindexed = ["git", "diff", "--no-ext-diff", "--quiet"] + indexed = unindexed + ["--cached", "HEAD"] + cmd = indexed + ["||"] + unindexed child = subprocess.run(cmd, stderr=subprocess.DEVNULL, env=denv) # "--quiet" git commands imply "--exit-code", which returns: # 1 if there are differences From 845ec4a8b752447dfd7e0e62380dd26a78ecf4af Mon Sep 17 00:00:00 2001 From: Danny Sepler Date: Mon, 21 Sep 2020 23:35:09 -0400 Subject: [PATCH 6/6] unindexed first --- xonsh/prompt/vc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xonsh/prompt/vc.py b/xonsh/prompt/vc.py index c89ecfa11..3e2a37374 100644 --- a/xonsh/prompt/vc.py +++ b/xonsh/prompt/vc.py @@ -179,7 +179,7 @@ def _git_dirty_working_directory(q, include_untracked): else: unindexed = ["git", "diff", "--no-ext-diff", "--quiet"] indexed = unindexed + ["--cached", "HEAD"] - cmd = indexed + ["||"] + unindexed + cmd = unindexed + ["||"] + indexed child = subprocess.run(cmd, stderr=subprocess.DEVNULL, env=denv) # "--quiet" git commands imply "--exit-code", which returns: # 1 if there are differences