From 4256c20d7fa3514651df10f4dbc38bcd8fa012e3 Mon Sep 17 00:00:00 2001 From: Keith Dahlby Date: Mon, 6 Feb 2017 11:03:38 -0600 Subject: [PATCH] Normalize line endings --- .gitattributes | 5 +- .vscode/settings.json | 8 +- .vscode/tasks.json | 132 +- CHANGELOG.md | 50 +- ISSUE_TEMPLATE.md | 46 +- LICENSE.txt | 14 +- PSScriptAnalyzerSettings.psd1 | 64 +- README.md | 384 +++--- chocolatey/tests/InstallChocolatey.Tests.ps1 | 420 +++---- chocolatey/tools/chocolateyInstall.ps1 | 100 +- chocolatey/tools/chocolateyUninstall.ps1 | 88 +- install.ps1 | 12 +- profile.example.ps1 | 40 +- src/CheckVersion.ps1 | 36 +- src/GitParamTabExpansion.ps1 | 334 ++--- src/GitPrompt.ps1 | 632 +++++----- src/GitTabExpansion.ps1 | 820 ++++++------ src/GitUtils.ps1 | 1162 +++++++++--------- src/TortoiseGit.ps1 | 170 +-- src/Utils.ps1 | 510 ++++---- src/en-US/about_posh-git.help.txt | 356 +++--- src/posh-git.psd1 | 148 +-- test/DefaultPrompt.Tests.ps1 | 190 +-- test/Get-GitBranch.Tests.ps1 | 32 +- test/Get-GitDirectory.Tests.ps1 | 138 +-- test/Get-GitStatus.Tests.ps1 | 828 ++++++------- test/GitParamTabExpansion.Tests.ps1 | 92 +- test/ModuleManifest.Tests.ps1 | 16 +- test/Ssh.Tests.ps1 | 48 +- test/TabExpansion.Tests.ps1 | 240 ++-- test/Utils.Tests.ps1 | 312 ++--- test/git-help.txt | 84 +- test/testDebugHarness.ps1 | 4 +- 33 files changed, 3757 insertions(+), 3758 deletions(-) diff --git a/.gitattributes b/.gitattributes index a4cc200a0..1a336bfbd 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,2 @@ -# Disable LF normalization for all files -* -text -*.yml text=auto +# Enable LF normalization for all files +* text=auto diff --git a/.vscode/settings.json b/.vscode/settings.json index 04e7501a6..d73c93a67 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,4 @@ -{ - "editor.rulers": [ 120 ], - "powershell.scriptAnalysis.settingsPath": "PSScriptAnalyzerSettings.psd1" -} +{ + "editor.rulers": [ 120 ], + "powershell.scriptAnalysis.settingsPath": "PSScriptAnalyzerSettings.psd1" +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 95d2b15d3..3b6cb45b4 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,66 +1,66 @@ -// Available variables which can be used inside of strings. -// ${workspaceRoot}: the root folder of the team -// ${file}: the current opened file -// ${relativeFile}: the current opened file relative to workspaceRoot -// ${fileBasename}: the current opened file's basename -// ${fileDirname}: the current opened file's dirname -// ${fileExtname}: the current opened file's extension -// ${cwd}: the current working directory of the spawned process -{ - // See https://go.microsoft.com/fwlink/?LinkId=733558 - // for the documentation about the tasks.json format - "version": "0.1.0", - - // Start PowerShell - "windows": { - "command": "${env.windir}\\sysnative\\windowspowershell\\v1.0\\PowerShell.exe" - }, - "linux": { - "command": "/usr/bin/powershell" - }, - "osx": { - "command": "/usr/local/bin/powershell" - }, - - // The command is a shell script - "isShellCommand": true, - - // Show the output window always - "showOutput": "always", - - "args": [ - "-NoProfile", "-ExecutionPolicy", "Bypass" - ], - - // Associate with test task runner - "tasks": [ - { - "taskName": "Test", - "suppressTaskName": true, - "isTestCommand": true, - "showOutput": "always", - "args": [ - "Write-Host 'Invoking Pester'; Invoke-Pester test -PesterOption @{IncludeVSCodeMarker=$true};", - "Invoke-Command { Write-Host 'Completed Test task in task runner.' }" - ], - "problemMatcher": [ - { - "owner": "powershell", - "fileLocation": ["absolute"], - "severity": "error", - "pattern": [ - { - "regexp": "^\\s*(\\[-\\]\\s*.*?)(\\d+)ms\\s*$", - "message": 1 - }, - { - "regexp": "^\\s+at\\s+[^,]+,\\s*(.*?):\\s+line\\s+(\\d+)$", - "file": 1, - "line": 2 - } - ] - } - ] - } - ] -} +// Available variables which can be used inside of strings. +// ${workspaceRoot}: the root folder of the team +// ${file}: the current opened file +// ${relativeFile}: the current opened file relative to workspaceRoot +// ${fileBasename}: the current opened file's basename +// ${fileDirname}: the current opened file's dirname +// ${fileExtname}: the current opened file's extension +// ${cwd}: the current working directory of the spawned process +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "0.1.0", + + // Start PowerShell + "windows": { + "command": "${env.windir}\\sysnative\\windowspowershell\\v1.0\\PowerShell.exe" + }, + "linux": { + "command": "/usr/bin/powershell" + }, + "osx": { + "command": "/usr/local/bin/powershell" + }, + + // The command is a shell script + "isShellCommand": true, + + // Show the output window always + "showOutput": "always", + + "args": [ + "-NoProfile", "-ExecutionPolicy", "Bypass" + ], + + // Associate with test task runner + "tasks": [ + { + "taskName": "Test", + "suppressTaskName": true, + "isTestCommand": true, + "showOutput": "always", + "args": [ + "Write-Host 'Invoking Pester'; Invoke-Pester test -PesterOption @{IncludeVSCodeMarker=$true};", + "Invoke-Command { Write-Host 'Completed Test task in task runner.' }" + ], + "problemMatcher": [ + { + "owner": "powershell", + "fileLocation": ["absolute"], + "severity": "error", + "pattern": [ + { + "regexp": "^\\s*(\\[-\\]\\s*.*?)(\\d+)ms\\s*$", + "message": 1 + }, + { + "regexp": "^\\s+at\\s+[^,]+,\\s*(.*?):\\s+line\\s+(\\d+)$", + "file": 1, + "line": 2 + } + ] + } + ] + } + ] +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 27bde6b32..db9347e8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,25 +1,25 @@ -# posh-git Release History - -## 0.7.0 - January 31, 2017 -This release has focused on improving the "getting started" experience by adding an `Add-PoshGitToProfile` command that -modifies the user's PowerShell profile script to import the posh-git module whenever PowerShell starts. -When posh-git is imported, it will automatically install a posh-git prompt that displays Git status summary information. -Work was also done to improve performance of Get-GitStatus when inside large Git repositories. -Work was begun to eliminate some obvious crashes on PowerShell on .NET Core but more work remains to be done. - -- Performance of Get-GitStatus on large repos has been improved -- Fix crash on PowerShell Core due to missing .NET types for WindowsIdentity/Principal -- Fix syntax error on setenv calls -- Fix temp path issue with ~ in 8.3 filenames -- Fix unable to find type [EnvironmentVariableTarget] in PowerShell on .NET Core -- Fix support for bare repository ([#291](https://github.com/dahlbyk/posh-git/issues/291)) -- Fewer errors generated in global $Error collection -- Remove error thrown by symbolic-ref and describe -- Update module import so that it sets the prompt function *iff* the user does not have a customized prompt function ([#217](https://github.com/dahlbyk/posh-git/issues/217)) -- Update profile.example.ps1 to remove prompt function and tweak how module is imported -- Add new commmand Add-PoshGitToProfile -- Add about_posh-git help topic -- Add new branch status to indicate upstream is gone ([#326](https://github.com/dahlbyk/posh-git/pull/326)) -- Add ahead/behind count to prompt ([#256](https://github.com/dahlbyk/posh-git/pull/256)) -- Add BranchBehindAndAheadDisplay setting to control count display (Full (default), Compact, Minimal) -- Switch $GitPromptSettings type from PSObject to PSCustomObject. On PowerShell v5 and higher, this preserves the definition order of properties in $GitPromptSettings making it easier to find properties. +# posh-git Release History + +## 0.7.0 - January 31, 2017 +This release has focused on improving the "getting started" experience by adding an `Add-PoshGitToProfile` command that +modifies the user's PowerShell profile script to import the posh-git module whenever PowerShell starts. +When posh-git is imported, it will automatically install a posh-git prompt that displays Git status summary information. +Work was also done to improve performance of Get-GitStatus when inside large Git repositories. +Work was begun to eliminate some obvious crashes on PowerShell on .NET Core but more work remains to be done. + +- Performance of Get-GitStatus on large repos has been improved +- Fix crash on PowerShell Core due to missing .NET types for WindowsIdentity/Principal +- Fix syntax error on setenv calls +- Fix temp path issue with ~ in 8.3 filenames +- Fix unable to find type [EnvironmentVariableTarget] in PowerShell on .NET Core +- Fix support for bare repository ([#291](https://github.com/dahlbyk/posh-git/issues/291)) +- Fewer errors generated in global $Error collection +- Remove error thrown by symbolic-ref and describe +- Update module import so that it sets the prompt function *iff* the user does not have a customized prompt function ([#217](https://github.com/dahlbyk/posh-git/issues/217)) +- Update profile.example.ps1 to remove prompt function and tweak how module is imported +- Add new commmand Add-PoshGitToProfile +- Add about_posh-git help topic +- Add new branch status to indicate upstream is gone ([#326](https://github.com/dahlbyk/posh-git/pull/326)) +- Add ahead/behind count to prompt ([#256](https://github.com/dahlbyk/posh-git/pull/256)) +- Add BranchBehindAndAheadDisplay setting to control count display (Full (default), Compact, Minimal) +- Switch $GitPromptSettings type from PSObject to PSCustomObject. On PowerShell v5 and higher, this preserves the definition order of properties in $GitPromptSettings making it easier to find properties. diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index df392197f..ddf617c05 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -1,23 +1,23 @@ - - -### System Details - -- posh-git version: -- PowerShell version: -- Git version: -- Operating system name and version: - - - -### Issue Description - -I am experiencing a problem with... + + +### System Details + +- posh-git version: +- PowerShell version: +- Git version: +- Operating system name and version: + + + +### Issue Description + +I am experiencing a problem with... diff --git a/LICENSE.txt b/LICENSE.txt index d81420990..7b3d7e3f2 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,7 +1,7 @@ -Copyright (c) 2010-2016 Keith Dahlby and contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +Copyright (c) 2010-2016 Keith Dahlby and contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/PSScriptAnalyzerSettings.psd1 b/PSScriptAnalyzerSettings.psd1 index caa200069..e3f881411 100644 --- a/PSScriptAnalyzerSettings.psd1 +++ b/PSScriptAnalyzerSettings.psd1 @@ -1,32 +1,32 @@ -@{ - # Use Severity when you want to limit the generated diagnostic records to a - # subset of: Error, Warning and Information. - # Uncomment the following line if you only want Errors and Warnings but - # not Information diagnostic records. - Severity = @('Error','Warning') - - # Use IncludeRules when you want to run only a subset of the default rule set. - #IncludeRules = @('PSAvoidDefaultValueSwitchParameter', - # 'PSMissingModuleManifestField', - # 'PSReservedCmdletChar', - # 'PSReservedParams', - # 'PSShouldProcess', - # 'PSUseApprovedVerbs', - # 'PSUseDeclaredVarsMoreThanAssigments') - - # Use ExcludeRules when you want to run most of the default set of rules except - # for a few rules you wish to "exclude". Note: if a rule is in both IncludeRules - # and ExcludeRules, the rule will be excluded. - ExcludeRules = @('PSAvoidUsingWriteHost', 'PSAvoidGlobalVars') - - # You can use the following entry to supply parameters to rules that take parameters. - # For instance, the PSAvoidUsingCmdletAliases rule takes a whitelist for aliases you - # want to allow. - Rules = @{ - # Do not flag 'cd' alias. - # PSAvoidUsingCmdletAliases = @{Whitelist = @('cd')} - - # Check if your script uses cmdlets that are compatible on PowerShell Core, version 6.0.0-alpha, on Linux. - # PSUseCompatibleCmdlets = @{Compatibility = @("core-6.0.0-alpha-linux")} - } -} +@{ + # Use Severity when you want to limit the generated diagnostic records to a + # subset of: Error, Warning and Information. + # Uncomment the following line if you only want Errors and Warnings but + # not Information diagnostic records. + Severity = @('Error','Warning') + + # Use IncludeRules when you want to run only a subset of the default rule set. + #IncludeRules = @('PSAvoidDefaultValueSwitchParameter', + # 'PSMissingModuleManifestField', + # 'PSReservedCmdletChar', + # 'PSReservedParams', + # 'PSShouldProcess', + # 'PSUseApprovedVerbs', + # 'PSUseDeclaredVarsMoreThanAssigments') + + # Use ExcludeRules when you want to run most of the default set of rules except + # for a few rules you wish to "exclude". Note: if a rule is in both IncludeRules + # and ExcludeRules, the rule will be excluded. + ExcludeRules = @('PSAvoidUsingWriteHost', 'PSAvoidGlobalVars') + + # You can use the following entry to supply parameters to rules that take parameters. + # For instance, the PSAvoidUsingCmdletAliases rule takes a whitelist for aliases you + # want to allow. + Rules = @{ + # Do not flag 'cd' alias. + # PSAvoidUsingCmdletAliases = @{Whitelist = @('cd')} + + # Check if your script uses cmdlets that are compatible on PowerShell Core, version 6.0.0-alpha, on Linux. + # PSUseCompatibleCmdlets = @{Compatibility = @("core-6.0.0-alpha-linux")} + } +} diff --git a/README.md b/README.md index e7aa1ff67..40fdadf38 100644 --- a/README.md +++ b/README.md @@ -1,192 +1,192 @@ -# posh-git - -[![Build status](https://ci.appveyor.com/api/projects/status/eb8erd5afaa01w80?svg=true)](https://ci.appveyor.com/project/dahlbyk/posh-git) -[![Join the chat at https://gitter.im/dahlbyk/posh-git](https://badges.gitter.im/dahlbyk/posh-git.svg)](https://gitter.im/dahlbyk/posh-git?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - -posh-git is a PowerShell module that integrates Git and PowerShell by providing Git status summary information that can be displayed in the PowerShell prompt, e.g.: -``` -C:\Users\Keith\GitHub\posh-git [master ≡ +0 ~1 -0 !]> -``` -posh-git also provides tab completion support for common git commands, branch names, paths and more. -For example, with posh-git, PowerShell can tab complete git commands like `checkout` by typing `git ch` and pressing the tab key. -That will tab complete to `git checkout` and if you keep pressing tab, it will cycle through other command matches such as `cherry` and `cherry-pick`. -You can also tab complete remote names and branch names e.g.: `git pull or ma` tab completes to `git pull origin master`. - -## Notes -Posh-git adds variables to your session to let you customize it, including `$GitPromptSettings`, `$GitTabSettings`, and `$TortoiseGitSettings`. -For an example of how to configure your PowerShell profile script to import the posh-git module and create a custom prompt function that displays git status info, see the `Customizing Your PowerShell Prompt` section below. - -Note on performance: Displaying file status in the git prompt for a very large repo can be prohibitively slow. -Rather than turn off file status entirely (`$GitPromptSettings.EnableFileStatus = $false`), you can disable it on a repo-by-repo basis by adding individual repository paths to `$GitPromptSettings.RepositoriesInWhichToDisableFileStatus`. - -## Installation -### Prerequisites -Before installing posh-git make sure the following prerequisites have been met. - -1. PowerShell 2.0 or higher. Check your PowerShell version by executing `$PSVersionTable.PSVersion`. - -2. Script execution policy must be set to either `RemoteSigned` or `Unrestricted`. - Check the script execution policy setting by executing `Get-ExecutionPolicy`. - If the policy is not set to one of the two required values, run PowerShell as Administrator and execute `Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -Confirm`. - -3. Git must be installed and available via the PATH environment variable. - Check that `git` is accessible from PowerShell by executing `git --version` from PowerShell. - If `git` is not recognized as the name of a command verify that you have Git installed. - If not, install Git from [https://git-scm.com](https://git-scm.com). - If you have Git installed, make sure the path to git.exe is in your PATH environment variable. - -### Installing posh-git via PowerShellGet -If you are on PowerShell version 5 or higher, execute the command below to install from the [PowerShell Gallery](https://www.powershellgallery.com/): - -``` -PowerShellGet\Install-Module posh-git -Scope CurrentUser -``` -You may be asked if you trust packages coming from the PowerShell Gallery. Answer yes to allow installation of this module to proceed. - -If you are on PowerShell version 3 or 4, you will need to install the [Package Management Preview for PowerShell 3 & 4](https://www.microsoft.com/en-us/download/details.aspx?id=51451) in order to run the command above. - -Note: If you get an error message from Install-Module about NuGet being required to interact with NuGet-based repositories, execute the following commands to bootstrap the NuGet provider: -``` -Install-PackageProvider NuGet -Force -Import-PackageProvider NuGet -Force -``` -Then retry the Install-Module command above. - -After you have successfully installed the posh-git module from the PowerShell Gallery, you will be able to update to a newer version by executing the command: -``` -Update-Module posh-git -``` - -### Installing posh-git via Chocolatey -If you have PowerShell version 2 or are having issues using Install-Module with PowerShell version 3 or 4, you can use [Chocolatey](https://chocolatey.org) to install posh-git. -If you don't have Chocolatey, you can install it from the [Chocolately Install page](https://chocolatey.org/install). -With Chocolatey installed, execute the following command to install posh-git: -``` -choco install poshgit -``` - -## Using posh-git -After you have installed posh-git, you need to configure your PowerShell session to use the posh-git module. - -### Step 1: Import posh-git -The first step is to import the module into your PowerShell session which will enable git tab completion. -You can do this with the command `Import-Module posh-git`. - -### Step 2: Import posh-git from Your PowerShell Profile -You do not want to have to manually execute the `Import-Module` command every time you open a new PowerShell prompt. -Let's have PowerShell import this module for you in each new PowerShell session. -We can do this by either executing the command `Add-PoshGitToProfile` or by editing your PowerShell profile script and adding the command `Import-Module posh-git`. - -If you want posh-git to be available in all your PowerShell hosts (console, ISE, etc) then execute `Add-PoshGitToProfile -AllHosts`. -This will add a line containing `Import-Module posh-git` to the file `$profile.CurrentUserAllHosts`. -If you want posh-git to be available in just the current host, then execute `Add-PoshGitToProfile`. -This will add the same command but to the file `$profile.CurrentUserCurrentHost`. - -If you'd prefer, you can manually edit the desired PowerShell profile script. -Open (or create) your profile script with the command `notepad $profile.CurrentUserAllHosts`. -In the profile script, add the following line: -``` -Import-Module posh-git -``` -Save the profile script, then close PowerShell and open a new PowerShell session. -Type `git fe` and then press tab. If posh-git has been imported, that command should tab complete to `git fetch`. - -### Step 3 (optional): Customize Your PowerShell Prompt -By default, posh-git will update your PowerShell prompt function to display Git status summary information when the current dir is inside a Git repository. -posh-git will not update your PowerShell prompt function if you have your own, customized prompt function that has been defined before importing posh-git. - -The posh-git prompt is a single line prompt that looks like this: -``` -C:\Users\Keith\GitHub\posh-git [master ≡ +0 ~1 -0 !]> -``` -You can customize the posh-git prompt or define your own custom prompt function. -The most common customization for the posh-git provided prompt is to make it span two lines which can be done with the following command: -``` -$GitPromptSettings.DefaultPromptSuffix = '`n$(''>'' * ($nestedPromptLevel + 1)) ' -``` -This will change the prompt to: -``` -C:\Users\Keith\GitHub\posh-git [master ≡ +0 ~1 -0 !] -> -``` -You can also customize the default prompt prefix text e.g.: -``` -$GitPromptSettings.DefaultPromptPrefix = '[$(hostname)] ' -``` -This will change the prompt to: -``` -[KEITH1] C:\Users\Keith\GitHub\posh-git [master ≡ +0 ~1 -0 !]> -``` -And if you would prefer to have any path under your home directory abbreviated with ~, you can change this setting: -``` -$GitPromptSettings.DefaultPromptAbbreviateHomeDirectory = $true -``` -This will change the prompt to the one shown below: -``` -~\GitHub\posh-git [master ≡ +0 ~1 -0 !]> -``` - -You can also create your own prompt function to show whatever information you want. -See the [Customizing Your PowerShell Prompt](https://github.com/dahlbyk/posh-git/wiki/Customizing-Your-PowerShell-Prompt) wiki page for details. - -## Git Status Summary Information -The Git status summary information provides a wealth of "Git status" information at a glance, all the time in your prompt. - -By default, the status summary has the following format: - - [{HEAD-name} S +A ~B -C !D | +E ~F -G !H W] - -* ` [` (`BeforeText`) -* `{HEAD-name}` is the current branch, or the SHA of a detached HEAD - * Cyan means the branch matches its remote - * Green means the branch is ahead of its remote (green light to push) - * Red means the branch is behind its remote - * Yellow means the branch is both ahead of and behind its remote -* S represents the branch status in relation to remote (tracked origin) branch. Note: This information reflects the state of the remote tracked branch after the last `git fetch/pull` of the remote. - * ≡ = The local branch in at the same commit level as the remote branch (`BranchIdenticalStatus`) - * ↑`` = The local branch is ahead of the remote branch by the specified number of commits; a 'git push' is required to update the remote branch (`BranchAheadStatus`) - * ↓`` = The local branch is behind the remote branch by the specified number of commits; a 'git pull' is required to update the local branch (`BranchBehindStatus`) - * ``↕`` = The local branch is both ahead of the remote branch by the specified number of commits (a) and behind by the specified number of commits (b); a rebase of the local branch is required before pushing local changes to the remote branch (`BranchBehindAndAheadStatus`). NOTE: this status is only available if $GitPromptSettings.BranchBehindAndAheadDisplay is set to 'Compact'. - * × = The local branch is tracking a branch that is gone from the remote (`BranchGoneStatus') -* ABCD represent the index; ` | ` (`DelimText`); EFGH represent the working directory - * `+` = Added files - * `~` = Modified files - * `-` = Removed files - * `!` = Conflicted files - * As in `git status`, index status is dark green and working directory status is dark red -* -* W represents the status of the working folder - * `!` = There are untracked changes in the working tree (`LocalStagedStatus`) - * `~` = There are staged changes in the working tree waiting to be committed (`LocalWorkingStatus`) - * None = There are no uncommitted or unstaged changes to the working tree (`LocalDefault`) -* `]` (`AfterText`) - -The symbols and surrounding text can be customized by the corresponding properties on `$GitPromptSettings`. - -For example, a status of `[master ≡ +0 ~2 -1 | +1 ~1 -0]` corresponds to the following `git status`: - - # On branch master - # - # Changes to be committed: - # (use "git reset HEAD ..." to unstage) - # - # modified: this-changed.txt - # modified: this-too.txt - # deleted: gone.ps1 - # - # Changed but not updated: - # (use "git add ..." to update what will be committed) - # (use "git checkout -- ..." to discard changes in working directory) - # - # modified: not-staged.ps1 - # - # Untracked files: - # (use "git add ..." to include in what will be committed) - # - # new.file - -## Based on work by: - - - Keith Dahlby, http://solutionizing.net/ - - Mark Embling, http://www.markembling.info/ - - Jeremy Skinner, http://www.jeremyskinner.co.uk/ +# posh-git + +[![Build status](https://ci.appveyor.com/api/projects/status/eb8erd5afaa01w80?svg=true)](https://ci.appveyor.com/project/dahlbyk/posh-git) +[![Join the chat at https://gitter.im/dahlbyk/posh-git](https://badges.gitter.im/dahlbyk/posh-git.svg)](https://gitter.im/dahlbyk/posh-git?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +posh-git is a PowerShell module that integrates Git and PowerShell by providing Git status summary information that can be displayed in the PowerShell prompt, e.g.: +``` +C:\Users\Keith\GitHub\posh-git [master ≡ +0 ~1 -0 !]> +``` +posh-git also provides tab completion support for common git commands, branch names, paths and more. +For example, with posh-git, PowerShell can tab complete git commands like `checkout` by typing `git ch` and pressing the tab key. +That will tab complete to `git checkout` and if you keep pressing tab, it will cycle through other command matches such as `cherry` and `cherry-pick`. +You can also tab complete remote names and branch names e.g.: `git pull or ma` tab completes to `git pull origin master`. + +## Notes +Posh-git adds variables to your session to let you customize it, including `$GitPromptSettings`, `$GitTabSettings`, and `$TortoiseGitSettings`. +For an example of how to configure your PowerShell profile script to import the posh-git module and create a custom prompt function that displays git status info, see the `Customizing Your PowerShell Prompt` section below. + +Note on performance: Displaying file status in the git prompt for a very large repo can be prohibitively slow. +Rather than turn off file status entirely (`$GitPromptSettings.EnableFileStatus = $false`), you can disable it on a repo-by-repo basis by adding individual repository paths to `$GitPromptSettings.RepositoriesInWhichToDisableFileStatus`. + +## Installation +### Prerequisites +Before installing posh-git make sure the following prerequisites have been met. + +1. PowerShell 2.0 or higher. Check your PowerShell version by executing `$PSVersionTable.PSVersion`. + +2. Script execution policy must be set to either `RemoteSigned` or `Unrestricted`. + Check the script execution policy setting by executing `Get-ExecutionPolicy`. + If the policy is not set to one of the two required values, run PowerShell as Administrator and execute `Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -Confirm`. + +3. Git must be installed and available via the PATH environment variable. + Check that `git` is accessible from PowerShell by executing `git --version` from PowerShell. + If `git` is not recognized as the name of a command verify that you have Git installed. + If not, install Git from [https://git-scm.com](https://git-scm.com). + If you have Git installed, make sure the path to git.exe is in your PATH environment variable. + +### Installing posh-git via PowerShellGet +If you are on PowerShell version 5 or higher, execute the command below to install from the [PowerShell Gallery](https://www.powershellgallery.com/): + +``` +PowerShellGet\Install-Module posh-git -Scope CurrentUser +``` +You may be asked if you trust packages coming from the PowerShell Gallery. Answer yes to allow installation of this module to proceed. + +If you are on PowerShell version 3 or 4, you will need to install the [Package Management Preview for PowerShell 3 & 4](https://www.microsoft.com/en-us/download/details.aspx?id=51451) in order to run the command above. + +Note: If you get an error message from Install-Module about NuGet being required to interact with NuGet-based repositories, execute the following commands to bootstrap the NuGet provider: +``` +Install-PackageProvider NuGet -Force +Import-PackageProvider NuGet -Force +``` +Then retry the Install-Module command above. + +After you have successfully installed the posh-git module from the PowerShell Gallery, you will be able to update to a newer version by executing the command: +``` +Update-Module posh-git +``` + +### Installing posh-git via Chocolatey +If you have PowerShell version 2 or are having issues using Install-Module with PowerShell version 3 or 4, you can use [Chocolatey](https://chocolatey.org) to install posh-git. +If you don't have Chocolatey, you can install it from the [Chocolately Install page](https://chocolatey.org/install). +With Chocolatey installed, execute the following command to install posh-git: +``` +choco install poshgit +``` + +## Using posh-git +After you have installed posh-git, you need to configure your PowerShell session to use the posh-git module. + +### Step 1: Import posh-git +The first step is to import the module into your PowerShell session which will enable git tab completion. +You can do this with the command `Import-Module posh-git`. + +### Step 2: Import posh-git from Your PowerShell Profile +You do not want to have to manually execute the `Import-Module` command every time you open a new PowerShell prompt. +Let's have PowerShell import this module for you in each new PowerShell session. +We can do this by either executing the command `Add-PoshGitToProfile` or by editing your PowerShell profile script and adding the command `Import-Module posh-git`. + +If you want posh-git to be available in all your PowerShell hosts (console, ISE, etc) then execute `Add-PoshGitToProfile -AllHosts`. +This will add a line containing `Import-Module posh-git` to the file `$profile.CurrentUserAllHosts`. +If you want posh-git to be available in just the current host, then execute `Add-PoshGitToProfile`. +This will add the same command but to the file `$profile.CurrentUserCurrentHost`. + +If you'd prefer, you can manually edit the desired PowerShell profile script. +Open (or create) your profile script with the command `notepad $profile.CurrentUserAllHosts`. +In the profile script, add the following line: +``` +Import-Module posh-git +``` +Save the profile script, then close PowerShell and open a new PowerShell session. +Type `git fe` and then press tab. If posh-git has been imported, that command should tab complete to `git fetch`. + +### Step 3 (optional): Customize Your PowerShell Prompt +By default, posh-git will update your PowerShell prompt function to display Git status summary information when the current dir is inside a Git repository. +posh-git will not update your PowerShell prompt function if you have your own, customized prompt function that has been defined before importing posh-git. + +The posh-git prompt is a single line prompt that looks like this: +``` +C:\Users\Keith\GitHub\posh-git [master ≡ +0 ~1 -0 !]> +``` +You can customize the posh-git prompt or define your own custom prompt function. +The most common customization for the posh-git provided prompt is to make it span two lines which can be done with the following command: +``` +$GitPromptSettings.DefaultPromptSuffix = '`n$(''>'' * ($nestedPromptLevel + 1)) ' +``` +This will change the prompt to: +``` +C:\Users\Keith\GitHub\posh-git [master ≡ +0 ~1 -0 !] +> +``` +You can also customize the default prompt prefix text e.g.: +``` +$GitPromptSettings.DefaultPromptPrefix = '[$(hostname)] ' +``` +This will change the prompt to: +``` +[KEITH1] C:\Users\Keith\GitHub\posh-git [master ≡ +0 ~1 -0 !]> +``` +And if you would prefer to have any path under your home directory abbreviated with ~, you can change this setting: +``` +$GitPromptSettings.DefaultPromptAbbreviateHomeDirectory = $true +``` +This will change the prompt to the one shown below: +``` +~\GitHub\posh-git [master ≡ +0 ~1 -0 !]> +``` + +You can also create your own prompt function to show whatever information you want. +See the [Customizing Your PowerShell Prompt](https://github.com/dahlbyk/posh-git/wiki/Customizing-Your-PowerShell-Prompt) wiki page for details. + +## Git Status Summary Information +The Git status summary information provides a wealth of "Git status" information at a glance, all the time in your prompt. + +By default, the status summary has the following format: + + [{HEAD-name} S +A ~B -C !D | +E ~F -G !H W] + +* ` [` (`BeforeText`) +* `{HEAD-name}` is the current branch, or the SHA of a detached HEAD + * Cyan means the branch matches its remote + * Green means the branch is ahead of its remote (green light to push) + * Red means the branch is behind its remote + * Yellow means the branch is both ahead of and behind its remote +* S represents the branch status in relation to remote (tracked origin) branch. Note: This information reflects the state of the remote tracked branch after the last `git fetch/pull` of the remote. + * ≡ = The local branch in at the same commit level as the remote branch (`BranchIdenticalStatus`) + * ↑`` = The local branch is ahead of the remote branch by the specified number of commits; a 'git push' is required to update the remote branch (`BranchAheadStatus`) + * ↓`` = The local branch is behind the remote branch by the specified number of commits; a 'git pull' is required to update the local branch (`BranchBehindStatus`) + * ``↕`` = The local branch is both ahead of the remote branch by the specified number of commits (a) and behind by the specified number of commits (b); a rebase of the local branch is required before pushing local changes to the remote branch (`BranchBehindAndAheadStatus`). NOTE: this status is only available if $GitPromptSettings.BranchBehindAndAheadDisplay is set to 'Compact'. + * × = The local branch is tracking a branch that is gone from the remote (`BranchGoneStatus') +* ABCD represent the index; ` | ` (`DelimText`); EFGH represent the working directory + * `+` = Added files + * `~` = Modified files + * `-` = Removed files + * `!` = Conflicted files + * As in `git status`, index status is dark green and working directory status is dark red +* +* W represents the status of the working folder + * `!` = There are untracked changes in the working tree (`LocalStagedStatus`) + * `~` = There are staged changes in the working tree waiting to be committed (`LocalWorkingStatus`) + * None = There are no uncommitted or unstaged changes to the working tree (`LocalDefault`) +* `]` (`AfterText`) + +The symbols and surrounding text can be customized by the corresponding properties on `$GitPromptSettings`. + +For example, a status of `[master ≡ +0 ~2 -1 | +1 ~1 -0]` corresponds to the following `git status`: + + # On branch master + # + # Changes to be committed: + # (use "git reset HEAD ..." to unstage) + # + # modified: this-changed.txt + # modified: this-too.txt + # deleted: gone.ps1 + # + # Changed but not updated: + # (use "git add ..." to update what will be committed) + # (use "git checkout -- ..." to discard changes in working directory) + # + # modified: not-staged.ps1 + # + # Untracked files: + # (use "git add ..." to include in what will be committed) + # + # new.file + +## Based on work by: + + - Keith Dahlby, http://solutionizing.net/ + - Mark Embling, http://www.markembling.info/ + - Jeremy Skinner, http://www.jeremyskinner.co.uk/ diff --git a/chocolatey/tests/InstallChocolatey.Tests.ps1 b/chocolatey/tests/InstallChocolatey.Tests.ps1 index 9c4c8b0aa..fe470feec 100644 --- a/chocolatey/tests/InstallChocolatey.Tests.ps1 +++ b/chocolatey/tests/InstallChocolatey.Tests.ps1 @@ -1,210 +1,210 @@ -$packageName = "poshgit" -cpack - -function Setup-Environment { - Cleanup - $env:poshGit = join-path (Resolve-Path .\Tests ) dahlbyk-posh-git-60be436.zip - $profileScript = "function Prompt(){ `$host.ui.RawUI.WindowTitle = `"My Prompt`" }" - (Set-Content $Profile -value $profileScript -Force) -} - -function Cleanup { - Clean-Temp - Remove-Item $env:ChocolateyInstall\lib\$packageName* -Recurse -Force -} - -function Clean-Temp { - if(Test-Path $env:Temp\Chocolatey\$packageName) {Remove-Item $env:Temp\Chocolatey\$packageName -Recurse -Force} -} - -function RunInstall { - cinst $packageName -source (Resolve-Path .) -} -$binRoot = join-path $env:systemdrive 'tools' -if($env:chocolatey_bin_root -ne $null){$binRoot = join-path $env:systemdrive $env:chocolatey_bin_root} -$poshgitPath = join-path $binRoot 'poshgit' -if(Test-Path $Profile) { $currentProfileScript = (Get-Content $Profile) } - -function Clean-Environment { - Set-Content $Profile -value $currentProfileScript -Force -} - -Describe "Install-Posh-Git" { - - It "WillRemvePreviousInstallVersion" { - Setup-Environment - try{ - Add-Content $profile -value ". '$poshgitPath\posh-git\profile.example.ps1'" - - RunInstall - - $newProfile = (Get-Content $Profile) - $pgitDir = [Array](Dir "$poshgitPath\*posh-git*\" | Sort-Object -Property LastWriteTime)[-1] - ($newProfile -like ". '$poshgitPath\posh-git\profile.example.ps1'").Count.should.be(0) - ($newProfile -like ". '$pgitDir\profile.example.ps1'").Count.should.be(1) - } - catch { - write-host (Get-Content $Profile) - throw - } - finally {Clean-Environment} - } - - It "WillNotAddDuplicateCallOnRepeatInstall" { - Setup-Environment - try{ - RunInstall - Cleanup - - RunInstall - - $newProfile = (Get-Content $Profile) - $pgitDir = [Array](Dir "$poshgitPath\*posh-git*\" | Sort-Object -Property LastWriteTime)[-1] - ($newProfile -like ". '$pgitDir\profile.example.ps1'").Count.should.be(1) - } - catch { - write-host (Get-Content $Profile) - throw - } - finally {Clean-Environment} - } - - It "WillPreserveOldPromptLogic" { - Setup-Environment - try{ - RunInstall - . $Profile - $host.ui.RawUI.WindowTitle = "bad" - - Prompt - - $host.ui.RawUI.WindowTitle.should.be("My Prompt") - } - catch { - write-host (Get-Content function:\prompt) - throw - } - finally { - Clean-Environment - } - } - - It "WillOutputVcsStatus" { - Setup-Environment - try{ - RunInstall - mkdir PoshTest - Pushd PoshTest - git init - . $Profile - $global:wh="" - New-Item function:\global:Write-Host -value "param([object] `$object, `$backgroundColor, `$foregroundColor, [switch] `$nonewline) try{Write-Output `$object;[string]`$global:wh += `$object.ToString()} catch{}" - - Prompt - - Popd - $wh.should.be("$pwd\PoshTest [master]") - } - catch { - write-output (Get-Content $Profile) - throw - } - finally { - Clean-Environment - if( Test-Path function:\Write-Host ) {Remove-Item function:\Write-Host} - if( Test-Path PoshTest ) {Remove-Item PoshTest -Force -Recurse} - } - } - - It "WillSucceedOnEmptyProfile" { - Setup-Environment - try{ - Remove-Item $Profile -Force - RunInstall - mkdir PoshTest - Pushd PoshTest - git init - . $Profile - $global:wh="" - New-Item function:\global:Write-Host -value "param([object] `$object, `$backgroundColor, `$foregroundColor, [switch] `$nonewline) try{Write-Output `$object;[string]`$global:wh += `$object.ToString()} catch{}" - - Prompt - - Popd - $wh.should.be("$pwd\PoshTest [master]") - } - catch { - write-output (Get-Content $Profile) - throw - } - finally { - Clean-Environment - if( Test-Path function:\Write-Host ) {Remove-Item function:\Write-Host} - if( Test-Path PoshTest ) {Remove-Item PoshTest -Force -Recurse} - } - } - - It "WillSucceedOnProfileWithPromptWithWriteHost" { - Cleanup - Setup-Environment - try{ - Remove-Item $Profile -Force - Add-Content $profile -value "function prompt {Write-Host 'Hi'}" -Force - RunInstall - mkdir PoshTest - Pushd PoshTest - git init - . $Profile - $global:wh="" - New-Item function:\global:Write-Host -value "param([object] `$object, `$backgroundColor, `$foregroundColor, [switch] `$nonewline) try{Write-Output `$object;[string]`$global:wh += `$object.ToString()} catch{}" - - Prompt - - Remove-Item function:\global:Write-Host - Popd - $wh.should.be("$pwd\PoshTest [master]") - } - catch { - write-output (Get-Content $Profile) - throw - } - finally { - Clean-Environment - if( Test-Path function:\Write-Host ) {Remove-Item function:\Write-Host} - if( Test-Path PoshTest ) {Remove-Item PoshTest -Force -Recurse} - } - } - - It "WillSucceedOnUpdatingFrom040" { - Cleanup - Setup-Environment - try{ - Remove-Item $Profile -Force - Add-Content $profile -value ". 'C:\tools\poshgit\dahlbyk-posh-git-60be436\profile.example.ps1'" -Force - RunInstall - mkdir PoshTest - Pushd PoshTest - git init - write-output (Get-Content function:\prompt) - . $Profile - $global:wh="" - New-Item function:\global:Write-Host -value "param([object] `$object, `$backgroundColor, `$foregroundColor, [switch] `$nonewline) try{Write-Output `$object;[string]`$global:wh += `$object.ToString()} catch{}" - - Prompt - - Remove-Item function:\global:Write-Host - Popd - $wh.should.be("$pwd\PoshTest [master]") - } - catch { - write-output (Get-Content $Profile) - throw - } - finally { - Clean-Environment - if( Test-Path function:\Write-Host ) {Remove-Item function:\Write-Host} - if( Test-Path PoshTest ) {Remove-Item PoshTest -Force -Recurse} - } - } - -} +$packageName = "poshgit" +cpack + +function Setup-Environment { + Cleanup + $env:poshGit = join-path (Resolve-Path .\Tests ) dahlbyk-posh-git-60be436.zip + $profileScript = "function Prompt(){ `$host.ui.RawUI.WindowTitle = `"My Prompt`" }" + (Set-Content $Profile -value $profileScript -Force) +} + +function Cleanup { + Clean-Temp + Remove-Item $env:ChocolateyInstall\lib\$packageName* -Recurse -Force +} + +function Clean-Temp { + if(Test-Path $env:Temp\Chocolatey\$packageName) {Remove-Item $env:Temp\Chocolatey\$packageName -Recurse -Force} +} + +function RunInstall { + cinst $packageName -source (Resolve-Path .) +} +$binRoot = join-path $env:systemdrive 'tools' +if($env:chocolatey_bin_root -ne $null){$binRoot = join-path $env:systemdrive $env:chocolatey_bin_root} +$poshgitPath = join-path $binRoot 'poshgit' +if(Test-Path $Profile) { $currentProfileScript = (Get-Content $Profile) } + +function Clean-Environment { + Set-Content $Profile -value $currentProfileScript -Force +} + +Describe "Install-Posh-Git" { + + It "WillRemvePreviousInstallVersion" { + Setup-Environment + try{ + Add-Content $profile -value ". '$poshgitPath\posh-git\profile.example.ps1'" + + RunInstall + + $newProfile = (Get-Content $Profile) + $pgitDir = [Array](Dir "$poshgitPath\*posh-git*\" | Sort-Object -Property LastWriteTime)[-1] + ($newProfile -like ". '$poshgitPath\posh-git\profile.example.ps1'").Count.should.be(0) + ($newProfile -like ". '$pgitDir\profile.example.ps1'").Count.should.be(1) + } + catch { + write-host (Get-Content $Profile) + throw + } + finally {Clean-Environment} + } + + It "WillNotAddDuplicateCallOnRepeatInstall" { + Setup-Environment + try{ + RunInstall + Cleanup + + RunInstall + + $newProfile = (Get-Content $Profile) + $pgitDir = [Array](Dir "$poshgitPath\*posh-git*\" | Sort-Object -Property LastWriteTime)[-1] + ($newProfile -like ". '$pgitDir\profile.example.ps1'").Count.should.be(1) + } + catch { + write-host (Get-Content $Profile) + throw + } + finally {Clean-Environment} + } + + It "WillPreserveOldPromptLogic" { + Setup-Environment + try{ + RunInstall + . $Profile + $host.ui.RawUI.WindowTitle = "bad" + + Prompt + + $host.ui.RawUI.WindowTitle.should.be("My Prompt") + } + catch { + write-host (Get-Content function:\prompt) + throw + } + finally { + Clean-Environment + } + } + + It "WillOutputVcsStatus" { + Setup-Environment + try{ + RunInstall + mkdir PoshTest + Pushd PoshTest + git init + . $Profile + $global:wh="" + New-Item function:\global:Write-Host -value "param([object] `$object, `$backgroundColor, `$foregroundColor, [switch] `$nonewline) try{Write-Output `$object;[string]`$global:wh += `$object.ToString()} catch{}" + + Prompt + + Popd + $wh.should.be("$pwd\PoshTest [master]") + } + catch { + write-output (Get-Content $Profile) + throw + } + finally { + Clean-Environment + if( Test-Path function:\Write-Host ) {Remove-Item function:\Write-Host} + if( Test-Path PoshTest ) {Remove-Item PoshTest -Force -Recurse} + } + } + + It "WillSucceedOnEmptyProfile" { + Setup-Environment + try{ + Remove-Item $Profile -Force + RunInstall + mkdir PoshTest + Pushd PoshTest + git init + . $Profile + $global:wh="" + New-Item function:\global:Write-Host -value "param([object] `$object, `$backgroundColor, `$foregroundColor, [switch] `$nonewline) try{Write-Output `$object;[string]`$global:wh += `$object.ToString()} catch{}" + + Prompt + + Popd + $wh.should.be("$pwd\PoshTest [master]") + } + catch { + write-output (Get-Content $Profile) + throw + } + finally { + Clean-Environment + if( Test-Path function:\Write-Host ) {Remove-Item function:\Write-Host} + if( Test-Path PoshTest ) {Remove-Item PoshTest -Force -Recurse} + } + } + + It "WillSucceedOnProfileWithPromptWithWriteHost" { + Cleanup + Setup-Environment + try{ + Remove-Item $Profile -Force + Add-Content $profile -value "function prompt {Write-Host 'Hi'}" -Force + RunInstall + mkdir PoshTest + Pushd PoshTest + git init + . $Profile + $global:wh="" + New-Item function:\global:Write-Host -value "param([object] `$object, `$backgroundColor, `$foregroundColor, [switch] `$nonewline) try{Write-Output `$object;[string]`$global:wh += `$object.ToString()} catch{}" + + Prompt + + Remove-Item function:\global:Write-Host + Popd + $wh.should.be("$pwd\PoshTest [master]") + } + catch { + write-output (Get-Content $Profile) + throw + } + finally { + Clean-Environment + if( Test-Path function:\Write-Host ) {Remove-Item function:\Write-Host} + if( Test-Path PoshTest ) {Remove-Item PoshTest -Force -Recurse} + } + } + + It "WillSucceedOnUpdatingFrom040" { + Cleanup + Setup-Environment + try{ + Remove-Item $Profile -Force + Add-Content $profile -value ". 'C:\tools\poshgit\dahlbyk-posh-git-60be436\profile.example.ps1'" -Force + RunInstall + mkdir PoshTest + Pushd PoshTest + git init + write-output (Get-Content function:\prompt) + . $Profile + $global:wh="" + New-Item function:\global:Write-Host -value "param([object] `$object, `$backgroundColor, `$foregroundColor, [switch] `$nonewline) try{Write-Output `$object;[string]`$global:wh += `$object.ToString()} catch{}" + + Prompt + + Remove-Item function:\global:Write-Host + Popd + $wh.should.be("$pwd\PoshTest [master]") + } + catch { + write-output (Get-Content $Profile) + throw + } + finally { + Clean-Environment + if( Test-Path function:\Write-Host ) {Remove-Item function:\Write-Host} + if( Test-Path PoshTest ) {Remove-Item PoshTest -Force -Recurse} + } + } + +} diff --git a/chocolatey/tools/chocolateyInstall.ps1 b/chocolatey/tools/chocolateyInstall.ps1 index 0f6caf308..f51c0f998 100644 --- a/chocolatey/tools/chocolateyInstall.ps1 +++ b/chocolatey/tools/chocolateyInstall.ps1 @@ -1,50 +1,50 @@ -try { - $binRoot = Get-BinRoot - $poshgitPath = join-path $binRoot 'poshgit' - - try { - if (test-path($poshgitPath)) { - Write-Host "Attempting to remove existing `'$poshgitPath`'." - remove-item $poshgitPath -recurse -force - } - } catch { - Write-Host "Could not remove `'$poshgitPath`'" - } - - $version = "v$Env:chocolateyPackageVersion" - if ($version -eq 'v') { $version = 'master' } - $poshGitInstall = if ($env:poshGit ) { $env:poshGit } else { "https://github.com/dahlbyk/posh-git/zipball/$version" } - $zip = Install-ChocolateyZipPackage 'poshgit' $poshGitInstall $poshgitPath - $currentVersionPath = Get-ChildItem "$poshgitPath\*posh-git*\" | Sort-Object -Property LastWriteTime | Select-Object -Last 1 - - if(Test-Path $PROFILE) { - $oldProfile = @(Get-Content $PROFILE) - - . $currentVersionPath\src\Utils.ps1 - $oldProfileEncoding = Get-FileEncoding $PROFILE - - $newProfile = @() - foreach($line in $oldProfile) { - if ($line -like '*PoshGitPrompt*') { continue; } - - if($line -like '. *posh-git*profile.example.ps1*') { - $line = ". '$currentVersionPath\profile.example.ps1'" - } - if($line -like 'Import-Module *\src\posh-git.psd1*') { - $line = "Import-Module '$currentVersionPath\src\posh-git.psd1'" - } - $newProfile += $line - } - Set-Content -path $profile -value $newProfile -Force -Encoding $oldProfileEncoding - } - - $installer = Join-Path $currentVersionPath 'install.ps1' - & $installer -} catch { - try { - if($oldProfile){ Set-Content -path $PROFILE -value $oldProfile -Force -Encoding $oldProfileEncoding } - } - catch {} - throw -} - +try { + $binRoot = Get-BinRoot + $poshgitPath = join-path $binRoot 'poshgit' + + try { + if (test-path($poshgitPath)) { + Write-Host "Attempting to remove existing `'$poshgitPath`'." + remove-item $poshgitPath -recurse -force + } + } catch { + Write-Host "Could not remove `'$poshgitPath`'" + } + + $version = "v$Env:chocolateyPackageVersion" + if ($version -eq 'v') { $version = 'master' } + $poshGitInstall = if ($env:poshGit ) { $env:poshGit } else { "https://github.com/dahlbyk/posh-git/zipball/$version" } + $zip = Install-ChocolateyZipPackage 'poshgit' $poshGitInstall $poshgitPath + $currentVersionPath = Get-ChildItem "$poshgitPath\*posh-git*\" | Sort-Object -Property LastWriteTime | Select-Object -Last 1 + + if(Test-Path $PROFILE) { + $oldProfile = @(Get-Content $PROFILE) + + . $currentVersionPath\src\Utils.ps1 + $oldProfileEncoding = Get-FileEncoding $PROFILE + + $newProfile = @() + foreach($line in $oldProfile) { + if ($line -like '*PoshGitPrompt*') { continue; } + + if($line -like '. *posh-git*profile.example.ps1*') { + $line = ". '$currentVersionPath\profile.example.ps1'" + } + if($line -like 'Import-Module *\src\posh-git.psd1*') { + $line = "Import-Module '$currentVersionPath\src\posh-git.psd1'" + } + $newProfile += $line + } + Set-Content -path $profile -value $newProfile -Force -Encoding $oldProfileEncoding + } + + $installer = Join-Path $currentVersionPath 'install.ps1' + & $installer +} catch { + try { + if($oldProfile){ Set-Content -path $PROFILE -value $oldProfile -Force -Encoding $oldProfileEncoding } + } + catch {} + throw +} + diff --git a/chocolatey/tools/chocolateyUninstall.ps1 b/chocolatey/tools/chocolateyUninstall.ps1 index 311fb9878..30ab3bba4 100644 --- a/chocolatey/tools/chocolateyUninstall.ps1 +++ b/chocolatey/tools/chocolateyUninstall.ps1 @@ -1,44 +1,44 @@ -try { - $binRoot = Get-BinRoot - $poshgitPath = join-path $binRoot 'poshgit' - - $currentVersionPath = Get-ChildItem "$poshgitPath\*posh-git*\" | Sort-Object -Property LastWriteTime | Select-Object -Last 1 - - if(Test-Path $PROFILE) { - $oldProfile = @(Get-Content $PROFILE) - - . $currentVersionPath\src\Utils.ps1 - $oldProfileEncoding = Get-FileEncoding $PROFILE - - $newProfile = @() - foreach($line in $oldProfile) { - if ($line -like '*PoshGitPrompt*') { continue; } - if ($line -like '*Load posh-git example profile*') { continue; } - - if($line -like '. *posh-git*profile.example.ps1*') { - continue; - } - if($line -like 'Import-Module *\src\posh-git.psd1*') { - continue; - } - $newProfile += $line - } - Set-Content -path $profile -value $newProfile -Force -Encoding $oldProfileEncoding - } - - try { - if (test-path($poshgitPath)) { - Write-Host "Attempting to remove existing `'$poshgitPath`'." - remove-item $poshgitPath -recurse -force - } - } catch { - Write-Host "Could not remove `'$poshgitPath`'" - } -} catch { - try { - if($oldProfile){ Set-Content -path $PROFILE -value $oldProfile -Force -Encoding $oldProfileEncoding } - } - catch {} - throw -} - +try { + $binRoot = Get-BinRoot + $poshgitPath = join-path $binRoot 'poshgit' + + $currentVersionPath = Get-ChildItem "$poshgitPath\*posh-git*\" | Sort-Object -Property LastWriteTime | Select-Object -Last 1 + + if(Test-Path $PROFILE) { + $oldProfile = @(Get-Content $PROFILE) + + . $currentVersionPath\src\Utils.ps1 + $oldProfileEncoding = Get-FileEncoding $PROFILE + + $newProfile = @() + foreach($line in $oldProfile) { + if ($line -like '*PoshGitPrompt*') { continue; } + if ($line -like '*Load posh-git example profile*') { continue; } + + if($line -like '. *posh-git*profile.example.ps1*') { + continue; + } + if($line -like 'Import-Module *\src\posh-git.psd1*') { + continue; + } + $newProfile += $line + } + Set-Content -path $profile -value $newProfile -Force -Encoding $oldProfileEncoding + } + + try { + if (test-path($poshgitPath)) { + Write-Host "Attempting to remove existing `'$poshgitPath`'." + remove-item $poshgitPath -recurse -force + } + } catch { + Write-Host "Could not remove `'$poshgitPath`'" + } +} catch { + try { + if($oldProfile){ Set-Content -path $PROFILE -value $oldProfile -Force -Encoding $oldProfileEncoding } + } + catch {} + throw +} + diff --git a/install.ps1 b/install.ps1 index bff83440a..c54135317 100644 --- a/install.ps1 +++ b/install.ps1 @@ -1,6 +1,6 @@ -param([switch]$WhatIf = $false, [switch]$Force = $false) - -$installDir = Split-Path $MyInvocation.MyCommand.Path -Parent - -Import-Module $installDir\src\posh-git.psd1 -Add-PoshGitToProfile -WhatIf:$WhatIf -Force:$Force +param([switch]$WhatIf = $false, [switch]$Force = $false) + +$installDir = Split-Path $MyInvocation.MyCommand.Path -Parent + +Import-Module $installDir\src\posh-git.psd1 +Add-PoshGitToProfile -WhatIf:$WhatIf -Force:$Force diff --git a/profile.example.ps1 b/profile.example.ps1 index 5adcad9ed..fe86c74b8 100644 --- a/profile.example.ps1 +++ b/profile.example.ps1 @@ -1,20 +1,20 @@ -# Import the posh-git module, first via installed posh-git module. -# If the module isn't installed, then attempt to load it from the cloned posh-git Git repo. -$poshGitModule = Get-Module posh-git -ListAvailable | Sort-Object Version -Descending | Select-Object -First 1 -if ($poshGitModule) { - $poshGitModule | Import-Module -} -elseif (Test-Path -LiteralPath ($modulePath = Join-Path (Split-Path $MyInvocation.MyCommand.Path -Parent) (Join-Path src 'posh-git.psd1'))) { - Import-Module $modulePath -} -else { - throw "Failed to import posh-git." -} - -# Settings for the prompt are in GitPrompt.ps1, so add any desired settings changes here. -# Example: -# $Global:GitPromptSettings.BranchBehindAndAheadDisplay = "Compact" - -Start-SshAgent -Quiet - -Write-Warning "posh-git's profile.example.ps1 will be removed in a future version. To avoid a change in behavior, copy its contents into your $PROFILE." +# Import the posh-git module, first via installed posh-git module. +# If the module isn't installed, then attempt to load it from the cloned posh-git Git repo. +$poshGitModule = Get-Module posh-git -ListAvailable | Sort-Object Version -Descending | Select-Object -First 1 +if ($poshGitModule) { + $poshGitModule | Import-Module +} +elseif (Test-Path -LiteralPath ($modulePath = Join-Path (Split-Path $MyInvocation.MyCommand.Path -Parent) (Join-Path src 'posh-git.psd1'))) { + Import-Module $modulePath +} +else { + throw "Failed to import posh-git." +} + +# Settings for the prompt are in GitPrompt.ps1, so add any desired settings changes here. +# Example: +# $Global:GitPromptSettings.BranchBehindAndAheadDisplay = "Compact" + +Start-SshAgent -Quiet + +Write-Warning "posh-git's profile.example.ps1 will be removed in a future version. To avoid a change in behavior, copy its contents into your $PROFILE." diff --git a/src/CheckVersion.ps1 b/src/CheckVersion.ps1 index 002f4d08a..99e1506f2 100644 --- a/src/CheckVersion.ps1 +++ b/src/CheckVersion.ps1 @@ -1,18 +1,18 @@ -$Global:GitMissing = $false - -if (!(Get-Command git -TotalCount 1 -ErrorAction SilentlyContinue)) { - Write-Warning "git command could not be found. Please create an alias or add it to your PATH." - $Global:GitMissing = $true - return -} - -$requiredVersion = [Version]'1.7.2' -if ([String](git --version 2> $null) -match '(?\d+(?:\.\d+)+)') { - $version = [Version]$Matches['ver'] -} -if ($version -lt $requiredVersion) { - Write-Warning "posh-git requires Git $requiredVersion or better. You have $version." - $false -} else { - $true -} +$Global:GitMissing = $false + +if (!(Get-Command git -TotalCount 1 -ErrorAction SilentlyContinue)) { + Write-Warning "git command could not be found. Please create an alias or add it to your PATH." + $Global:GitMissing = $true + return +} + +$requiredVersion = [Version]'1.7.2' +if ([String](git --version 2> $null) -match '(?\d+(?:\.\d+)+)') { + $version = [Version]$Matches['ver'] +} +if ($version -lt $requiredVersion) { + Write-Warning "posh-git requires Git $requiredVersion or better. You have $version." + $false +} else { + $true +} diff --git a/src/GitParamTabExpansion.ps1 b/src/GitParamTabExpansion.ps1 index 058be9fc1..1b29aa3f7 100644 --- a/src/GitParamTabExpansion.ps1 +++ b/src/GitParamTabExpansion.ps1 @@ -1,167 +1,167 @@ -# Variable is used in GitTabExpansion.ps1 -[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] -$shortGitParams = @{ - add = 'n v f i p e u A N' - bisect = '' - blame = 'b L l t S p M C h c f n s e w' - branch = 'd D l f m M r a v vv q t u' - checkout = 'q f b B t l m p' - cherry = 'v' - 'cherry-pick' = 'e x r m n s S X' - clean = 'd f i n q e x X' - clone = 'l s q v n o b u c' - commit = 'a p C c z F m t s n e i o u v q S' - config = 'f l z e' - diff = 'p u s U z B M C D l S G O R a b w W' - difftool = 'd y t x g' - fetch = 'a f k p n t u q v' - grep = 'a i I w v h H E G P F n l L O z c p C A B W f e q' - help = 'a g i m w' - init = 'q' - log = 'L n i E F g c c m r t' - merge = 'e n s X q v S m' - mergetool = 't y' - mv = 'f k n v' - notes = 'f m F C c n s q v' - prune = 'n v' - pull = 'q v e n s X r a f k u' - push = 'n f u q v' - rebase = 'm s X S q v n C f i p x' - remote = 'v' - reset = 'q p' - revert = 'e m n S s X' - rm = 'f n r q' - shortlog = 'n s e w' - stash = 'p k u a q' - status = 's b u z' - submodule = 'q b f n N' - tag = 'a s u f d v n l m F' - whatchanged = 'p' -} - -# Variable is used in GitTabExpansion.ps1 -[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] -$longGitParams = @{ - add = 'dry-run verbose force interactive patch edit update all no-ignore-removal no-all ignore-removal intent-to-add refresh ignore-errors ignore-missing' - bisect = 'no-checkout term-old term-new' - blame = 'root show-stats reverse porcelain line-porcelain incremental encoding= contents date score-debug show-name show-number show-email abbrev' - branch = 'color no-color list abbrev= no-abbrev column no-column merged no-merged contains set-upstream track no-track set-upstream-to= unset-upstream edit-description delete create-reflog force move all verbose quiet' - checkout = 'quiet force ours theirs track no-track detach orphan ignore-skip-worktree-bits merge conflict= patch' - 'cherry-pick' = 'edit mainline no-commit signoff gpg-sign ff allow-empty allow-empty-message keep-redundant-commits strategy= strategy-option= ´continue quit abort' - clean = 'force interactive dry-run quiet exclude=' - clone = 'local no-hardlinks shared reference quiet verbose progress no-checkout bare mirror origin branch upload-pack template= config depth single-branch no-single-branch recursive recurse-submodules separate-git-dir=' - commit = 'all patch reuse-message reedit-message fixup squash reset-author short branch porcelain long null file author date message template signoff no-verify allow-empty allow-empty-message cleanup= edit no-edit ammend no-post-rewrite include only untracked-files verbose quiet dry-run status no-status gpg-sign no-gpg-sign' - config = 'replace-all add get get-all get-regexp get-urlmatch global system local file blob remove-section rename-section unset unset-all list bool int bool-or-int path null get-colorbool get-color edit includes no-includes' - describe = 'dirty all tags contains abbrev candidates= exact-match debug long match always first-parent' - diff = 'cached patch no-patch unified= raw patch-with-raw minimal patience histogram diff-algorithm= stat numstat shortstat dirstat summary patch-with-stat name-only name-status submodule color no-color word-diff word-diff-regex color-words no-renames check full-index binary apprev break-rewrites find-renames find-copies find-copies-harder irreversible-delete diff-filter= pickaxe-all pickaxe-regex relative text ignore-space-at-eol ignore-space-change ignore-all-space ignore-blank-lines inter-hunk-context= function-context exit-code quiet ext-diff no-ext-diff textconv no-textconv ignore-submodules src-prefix dst-prefix no-prefix' - difftool = 'dir-diff no-prompt prompt tool= tool-help no-symlinks symlinks extcmd= gui' - fetch = 'all append depth= unshallow update-shallow dry-run force keep multiple prune no-tags tags recurse-submodules= no-recurse-submodules submodule-prefix= recurse-submodules-default= update-head-ok upload-pack quiet verbose progress' - gc = 'aggressive auto prune= no-prune quiet force' - grep = 'cached no-index untracked no-exclude-standard exclude-standard text textconv no-textconv ignore-case max-depth word-regexp invert-match full-name extended-regexp basic-regexp perl-regexp fixed-strings line-number files-with-matches open-file-in-pager null count color no-color break heading show-function context after-context before-context function-context and or not all-match quiet' - help = 'all guides info man web' - init = 'quiet bare template= separate-git-dir= shared=' - log = 'follow no-decorate decorate source use-mailmap full-diff log-size max-count skip since after until before author committer grep-reflog grep all-match regexp-ignore-case basic-regexp extended-regexp fixed-strings perl-regexp remove-empty merges no-merges min-parents max-parents no-min-parents no-max-parents first-parent not all branches tags remote glob= exclude= ignore-missing bisect stdin cherry-mark cherry-pick left-only right-only cherry walk-reflogs merge boundary simplify-by-decoration full-history dense sparse simplify-merges ancestry-path date-order author-date-order topo-order reverse objects objects-edge unpacked no-walk= do-walk pretty format= abbrev-commit no-abbrev-commit oneline encoding= notes no-notes standard-notes no-standard-notes show-signature relative-date date= parents children left-right graph show-linear-break ' - merge = 'commit no-commit edit no-edit ff no-ff ff-only log no-log stat no-stat squash no-squash strategy strategy-option verify-signatures no-verify-signatures summary no-summary quiet verbose progress no-progress gpg-sign rerere-autoupdate no-rerere-autoupdate abort' - mergetool = 'tool= tool-help no-prompt prompt' - mv = 'force dry-run verbose' - notes = 'force message file reuse-message reedit-message ref ignore-missing stdin dry-run strategy= commit abort quiet verbose' - prune = 'dry-run verbose expire' - pull = 'quiet verbose recurse-submodules= no-recurse-submodules= commit no-commit edit no-edit ff no-ff ff-only log no-log stat no-stat squash no-squash strategy= strategy-option= verify-signatures no-verify-signatures summary no-summary rebase= no-rebase all append depth= unshallow update-shallow force keep no-tags update-head-ok upload-pack progress' - push = 'all prune mirror dry-run porcelain delete tags follow-tags receive-pack= exec= force-with-lease= no-force-with-lease force repo= set-upstream thin no-thin quiet verbose progress recurse-submodules= verify no-verify' - rebase = 'onto continue abort keep-empty skip edit-todo merge strategy= strategy-option= gpg-sign quiet verbose stat no-stat no-verify verify force-rebase fork-point no-fork-point ignore-whitespace whitespace= committer-date-is-author-date ignore-date interactive preserve-merges exec root autosquash no-autosquash autostash no-autostash no-ff' - reflog = 'stale-fix expire= expire-unreachable= all updateref rewrite verbose' - remote = 'verbose' - reset = 'patch quiet soft mixed hard merge keep' - revert = 'edit mainline no-edit no-commit gpg-sign signoff strategy= strategy-option continue quit abort' - rm = 'force dry-run cached ignore-unmatch quiet' - shortlog = 'numbered summary email format=' - show = 'pretty= format= abbrev-commit no-abbrev-commit oneline encoding= notes no-notes show-notes no-standard-notes standard-notes show-signature' - stash = 'patch no-keep-index keep-index include-untracked all quiet index' - status = 'short branch porcelain long untracked-files ignore-submodules ignored column no-column' - submodule = 'quiet branch force cached files summary-limit remote no-fetch checkout merge rebase init name reference recursive depth' - tag = 'annotate sign local-user force delete verify list sort column no-column contains points-at message file cleanup' - whatchanged = 'since' -} - -# Variable is used in GitTabExpansion.ps1 -[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] -$gitParamValues = @{ - blame = @{ - encoding = 'utf-8 none' - } - branch = @{ - color = 'always never auto' - abbrev = '7 8 9 10' - } - checkout = @{ - conflict = 'merge diff3' - } - 'cherry-pick' = @{ - strategy = 'resolve recursive octopus ours subtree' - } - commit = @{ - 'cleanup' = 'strip whitespace verbatim scissors default' - } - diff = @{ - unified = '0 1 2 3 4 5' - 'diff-algorithm' = 'default patience minimal histogram myers' - color = 'always never auto' - 'word-diff' = 'color plain porcelain none' - abbrev = '7 8 9 10' - 'diff-filter' = 'A C D M R T U X B *' - 'inter-hunk-context' = '0 1 2 3 4 5' - 'ignore-submodules' = 'none untracked dirty all' - } - difftool = @{ - tool = 'vimdiff vimdiff2 araxis bc3 codecompare deltawalker diffmerge diffuse ecmerge emerge gvimdiff gvimdiff2 kdiff3 kompare meld opendiff p4merge tkdiff xxdiff' - } - fetch = @{ - 'recurse-submodules' = 'yes on-demand no' - 'recurse-submodules-default' = 'yes on-demand' - } - init = @{ - shared = 'false true umask group all world everybody o' - } - log = @{ - decorate = 'short full no' - 'no-walk' = 'sorted unsorted' - pretty = 'oneline short medium full fuller email raw' - format = 'oneline short medium full fuller email raw' - encoding = 'UTF-8' - date = 'relative local default iso rfc short raw' - } - merge = @{ - strategy = 'resolve recursive octopus ours subtree' - log = '1 2 3 4 5 6 7 8 9' - } - mergetool = @{ - tool = 'vimdiff vimdiff2 araxis bc3 codecompare deltawalker diffmerge diffuse ecmerge emerge gvimdiff gvimdiff2 kdiff3 kompare meld opendiff p4merge tkdiff xxdiff' - } - notes = @{ - strategy = 'manual ours theirs union cat_sort_uniq' - } - pull = @{ - strategy = 'resolve recursive octopus ours subtree' - 'recurse-submodules' = 'yes on-demand no' - 'no-recurse-submodules' = 'yes on-demand no' - rebase = 'false true preserve' - } - push = @{ - 'recurse-submodules' = 'check on-demand' - } - rebase = @{ - strategy = 'resolve recursive octopus ours subtree' - } - revert = @{ - strategy = 'resolve recursive octopus ours subtree' - } - show = @{ - pretty = 'oneline short medium full fuller email raw' - format = 'oneline short medium full fuller email raw' - encoding = 'utf-8' - } - status = @{ - 'untracked-files' = 'no normal all' - 'ignore-submodules' = 'none untracked dirty all' - } -} +# Variable is used in GitTabExpansion.ps1 +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] +$shortGitParams = @{ + add = 'n v f i p e u A N' + bisect = '' + blame = 'b L l t S p M C h c f n s e w' + branch = 'd D l f m M r a v vv q t u' + checkout = 'q f b B t l m p' + cherry = 'v' + 'cherry-pick' = 'e x r m n s S X' + clean = 'd f i n q e x X' + clone = 'l s q v n o b u c' + commit = 'a p C c z F m t s n e i o u v q S' + config = 'f l z e' + diff = 'p u s U z B M C D l S G O R a b w W' + difftool = 'd y t x g' + fetch = 'a f k p n t u q v' + grep = 'a i I w v h H E G P F n l L O z c p C A B W f e q' + help = 'a g i m w' + init = 'q' + log = 'L n i E F g c c m r t' + merge = 'e n s X q v S m' + mergetool = 't y' + mv = 'f k n v' + notes = 'f m F C c n s q v' + prune = 'n v' + pull = 'q v e n s X r a f k u' + push = 'n f u q v' + rebase = 'm s X S q v n C f i p x' + remote = 'v' + reset = 'q p' + revert = 'e m n S s X' + rm = 'f n r q' + shortlog = 'n s e w' + stash = 'p k u a q' + status = 's b u z' + submodule = 'q b f n N' + tag = 'a s u f d v n l m F' + whatchanged = 'p' +} + +# Variable is used in GitTabExpansion.ps1 +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] +$longGitParams = @{ + add = 'dry-run verbose force interactive patch edit update all no-ignore-removal no-all ignore-removal intent-to-add refresh ignore-errors ignore-missing' + bisect = 'no-checkout term-old term-new' + blame = 'root show-stats reverse porcelain line-porcelain incremental encoding= contents date score-debug show-name show-number show-email abbrev' + branch = 'color no-color list abbrev= no-abbrev column no-column merged no-merged contains set-upstream track no-track set-upstream-to= unset-upstream edit-description delete create-reflog force move all verbose quiet' + checkout = 'quiet force ours theirs track no-track detach orphan ignore-skip-worktree-bits merge conflict= patch' + 'cherry-pick' = 'edit mainline no-commit signoff gpg-sign ff allow-empty allow-empty-message keep-redundant-commits strategy= strategy-option= ´continue quit abort' + clean = 'force interactive dry-run quiet exclude=' + clone = 'local no-hardlinks shared reference quiet verbose progress no-checkout bare mirror origin branch upload-pack template= config depth single-branch no-single-branch recursive recurse-submodules separate-git-dir=' + commit = 'all patch reuse-message reedit-message fixup squash reset-author short branch porcelain long null file author date message template signoff no-verify allow-empty allow-empty-message cleanup= edit no-edit ammend no-post-rewrite include only untracked-files verbose quiet dry-run status no-status gpg-sign no-gpg-sign' + config = 'replace-all add get get-all get-regexp get-urlmatch global system local file blob remove-section rename-section unset unset-all list bool int bool-or-int path null get-colorbool get-color edit includes no-includes' + describe = 'dirty all tags contains abbrev candidates= exact-match debug long match always first-parent' + diff = 'cached patch no-patch unified= raw patch-with-raw minimal patience histogram diff-algorithm= stat numstat shortstat dirstat summary patch-with-stat name-only name-status submodule color no-color word-diff word-diff-regex color-words no-renames check full-index binary apprev break-rewrites find-renames find-copies find-copies-harder irreversible-delete diff-filter= pickaxe-all pickaxe-regex relative text ignore-space-at-eol ignore-space-change ignore-all-space ignore-blank-lines inter-hunk-context= function-context exit-code quiet ext-diff no-ext-diff textconv no-textconv ignore-submodules src-prefix dst-prefix no-prefix' + difftool = 'dir-diff no-prompt prompt tool= tool-help no-symlinks symlinks extcmd= gui' + fetch = 'all append depth= unshallow update-shallow dry-run force keep multiple prune no-tags tags recurse-submodules= no-recurse-submodules submodule-prefix= recurse-submodules-default= update-head-ok upload-pack quiet verbose progress' + gc = 'aggressive auto prune= no-prune quiet force' + grep = 'cached no-index untracked no-exclude-standard exclude-standard text textconv no-textconv ignore-case max-depth word-regexp invert-match full-name extended-regexp basic-regexp perl-regexp fixed-strings line-number files-with-matches open-file-in-pager null count color no-color break heading show-function context after-context before-context function-context and or not all-match quiet' + help = 'all guides info man web' + init = 'quiet bare template= separate-git-dir= shared=' + log = 'follow no-decorate decorate source use-mailmap full-diff log-size max-count skip since after until before author committer grep-reflog grep all-match regexp-ignore-case basic-regexp extended-regexp fixed-strings perl-regexp remove-empty merges no-merges min-parents max-parents no-min-parents no-max-parents first-parent not all branches tags remote glob= exclude= ignore-missing bisect stdin cherry-mark cherry-pick left-only right-only cherry walk-reflogs merge boundary simplify-by-decoration full-history dense sparse simplify-merges ancestry-path date-order author-date-order topo-order reverse objects objects-edge unpacked no-walk= do-walk pretty format= abbrev-commit no-abbrev-commit oneline encoding= notes no-notes standard-notes no-standard-notes show-signature relative-date date= parents children left-right graph show-linear-break ' + merge = 'commit no-commit edit no-edit ff no-ff ff-only log no-log stat no-stat squash no-squash strategy strategy-option verify-signatures no-verify-signatures summary no-summary quiet verbose progress no-progress gpg-sign rerere-autoupdate no-rerere-autoupdate abort' + mergetool = 'tool= tool-help no-prompt prompt' + mv = 'force dry-run verbose' + notes = 'force message file reuse-message reedit-message ref ignore-missing stdin dry-run strategy= commit abort quiet verbose' + prune = 'dry-run verbose expire' + pull = 'quiet verbose recurse-submodules= no-recurse-submodules= commit no-commit edit no-edit ff no-ff ff-only log no-log stat no-stat squash no-squash strategy= strategy-option= verify-signatures no-verify-signatures summary no-summary rebase= no-rebase all append depth= unshallow update-shallow force keep no-tags update-head-ok upload-pack progress' + push = 'all prune mirror dry-run porcelain delete tags follow-tags receive-pack= exec= force-with-lease= no-force-with-lease force repo= set-upstream thin no-thin quiet verbose progress recurse-submodules= verify no-verify' + rebase = 'onto continue abort keep-empty skip edit-todo merge strategy= strategy-option= gpg-sign quiet verbose stat no-stat no-verify verify force-rebase fork-point no-fork-point ignore-whitespace whitespace= committer-date-is-author-date ignore-date interactive preserve-merges exec root autosquash no-autosquash autostash no-autostash no-ff' + reflog = 'stale-fix expire= expire-unreachable= all updateref rewrite verbose' + remote = 'verbose' + reset = 'patch quiet soft mixed hard merge keep' + revert = 'edit mainline no-edit no-commit gpg-sign signoff strategy= strategy-option continue quit abort' + rm = 'force dry-run cached ignore-unmatch quiet' + shortlog = 'numbered summary email format=' + show = 'pretty= format= abbrev-commit no-abbrev-commit oneline encoding= notes no-notes show-notes no-standard-notes standard-notes show-signature' + stash = 'patch no-keep-index keep-index include-untracked all quiet index' + status = 'short branch porcelain long untracked-files ignore-submodules ignored column no-column' + submodule = 'quiet branch force cached files summary-limit remote no-fetch checkout merge rebase init name reference recursive depth' + tag = 'annotate sign local-user force delete verify list sort column no-column contains points-at message file cleanup' + whatchanged = 'since' +} + +# Variable is used in GitTabExpansion.ps1 +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] +$gitParamValues = @{ + blame = @{ + encoding = 'utf-8 none' + } + branch = @{ + color = 'always never auto' + abbrev = '7 8 9 10' + } + checkout = @{ + conflict = 'merge diff3' + } + 'cherry-pick' = @{ + strategy = 'resolve recursive octopus ours subtree' + } + commit = @{ + 'cleanup' = 'strip whitespace verbatim scissors default' + } + diff = @{ + unified = '0 1 2 3 4 5' + 'diff-algorithm' = 'default patience minimal histogram myers' + color = 'always never auto' + 'word-diff' = 'color plain porcelain none' + abbrev = '7 8 9 10' + 'diff-filter' = 'A C D M R T U X B *' + 'inter-hunk-context' = '0 1 2 3 4 5' + 'ignore-submodules' = 'none untracked dirty all' + } + difftool = @{ + tool = 'vimdiff vimdiff2 araxis bc3 codecompare deltawalker diffmerge diffuse ecmerge emerge gvimdiff gvimdiff2 kdiff3 kompare meld opendiff p4merge tkdiff xxdiff' + } + fetch = @{ + 'recurse-submodules' = 'yes on-demand no' + 'recurse-submodules-default' = 'yes on-demand' + } + init = @{ + shared = 'false true umask group all world everybody o' + } + log = @{ + decorate = 'short full no' + 'no-walk' = 'sorted unsorted' + pretty = 'oneline short medium full fuller email raw' + format = 'oneline short medium full fuller email raw' + encoding = 'UTF-8' + date = 'relative local default iso rfc short raw' + } + merge = @{ + strategy = 'resolve recursive octopus ours subtree' + log = '1 2 3 4 5 6 7 8 9' + } + mergetool = @{ + tool = 'vimdiff vimdiff2 araxis bc3 codecompare deltawalker diffmerge diffuse ecmerge emerge gvimdiff gvimdiff2 kdiff3 kompare meld opendiff p4merge tkdiff xxdiff' + } + notes = @{ + strategy = 'manual ours theirs union cat_sort_uniq' + } + pull = @{ + strategy = 'resolve recursive octopus ours subtree' + 'recurse-submodules' = 'yes on-demand no' + 'no-recurse-submodules' = 'yes on-demand no' + rebase = 'false true preserve' + } + push = @{ + 'recurse-submodules' = 'check on-demand' + } + rebase = @{ + strategy = 'resolve recursive octopus ours subtree' + } + revert = @{ + strategy = 'resolve recursive octopus ours subtree' + } + show = @{ + pretty = 'oneline short medium full fuller email raw' + format = 'oneline short medium full fuller email raw' + encoding = 'utf-8' + } + status = @{ + 'untracked-files' = 'no normal all' + 'ignore-submodules' = 'none untracked dirty all' + } +} diff --git a/src/GitPrompt.ps1 b/src/GitPrompt.ps1 index c2bef5319..ce3f9b2bd 100644 --- a/src/GitPrompt.ps1 +++ b/src/GitPrompt.ps1 @@ -1,316 +1,316 @@ -# Inspired by Mark Embling -# http://www.markembling.info/view/my-ideal-powershell-prompt-with-git-integration - -$global:GitPromptSettings = [pscustomobject]@{ - DefaultForegroundColor = $Host.UI.RawUI.ForegroundColor - - BeforeText = ' [' - BeforeForegroundColor = [ConsoleColor]::Yellow - BeforeBackgroundColor = $Host.UI.RawUI.BackgroundColor - - DelimText = ' |' - DelimForegroundColor = [ConsoleColor]::Yellow - DelimBackgroundColor = $Host.UI.RawUI.BackgroundColor - - AfterText = ']' - AfterForegroundColor = [ConsoleColor]::Yellow - AfterBackgroundColor = $Host.UI.RawUI.BackgroundColor - - FileAddedText = '+' - FileModifiedText = '~' - FileRemovedText = '-' - FileConflictedText = '!' - - LocalDefaultStatusSymbol = $null - LocalDefaultStatusForegroundColor = [ConsoleColor]::DarkGreen - LocalDefaultStatusForegroundBrightColor = [ConsoleColor]::Green - LocalDefaultStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor - - LocalWorkingStatusSymbol = '!' - LocalWorkingStatusForegroundColor = [ConsoleColor]::DarkRed - LocalWorkingStatusForegroundBrightColor = [ConsoleColor]::Red - LocalWorkingStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor - - LocalStagedStatusSymbol = '~' - LocalStagedStatusForegroundColor = [ConsoleColor]::Cyan - LocalStagedStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor - - BranchUntrackedSymbol = $null - BranchForegroundColor = [ConsoleColor]::Cyan - BranchBackgroundColor = $Host.UI.RawUI.BackgroundColor - - BranchGoneStatusSymbol = [char]0x00D7 # × Multiplication sign - BranchGoneStatusForegroundColor = [ConsoleColor]::DarkCyan - BranchGoneStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor - - BranchIdenticalStatusToSymbol = [char]0x2261 # ≡ Three horizontal lines - BranchIdenticalStatusToForegroundColor = [ConsoleColor]::Cyan - BranchIdenticalStatusToBackgroundColor = $Host.UI.RawUI.BackgroundColor - - BranchAheadStatusSymbol = [char]0x2191 # ↑ Up arrow - BranchAheadStatusForegroundColor = [ConsoleColor]::Green - BranchAheadStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor - - BranchBehindStatusSymbol = [char]0x2193 # ↓ Down arrow - BranchBehindStatusForegroundColor = [ConsoleColor]::Red - BranchBehindStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor - - BranchBehindAndAheadStatusSymbol = [char]0x2195 # ↕ Up & Down arrow - BranchBehindAndAheadStatusForegroundColor = [ConsoleColor]::Yellow - BranchBehindAndAheadStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor - - BeforeIndexText = "" - BeforeIndexForegroundColor = [ConsoleColor]::DarkGreen - BeforeIndexForegroundBrightColor = [ConsoleColor]::Green - BeforeIndexBackgroundColor = $Host.UI.RawUI.BackgroundColor - - IndexForegroundColor = [ConsoleColor]::DarkGreen - IndexForegroundBrightColor = [ConsoleColor]::Green - IndexBackgroundColor = $Host.UI.RawUI.BackgroundColor - - WorkingForegroundColor = [ConsoleColor]::DarkRed - WorkingForegroundBrightColor = [ConsoleColor]::Red - WorkingBackgroundColor = $Host.UI.RawUI.BackgroundColor - - EnableStashStatus = $false - BeforeStashText = ' (' - BeforeStashBackgroundColor = $Host.UI.RawUI.BackgroundColor - BeforeStashForegroundColor = [ConsoleColor]::Red - AfterStashText = ')' - AfterStashBackgroundColor = $Host.UI.RawUI.BackgroundColor - AfterStashForegroundColor = [ConsoleColor]::Red - StashBackgroundColor = $Host.UI.RawUI.BackgroundColor - StashForegroundColor = [ConsoleColor]::Red - - ShowStatusWhenZero = $true - - AutoRefreshIndex = $true - - # Valid values are "Full", "Compact", and "Minimal" - BranchBehindAndAheadDisplay = "Full" - - EnablePromptStatus = !$Global:GitMissing - EnableFileStatus = $true - EnableFileStatusFromCache = $null - RepositoriesInWhichToDisableFileStatus = @( ) # Array of repository paths - DescribeStyle = '' - - EnableWindowTitle = 'posh~git ~ ' - - DefaultPromptPrefix = '' - DefaultPromptSuffix = '$(''>'' * ($nestedPromptLevel + 1)) ' - DefaultPromptDebugSuffix = ' [DBG]$(''>'' * ($nestedPromptLevel + 1)) ' - DefaultPromptEnableTiming = $false - DefaultPromptAbbreviateHomeDirectory = $false - - Debug = $false - - BranchNameLimit = 0 - TruncatedBranchSuffix = '...' -} - -# PowerShell 5.x only runs on Windows so use .NET types to determine isAdminProcess -# Or if we are on v6 or higher, check the $IsWindows pre-defined variable. -if (($PSVersionTable.PSVersion.Major -le 5) -or $IsWindows) { - $currentUser = [Security.Principal.WindowsPrincipal]([Security.Principal.WindowsIdentity]::GetCurrent()) - $isAdminProcess = $currentUser.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) -} -else { - # Must be Linux or OSX, so use the id util. Root has userid of 0. - $isAdminProcess = 0 -eq (id -u) -} - -$adminHeader = if ($isAdminProcess) { 'Administrator: ' } else { '' } - -$WindowTitleSupported = $true -if (Get-Module NuGet) { - $WindowTitleSupported = $false -} - -function Write-Prompt($Object, $ForegroundColor, $BackgroundColor = -1) { - if ($BackgroundColor -lt 0) { - Write-Host $Object -NoNewLine -ForegroundColor $ForegroundColor - } else { - Write-Host $Object -NoNewLine -ForegroundColor $ForegroundColor -BackgroundColor $BackgroundColor - } -} - -function Format-BranchName($branchName){ - $s = $global:GitPromptSettings - - if($s.BranchNameLimit -gt 0 -and $branchName.Length -gt $s.BranchNameLimit) - { - $branchName = "{0}{1}" -f $branchName.Substring(0,$s.BranchNameLimit), $s.TruncatedBranchSuffix - } - - return $branchName -} - -function Write-GitStatus($status) { - $s = $global:GitPromptSettings - if ($status -and $s) { - Write-Prompt $s.BeforeText -BackgroundColor $s.BeforeBackgroundColor -ForegroundColor $s.BeforeForegroundColor - - $branchStatusText = $null - $branchStatusBackgroundColor = $s.BranchBackgroundColor - $branchStatusForegroundColor = $s.BranchForegroundColor - - if (!$status.Upstream) { - $branchStatusText = $s.BranchUntrackedSymbol - } elseif ($status.UpstreamGone -eq $true) { - # Upstream branch is gone - $branchStatusText = $s.BranchGoneStatusSymbol - $branchStatusBackgroundColor = $s.BranchGoneStatusBackgroundColor - $branchStatusForegroundColor = $s.BranchGoneStatusForegroundColor - } elseif ($status.BehindBy -eq 0 -and $status.AheadBy -eq 0) { - # We are aligned with remote - $branchStatusText = $s.BranchIdenticalStatusToSymbol - $branchStatusBackgroundColor = $s.BranchIdenticalStatusToBackgroundColor - $branchStatusForegroundColor = $s.BranchIdenticalStatusToForegroundColor - } elseif ($status.BehindBy -ge 1 -and $status.AheadBy -ge 1) { - # We are both behind and ahead of remote - if ($s.BranchBehindAndAheadDisplay -eq "Full") { - $branchStatusText = ("{0}{1} {2}{3}" -f $s.BranchBehindStatusSymbol, $status.BehindBy, $s.BranchAheadStatusSymbol, $status.AheadBy) - } elseif ($s.BranchBehindAndAheadDisplay -eq "Compact") { - $branchStatusText = ("{0}{1}{2}" -f $status.BehindBy, $s.BranchBehindAndAheadStatusSymbol, $status.AheadBy) - } else { - $branchStatusText = $s.BranchBehindAndAheadStatusSymbol - } - $branchStatusBackgroundColor = $s.BranchBehindAndAheadStatusBackgroundColor - $branchStatusForegroundColor = $s.BranchBehindAndAheadStatusForegroundColor - } elseif ($status.BehindBy -ge 1) { - # We are behind remote - if ($s.BranchBehindAndAheadDisplay -eq "Full" -Or $s.BranchBehindAndAheadDisplay -eq "Compact") { - $branchStatusText = ("{0}{1}" -f $s.BranchBehindStatusSymbol, $status.BehindBy) - } else { - $branchStatusText = $s.BranchBehindStatusSymbol - } - $branchStatusBackgroundColor = $s.BranchBehindStatusBackgroundColor - $branchStatusForegroundColor = $s.BranchBehindStatusForegroundColor - } elseif ($status.AheadBy -ge 1) { - # We are ahead of remote - if ($s.BranchBehindAndAheadDisplay -eq "Full" -Or $s.BranchBehindAndAheadDisplay -eq "Compact") { - $branchStatusText = ("{0}{1}" -f $s.BranchAheadStatusSymbol, $status.AheadBy) - } else { - $branchStatusText = $s.BranchAheadStatusSymbol - } - $branchStatusBackgroundColor = $s.BranchAheadStatusBackgroundColor - $branchStatusForegroundColor = $s.BranchAheadStatusForegroundColor - } else { - # This condition should not be possible but defaulting the variables to be safe - $branchStatusText = "?" - } - - Write-Prompt (Format-BranchName($status.Branch)) -BackgroundColor $branchStatusBackgroundColor -ForegroundColor $branchStatusForegroundColor - - if ($branchStatusText) { - Write-Prompt (" {0}" -f $branchStatusText) -BackgroundColor $branchStatusBackgroundColor -ForegroundColor $branchStatusForegroundColor - } - - if($s.EnableFileStatus -and $status.HasIndex) { - Write-Prompt $s.BeforeIndexText -BackgroundColor $s.BeforeIndexBackgroundColor -ForegroundColor $s.BeforeIndexForegroundColor - - if($s.ShowStatusWhenZero -or $status.Index.Added) { - Write-Prompt (" $($s.FileAddedText)$($status.Index.Added.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor - } - if($s.ShowStatusWhenZero -or $status.Index.Modified) { - Write-Prompt (" $($s.FileModifiedText)$($status.Index.Modified.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor - } - if($s.ShowStatusWhenZero -or $status.Index.Deleted) { - Write-Prompt (" $($s.FileRemovedText)$($status.Index.Deleted.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor - } - - if ($status.Index.Unmerged) { - Write-Prompt (" $($s.FileConflictedText)$($status.Index.Unmerged.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor - } - - if($status.HasWorking) { - Write-Prompt $s.DelimText -BackgroundColor $s.DelimBackgroundColor -ForegroundColor $s.DelimForegroundColor - } - } - - if($s.EnableFileStatus -and $status.HasWorking) { - if($s.ShowStatusWhenZero -or $status.Working.Added) { - Write-Prompt (" $($s.FileAddedText)$($status.Working.Added.Count)") -BackgroundColor $s.WorkingBackgroundColor -ForegroundColor $s.WorkingForegroundColor - } - if($s.ShowStatusWhenZero -or $status.Working.Modified) { - Write-Prompt (" $($s.FileModifiedText)$($status.Working.Modified.Count)") -BackgroundColor $s.WorkingBackgroundColor -ForegroundColor $s.WorkingForegroundColor - } - if($s.ShowStatusWhenZero -or $status.Working.Deleted) { - Write-Prompt (" $($s.FileRemovedText)$($status.Working.Deleted.Count)") -BackgroundColor $s.WorkingBackgroundColor -ForegroundColor $s.WorkingForegroundColor - } - - if ($status.Working.Unmerged) { - Write-Prompt (" $($s.FileConflictedText)$($status.Working.Unmerged.Count)") -BackgroundColor $s.WorkingBackgroundColor -ForegroundColor $s.WorkingForegroundColor - } - } - - if ($status.HasWorking) { - # We have un-staged files in the working tree - $localStatusSymbol = $s.LocalWorkingStatusSymbol - $localStatusBackgroundColor = $s.LocalWorkingStatusBackgroundColor - $localStatusForegroundColor = $s.LocalWorkingStatusForegroundColor - } elseif ($status.HasIndex) { - # We have staged but uncommited files - $localStatusSymbol = $s.LocalStagedStatusSymbol - $localStatusBackgroundColor = $s.LocalStagedStatusBackgroundColor - $localStatusForegroundColor = $s.LocalStagedStatusForegroundColor - } else { - # No uncommited changes - $localStatusSymbol = $s.LocalDefaultStatusSymbol - $localStatusBackgroundColor = $s.LocalDefaultStatusBackgroundColor - $localStatusForegroundColor = $s.LocalDefaultStatusForegroundColor - } - - if ($localStatusSymbol) { - Write-Prompt (" {0}" -f $localStatusSymbol) -BackgroundColor $localStatusBackgroundColor -ForegroundColor $localStatusForegroundColor - } - - if ($s.EnableStashStatus -and ($status.StashCount -gt 0)) { - Write-Prompt $s.BeforeStashText -BackgroundColor $s.BeforeStashBackgroundColor -ForegroundColor $s.BeforeStashForegroundColor - Write-Prompt $status.StashCount -BackgroundColor $s.StashBackgroundColor -ForegroundColor $s.StashForegroundColor - Write-Prompt $s.AfterStashText -BackgroundColor $s.AfterStashBackgroundColor -ForegroundColor $s.AfterStashForegroundColor - } - - Write-Prompt $s.AfterText -BackgroundColor $s.AfterBackgroundColor -ForegroundColor $s.AfterForegroundColor - - if ($WindowTitleSupported -and $s.EnableWindowTitle) { - if( -not $Global:PreviousWindowTitle ) { - $Global:PreviousWindowTitle = $Host.UI.RawUI.WindowTitle - } - $repoName = Split-Path -Leaf (Split-Path $status.GitDir) - $prefix = if ($s.EnableWindowTitle -is [string]) { $s.EnableWindowTitle } else { '' } - $Host.UI.RawUI.WindowTitle = "$script:adminHeader$prefix$repoName [$($status.Branch)]" - } - } elseif ( $Global:PreviousWindowTitle ) { - $Host.UI.RawUI.WindowTitle = $Global:PreviousWindowTitle - } -} - -if(!(Test-Path Variable:Global:VcsPromptStatuses)) { - $Global:VcsPromptStatuses = @() -} -$s = $global:GitPromptSettings - -# Override some of the normal colors if the background color is set to the default DarkMagenta. -if ($Host.UI.RawUI.BackgroundColor -eq [ConsoleColor]::DarkMagenta) { - $s.LocalDefaultStatusForegroundColor = $s.LocalDefaultStatusForegroundBrightColor - $s.LocalWorkingStatusForegroundColor = $s.LocalWorkingStatusForegroundBrightColor - - $s.BeforeIndexForegroundColor = $s.BeforeIndexForegroundBrightColor - $s.IndexForegroundColor = $s.IndexForegroundBrightColor - - $s.WorkingForegroundColor = $s.WorkingForegroundBrightColor -} - -function Global:Write-VcsStatus { - $Global:VcsPromptStatuses | ForEach-Object { & $_ } -} - -# Add scriptblock that will execute for Write-VcsStatus -$PoshGitVcsPrompt = { - $Global:GitStatus = Get-GitStatus - Write-GitStatus $GitStatus -} - -$Global:VcsPromptStatuses += $PoshGitVcsPrompt +# Inspired by Mark Embling +# http://www.markembling.info/view/my-ideal-powershell-prompt-with-git-integration + +$global:GitPromptSettings = [pscustomobject]@{ + DefaultForegroundColor = $Host.UI.RawUI.ForegroundColor + + BeforeText = ' [' + BeforeForegroundColor = [ConsoleColor]::Yellow + BeforeBackgroundColor = $Host.UI.RawUI.BackgroundColor + + DelimText = ' |' + DelimForegroundColor = [ConsoleColor]::Yellow + DelimBackgroundColor = $Host.UI.RawUI.BackgroundColor + + AfterText = ']' + AfterForegroundColor = [ConsoleColor]::Yellow + AfterBackgroundColor = $Host.UI.RawUI.BackgroundColor + + FileAddedText = '+' + FileModifiedText = '~' + FileRemovedText = '-' + FileConflictedText = '!' + + LocalDefaultStatusSymbol = $null + LocalDefaultStatusForegroundColor = [ConsoleColor]::DarkGreen + LocalDefaultStatusForegroundBrightColor = [ConsoleColor]::Green + LocalDefaultStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor + + LocalWorkingStatusSymbol = '!' + LocalWorkingStatusForegroundColor = [ConsoleColor]::DarkRed + LocalWorkingStatusForegroundBrightColor = [ConsoleColor]::Red + LocalWorkingStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor + + LocalStagedStatusSymbol = '~' + LocalStagedStatusForegroundColor = [ConsoleColor]::Cyan + LocalStagedStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor + + BranchUntrackedSymbol = $null + BranchForegroundColor = [ConsoleColor]::Cyan + BranchBackgroundColor = $Host.UI.RawUI.BackgroundColor + + BranchGoneStatusSymbol = [char]0x00D7 # × Multiplication sign + BranchGoneStatusForegroundColor = [ConsoleColor]::DarkCyan + BranchGoneStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor + + BranchIdenticalStatusToSymbol = [char]0x2261 # ≡ Three horizontal lines + BranchIdenticalStatusToForegroundColor = [ConsoleColor]::Cyan + BranchIdenticalStatusToBackgroundColor = $Host.UI.RawUI.BackgroundColor + + BranchAheadStatusSymbol = [char]0x2191 # ↑ Up arrow + BranchAheadStatusForegroundColor = [ConsoleColor]::Green + BranchAheadStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor + + BranchBehindStatusSymbol = [char]0x2193 # ↓ Down arrow + BranchBehindStatusForegroundColor = [ConsoleColor]::Red + BranchBehindStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor + + BranchBehindAndAheadStatusSymbol = [char]0x2195 # ↕ Up & Down arrow + BranchBehindAndAheadStatusForegroundColor = [ConsoleColor]::Yellow + BranchBehindAndAheadStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor + + BeforeIndexText = "" + BeforeIndexForegroundColor = [ConsoleColor]::DarkGreen + BeforeIndexForegroundBrightColor = [ConsoleColor]::Green + BeforeIndexBackgroundColor = $Host.UI.RawUI.BackgroundColor + + IndexForegroundColor = [ConsoleColor]::DarkGreen + IndexForegroundBrightColor = [ConsoleColor]::Green + IndexBackgroundColor = $Host.UI.RawUI.BackgroundColor + + WorkingForegroundColor = [ConsoleColor]::DarkRed + WorkingForegroundBrightColor = [ConsoleColor]::Red + WorkingBackgroundColor = $Host.UI.RawUI.BackgroundColor + + EnableStashStatus = $false + BeforeStashText = ' (' + BeforeStashBackgroundColor = $Host.UI.RawUI.BackgroundColor + BeforeStashForegroundColor = [ConsoleColor]::Red + AfterStashText = ')' + AfterStashBackgroundColor = $Host.UI.RawUI.BackgroundColor + AfterStashForegroundColor = [ConsoleColor]::Red + StashBackgroundColor = $Host.UI.RawUI.BackgroundColor + StashForegroundColor = [ConsoleColor]::Red + + ShowStatusWhenZero = $true + + AutoRefreshIndex = $true + + # Valid values are "Full", "Compact", and "Minimal" + BranchBehindAndAheadDisplay = "Full" + + EnablePromptStatus = !$Global:GitMissing + EnableFileStatus = $true + EnableFileStatusFromCache = $null + RepositoriesInWhichToDisableFileStatus = @( ) # Array of repository paths + DescribeStyle = '' + + EnableWindowTitle = 'posh~git ~ ' + + DefaultPromptPrefix = '' + DefaultPromptSuffix = '$(''>'' * ($nestedPromptLevel + 1)) ' + DefaultPromptDebugSuffix = ' [DBG]$(''>'' * ($nestedPromptLevel + 1)) ' + DefaultPromptEnableTiming = $false + DefaultPromptAbbreviateHomeDirectory = $false + + Debug = $false + + BranchNameLimit = 0 + TruncatedBranchSuffix = '...' +} + +# PowerShell 5.x only runs on Windows so use .NET types to determine isAdminProcess +# Or if we are on v6 or higher, check the $IsWindows pre-defined variable. +if (($PSVersionTable.PSVersion.Major -le 5) -or $IsWindows) { + $currentUser = [Security.Principal.WindowsPrincipal]([Security.Principal.WindowsIdentity]::GetCurrent()) + $isAdminProcess = $currentUser.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) +} +else { + # Must be Linux or OSX, so use the id util. Root has userid of 0. + $isAdminProcess = 0 -eq (id -u) +} + +$adminHeader = if ($isAdminProcess) { 'Administrator: ' } else { '' } + +$WindowTitleSupported = $true +if (Get-Module NuGet) { + $WindowTitleSupported = $false +} + +function Write-Prompt($Object, $ForegroundColor, $BackgroundColor = -1) { + if ($BackgroundColor -lt 0) { + Write-Host $Object -NoNewLine -ForegroundColor $ForegroundColor + } else { + Write-Host $Object -NoNewLine -ForegroundColor $ForegroundColor -BackgroundColor $BackgroundColor + } +} + +function Format-BranchName($branchName){ + $s = $global:GitPromptSettings + + if($s.BranchNameLimit -gt 0 -and $branchName.Length -gt $s.BranchNameLimit) + { + $branchName = "{0}{1}" -f $branchName.Substring(0,$s.BranchNameLimit), $s.TruncatedBranchSuffix + } + + return $branchName +} + +function Write-GitStatus($status) { + $s = $global:GitPromptSettings + if ($status -and $s) { + Write-Prompt $s.BeforeText -BackgroundColor $s.BeforeBackgroundColor -ForegroundColor $s.BeforeForegroundColor + + $branchStatusText = $null + $branchStatusBackgroundColor = $s.BranchBackgroundColor + $branchStatusForegroundColor = $s.BranchForegroundColor + + if (!$status.Upstream) { + $branchStatusText = $s.BranchUntrackedSymbol + } elseif ($status.UpstreamGone -eq $true) { + # Upstream branch is gone + $branchStatusText = $s.BranchGoneStatusSymbol + $branchStatusBackgroundColor = $s.BranchGoneStatusBackgroundColor + $branchStatusForegroundColor = $s.BranchGoneStatusForegroundColor + } elseif ($status.BehindBy -eq 0 -and $status.AheadBy -eq 0) { + # We are aligned with remote + $branchStatusText = $s.BranchIdenticalStatusToSymbol + $branchStatusBackgroundColor = $s.BranchIdenticalStatusToBackgroundColor + $branchStatusForegroundColor = $s.BranchIdenticalStatusToForegroundColor + } elseif ($status.BehindBy -ge 1 -and $status.AheadBy -ge 1) { + # We are both behind and ahead of remote + if ($s.BranchBehindAndAheadDisplay -eq "Full") { + $branchStatusText = ("{0}{1} {2}{3}" -f $s.BranchBehindStatusSymbol, $status.BehindBy, $s.BranchAheadStatusSymbol, $status.AheadBy) + } elseif ($s.BranchBehindAndAheadDisplay -eq "Compact") { + $branchStatusText = ("{0}{1}{2}" -f $status.BehindBy, $s.BranchBehindAndAheadStatusSymbol, $status.AheadBy) + } else { + $branchStatusText = $s.BranchBehindAndAheadStatusSymbol + } + $branchStatusBackgroundColor = $s.BranchBehindAndAheadStatusBackgroundColor + $branchStatusForegroundColor = $s.BranchBehindAndAheadStatusForegroundColor + } elseif ($status.BehindBy -ge 1) { + # We are behind remote + if ($s.BranchBehindAndAheadDisplay -eq "Full" -Or $s.BranchBehindAndAheadDisplay -eq "Compact") { + $branchStatusText = ("{0}{1}" -f $s.BranchBehindStatusSymbol, $status.BehindBy) + } else { + $branchStatusText = $s.BranchBehindStatusSymbol + } + $branchStatusBackgroundColor = $s.BranchBehindStatusBackgroundColor + $branchStatusForegroundColor = $s.BranchBehindStatusForegroundColor + } elseif ($status.AheadBy -ge 1) { + # We are ahead of remote + if ($s.BranchBehindAndAheadDisplay -eq "Full" -Or $s.BranchBehindAndAheadDisplay -eq "Compact") { + $branchStatusText = ("{0}{1}" -f $s.BranchAheadStatusSymbol, $status.AheadBy) + } else { + $branchStatusText = $s.BranchAheadStatusSymbol + } + $branchStatusBackgroundColor = $s.BranchAheadStatusBackgroundColor + $branchStatusForegroundColor = $s.BranchAheadStatusForegroundColor + } else { + # This condition should not be possible but defaulting the variables to be safe + $branchStatusText = "?" + } + + Write-Prompt (Format-BranchName($status.Branch)) -BackgroundColor $branchStatusBackgroundColor -ForegroundColor $branchStatusForegroundColor + + if ($branchStatusText) { + Write-Prompt (" {0}" -f $branchStatusText) -BackgroundColor $branchStatusBackgroundColor -ForegroundColor $branchStatusForegroundColor + } + + if($s.EnableFileStatus -and $status.HasIndex) { + Write-Prompt $s.BeforeIndexText -BackgroundColor $s.BeforeIndexBackgroundColor -ForegroundColor $s.BeforeIndexForegroundColor + + if($s.ShowStatusWhenZero -or $status.Index.Added) { + Write-Prompt (" $($s.FileAddedText)$($status.Index.Added.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor + } + if($s.ShowStatusWhenZero -or $status.Index.Modified) { + Write-Prompt (" $($s.FileModifiedText)$($status.Index.Modified.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor + } + if($s.ShowStatusWhenZero -or $status.Index.Deleted) { + Write-Prompt (" $($s.FileRemovedText)$($status.Index.Deleted.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor + } + + if ($status.Index.Unmerged) { + Write-Prompt (" $($s.FileConflictedText)$($status.Index.Unmerged.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor + } + + if($status.HasWorking) { + Write-Prompt $s.DelimText -BackgroundColor $s.DelimBackgroundColor -ForegroundColor $s.DelimForegroundColor + } + } + + if($s.EnableFileStatus -and $status.HasWorking) { + if($s.ShowStatusWhenZero -or $status.Working.Added) { + Write-Prompt (" $($s.FileAddedText)$($status.Working.Added.Count)") -BackgroundColor $s.WorkingBackgroundColor -ForegroundColor $s.WorkingForegroundColor + } + if($s.ShowStatusWhenZero -or $status.Working.Modified) { + Write-Prompt (" $($s.FileModifiedText)$($status.Working.Modified.Count)") -BackgroundColor $s.WorkingBackgroundColor -ForegroundColor $s.WorkingForegroundColor + } + if($s.ShowStatusWhenZero -or $status.Working.Deleted) { + Write-Prompt (" $($s.FileRemovedText)$($status.Working.Deleted.Count)") -BackgroundColor $s.WorkingBackgroundColor -ForegroundColor $s.WorkingForegroundColor + } + + if ($status.Working.Unmerged) { + Write-Prompt (" $($s.FileConflictedText)$($status.Working.Unmerged.Count)") -BackgroundColor $s.WorkingBackgroundColor -ForegroundColor $s.WorkingForegroundColor + } + } + + if ($status.HasWorking) { + # We have un-staged files in the working tree + $localStatusSymbol = $s.LocalWorkingStatusSymbol + $localStatusBackgroundColor = $s.LocalWorkingStatusBackgroundColor + $localStatusForegroundColor = $s.LocalWorkingStatusForegroundColor + } elseif ($status.HasIndex) { + # We have staged but uncommited files + $localStatusSymbol = $s.LocalStagedStatusSymbol + $localStatusBackgroundColor = $s.LocalStagedStatusBackgroundColor + $localStatusForegroundColor = $s.LocalStagedStatusForegroundColor + } else { + # No uncommited changes + $localStatusSymbol = $s.LocalDefaultStatusSymbol + $localStatusBackgroundColor = $s.LocalDefaultStatusBackgroundColor + $localStatusForegroundColor = $s.LocalDefaultStatusForegroundColor + } + + if ($localStatusSymbol) { + Write-Prompt (" {0}" -f $localStatusSymbol) -BackgroundColor $localStatusBackgroundColor -ForegroundColor $localStatusForegroundColor + } + + if ($s.EnableStashStatus -and ($status.StashCount -gt 0)) { + Write-Prompt $s.BeforeStashText -BackgroundColor $s.BeforeStashBackgroundColor -ForegroundColor $s.BeforeStashForegroundColor + Write-Prompt $status.StashCount -BackgroundColor $s.StashBackgroundColor -ForegroundColor $s.StashForegroundColor + Write-Prompt $s.AfterStashText -BackgroundColor $s.AfterStashBackgroundColor -ForegroundColor $s.AfterStashForegroundColor + } + + Write-Prompt $s.AfterText -BackgroundColor $s.AfterBackgroundColor -ForegroundColor $s.AfterForegroundColor + + if ($WindowTitleSupported -and $s.EnableWindowTitle) { + if( -not $Global:PreviousWindowTitle ) { + $Global:PreviousWindowTitle = $Host.UI.RawUI.WindowTitle + } + $repoName = Split-Path -Leaf (Split-Path $status.GitDir) + $prefix = if ($s.EnableWindowTitle -is [string]) { $s.EnableWindowTitle } else { '' } + $Host.UI.RawUI.WindowTitle = "$script:adminHeader$prefix$repoName [$($status.Branch)]" + } + } elseif ( $Global:PreviousWindowTitle ) { + $Host.UI.RawUI.WindowTitle = $Global:PreviousWindowTitle + } +} + +if(!(Test-Path Variable:Global:VcsPromptStatuses)) { + $Global:VcsPromptStatuses = @() +} +$s = $global:GitPromptSettings + +# Override some of the normal colors if the background color is set to the default DarkMagenta. +if ($Host.UI.RawUI.BackgroundColor -eq [ConsoleColor]::DarkMagenta) { + $s.LocalDefaultStatusForegroundColor = $s.LocalDefaultStatusForegroundBrightColor + $s.LocalWorkingStatusForegroundColor = $s.LocalWorkingStatusForegroundBrightColor + + $s.BeforeIndexForegroundColor = $s.BeforeIndexForegroundBrightColor + $s.IndexForegroundColor = $s.IndexForegroundBrightColor + + $s.WorkingForegroundColor = $s.WorkingForegroundBrightColor +} + +function Global:Write-VcsStatus { + $Global:VcsPromptStatuses | ForEach-Object { & $_ } +} + +# Add scriptblock that will execute for Write-VcsStatus +$PoshGitVcsPrompt = { + $Global:GitStatus = Get-GitStatus + Write-GitStatus $GitStatus +} + +$Global:VcsPromptStatuses += $PoshGitVcsPrompt diff --git a/src/GitTabExpansion.ps1 b/src/GitTabExpansion.ps1 index 5b6b74187..ed824b556 100644 --- a/src/GitTabExpansion.ps1 +++ b/src/GitTabExpansion.ps1 @@ -1,410 +1,410 @@ -# Initial implementation by Jeremy Skinner -# http://www.jeremyskinner.co.uk/2010/03/07/using-git-with-windows-powershell/ - -$Global:GitTabSettings = New-Object PSObject -Property @{ - AllCommands = $false -} - -$subcommands = @{ - bisect = 'start bad good skip reset visualize replay log run' - notes = 'edit show' - reflog = 'expire delete show' - remote = 'add rename rm set-head show prune update' - stash = 'list show drop pop apply branch save clear create' - submodule = 'add status init update summary foreach sync' - svn = 'init fetch clone rebase dcommit branch tag log blame find-rev set-tree create-ignore show-ignore mkdirs commit-diff info proplist propget show-externals gc reset' - tfs = 'bootstrap checkin checkintool ct cleanup cleanup-workspaces clone diagnostics fetch help init pull quick-clone rcheckin shelve shelve-list unshelve verify' - flow = 'init feature bugfix release hotfix support help version config finish delete publish rebase' - worktree = 'add list prune' -} - -$gitflowsubcommands = @{ - init = 'help' - feature = 'list start finish publish track diff rebase checkout pull help delete' - bugfix = 'list start finish publish track diff rebase checkout pull help delete' - release = 'list start finish track publish help delete' - hotfix = 'list start finish track publish help delete' - support = 'list start help' - config = 'list set base' -} - -function script:gitCmdOperations($commands, $command, $filter) { - $commands.$command -split ' ' | Where-Object { $_ -like "$filter*" } -} - -$script:someCommands = @('add','am','annotate','archive','bisect','blame','branch','bundle','checkout','cherry', - 'cherry-pick','citool','clean','clone','commit','config','describe','diff','difftool','fetch', - 'format-patch','gc','grep','gui','help','init','instaweb','log','merge','mergetool','mv', - 'notes','prune','pull','push','rebase','reflog','remote','rerere','reset','revert','rm', - 'shortlog','show','stash','status','submodule','svn','tag','whatchanged', 'worktree') - -$script:gitCommandsWithLongParams = $longGitParams.Keys -join '|' -$script:gitCommandsWithShortParams = $shortGitParams.Keys -join '|' -$script:gitCommandsWithParamValues = $gitParamValues.Keys -join '|' - -try { - if ($null -ne (git help -a 2>&1 | Select-String flow)) { - $script:someCommands += 'flow' - } -} -catch { - Write-Debug "Search for 'flow' in 'git help' output failed with error: $_" -} - -function script:gitCommands($filter, $includeAliases) { - $cmdList = @() - if (-not $global:GitTabSettings.AllCommands) { - $cmdList += $someCommands -like "$filter*" - } - else { - $cmdList += git help --all | - Where-Object { $_ -match '^ \S.*' } | - ForEach-Object { $_.Split(' ', [StringSplitOptions]::RemoveEmptyEntries) } | - Where-Object { $_ -like "$filter*" } - } - - if ($includeAliases) { - $cmdList += gitAliases $filter - } - - $cmdList | Sort-Object -} - -function script:gitRemotes($filter) { - git remote | Where-Object { $_ -like "$filter*" } -} - -function script:gitBranches($filter, $includeHEAD = $false, $prefix = '') { - if ($filter -match "^(?\S*\.{2,3})(?.*)") { - $prefix += $matches['from'] - $filter = $matches['to'] - } - - $branches = @(git branch --no-color | ForEach-Object { if (($_ -notmatch "^\* \(HEAD detached .+\)$") -and ($_ -match "^\*?\s*(?.*)")) { $matches['ref'] } }) + - @(git branch --no-color -r | ForEach-Object { if ($_ -match "^ (?\S+)(?: -> .+)?") { $matches['ref'] } }) + - @(if ($includeHEAD) { 'HEAD','FETCH_HEAD','ORIG_HEAD','MERGE_HEAD' }) - - $branches | - Where-Object { $_ -ne '(no branch)' -and $_ -like "$filter*" } | - ForEach-Object { $prefix + $_ } -} - -function script:gitRemoteUniqueBranches($filter) { - git branch --no-color -r | - ForEach-Object { if ($_ -match "^ (?[^/]+)/(?\S+)(?! -> .+)?$") { $matches['branch'] } } | - Group-Object -NoElement | - Where-Object { $_.Count -eq 1 } | - Select-Object -ExpandProperty Name | - Where-Object { $_ -like "$filter*" } -} - -function script:gitTags($filter, $prefix = '') { - git tag | - Where-Object { $_ -like "$filter*" } | - ForEach-Object { $prefix + $_ } -} - -function script:gitFeatures($filter, $command){ - $featurePrefix = git config --local --get "gitflow.prefix.$command" - $branches = @(git branch --no-color | ForEach-Object { if ($_ -match "^\*?\s*$featurePrefix(?.*)") { $matches['ref'] } }) - $branches | - Where-Object { $_ -ne '(no branch)' -and $_ -like "$filter*" } | - ForEach-Object { $prefix + $_ } -} - -function script:gitRemoteBranches($remote, $ref, $filter, $prefix = '') { - git branch --no-color -r | - Where-Object { $_ -like " $remote/$filter*" } | - ForEach-Object { $prefix + $ref + ($_ -replace " $remote/","") } -} - -function script:gitStashes($filter) { - (git stash list) -replace ':.*','' | - Where-Object { $_ -like "$filter*" } | - ForEach-Object { "'$_'" } -} - -function script:gitTfsShelvesets($filter) { - (git tfs shelve-list) | - Where-Object { $_ -like "$filter*" } | - ForEach-Object { "'$_'" } -} - -function script:gitFiles($filter, $files) { - $files | Sort-Object | - Where-Object { $_ -like "$filter*" } | - ForEach-Object { if ($_ -like '* *') { "'$_'" } else { $_ } } -} - -function script:gitIndex($filter) { - gitFiles $filter $GitStatus.Index -} - -function script:gitAddFiles($filter) { - gitFiles $filter (@($GitStatus.Working.Unmerged) + @($GitStatus.Working.Modified) + @($GitStatus.Working.Added)) -} - -function script:gitCheckoutFiles($filter) { - gitFiles $filter (@($GitStatus.Working.Unmerged) + @($GitStatus.Working.Modified) + @($GitStatus.Working.Deleted)) -} - -function script:gitDiffFiles($filter, $staged) { - if ($staged) { - gitFiles $filter $GitStatus.Index.Modified - } - else { - gitFiles $filter (@($GitStatus.Working.Unmerged) + @($GitStatus.Working.Modified) + @($GitStatus.Index.Modified)) - } -} - -function script:gitMergeFiles($filter) { - gitFiles $filter $GitStatus.Working.Unmerged -} - -function script:gitDeleted($filter) { - gitFiles $filter $GitStatus.Working.Deleted -} - -function script:gitAliases($filter) { - git config --get-regexp ^alias\. | ForEach-Object{ - if ($_ -match "^alias\.(?\S+) .*") { - $alias = $Matches['alias'] - if ($alias -like "$filter*") { - $alias - } - } - } | Sort-Object -} - -function script:expandGitAlias($cmd, $rest) { - if ((git config --get-regexp "^alias\.$cmd`$") -match "^alias\.$cmd (?[^!].*)`$") { - return "git $($Matches['cmd'])$rest" - } - else { - return "git $cmd$rest" - } -} - -function script:expandLongParams($cmd, $filter) { - $longGitParams[$cmd] -split ' ' | - Where-Object { $_ -like "$filter*" } | - Sort-Object | - ForEach-Object { -join ("--", $_) } -} - -function script:expandShortParams($cmd, $filter) { - $shortGitParams[$cmd] -split ' ' | - Where-Object { $_ -like "$filter*" } | - Sort-Object | - ForEach-Object { -join ("-", $_) } -} - -function script:expandParamValues($cmd, $param, $filter) { - $gitParamValues[$cmd][$param] -split ' ' | - Where-Object { $_ -like "$filter*" } | - Sort-Object | - ForEach-Object { -join ("--", $param, "=", $_) } -} - -function GitTabExpansion($lastBlock) { - $res = Invoke-Utf8ConsoleCommand { GitTabExpansionInternal $lastBlock } - $res -} - -function GitTabExpansionInternal($lastBlock) { - $ignoreGitParams = '(?\s+-(?:[aA-zZ0-9]+|-[aA-zZ0-9][aA-zZ0-9-]*)(?:=\S+)?)*' - - if ($lastBlock -match "^$(Get-AliasPattern git) (?\S+)(? .*)$") { - $lastBlock = expandGitAlias $Matches['cmd'] $Matches['args'] - } - - # Handles tgit (tortoisegit) - if ($lastBlock -match "^$(Get-AliasPattern tgit) (?\S*)$") { - # Need return statement to prevent fall-through. - return $Global:TortoiseGitSettings.TortoiseGitCommands.Keys.GetEnumerator() | Sort-Object | Where-Object { $_ -like "$($matches['cmd'])*" } - } - - # Handles gitk - if ($lastBlock -match "^$(Get-AliasPattern gitk).* (?\S*)$"){ - return gitBranches $matches['ref'] $true - } - - switch -regex ($lastBlock -replace "^$(Get-AliasPattern git) ","") { - - # Handles git - "^(?$($subcommands.Keys -join '|'))\s+(?\S*)$" { - gitCmdOperations $subcommands $matches['cmd'] $matches['op'] - } - - # Handles git flow - "^flow (?$($gitflowsubcommands.Keys -join '|'))\s+(?\S*)$" { - gitCmdOperations $gitflowsubcommands $matches['cmd'] $matches['op'] - } - - # Handles git flow - "^flow (?\S*)\s+(?\S*)\s+(?\S*)$" { - gitFeatures $matches['name'] $matches['command'] - } - - # Handles git remote (rename|rm|set-head|set-branches|set-url|show|prune) - "^remote.* (?:rename|rm|set-head|set-branches|set-url|show|prune).* (?\S*)$" { - gitRemotes $matches['remote'] - } - - # Handles git stash (show|apply|drop|pop|branch) - "^stash (?:show|apply|drop|pop|branch).* (?\S*)$" { - gitStashes $matches['stash'] - } - - # Handles git bisect (bad|good|reset|skip) - "^bisect (?:bad|good|reset|skip).* (?\S*)$" { - gitBranches $matches['ref'] $true - } - - # Handles git tfs unshelve - "^tfs +unshelve.* (?\S*)$" { - gitTfsShelvesets $matches['shelveset'] - } - - # Handles git branch -d|-D|-m|-M - # Handles git branch - "^branch.* (?\S*)$" { - gitBranches $matches['branch'] - } - - # Handles git (commands & aliases) - "^(?\S*)$" { - gitCommands $matches['cmd'] $TRUE - } - - # Handles git help (commands only) - "^help (?\S*)$" { - gitCommands $matches['cmd'] $FALSE - } - - # Handles git push remote : - # Handles git push remote +: - "^push${ignoreGitParams}\s+(?[^\s-]\S*).*\s+(?\+?)(?[^\s\:]*\:)(?\S*)$" { - gitRemoteBranches $matches['remote'] $matches['ref'] $matches['branch'] -prefix $matches['force'] - } - - # Handles git push remote - # Handles git push remote + - # Handles git pull remote - "^(?:push|pull)${ignoreGitParams}\s+(?[^\s-]\S*).*\s+(?\+?)(?[^\s\:]*)$" { - gitBranches $matches['ref'] -prefix $matches['force'] - gitTags $matches['ref'] -prefix $matches['force'] - } - - # Handles git pull - # Handles git push - # Handles git fetch - "^(?:push|pull|fetch)${ignoreGitParams}\s+(?\S*)$" { - gitRemotes $matches['remote'] - } - - # Handles git reset HEAD - # Handles git reset HEAD -- - "^reset.* HEAD(?:\s+--)? (?\S*)$" { - gitIndex $matches['path'] - } - - # Handles git - "^commit.*-C\s+(?\S*)$" { - gitBranches $matches['ref'] $true - } - - # Handles git add - "^add.* (?\S*)$" { - gitAddFiles $matches['files'] - } - - # Handles git checkout -- - "^checkout.* -- (?\S*)$" { - gitCheckoutFiles $matches['files'] - } - - # Handles git rm - "^rm.* (?\S*)$" { - gitDeleted $matches['index'] - } - - # Handles git diff/difftool - "^(?:diff|difftool)(?:.* (?(?:--cached|--staged))|.*) (?\S*)$" { - gitDiffFiles $matches['files'] $matches['staged'] - } - - # Handles git merge/mergetool - "^(?:merge|mergetool).* (?\S*)$" { - gitMergeFiles $matches['files'] - } - - # Handles git checkout - "^(?:checkout).* (?\S*)$" { - gitBranches $matches['ref'] $true - gitRemoteUniqueBranches $matches['ref'] - gitTags $matches['ref'] - } - - # Handles git worktree add - "^worktree add.* (?\S+) (?\S*)$" { - gitBranches $matches['ref'] - } - - # Handles git - "^(?:cherry|cherry-pick|diff|difftool|log|merge|rebase|reflog\s+show|reset|revert|show).* (?\S*)$" { - gitBranches $matches['ref'] $true - gitTags $matches['ref'] - } - - # Handles git --= - "^(?$gitCommandsWithParamValues).* --(?[^=]+)=(?\S*)$" { - expandParamValues $matches['cmd'] $matches['param'] $matches['value'] - } - - # Handles git -- - "^(?$gitCommandsWithLongParams).* --(?\S*)$" { - expandLongParams $matches['cmd'] $matches['param'] - } - - # Handles git - - "^(?$gitCommandsWithShortParams).* -(?\S*)$" { - expandShortParams $matches['cmd'] $matches['shortparam'] - } - } -} - -$PowerTab_RegisterTabExpansion = if (Get-Module -Name powertab) { Get-Command Register-TabExpansion -Module powertab -ErrorAction SilentlyContinue } -if ($PowerTab_RegisterTabExpansion) { - & $PowerTab_RegisterTabExpansion "git.exe" -Type Command { - param($Context, [ref]$TabExpansionHasOutput, [ref]$QuoteSpaces) # 1: - - $line = $Context.Line - $lastBlock = [regex]::Split($line, '[|;]')[-1].TrimStart() - $TabExpansionHasOutput.Value = $true - GitTabExpansion $lastBlock - } - return -} - -if (Test-Path Function:\TabExpansion) { - Rename-Item Function:\TabExpansion TabExpansionBackup -} - -function TabExpansion($line, $lastWord) { - $lastBlock = [regex]::Split($line, '[|;]')[-1].TrimStart() - - switch -regex ($lastBlock) { - # Execute git tab completion for all git-related commands - "^$(Get-AliasPattern git) (.*)" { GitTabExpansion $lastBlock } - "^$(Get-AliasPattern tgit) (.*)" { GitTabExpansion $lastBlock } - "^$(Get-AliasPattern gitk) (.*)" { GitTabExpansion $lastBlock } - - # Fall back on existing tab expansion - default { - if (Test-Path Function:\TabExpansionBackup) { - TabExpansionBackup $line $lastWord - } - } - } -} +# Initial implementation by Jeremy Skinner +# http://www.jeremyskinner.co.uk/2010/03/07/using-git-with-windows-powershell/ + +$Global:GitTabSettings = New-Object PSObject -Property @{ + AllCommands = $false +} + +$subcommands = @{ + bisect = 'start bad good skip reset visualize replay log run' + notes = 'edit show' + reflog = 'expire delete show' + remote = 'add rename rm set-head show prune update' + stash = 'list show drop pop apply branch save clear create' + submodule = 'add status init update summary foreach sync' + svn = 'init fetch clone rebase dcommit branch tag log blame find-rev set-tree create-ignore show-ignore mkdirs commit-diff info proplist propget show-externals gc reset' + tfs = 'bootstrap checkin checkintool ct cleanup cleanup-workspaces clone diagnostics fetch help init pull quick-clone rcheckin shelve shelve-list unshelve verify' + flow = 'init feature bugfix release hotfix support help version config finish delete publish rebase' + worktree = 'add list prune' +} + +$gitflowsubcommands = @{ + init = 'help' + feature = 'list start finish publish track diff rebase checkout pull help delete' + bugfix = 'list start finish publish track diff rebase checkout pull help delete' + release = 'list start finish track publish help delete' + hotfix = 'list start finish track publish help delete' + support = 'list start help' + config = 'list set base' +} + +function script:gitCmdOperations($commands, $command, $filter) { + $commands.$command -split ' ' | Where-Object { $_ -like "$filter*" } +} + +$script:someCommands = @('add','am','annotate','archive','bisect','blame','branch','bundle','checkout','cherry', + 'cherry-pick','citool','clean','clone','commit','config','describe','diff','difftool','fetch', + 'format-patch','gc','grep','gui','help','init','instaweb','log','merge','mergetool','mv', + 'notes','prune','pull','push','rebase','reflog','remote','rerere','reset','revert','rm', + 'shortlog','show','stash','status','submodule','svn','tag','whatchanged', 'worktree') + +$script:gitCommandsWithLongParams = $longGitParams.Keys -join '|' +$script:gitCommandsWithShortParams = $shortGitParams.Keys -join '|' +$script:gitCommandsWithParamValues = $gitParamValues.Keys -join '|' + +try { + if ($null -ne (git help -a 2>&1 | Select-String flow)) { + $script:someCommands += 'flow' + } +} +catch { + Write-Debug "Search for 'flow' in 'git help' output failed with error: $_" +} + +function script:gitCommands($filter, $includeAliases) { + $cmdList = @() + if (-not $global:GitTabSettings.AllCommands) { + $cmdList += $someCommands -like "$filter*" + } + else { + $cmdList += git help --all | + Where-Object { $_ -match '^ \S.*' } | + ForEach-Object { $_.Split(' ', [StringSplitOptions]::RemoveEmptyEntries) } | + Where-Object { $_ -like "$filter*" } + } + + if ($includeAliases) { + $cmdList += gitAliases $filter + } + + $cmdList | Sort-Object +} + +function script:gitRemotes($filter) { + git remote | Where-Object { $_ -like "$filter*" } +} + +function script:gitBranches($filter, $includeHEAD = $false, $prefix = '') { + if ($filter -match "^(?\S*\.{2,3})(?.*)") { + $prefix += $matches['from'] + $filter = $matches['to'] + } + + $branches = @(git branch --no-color | ForEach-Object { if (($_ -notmatch "^\* \(HEAD detached .+\)$") -and ($_ -match "^\*?\s*(?.*)")) { $matches['ref'] } }) + + @(git branch --no-color -r | ForEach-Object { if ($_ -match "^ (?\S+)(?: -> .+)?") { $matches['ref'] } }) + + @(if ($includeHEAD) { 'HEAD','FETCH_HEAD','ORIG_HEAD','MERGE_HEAD' }) + + $branches | + Where-Object { $_ -ne '(no branch)' -and $_ -like "$filter*" } | + ForEach-Object { $prefix + $_ } +} + +function script:gitRemoteUniqueBranches($filter) { + git branch --no-color -r | + ForEach-Object { if ($_ -match "^ (?[^/]+)/(?\S+)(?! -> .+)?$") { $matches['branch'] } } | + Group-Object -NoElement | + Where-Object { $_.Count -eq 1 } | + Select-Object -ExpandProperty Name | + Where-Object { $_ -like "$filter*" } +} + +function script:gitTags($filter, $prefix = '') { + git tag | + Where-Object { $_ -like "$filter*" } | + ForEach-Object { $prefix + $_ } +} + +function script:gitFeatures($filter, $command){ + $featurePrefix = git config --local --get "gitflow.prefix.$command" + $branches = @(git branch --no-color | ForEach-Object { if ($_ -match "^\*?\s*$featurePrefix(?.*)") { $matches['ref'] } }) + $branches | + Where-Object { $_ -ne '(no branch)' -and $_ -like "$filter*" } | + ForEach-Object { $prefix + $_ } +} + +function script:gitRemoteBranches($remote, $ref, $filter, $prefix = '') { + git branch --no-color -r | + Where-Object { $_ -like " $remote/$filter*" } | + ForEach-Object { $prefix + $ref + ($_ -replace " $remote/","") } +} + +function script:gitStashes($filter) { + (git stash list) -replace ':.*','' | + Where-Object { $_ -like "$filter*" } | + ForEach-Object { "'$_'" } +} + +function script:gitTfsShelvesets($filter) { + (git tfs shelve-list) | + Where-Object { $_ -like "$filter*" } | + ForEach-Object { "'$_'" } +} + +function script:gitFiles($filter, $files) { + $files | Sort-Object | + Where-Object { $_ -like "$filter*" } | + ForEach-Object { if ($_ -like '* *') { "'$_'" } else { $_ } } +} + +function script:gitIndex($filter) { + gitFiles $filter $GitStatus.Index +} + +function script:gitAddFiles($filter) { + gitFiles $filter (@($GitStatus.Working.Unmerged) + @($GitStatus.Working.Modified) + @($GitStatus.Working.Added)) +} + +function script:gitCheckoutFiles($filter) { + gitFiles $filter (@($GitStatus.Working.Unmerged) + @($GitStatus.Working.Modified) + @($GitStatus.Working.Deleted)) +} + +function script:gitDiffFiles($filter, $staged) { + if ($staged) { + gitFiles $filter $GitStatus.Index.Modified + } + else { + gitFiles $filter (@($GitStatus.Working.Unmerged) + @($GitStatus.Working.Modified) + @($GitStatus.Index.Modified)) + } +} + +function script:gitMergeFiles($filter) { + gitFiles $filter $GitStatus.Working.Unmerged +} + +function script:gitDeleted($filter) { + gitFiles $filter $GitStatus.Working.Deleted +} + +function script:gitAliases($filter) { + git config --get-regexp ^alias\. | ForEach-Object{ + if ($_ -match "^alias\.(?\S+) .*") { + $alias = $Matches['alias'] + if ($alias -like "$filter*") { + $alias + } + } + } | Sort-Object +} + +function script:expandGitAlias($cmd, $rest) { + if ((git config --get-regexp "^alias\.$cmd`$") -match "^alias\.$cmd (?[^!].*)`$") { + return "git $($Matches['cmd'])$rest" + } + else { + return "git $cmd$rest" + } +} + +function script:expandLongParams($cmd, $filter) { + $longGitParams[$cmd] -split ' ' | + Where-Object { $_ -like "$filter*" } | + Sort-Object | + ForEach-Object { -join ("--", $_) } +} + +function script:expandShortParams($cmd, $filter) { + $shortGitParams[$cmd] -split ' ' | + Where-Object { $_ -like "$filter*" } | + Sort-Object | + ForEach-Object { -join ("-", $_) } +} + +function script:expandParamValues($cmd, $param, $filter) { + $gitParamValues[$cmd][$param] -split ' ' | + Where-Object { $_ -like "$filter*" } | + Sort-Object | + ForEach-Object { -join ("--", $param, "=", $_) } +} + +function GitTabExpansion($lastBlock) { + $res = Invoke-Utf8ConsoleCommand { GitTabExpansionInternal $lastBlock } + $res +} + +function GitTabExpansionInternal($lastBlock) { + $ignoreGitParams = '(?\s+-(?:[aA-zZ0-9]+|-[aA-zZ0-9][aA-zZ0-9-]*)(?:=\S+)?)*' + + if ($lastBlock -match "^$(Get-AliasPattern git) (?\S+)(? .*)$") { + $lastBlock = expandGitAlias $Matches['cmd'] $Matches['args'] + } + + # Handles tgit (tortoisegit) + if ($lastBlock -match "^$(Get-AliasPattern tgit) (?\S*)$") { + # Need return statement to prevent fall-through. + return $Global:TortoiseGitSettings.TortoiseGitCommands.Keys.GetEnumerator() | Sort-Object | Where-Object { $_ -like "$($matches['cmd'])*" } + } + + # Handles gitk + if ($lastBlock -match "^$(Get-AliasPattern gitk).* (?\S*)$"){ + return gitBranches $matches['ref'] $true + } + + switch -regex ($lastBlock -replace "^$(Get-AliasPattern git) ","") { + + # Handles git + "^(?$($subcommands.Keys -join '|'))\s+(?\S*)$" { + gitCmdOperations $subcommands $matches['cmd'] $matches['op'] + } + + # Handles git flow + "^flow (?$($gitflowsubcommands.Keys -join '|'))\s+(?\S*)$" { + gitCmdOperations $gitflowsubcommands $matches['cmd'] $matches['op'] + } + + # Handles git flow + "^flow (?\S*)\s+(?\S*)\s+(?\S*)$" { + gitFeatures $matches['name'] $matches['command'] + } + + # Handles git remote (rename|rm|set-head|set-branches|set-url|show|prune) + "^remote.* (?:rename|rm|set-head|set-branches|set-url|show|prune).* (?\S*)$" { + gitRemotes $matches['remote'] + } + + # Handles git stash (show|apply|drop|pop|branch) + "^stash (?:show|apply|drop|pop|branch).* (?\S*)$" { + gitStashes $matches['stash'] + } + + # Handles git bisect (bad|good|reset|skip) + "^bisect (?:bad|good|reset|skip).* (?\S*)$" { + gitBranches $matches['ref'] $true + } + + # Handles git tfs unshelve + "^tfs +unshelve.* (?\S*)$" { + gitTfsShelvesets $matches['shelveset'] + } + + # Handles git branch -d|-D|-m|-M + # Handles git branch + "^branch.* (?\S*)$" { + gitBranches $matches['branch'] + } + + # Handles git (commands & aliases) + "^(?\S*)$" { + gitCommands $matches['cmd'] $TRUE + } + + # Handles git help (commands only) + "^help (?\S*)$" { + gitCommands $matches['cmd'] $FALSE + } + + # Handles git push remote : + # Handles git push remote +: + "^push${ignoreGitParams}\s+(?[^\s-]\S*).*\s+(?\+?)(?[^\s\:]*\:)(?\S*)$" { + gitRemoteBranches $matches['remote'] $matches['ref'] $matches['branch'] -prefix $matches['force'] + } + + # Handles git push remote + # Handles git push remote + + # Handles git pull remote + "^(?:push|pull)${ignoreGitParams}\s+(?[^\s-]\S*).*\s+(?\+?)(?[^\s\:]*)$" { + gitBranches $matches['ref'] -prefix $matches['force'] + gitTags $matches['ref'] -prefix $matches['force'] + } + + # Handles git pull + # Handles git push + # Handles git fetch + "^(?:push|pull|fetch)${ignoreGitParams}\s+(?\S*)$" { + gitRemotes $matches['remote'] + } + + # Handles git reset HEAD + # Handles git reset HEAD -- + "^reset.* HEAD(?:\s+--)? (?\S*)$" { + gitIndex $matches['path'] + } + + # Handles git + "^commit.*-C\s+(?\S*)$" { + gitBranches $matches['ref'] $true + } + + # Handles git add + "^add.* (?\S*)$" { + gitAddFiles $matches['files'] + } + + # Handles git checkout -- + "^checkout.* -- (?\S*)$" { + gitCheckoutFiles $matches['files'] + } + + # Handles git rm + "^rm.* (?\S*)$" { + gitDeleted $matches['index'] + } + + # Handles git diff/difftool + "^(?:diff|difftool)(?:.* (?(?:--cached|--staged))|.*) (?\S*)$" { + gitDiffFiles $matches['files'] $matches['staged'] + } + + # Handles git merge/mergetool + "^(?:merge|mergetool).* (?\S*)$" { + gitMergeFiles $matches['files'] + } + + # Handles git checkout + "^(?:checkout).* (?\S*)$" { + gitBranches $matches['ref'] $true + gitRemoteUniqueBranches $matches['ref'] + gitTags $matches['ref'] + } + + # Handles git worktree add + "^worktree add.* (?\S+) (?\S*)$" { + gitBranches $matches['ref'] + } + + # Handles git + "^(?:cherry|cherry-pick|diff|difftool|log|merge|rebase|reflog\s+show|reset|revert|show).* (?\S*)$" { + gitBranches $matches['ref'] $true + gitTags $matches['ref'] + } + + # Handles git --= + "^(?$gitCommandsWithParamValues).* --(?[^=]+)=(?\S*)$" { + expandParamValues $matches['cmd'] $matches['param'] $matches['value'] + } + + # Handles git -- + "^(?$gitCommandsWithLongParams).* --(?\S*)$" { + expandLongParams $matches['cmd'] $matches['param'] + } + + # Handles git - + "^(?$gitCommandsWithShortParams).* -(?\S*)$" { + expandShortParams $matches['cmd'] $matches['shortparam'] + } + } +} + +$PowerTab_RegisterTabExpansion = if (Get-Module -Name powertab) { Get-Command Register-TabExpansion -Module powertab -ErrorAction SilentlyContinue } +if ($PowerTab_RegisterTabExpansion) { + & $PowerTab_RegisterTabExpansion "git.exe" -Type Command { + param($Context, [ref]$TabExpansionHasOutput, [ref]$QuoteSpaces) # 1: + + $line = $Context.Line + $lastBlock = [regex]::Split($line, '[|;]')[-1].TrimStart() + $TabExpansionHasOutput.Value = $true + GitTabExpansion $lastBlock + } + return +} + +if (Test-Path Function:\TabExpansion) { + Rename-Item Function:\TabExpansion TabExpansionBackup +} + +function TabExpansion($line, $lastWord) { + $lastBlock = [regex]::Split($line, '[|;]')[-1].TrimStart() + + switch -regex ($lastBlock) { + # Execute git tab completion for all git-related commands + "^$(Get-AliasPattern git) (.*)" { GitTabExpansion $lastBlock } + "^$(Get-AliasPattern tgit) (.*)" { GitTabExpansion $lastBlock } + "^$(Get-AliasPattern gitk) (.*)" { GitTabExpansion $lastBlock } + + # Fall back on existing tab expansion + default { + if (Test-Path Function:\TabExpansionBackup) { + TabExpansionBackup $line $lastWord + } + } + } +} diff --git a/src/GitUtils.ps1 b/src/GitUtils.ps1 index 36f7c2519..6848953dd 100644 --- a/src/GitUtils.ps1 +++ b/src/GitUtils.ps1 @@ -1,581 +1,581 @@ -# Inspired by Mark Embling -# http://www.markembling.info/view/my-ideal-powershell-prompt-with-git-integration - -<# -.SYNOPSIS - Gets the path to the current repository's .git dir. -.DESCRIPTION - Gets the path to the current repository's .git dir. Or if the repository - is a bare repository, the root directory of the bare repository. -.EXAMPLE - PS C:\GitHub\posh-git\tests> Get-GitDirectory - Returns C:\GitHub\posh-git\.git -.INPUTS - None. -.OUTPUTS - System.String -#> -function Get-GitDirectory { - $pathInfo = Microsoft.PowerShell.Management\Get-Location - if (!$pathInfo -or ($pathInfo.Provider.Name -ne 'FileSystem')) { - $null - } - elseif ($Env:GIT_DIR) { - $Env:GIT_DIR -replace '\\|/', [System.IO.Path]::DirectorySeparatorChar - } - else { - $currentDir = Get-Item $pathInfo -Force - while ($currentDir) { - $gitDirPath = Join-Path $currentDir.FullName .git - if (Test-Path -LiteralPath $gitDirPath -PathType Container) { - return $gitDirPath - } - - $headPath = Join-Path $currentDir.FullName HEAD - if (Test-Path -LiteralPath $headPath -PathType Leaf) { - $refsPath = Join-Path $currentDir.FullName refs - $objsPath = Join-Path $currentDir.FullName objects - if ((Test-Path -LiteralPath $refsPath -PathType Container) -and - (Test-Path -LiteralPath $objsPath -PathType Container)) { - - $bareDir = Invoke-Utf8ConsoleCommand { git rev-parse --git-dir 2>$null } - if ($bareDir -and (Test-Path -LiteralPath $bareDir -PathType Container)) { - $resolvedBareDir = (Resolve-Path $bareDir).Path - return $resolvedBareDir - } - } - } - - $currentDir = $currentDir.Parent - } - } -} - -function Get-GitBranch($gitDir = $(Get-GitDirectory), [Diagnostics.Stopwatch]$sw) { - if (!$gitDir) { return } - - Invoke-Utf8ConsoleCommand { - dbg 'Finding branch' $sw - $r = ''; $b = ''; $c = '' - if (Test-Path $gitDir\rebase-merge\interactive) { - dbg 'Found rebase-merge\interactive' $sw - $r = '|REBASE-i' - $b = "$(Get-Content $gitDir\rebase-merge\head-name)" - } - elseif (Test-Path $gitDir\rebase-merge) { - dbg 'Found rebase-merge' $sw - $r = '|REBASE-m' - $b = "$(Get-Content $gitDir\rebase-merge\head-name)" - } - else { - if (Test-Path $gitDir\rebase-apply) { - dbg 'Found rebase-apply' $sw - if (Test-Path $gitDir\rebase-apply\rebasing) { - dbg 'Found rebase-apply\rebasing' $sw - $r = '|REBASE' - } - elseif (Test-Path $gitDir\rebase-apply\applying) { - dbg 'Found rebase-apply\applying' $sw - $r = '|AM' - } - else { - dbg 'Found rebase-apply' $sw - $r = '|AM/REBASE' - } - } - elseif (Test-Path $gitDir\MERGE_HEAD) { - dbg 'Found MERGE_HEAD' $sw - $r = '|MERGING' - } - elseif (Test-Path $gitDir\CHERRY_PICK_HEAD) { - dbg 'Found CHERRY_PICK_HEAD' $sw - $r = '|CHERRY-PICKING' - } - elseif (Test-Path $gitDir\BISECT_LOG) { - dbg 'Found BISECT_LOG' $sw - $r = '|BISECTING' - } - - $b = Invoke-NullCoalescing ` - { dbg 'Trying symbolic-ref' $sw; git symbolic-ref HEAD -q 2>$null } ` - { '({0})' -f (Invoke-NullCoalescing ` - { - dbg 'Trying describe' $sw - switch ($Global:GitPromptSettings.DescribeStyle) { - 'contains' { git describe --contains HEAD 2>$null } - 'branch' { git describe --contains --all HEAD 2>$null } - 'describe' { git describe HEAD 2>$null } - default { git tag --points-at HEAD 2>$null } - } - } ` - { - dbg 'Falling back on parsing HEAD' $sw - $ref = $null - - if (Test-Path $gitDir\HEAD) { - dbg 'Reading from .git\HEAD' $sw - $ref = Get-Content $gitDir\HEAD 2>$null - } - else { - dbg 'Trying rev-parse' $sw - $ref = git rev-parse HEAD 2>$null - } - - if ($ref -match 'ref: (?.+)') { - return $Matches['ref'] - } - elseif ($ref -and $ref.Length -ge 7) { - return $ref.Substring(0,7)+'...' - } - else { - return 'unknown' - } - } - ) } - } - - dbg 'Inside git directory?' $sw - if ('true' -eq $(git rev-parse --is-inside-git-dir 2>$null)) { - dbg 'Inside git directory' $sw - if ('true' -eq $(git rev-parse --is-bare-repository 2>$null)) { - $c = 'BARE:' - } - else { - $b = 'GIT_DIR!' - } - } - - "$c$($b -replace 'refs/heads/','')$r" - } -} - -function GetUniquePaths($pathCollections) { - $hash = New-Object System.Collections.Specialized.OrderedDictionary - - foreach ($pathCollection in $pathCollections) { - foreach ($path in $pathCollection) { - $hash[$path] = 1 - } - } - - $hash.Keys -} - -$castStringSeq = [Linq.Enumerable].GetMethod("Cast").MakeGenericMethod([string]) - -function Get-GitStatus($gitDir = (Get-GitDirectory)) { - $settings = $Global:GitPromptSettings - $enabled = (-not $settings) -or $settings.EnablePromptStatus - if ($enabled -and $gitDir) { - if($settings.Debug) { - $sw = [Diagnostics.Stopwatch]::StartNew(); Write-Host '' - } - else { - $sw = $null - } - - $branch = $null - $aheadBy = 0 - $behindBy = 0 - $gone = $false - $indexAdded = New-Object System.Collections.Generic.List[string] - $indexModified = New-Object System.Collections.Generic.List[string] - $indexDeleted = New-Object System.Collections.Generic.List[string] - $indexUnmerged = New-Object System.Collections.Generic.List[string] - $filesAdded = New-Object System.Collections.Generic.List[string] - $filesModified = New-Object System.Collections.Generic.List[string] - $filesDeleted = New-Object System.Collections.Generic.List[string] - $filesUnmerged = New-Object System.Collections.Generic.List[string] - $stashCount = 0 - - if($settings.EnableFileStatus -and !$(InDotGitOrBareRepoDir $gitDir) -and !$(InDisabledRepository)) { - if ($settings.EnableFileStatusFromCache -eq $null) { - $settings.EnableFileStatusFromCache = (Get-Module GitStatusCachePoshClient) -ne $null - } - - if ($settings.EnableFileStatusFromCache) { - dbg 'Getting status from cache' $sw - $cacheResponse = Get-GitStatusFromCache - dbg 'Parsing status' $sw - - $indexAdded.AddRange($castStringSeq.Invoke($null, (,@($cacheResponse.IndexAdded)))) - $indexModified.AddRange($castStringSeq.Invoke($null, (,@($cacheResponse.IndexModified)))) - foreach ($indexRenamed in $cacheResponse.IndexRenamed) { - $indexModified.Add($indexRenamed.Old) - } - $indexDeleted.AddRange($castStringSeq.Invoke($null, (,@($cacheResponse.IndexDeleted)))) - $indexUnmerged.AddRange($castStringSeq.Invoke($null, (,@($cacheResponse.Conflicted)))) - - $filesAdded.AddRange($castStringSeq.Invoke($null, (,@($cacheResponse.WorkingAdded)))) - $filesModified.AddRange($castStringSeq.Invoke($null, (,@($cacheResponse.WorkingModified)))) - foreach ($workingRenamed in $cacheResponse.WorkingRenamed) { - $filesModified.Add($workingRenamed.Old) - } - $filesDeleted.AddRange($castStringSeq.Invoke($null, (,@($cacheResponse.WorkingDeleted)))) - $filesUnmerged.AddRange($castStringSeq.Invoke($null, (,@($cacheResponse.Conflicted)))) - - $branch = $cacheResponse.Branch - $upstream = $cacheResponse.Upstream - $gone = $cacheResponse.UpstreamGone - $aheadBy = $cacheResponse.AheadBy - $behindBy = $cacheResponse.BehindBy - - if ($cacheResponse.Stashes) { $stashCount = $cacheResponse.Stashes.Length } - if ($cacheResponse.State) { $branch += "|" + $cacheResponse.State } - } else { - dbg 'Getting status' $sw - $status = Invoke-Utf8ConsoleCommand { git -c color.status=false status --short --branch 2>$null } - if($settings.EnableStashStatus) { - dbg 'Getting stash count' $sw - $stashCount = $null | git stash list 2>$null | measure-object | Select-Object -expand Count - } - - dbg 'Parsing status' $sw - switch -regex ($status) { - '^(?[^#])(?.) (?.*?)(?: -> (?.*))?$' { - if ($sw) { dbg "Status: $_" $sw } - - switch ($matches['index']) { - 'A' { $null = $indexAdded.Add($matches['path1']); break } - 'M' { $null = $indexModified.Add($matches['path1']); break } - 'R' { $null = $indexModified.Add($matches['path1']); break } - 'C' { $null = $indexModified.Add($matches['path1']); break } - 'D' { $null = $indexDeleted.Add($matches['path1']); break } - 'U' { $null = $indexUnmerged.Add($matches['path1']); break } - } - switch ($matches['working']) { - '?' { $null = $filesAdded.Add($matches['path1']); break } - 'A' { $null = $filesAdded.Add($matches['path1']); break } - 'M' { $null = $filesModified.Add($matches['path1']); break } - 'D' { $null = $filesDeleted.Add($matches['path1']); break } - 'U' { $null = $filesUnmerged.Add($matches['path1']); break } - } - continue - } - - '^## (?\S+?)(?:\.\.\.(?\S+))?(?: \[(?:ahead (?\d+))?(?:, )?(?:behind (?\d+))?(?gone)?\])?$' { - if ($sw) { dbg "Status: $_" $sw } - - $branch = $matches['branch'] - $upstream = $matches['upstream'] - $aheadBy = [int]$matches['ahead'] - $behindBy = [int]$matches['behind'] - $gone = [string]$matches['gone'] -eq 'gone' - continue - } - - '^## Initial commit on (?\S+)$' { - if ($sw) { dbg "Status: $_" $sw } - - $branch = $matches['branch'] - continue - } - - default { if ($sw) { dbg "Status: $_" $sw } } - - } - } - } - - if(!$branch) { $branch = Get-GitBranch $gitDir $sw } - - dbg 'Building status object' $sw - # - # This collection is used twice, so create the array just once - $filesAdded = $filesAdded.ToArray() - - $indexPaths = @(GetUniquePaths $indexAdded,$indexModified,$indexDeleted,$indexUnmerged) - $workingPaths = @(GetUniquePaths $filesAdded,$filesModified,$filesDeleted,$filesUnmerged) - $index = (,$indexPaths) | - Add-Member -PassThru NoteProperty Added $indexAdded.ToArray() | - Add-Member -PassThru NoteProperty Modified $indexModified.ToArray() | - Add-Member -PassThru NoteProperty Deleted $indexDeleted.ToArray() | - Add-Member -PassThru NoteProperty Unmerged $indexUnmerged.ToArray() - - $working = (,$workingPaths) | - Add-Member -PassThru NoteProperty Added $filesAdded | - Add-Member -PassThru NoteProperty Modified $filesModified.ToArray() | - Add-Member -PassThru NoteProperty Deleted $filesDeleted.ToArray() | - Add-Member -PassThru NoteProperty Unmerged $filesUnmerged.ToArray() - - $result = New-Object PSObject -Property @{ - GitDir = $gitDir - Branch = $branch - AheadBy = $aheadBy - BehindBy = $behindBy - UpstreamGone = $gone - Upstream = $upstream - HasIndex = [bool]$index - Index = $index - HasWorking = [bool]$working - Working = $working - HasUntracked = [bool]$filesAdded - StashCount = $stashCount - } - - dbg 'Finished' $sw - if($sw) { $sw.Stop() } - return $result - } -} - -function InDisabledRepository { - $currentLocation = Get-Location - - foreach ($repo in $Global:GitPromptSettings.RepositoriesInWhichToDisableFileStatus) { - if ($currentLocation -like "$repo*") { - return $true - } - } - - return $false -} - -function InDotGitOrBareRepoDir([string][ValidateNotNullOrEmpty()]$GitDir) { - # A UNC path has no drive so it's better to use the ProviderPath e.g. "\\server\share". - # However for any path with a drive defined, it's better to use the Path property. - # In this case, ProviderPath is "\LocalMachine\My"" whereas Path is "Cert:\LocalMachine\My". - # The latter is more desirable. - $pathInfo = Microsoft.PowerShell.Management\Get-Location - $currentPath = if ($pathInfo.Drive) { $pathInfo.Path } else { $pathInfo.ProviderPath } - $res = $currentPath.StartsWith($GitDir, (Get-PathStringComparison)) - $res -} - -function Enable-GitColors { - Write-Warning 'Enable-GitColors is Obsolete and will be removed in a future version of posh-git.' -} - -function Get-AliasPattern($exe) { - $aliases = @($exe) + @(Get-Alias | Where-Object { $_.Definition -eq $exe } | Select-Object -Exp Name) - "($($aliases -join '|'))" -} - -function setenv($key, $value) { - [void][Environment]::SetEnvironmentVariable($key, $value) - Set-TempEnv $key $value -} - -function Get-TempEnv($key) { - $path = Get-TempEnvPath($key) - if (Test-Path $path) { - $value = Get-Content $path - [void][Environment]::SetEnvironmentVariable($key, $value) - } -} - -function Set-TempEnv($key, $value) { - $path = Get-TempEnvPath($key) - if ($value -eq $null) { - if (Test-Path $path) { - Remove-Item $path - } - } - else { - New-Item $path -Force -ItemType File > $null - $value | Out-File -FilePath $path -Encoding ascii -Force - } -} - -function Get-TempEnvPath($key){ - $path = Join-Path ([System.IO.Path]::GetTempPath()) ".ssh\$key.env" - return $path -} - -# Retrieve the current SSH agent PID (or zero). Can be used to determine if there -# is a running agent. -function Get-SshAgent() { - if ($env:GIT_SSH -imatch 'plink') { - $pageantPid = Get-Process | Where-Object { $_.Name -eq 'pageant' } | Select-Object -ExpandProperty Id -First 1 - if ($null -ne $pageantPid) { return $pageantPid } - } - else { - $agentPid = $Env:SSH_AGENT_PID - if ($agentPid) { - $sshAgentProcess = Get-Process | Where-Object { ($_.Id -eq $agentPid) -and ($_.Name -eq 'ssh-agent') } - if ($null -ne $sshAgentProcess) { - return $agentPid - } - else { - setenv 'SSH_AGENT_PID' $null - setenv 'SSH_AUTH_SOCK' $null - } - } - } - - return 0 -} - -# Attempt to guess Pageant's location -function Find-Pageant() { - Write-Verbose "Pageant not in path. Trying to guess location." - - $gitSsh = $env:GIT_SSH - if ($gitSsh -and (test-path $gitSsh)) { - $pageant = join-path (split-path $gitSsh) pageant - } - - if (!(get-command $pageant -Erroraction SilentlyContinue)) { - return # Guessing failed. - } - else { - return $pageant - } -} - -# Attempt to guess $program's location. For ssh-agent/ssh-add. -function Find-Ssh($program = 'ssh-agent') { - Write-Verbose "$program not in path. Trying to guess location." - $gitItem = Get-Command git -Erroraction SilentlyContinue | Get-Item - if ($null -eq $gitItem) { - Write-Warning 'git not in path' - return - } - - $sshLocation = join-path $gitItem.directory.parent.fullname bin/$program - if (get-command $sshLocation -Erroraction SilentlyContinue) { - return $sshLocation - } - - $sshLocation = join-path $gitItem.directory.parent.fullname usr/bin/$program - if (get-command $sshLocation -Erroraction SilentlyContinue) { - return $sshLocation - } -} - -# Loosely based on bash script from http://help.github.com/ssh-key-passphrases/ -function Start-SshAgent([switch]$Quiet) { - [int]$agentPid = Get-SshAgent - if ($agentPid -gt 0) { - if (!$Quiet) { - $agentName = Get-Process -Id $agentPid | Select-Object -ExpandProperty Name - if (!$agentName) { $agentName = "SSH Agent" } - Write-Host "$agentName is already running (pid $($agentPid))" - } - return - } - - if ($env:GIT_SSH -imatch 'plink') { - Write-Host "GIT_SSH set to $($env:GIT_SSH), using Pageant as SSH agent." - - $pageant = Get-Command pageant -TotalCount 1 -Erroraction SilentlyContinue - $pageant = if ($pageant) { $pageant } else { Find-Pageant } - if (!$pageant) { - Write-Warning "Could not find Pageant." - return - } - - Start-Process -NoNewWindow $pageant - } - else { - $sshAgent = Get-Command ssh-agent -TotalCount 1 -ErrorAction SilentlyContinue - $sshAgent = if ($sshAgent) { $sshAgent } else { Find-Ssh('ssh-agent') } - if (!$sshAgent) { - Write-Warning 'Could not find ssh-agent' - return - } - - & $sshAgent | ForEach-Object { - if ($_ -match '(?[^=]+)=(?[^;]+);') { - setenv $Matches['key'] $Matches['value'] - } - } - } - - Add-SshKey -} - -function Get-SshPath($File = 'id_rsa') { - # Avoid paths with path separator char since it is different on Linux/macOS. - # Also avoid ~ as it is invalid if the user is cd'd into say cert:\ or hklm:\. - # Also, apparently using the PowerShell built-in $HOME variable may not cut it for msysGit with has different - # ideas about the path to the user's home dir e.g. /c/Users/Keith - $homePath = Invoke-NullCoalescing $Env:HOME $Home - Join-Path $homePath (Join-Path .ssh $File) -} - -<# -.SYNOPSIS - Add a key to the SSH agent -.DESCRIPTION - Adds one or more SSH keys to the SSH agent. -.EXAMPLE - PS C:\> Add-SshKey - Adds ~\.ssh\id_rsa to the SSH agent. -.EXAMPLE - PS C:\> Add-SshKey ~\.ssh\mykey, ~\.ssh\myotherkey - Adds ~\.ssh\mykey and ~\.ssh\myotherkey to the SSH agent. -.INPUTS - None. - You cannot pipe input to this cmdlet. -#> -function Add-SshKey() { - if ($env:GIT_SSH -imatch 'plink') { - $pageant = Get-Command pageant -Erroraction SilentlyContinue | Select-Object -First 1 -ExpandProperty Name - $pageant = if ($pageant) { $pageant } else { Find-Pageant } - if (!$pageant) { - Write-Warning 'Could not find Pageant' - return - } - - if ($args.Count -eq 0) { - $keyPath = Join-Path $Env:HOME .ssh - $keys = Get-ChildItem $keyPath/*.ppk -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName - & $pageant $keys - } - else { - foreach ($value in $args) { - & $pageant $value - } - } - } - else { - $sshAdd = Get-Command ssh-add -TotalCount 1 -ErrorAction SilentlyContinue - $sshAdd = if ($sshAdd) { $sshAdd } else { Find-Ssh('ssh-add') } - if (!$sshAdd) { - Write-Warning 'Could not find ssh-add' - return - } - - if ($args.Count -eq 0) { - & $sshAdd - } - else { - foreach ($value in $args) { - & $sshAdd $value - } - } - } -} - -# Stop a running SSH agent -function Stop-SshAgent() { - [int]$agentPid = Get-SshAgent - if ($agentPid -gt 0) { - # Stop agent process - $proc = Get-Process -Id $agentPid -ErrorAction SilentlyContinue - if ($null -ne $proc) { - Stop-Process $agentPid - } - - setenv 'SSH_AGENT_PID' $null - setenv 'SSH_AUTH_SOCK' $null - } -} - -function Update-AllBranches($Upstream = 'master', [switch]$Quiet) { - $head = git rev-parse --abbrev-ref HEAD - git checkout -q $Upstream - $branches = Invoke-Utf8ConsoleCommand { (git branch --no-color --no-merged) } | Where-Object { $_ -notmatch '^\* ' } - foreach ($line in $branches) { - $branch = $line.SubString(2) - if (!$Quiet) { Write-Host "Rebasing $branch onto $Upstream..." } - - git rebase -q $Upstream $branch > $null 2> $null - if ($LASTEXITCODE) { - git rebase --abort - Write-Warning "Rebase failed for $branch" - } - } - git checkout -q $head -} +# Inspired by Mark Embling +# http://www.markembling.info/view/my-ideal-powershell-prompt-with-git-integration + +<# +.SYNOPSIS + Gets the path to the current repository's .git dir. +.DESCRIPTION + Gets the path to the current repository's .git dir. Or if the repository + is a bare repository, the root directory of the bare repository. +.EXAMPLE + PS C:\GitHub\posh-git\tests> Get-GitDirectory + Returns C:\GitHub\posh-git\.git +.INPUTS + None. +.OUTPUTS + System.String +#> +function Get-GitDirectory { + $pathInfo = Microsoft.PowerShell.Management\Get-Location + if (!$pathInfo -or ($pathInfo.Provider.Name -ne 'FileSystem')) { + $null + } + elseif ($Env:GIT_DIR) { + $Env:GIT_DIR -replace '\\|/', [System.IO.Path]::DirectorySeparatorChar + } + else { + $currentDir = Get-Item $pathInfo -Force + while ($currentDir) { + $gitDirPath = Join-Path $currentDir.FullName .git + if (Test-Path -LiteralPath $gitDirPath -PathType Container) { + return $gitDirPath + } + + $headPath = Join-Path $currentDir.FullName HEAD + if (Test-Path -LiteralPath $headPath -PathType Leaf) { + $refsPath = Join-Path $currentDir.FullName refs + $objsPath = Join-Path $currentDir.FullName objects + if ((Test-Path -LiteralPath $refsPath -PathType Container) -and + (Test-Path -LiteralPath $objsPath -PathType Container)) { + + $bareDir = Invoke-Utf8ConsoleCommand { git rev-parse --git-dir 2>$null } + if ($bareDir -and (Test-Path -LiteralPath $bareDir -PathType Container)) { + $resolvedBareDir = (Resolve-Path $bareDir).Path + return $resolvedBareDir + } + } + } + + $currentDir = $currentDir.Parent + } + } +} + +function Get-GitBranch($gitDir = $(Get-GitDirectory), [Diagnostics.Stopwatch]$sw) { + if (!$gitDir) { return } + + Invoke-Utf8ConsoleCommand { + dbg 'Finding branch' $sw + $r = ''; $b = ''; $c = '' + if (Test-Path $gitDir\rebase-merge\interactive) { + dbg 'Found rebase-merge\interactive' $sw + $r = '|REBASE-i' + $b = "$(Get-Content $gitDir\rebase-merge\head-name)" + } + elseif (Test-Path $gitDir\rebase-merge) { + dbg 'Found rebase-merge' $sw + $r = '|REBASE-m' + $b = "$(Get-Content $gitDir\rebase-merge\head-name)" + } + else { + if (Test-Path $gitDir\rebase-apply) { + dbg 'Found rebase-apply' $sw + if (Test-Path $gitDir\rebase-apply\rebasing) { + dbg 'Found rebase-apply\rebasing' $sw + $r = '|REBASE' + } + elseif (Test-Path $gitDir\rebase-apply\applying) { + dbg 'Found rebase-apply\applying' $sw + $r = '|AM' + } + else { + dbg 'Found rebase-apply' $sw + $r = '|AM/REBASE' + } + } + elseif (Test-Path $gitDir\MERGE_HEAD) { + dbg 'Found MERGE_HEAD' $sw + $r = '|MERGING' + } + elseif (Test-Path $gitDir\CHERRY_PICK_HEAD) { + dbg 'Found CHERRY_PICK_HEAD' $sw + $r = '|CHERRY-PICKING' + } + elseif (Test-Path $gitDir\BISECT_LOG) { + dbg 'Found BISECT_LOG' $sw + $r = '|BISECTING' + } + + $b = Invoke-NullCoalescing ` + { dbg 'Trying symbolic-ref' $sw; git symbolic-ref HEAD -q 2>$null } ` + { '({0})' -f (Invoke-NullCoalescing ` + { + dbg 'Trying describe' $sw + switch ($Global:GitPromptSettings.DescribeStyle) { + 'contains' { git describe --contains HEAD 2>$null } + 'branch' { git describe --contains --all HEAD 2>$null } + 'describe' { git describe HEAD 2>$null } + default { git tag --points-at HEAD 2>$null } + } + } ` + { + dbg 'Falling back on parsing HEAD' $sw + $ref = $null + + if (Test-Path $gitDir\HEAD) { + dbg 'Reading from .git\HEAD' $sw + $ref = Get-Content $gitDir\HEAD 2>$null + } + else { + dbg 'Trying rev-parse' $sw + $ref = git rev-parse HEAD 2>$null + } + + if ($ref -match 'ref: (?.+)') { + return $Matches['ref'] + } + elseif ($ref -and $ref.Length -ge 7) { + return $ref.Substring(0,7)+'...' + } + else { + return 'unknown' + } + } + ) } + } + + dbg 'Inside git directory?' $sw + if ('true' -eq $(git rev-parse --is-inside-git-dir 2>$null)) { + dbg 'Inside git directory' $sw + if ('true' -eq $(git rev-parse --is-bare-repository 2>$null)) { + $c = 'BARE:' + } + else { + $b = 'GIT_DIR!' + } + } + + "$c$($b -replace 'refs/heads/','')$r" + } +} + +function GetUniquePaths($pathCollections) { + $hash = New-Object System.Collections.Specialized.OrderedDictionary + + foreach ($pathCollection in $pathCollections) { + foreach ($path in $pathCollection) { + $hash[$path] = 1 + } + } + + $hash.Keys +} + +$castStringSeq = [Linq.Enumerable].GetMethod("Cast").MakeGenericMethod([string]) + +function Get-GitStatus($gitDir = (Get-GitDirectory)) { + $settings = $Global:GitPromptSettings + $enabled = (-not $settings) -or $settings.EnablePromptStatus + if ($enabled -and $gitDir) { + if($settings.Debug) { + $sw = [Diagnostics.Stopwatch]::StartNew(); Write-Host '' + } + else { + $sw = $null + } + + $branch = $null + $aheadBy = 0 + $behindBy = 0 + $gone = $false + $indexAdded = New-Object System.Collections.Generic.List[string] + $indexModified = New-Object System.Collections.Generic.List[string] + $indexDeleted = New-Object System.Collections.Generic.List[string] + $indexUnmerged = New-Object System.Collections.Generic.List[string] + $filesAdded = New-Object System.Collections.Generic.List[string] + $filesModified = New-Object System.Collections.Generic.List[string] + $filesDeleted = New-Object System.Collections.Generic.List[string] + $filesUnmerged = New-Object System.Collections.Generic.List[string] + $stashCount = 0 + + if($settings.EnableFileStatus -and !$(InDotGitOrBareRepoDir $gitDir) -and !$(InDisabledRepository)) { + if ($settings.EnableFileStatusFromCache -eq $null) { + $settings.EnableFileStatusFromCache = (Get-Module GitStatusCachePoshClient) -ne $null + } + + if ($settings.EnableFileStatusFromCache) { + dbg 'Getting status from cache' $sw + $cacheResponse = Get-GitStatusFromCache + dbg 'Parsing status' $sw + + $indexAdded.AddRange($castStringSeq.Invoke($null, (,@($cacheResponse.IndexAdded)))) + $indexModified.AddRange($castStringSeq.Invoke($null, (,@($cacheResponse.IndexModified)))) + foreach ($indexRenamed in $cacheResponse.IndexRenamed) { + $indexModified.Add($indexRenamed.Old) + } + $indexDeleted.AddRange($castStringSeq.Invoke($null, (,@($cacheResponse.IndexDeleted)))) + $indexUnmerged.AddRange($castStringSeq.Invoke($null, (,@($cacheResponse.Conflicted)))) + + $filesAdded.AddRange($castStringSeq.Invoke($null, (,@($cacheResponse.WorkingAdded)))) + $filesModified.AddRange($castStringSeq.Invoke($null, (,@($cacheResponse.WorkingModified)))) + foreach ($workingRenamed in $cacheResponse.WorkingRenamed) { + $filesModified.Add($workingRenamed.Old) + } + $filesDeleted.AddRange($castStringSeq.Invoke($null, (,@($cacheResponse.WorkingDeleted)))) + $filesUnmerged.AddRange($castStringSeq.Invoke($null, (,@($cacheResponse.Conflicted)))) + + $branch = $cacheResponse.Branch + $upstream = $cacheResponse.Upstream + $gone = $cacheResponse.UpstreamGone + $aheadBy = $cacheResponse.AheadBy + $behindBy = $cacheResponse.BehindBy + + if ($cacheResponse.Stashes) { $stashCount = $cacheResponse.Stashes.Length } + if ($cacheResponse.State) { $branch += "|" + $cacheResponse.State } + } else { + dbg 'Getting status' $sw + $status = Invoke-Utf8ConsoleCommand { git -c color.status=false status --short --branch 2>$null } + if($settings.EnableStashStatus) { + dbg 'Getting stash count' $sw + $stashCount = $null | git stash list 2>$null | measure-object | Select-Object -expand Count + } + + dbg 'Parsing status' $sw + switch -regex ($status) { + '^(?[^#])(?.) (?.*?)(?: -> (?.*))?$' { + if ($sw) { dbg "Status: $_" $sw } + + switch ($matches['index']) { + 'A' { $null = $indexAdded.Add($matches['path1']); break } + 'M' { $null = $indexModified.Add($matches['path1']); break } + 'R' { $null = $indexModified.Add($matches['path1']); break } + 'C' { $null = $indexModified.Add($matches['path1']); break } + 'D' { $null = $indexDeleted.Add($matches['path1']); break } + 'U' { $null = $indexUnmerged.Add($matches['path1']); break } + } + switch ($matches['working']) { + '?' { $null = $filesAdded.Add($matches['path1']); break } + 'A' { $null = $filesAdded.Add($matches['path1']); break } + 'M' { $null = $filesModified.Add($matches['path1']); break } + 'D' { $null = $filesDeleted.Add($matches['path1']); break } + 'U' { $null = $filesUnmerged.Add($matches['path1']); break } + } + continue + } + + '^## (?\S+?)(?:\.\.\.(?\S+))?(?: \[(?:ahead (?\d+))?(?:, )?(?:behind (?\d+))?(?gone)?\])?$' { + if ($sw) { dbg "Status: $_" $sw } + + $branch = $matches['branch'] + $upstream = $matches['upstream'] + $aheadBy = [int]$matches['ahead'] + $behindBy = [int]$matches['behind'] + $gone = [string]$matches['gone'] -eq 'gone' + continue + } + + '^## Initial commit on (?\S+)$' { + if ($sw) { dbg "Status: $_" $sw } + + $branch = $matches['branch'] + continue + } + + default { if ($sw) { dbg "Status: $_" $sw } } + + } + } + } + + if(!$branch) { $branch = Get-GitBranch $gitDir $sw } + + dbg 'Building status object' $sw + # + # This collection is used twice, so create the array just once + $filesAdded = $filesAdded.ToArray() + + $indexPaths = @(GetUniquePaths $indexAdded,$indexModified,$indexDeleted,$indexUnmerged) + $workingPaths = @(GetUniquePaths $filesAdded,$filesModified,$filesDeleted,$filesUnmerged) + $index = (,$indexPaths) | + Add-Member -PassThru NoteProperty Added $indexAdded.ToArray() | + Add-Member -PassThru NoteProperty Modified $indexModified.ToArray() | + Add-Member -PassThru NoteProperty Deleted $indexDeleted.ToArray() | + Add-Member -PassThru NoteProperty Unmerged $indexUnmerged.ToArray() + + $working = (,$workingPaths) | + Add-Member -PassThru NoteProperty Added $filesAdded | + Add-Member -PassThru NoteProperty Modified $filesModified.ToArray() | + Add-Member -PassThru NoteProperty Deleted $filesDeleted.ToArray() | + Add-Member -PassThru NoteProperty Unmerged $filesUnmerged.ToArray() + + $result = New-Object PSObject -Property @{ + GitDir = $gitDir + Branch = $branch + AheadBy = $aheadBy + BehindBy = $behindBy + UpstreamGone = $gone + Upstream = $upstream + HasIndex = [bool]$index + Index = $index + HasWorking = [bool]$working + Working = $working + HasUntracked = [bool]$filesAdded + StashCount = $stashCount + } + + dbg 'Finished' $sw + if($sw) { $sw.Stop() } + return $result + } +} + +function InDisabledRepository { + $currentLocation = Get-Location + + foreach ($repo in $Global:GitPromptSettings.RepositoriesInWhichToDisableFileStatus) { + if ($currentLocation -like "$repo*") { + return $true + } + } + + return $false +} + +function InDotGitOrBareRepoDir([string][ValidateNotNullOrEmpty()]$GitDir) { + # A UNC path has no drive so it's better to use the ProviderPath e.g. "\\server\share". + # However for any path with a drive defined, it's better to use the Path property. + # In this case, ProviderPath is "\LocalMachine\My"" whereas Path is "Cert:\LocalMachine\My". + # The latter is more desirable. + $pathInfo = Microsoft.PowerShell.Management\Get-Location + $currentPath = if ($pathInfo.Drive) { $pathInfo.Path } else { $pathInfo.ProviderPath } + $res = $currentPath.StartsWith($GitDir, (Get-PathStringComparison)) + $res +} + +function Enable-GitColors { + Write-Warning 'Enable-GitColors is Obsolete and will be removed in a future version of posh-git.' +} + +function Get-AliasPattern($exe) { + $aliases = @($exe) + @(Get-Alias | Where-Object { $_.Definition -eq $exe } | Select-Object -Exp Name) + "($($aliases -join '|'))" +} + +function setenv($key, $value) { + [void][Environment]::SetEnvironmentVariable($key, $value) + Set-TempEnv $key $value +} + +function Get-TempEnv($key) { + $path = Get-TempEnvPath($key) + if (Test-Path $path) { + $value = Get-Content $path + [void][Environment]::SetEnvironmentVariable($key, $value) + } +} + +function Set-TempEnv($key, $value) { + $path = Get-TempEnvPath($key) + if ($value -eq $null) { + if (Test-Path $path) { + Remove-Item $path + } + } + else { + New-Item $path -Force -ItemType File > $null + $value | Out-File -FilePath $path -Encoding ascii -Force + } +} + +function Get-TempEnvPath($key){ + $path = Join-Path ([System.IO.Path]::GetTempPath()) ".ssh\$key.env" + return $path +} + +# Retrieve the current SSH agent PID (or zero). Can be used to determine if there +# is a running agent. +function Get-SshAgent() { + if ($env:GIT_SSH -imatch 'plink') { + $pageantPid = Get-Process | Where-Object { $_.Name -eq 'pageant' } | Select-Object -ExpandProperty Id -First 1 + if ($null -ne $pageantPid) { return $pageantPid } + } + else { + $agentPid = $Env:SSH_AGENT_PID + if ($agentPid) { + $sshAgentProcess = Get-Process | Where-Object { ($_.Id -eq $agentPid) -and ($_.Name -eq 'ssh-agent') } + if ($null -ne $sshAgentProcess) { + return $agentPid + } + else { + setenv 'SSH_AGENT_PID' $null + setenv 'SSH_AUTH_SOCK' $null + } + } + } + + return 0 +} + +# Attempt to guess Pageant's location +function Find-Pageant() { + Write-Verbose "Pageant not in path. Trying to guess location." + + $gitSsh = $env:GIT_SSH + if ($gitSsh -and (test-path $gitSsh)) { + $pageant = join-path (split-path $gitSsh) pageant + } + + if (!(get-command $pageant -Erroraction SilentlyContinue)) { + return # Guessing failed. + } + else { + return $pageant + } +} + +# Attempt to guess $program's location. For ssh-agent/ssh-add. +function Find-Ssh($program = 'ssh-agent') { + Write-Verbose "$program not in path. Trying to guess location." + $gitItem = Get-Command git -Erroraction SilentlyContinue | Get-Item + if ($null -eq $gitItem) { + Write-Warning 'git not in path' + return + } + + $sshLocation = join-path $gitItem.directory.parent.fullname bin/$program + if (get-command $sshLocation -Erroraction SilentlyContinue) { + return $sshLocation + } + + $sshLocation = join-path $gitItem.directory.parent.fullname usr/bin/$program + if (get-command $sshLocation -Erroraction SilentlyContinue) { + return $sshLocation + } +} + +# Loosely based on bash script from http://help.github.com/ssh-key-passphrases/ +function Start-SshAgent([switch]$Quiet) { + [int]$agentPid = Get-SshAgent + if ($agentPid -gt 0) { + if (!$Quiet) { + $agentName = Get-Process -Id $agentPid | Select-Object -ExpandProperty Name + if (!$agentName) { $agentName = "SSH Agent" } + Write-Host "$agentName is already running (pid $($agentPid))" + } + return + } + + if ($env:GIT_SSH -imatch 'plink') { + Write-Host "GIT_SSH set to $($env:GIT_SSH), using Pageant as SSH agent." + + $pageant = Get-Command pageant -TotalCount 1 -Erroraction SilentlyContinue + $pageant = if ($pageant) { $pageant } else { Find-Pageant } + if (!$pageant) { + Write-Warning "Could not find Pageant." + return + } + + Start-Process -NoNewWindow $pageant + } + else { + $sshAgent = Get-Command ssh-agent -TotalCount 1 -ErrorAction SilentlyContinue + $sshAgent = if ($sshAgent) { $sshAgent } else { Find-Ssh('ssh-agent') } + if (!$sshAgent) { + Write-Warning 'Could not find ssh-agent' + return + } + + & $sshAgent | ForEach-Object { + if ($_ -match '(?[^=]+)=(?[^;]+);') { + setenv $Matches['key'] $Matches['value'] + } + } + } + + Add-SshKey +} + +function Get-SshPath($File = 'id_rsa') { + # Avoid paths with path separator char since it is different on Linux/macOS. + # Also avoid ~ as it is invalid if the user is cd'd into say cert:\ or hklm:\. + # Also, apparently using the PowerShell built-in $HOME variable may not cut it for msysGit with has different + # ideas about the path to the user's home dir e.g. /c/Users/Keith + $homePath = Invoke-NullCoalescing $Env:HOME $Home + Join-Path $homePath (Join-Path .ssh $File) +} + +<# +.SYNOPSIS + Add a key to the SSH agent +.DESCRIPTION + Adds one or more SSH keys to the SSH agent. +.EXAMPLE + PS C:\> Add-SshKey + Adds ~\.ssh\id_rsa to the SSH agent. +.EXAMPLE + PS C:\> Add-SshKey ~\.ssh\mykey, ~\.ssh\myotherkey + Adds ~\.ssh\mykey and ~\.ssh\myotherkey to the SSH agent. +.INPUTS + None. + You cannot pipe input to this cmdlet. +#> +function Add-SshKey() { + if ($env:GIT_SSH -imatch 'plink') { + $pageant = Get-Command pageant -Erroraction SilentlyContinue | Select-Object -First 1 -ExpandProperty Name + $pageant = if ($pageant) { $pageant } else { Find-Pageant } + if (!$pageant) { + Write-Warning 'Could not find Pageant' + return + } + + if ($args.Count -eq 0) { + $keyPath = Join-Path $Env:HOME .ssh + $keys = Get-ChildItem $keyPath/*.ppk -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName + & $pageant $keys + } + else { + foreach ($value in $args) { + & $pageant $value + } + } + } + else { + $sshAdd = Get-Command ssh-add -TotalCount 1 -ErrorAction SilentlyContinue + $sshAdd = if ($sshAdd) { $sshAdd } else { Find-Ssh('ssh-add') } + if (!$sshAdd) { + Write-Warning 'Could not find ssh-add' + return + } + + if ($args.Count -eq 0) { + & $sshAdd + } + else { + foreach ($value in $args) { + & $sshAdd $value + } + } + } +} + +# Stop a running SSH agent +function Stop-SshAgent() { + [int]$agentPid = Get-SshAgent + if ($agentPid -gt 0) { + # Stop agent process + $proc = Get-Process -Id $agentPid -ErrorAction SilentlyContinue + if ($null -ne $proc) { + Stop-Process $agentPid + } + + setenv 'SSH_AGENT_PID' $null + setenv 'SSH_AUTH_SOCK' $null + } +} + +function Update-AllBranches($Upstream = 'master', [switch]$Quiet) { + $head = git rev-parse --abbrev-ref HEAD + git checkout -q $Upstream + $branches = Invoke-Utf8ConsoleCommand { (git branch --no-color --no-merged) } | Where-Object { $_ -notmatch '^\* ' } + foreach ($line in $branches) { + $branch = $line.SubString(2) + if (!$Quiet) { Write-Host "Rebasing $branch onto $Upstream..." } + + git rebase -q $Upstream $branch > $null 2> $null + if ($LASTEXITCODE) { + git rebase --abort + Write-Warning "Rebase failed for $branch" + } + } + git checkout -q $head +} diff --git a/src/TortoiseGit.ps1 b/src/TortoiseGit.ps1 index 961e9ba35..27d03c00a 100644 --- a/src/TortoiseGit.ps1 +++ b/src/TortoiseGit.ps1 @@ -1,85 +1,85 @@ -# TortoiseGit - -function private:Get-TortoiseGitPath { - if ((Test-Path "C:\Program Files\TortoiseGit\bin\TortoiseGitProc.exe") -eq $true) { - # TortoiseGit 1.8.0 renamed TortoiseProc to TortoiseGitProc. - return "C:\Program Files\TortoiseGit\bin\TortoiseGitProc.exe" - } - - return "C:\Program Files\TortoiseGit\bin\TortoiseProc.exe" -} - -$Global:TortoiseGitSettings = new-object PSObject -Property @{ - TortoiseGitPath = (Get-TortoiseGitPath) - TortoiseGitCommands = @{ - "about" = "about"; - "add" = "add"; - "blame" = "blame"; - "cat" = "cat"; - "cleanup" = "cleanup"; - "clean" = "cleanup"; - "commit" = "commit"; - "conflicteditor" = "conflicteditor"; - "createpatch" = "createpatch"; - "patch" = "createpatch"; - "diff" = "diff"; - "export" = "export"; - "help" = "help"; - "ignore" = "ignore"; - "log" = "log"; - "merge" = "merge"; - "pull" = "pull"; - "push" = "push"; - "rebase" = "rebase"; - "refbrowse" = "refbrowse"; - "reflog" = "reflog"; - "remove" = "remove"; - "rm" = "remove"; - "rename" = "rename"; - "mv" = "rename"; - "repocreate" = "repocreate"; - "init" = "repocreate"; - "repostatus" = "repostatus"; - "status" = "repostatus"; - "resolve" = "resolve"; - "revert" = "revert"; - "settings" = "settings"; - "config" = "settings"; - "stash" = "stash"; - "stashapply" = "stashapply"; - "stashsave" = "stashsave"; - "subadd" = "subadd"; - "subsync" = "subsync"; - "subupdate" = "subupdate"; - "switch" = "switch"; - "checkout" = "switch"; - "fetch" = "sync"; - "sync" = "sync"; - } -} - -function tgit { - if($args) { - # Replace any aliases with actual TortoiseGit commands - if ($Global:TortoiseGitSettings.TortoiseGitCommands.ContainsKey($args[0])) { - $args[0] = $Global:TortoiseGitSettings.TortoiseGitCommands.Get_Item($args[0]) - } - - if($args[0] -eq "help") { - # Replace the built-in help behaviour with just a list of commands - $Global:TortoiseGitSettings.TortoiseGitCommands.Values.GetEnumerator() | Sort-Object | Get-Unique - return - } - - $newArgs = @() - $newArgs += "/command:" + $args[0] - - $cmd = $args[0] - - if($args.length -gt 1) { - $args[1..$args.length] | ForEach-Object { $newArgs += $_ } - } - - & $Global:TortoiseGitSettings.TortoiseGitPath $newArgs - } -} +# TortoiseGit + +function private:Get-TortoiseGitPath { + if ((Test-Path "C:\Program Files\TortoiseGit\bin\TortoiseGitProc.exe") -eq $true) { + # TortoiseGit 1.8.0 renamed TortoiseProc to TortoiseGitProc. + return "C:\Program Files\TortoiseGit\bin\TortoiseGitProc.exe" + } + + return "C:\Program Files\TortoiseGit\bin\TortoiseProc.exe" +} + +$Global:TortoiseGitSettings = new-object PSObject -Property @{ + TortoiseGitPath = (Get-TortoiseGitPath) + TortoiseGitCommands = @{ + "about" = "about"; + "add" = "add"; + "blame" = "blame"; + "cat" = "cat"; + "cleanup" = "cleanup"; + "clean" = "cleanup"; + "commit" = "commit"; + "conflicteditor" = "conflicteditor"; + "createpatch" = "createpatch"; + "patch" = "createpatch"; + "diff" = "diff"; + "export" = "export"; + "help" = "help"; + "ignore" = "ignore"; + "log" = "log"; + "merge" = "merge"; + "pull" = "pull"; + "push" = "push"; + "rebase" = "rebase"; + "refbrowse" = "refbrowse"; + "reflog" = "reflog"; + "remove" = "remove"; + "rm" = "remove"; + "rename" = "rename"; + "mv" = "rename"; + "repocreate" = "repocreate"; + "init" = "repocreate"; + "repostatus" = "repostatus"; + "status" = "repostatus"; + "resolve" = "resolve"; + "revert" = "revert"; + "settings" = "settings"; + "config" = "settings"; + "stash" = "stash"; + "stashapply" = "stashapply"; + "stashsave" = "stashsave"; + "subadd" = "subadd"; + "subsync" = "subsync"; + "subupdate" = "subupdate"; + "switch" = "switch"; + "checkout" = "switch"; + "fetch" = "sync"; + "sync" = "sync"; + } +} + +function tgit { + if($args) { + # Replace any aliases with actual TortoiseGit commands + if ($Global:TortoiseGitSettings.TortoiseGitCommands.ContainsKey($args[0])) { + $args[0] = $Global:TortoiseGitSettings.TortoiseGitCommands.Get_Item($args[0]) + } + + if($args[0] -eq "help") { + # Replace the built-in help behaviour with just a list of commands + $Global:TortoiseGitSettings.TortoiseGitCommands.Values.GetEnumerator() | Sort-Object | Get-Unique + return + } + + $newArgs = @() + $newArgs += "/command:" + $args[0] + + $cmd = $args[0] + + if($args.length -gt 1) { + $args[1..$args.length] | ForEach-Object { $newArgs += $_ } + } + + & $Global:TortoiseGitSettings.TortoiseGitPath $newArgs + } +} diff --git a/src/Utils.ps1 b/src/Utils.ps1 index 0db595a54..3a9aa4c09 100644 --- a/src/Utils.ps1 +++ b/src/Utils.ps1 @@ -1,255 +1,255 @@ -# Need this variable as long as we support PS v2 -$ModuleBasePath = Split-Path $MyInvocation.MyCommand.Path -Parent - -# Store error records generated by stderr output when invoking an executable -# This can be accessed from the user's session by executing: -# PS> $m = Get-Module posh-git -# PS> & $m Get-Variable invokeErrors -ValueOnly -$invokeErrors = New-Object System.Collections.ArrayList 256 - -# General Utility Functions - -function Invoke-NullCoalescing { - $result = $null - foreach($arg in $args) { - if ($arg -is [ScriptBlock]) { - $result = & $arg - } - else { - $result = $arg - } - if ($result) { break } - } - $result -} - -Set-Alias ?? Invoke-NullCoalescing -Force - -function Invoke-Utf8ConsoleCommand([ScriptBlock]$cmd) { - $currentEncoding = [Console]::OutputEncoding - $errorCount = $global:Error.Count - try { - # A native executable that writes to stderr AND has its stderr redirected will generate non-terminating - # error records if the user has set $ErrorActionPreference to Stop. Override that value in this scope. - $ErrorActionPreference = 'Continue' - if ($currentEncoding.IsSingleByte) { - [Console]::OutputEncoding = [Text.Encoding]::UTF8 - } - & $cmd - } - finally { - if ($currentEncoding.IsSingleByte) { - [Console]::OutputEncoding = $currentEncoding - } - - # Clear out stderr output that was added to the $Error collection, putting those errors in a module variable - if ($global:Error.Count -gt $errorCount) { - $numNewErrors = $global:Error.Count - $errorCount - $invokeErrors.InsertRange(0, $global:Error.GetRange(0, $numNewErrors)) - if ($invokeErrors.Count -gt 256) { - $invokeErrors.RemoveRange(256, ($invokeErrors.Count - 256)) - } - $global:Error.RemoveRange(0, $numNewErrors) - } - } -} - -<# -.SYNOPSIS - Configures your PowerShell profile (startup) script to import the posh-git - module when PowerShell starts. -.DESCRIPTION - Checks if your PowerShell profile script is not already importing posh-git - and if not, adds a command to import the posh-git module. This will cause - PowerShell to load posh-git whenever PowerShell starts. -.PARAMETER AllHosts - By default, this command modifies the CurrentUserCurrentHost profile - script. By specifying the AllHosts switch, the command updates the - CurrentUserAllHosts profile. -.PARAMETER Force - Do not check if the specified profile script is already importing - posh-git. Just add Import-Module posh-git command. -.EXAMPLE - PS C:\> Add-PoshGitToProfile - Updates your profile script for the current PowerShell host to import the - posh-git module when the current PowerShell host starts. -.EXAMPLE - PS C:\> Add-PoshGitToProfile -AllHost - Updates your profile script for all PowerShell hosts to import the posh-git - module whenever any PowerShell host starts. -.INPUTS - None. -.OUTPUTS - None. -#> -function Add-PoshGitToProfile([switch]$AllHosts, [switch]$Force, [switch]$WhatIf) { - $underTest = $false - - $profilePath = if ($AllHosts) { $PROFILE.CurrentUserAllHosts } else { $PROFILE.CurrentUserCurrentHost } - - # Under test, we override some variables using $args as a backdoor. - # TODO: Can we just turn these into optional parameters with well-defined behavior? - if (($args.Count -gt 0) -and ($args[0] -is [string])) { - $profilePath = [string]$args[0] - $underTest = $true - if ($args.Count -gt 1) { - $ModuleBasePath = [string]$args[1] - } - } - - if (!$profilePath) { $profilePath = $PROFILE } - - if (!$Force) { - # Search the user's profiles to see if any are using posh-git already, there is an extra search - # ($profilePath) taking place to accomodate the Pester tests. - $importedInProfile = Test-PoshGitImportedInScript $profilePath - if (!$importedInProfile -and !$underTest) { - $importedInProfile = Test-PoshGitImportedInScript $PROFILE - } - if (!$importedInProfile -and !$underTest) { - $importedInProfile = Test-PoshGitImportedInScript $PROFILE.CurrentUserCurrentHost - } - if (!$importedInProfile -and !$underTest) { - $importedInProfile = Test-PoshGitImportedInScript $PROFILE.CurrentUserAllHosts - } - if (!$importedInProfile -and !$underTest) { - $importedInProfile = Test-PoshGitImportedInScript $PROFILE.AllUsersCurrentHost - } - if (!$importedInProfile -and !$underTest) { - $importedInProfile = Test-PoshGitImportedInScript $PROFILE.AllUsersAllHosts - } - - if ($importedInProfile) { - Write-Warning "Skipping add of posh-git import to file '$profilePath'." - Write-Warning "posh-git appears to already be imported in one of your profile scripts." - Write-Warning "If you want to force the add, use the -Force parameter." - return - } - } - - if (!$profilePath) { - Write-Warning "Skipping add of posh-git import; no profile found." - Write-Verbose "`$profilePath = '$profilePath'" - Write-Verbose "`$PROFILE = '$PROFILE'" - Write-Verbose "CurrentUserCurrentHost = '$($PROFILE.CurrentUserCurrentHost)'" - Write-Verbose "CurrentUserAllHosts = '$($PROFILE.CurrentUserAllHosts)'" - Write-Verbose "AllUsersCurrentHost = '$($PROFILE.AllUsersCurrentHost)'" - Write-Verbose "AllUsersAllHosts = '$($PROFILE.AllUsersAllHosts)'" - return - } - - # Check if the location of this module file is in the PSModulePath - if (Test-InPSModulePath $ModuleBasePath) { - $profileContent = "`nImport-Module posh-git" - } - else { - $profileContent = "`nImport-Module '$ModuleBasePath\posh-git.psd1'" - } - - Add-Content -LiteralPath $profilePath -Value $profileContent -Encoding UTF8 -WhatIf:$WhatIf -} - -<# -.SYNOPSIS - Gets the file encoding of the specified file. -.DESCRIPTION - Gets the file encoding of the specified file. -.PARAMETER Path - Path to the file to check. The file must exist. -.EXAMPLE - PS C:\> Get-FileEncoding $profile - Get's the file encoding of the profile file. -.INPUTS - None. -.OUTPUTS - [System.String] -.NOTES - Adapted from http://www.west-wind.com/Weblog/posts/197245.aspx -#> -function Get-FileEncoding($Path) { - $bytes = [byte[]](Get-Content $Path -Encoding byte -ReadCount 4 -TotalCount 4) - - if (!$bytes) { return 'utf8' } - - switch -regex ('{0:x2}{1:x2}{2:x2}{3:x2}' -f $bytes[0],$bytes[1],$bytes[2],$bytes[3]) { - '^efbbbf' { return 'utf8' } - '^2b2f76' { return 'utf7' } - '^fffe' { return 'unicode' } - '^feff' { return 'bigendianunicode' } - '^0000feff' { return 'utf32' } - default { return 'ascii' } - } -} - -<# -.SYNOPSIS - Gets a StringComparison enum value appropriate for comparing paths on the OS platform. -.DESCRIPTION - Gets a StringComparison enum value appropriate for comparing paths on the OS platform. -.EXAMPLE - PS C:\> $pathStringComparison = Get-PathStringComparison -.INPUTS - None -.OUTPUTS - [System.StringComparison] -#> -function Get-PathStringComparison { - # File system paths are case-sensitive on Linux and case-insensitive on Windows and macOS - if (($PSVersionTable.PSVersion.Major -ge 6) -and $IsLinux) { - [System.StringComparison]::Ordinal - } - else { - [System.StringComparison]::OrdinalIgnoreCase - } -} - -function Get-PSModulePath { - $modulePaths = $Env:PSModulePath -split ';' - $modulePaths -} - -function Test-InPSModulePath { - param ( - [Parameter(Position=0, Mandatory=$true)] - [ValidateNotNull()] - [string] - $Path - ) - - $modulePaths = Get-PSModulePath - if (!$modulePaths) { return $false } - - $pathStringComparison = Get-PathStringComparison - $Path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path) - $inModulePath = @($modulePaths | Where-Object { $Path.StartsWith($_.TrimEnd([System.IO.Path]::DirectorySeparatorChar), $pathStringComparison) }).Count -gt 0 - - if ($inModulePath -and ('src' -eq (Split-Path $Path -Leaf))) { - Write-Warning 'posh-git repository structure is incompatible with %PSModulePath%.' - Write-Warning 'Importing with absolute path instead.' - return $false - } - - $inModulePath -} - -function Test-PoshGitImportedInScript { - param ( - [Parameter(Position=0)] - [string] - $Path - ) - - if (!$Path -or !(Test-Path -LiteralPath $Path)) { - return $false - } - - $match = (@(Get-Content $Path -ErrorAction SilentlyContinue) -match 'posh-git').Count -gt 0 - if ($match) { Write-Verbose "posh-git found in '$Path'" } - $match -} - -function dbg($Message, [Diagnostics.Stopwatch]$Stopwatch) { - if ($Stopwatch) { - Write-Verbose ('{0:00000}:{1}' -f $Stopwatch.ElapsedMilliseconds,$Message) -Verbose # -ForegroundColor Yellow - } -} +# Need this variable as long as we support PS v2 +$ModuleBasePath = Split-Path $MyInvocation.MyCommand.Path -Parent + +# Store error records generated by stderr output when invoking an executable +# This can be accessed from the user's session by executing: +# PS> $m = Get-Module posh-git +# PS> & $m Get-Variable invokeErrors -ValueOnly +$invokeErrors = New-Object System.Collections.ArrayList 256 + +# General Utility Functions + +function Invoke-NullCoalescing { + $result = $null + foreach($arg in $args) { + if ($arg -is [ScriptBlock]) { + $result = & $arg + } + else { + $result = $arg + } + if ($result) { break } + } + $result +} + +Set-Alias ?? Invoke-NullCoalescing -Force + +function Invoke-Utf8ConsoleCommand([ScriptBlock]$cmd) { + $currentEncoding = [Console]::OutputEncoding + $errorCount = $global:Error.Count + try { + # A native executable that writes to stderr AND has its stderr redirected will generate non-terminating + # error records if the user has set $ErrorActionPreference to Stop. Override that value in this scope. + $ErrorActionPreference = 'Continue' + if ($currentEncoding.IsSingleByte) { + [Console]::OutputEncoding = [Text.Encoding]::UTF8 + } + & $cmd + } + finally { + if ($currentEncoding.IsSingleByte) { + [Console]::OutputEncoding = $currentEncoding + } + + # Clear out stderr output that was added to the $Error collection, putting those errors in a module variable + if ($global:Error.Count -gt $errorCount) { + $numNewErrors = $global:Error.Count - $errorCount + $invokeErrors.InsertRange(0, $global:Error.GetRange(0, $numNewErrors)) + if ($invokeErrors.Count -gt 256) { + $invokeErrors.RemoveRange(256, ($invokeErrors.Count - 256)) + } + $global:Error.RemoveRange(0, $numNewErrors) + } + } +} + +<# +.SYNOPSIS + Configures your PowerShell profile (startup) script to import the posh-git + module when PowerShell starts. +.DESCRIPTION + Checks if your PowerShell profile script is not already importing posh-git + and if not, adds a command to import the posh-git module. This will cause + PowerShell to load posh-git whenever PowerShell starts. +.PARAMETER AllHosts + By default, this command modifies the CurrentUserCurrentHost profile + script. By specifying the AllHosts switch, the command updates the + CurrentUserAllHosts profile. +.PARAMETER Force + Do not check if the specified profile script is already importing + posh-git. Just add Import-Module posh-git command. +.EXAMPLE + PS C:\> Add-PoshGitToProfile + Updates your profile script for the current PowerShell host to import the + posh-git module when the current PowerShell host starts. +.EXAMPLE + PS C:\> Add-PoshGitToProfile -AllHost + Updates your profile script for all PowerShell hosts to import the posh-git + module whenever any PowerShell host starts. +.INPUTS + None. +.OUTPUTS + None. +#> +function Add-PoshGitToProfile([switch]$AllHosts, [switch]$Force, [switch]$WhatIf) { + $underTest = $false + + $profilePath = if ($AllHosts) { $PROFILE.CurrentUserAllHosts } else { $PROFILE.CurrentUserCurrentHost } + + # Under test, we override some variables using $args as a backdoor. + # TODO: Can we just turn these into optional parameters with well-defined behavior? + if (($args.Count -gt 0) -and ($args[0] -is [string])) { + $profilePath = [string]$args[0] + $underTest = $true + if ($args.Count -gt 1) { + $ModuleBasePath = [string]$args[1] + } + } + + if (!$profilePath) { $profilePath = $PROFILE } + + if (!$Force) { + # Search the user's profiles to see if any are using posh-git already, there is an extra search + # ($profilePath) taking place to accomodate the Pester tests. + $importedInProfile = Test-PoshGitImportedInScript $profilePath + if (!$importedInProfile -and !$underTest) { + $importedInProfile = Test-PoshGitImportedInScript $PROFILE + } + if (!$importedInProfile -and !$underTest) { + $importedInProfile = Test-PoshGitImportedInScript $PROFILE.CurrentUserCurrentHost + } + if (!$importedInProfile -and !$underTest) { + $importedInProfile = Test-PoshGitImportedInScript $PROFILE.CurrentUserAllHosts + } + if (!$importedInProfile -and !$underTest) { + $importedInProfile = Test-PoshGitImportedInScript $PROFILE.AllUsersCurrentHost + } + if (!$importedInProfile -and !$underTest) { + $importedInProfile = Test-PoshGitImportedInScript $PROFILE.AllUsersAllHosts + } + + if ($importedInProfile) { + Write-Warning "Skipping add of posh-git import to file '$profilePath'." + Write-Warning "posh-git appears to already be imported in one of your profile scripts." + Write-Warning "If you want to force the add, use the -Force parameter." + return + } + } + + if (!$profilePath) { + Write-Warning "Skipping add of posh-git import; no profile found." + Write-Verbose "`$profilePath = '$profilePath'" + Write-Verbose "`$PROFILE = '$PROFILE'" + Write-Verbose "CurrentUserCurrentHost = '$($PROFILE.CurrentUserCurrentHost)'" + Write-Verbose "CurrentUserAllHosts = '$($PROFILE.CurrentUserAllHosts)'" + Write-Verbose "AllUsersCurrentHost = '$($PROFILE.AllUsersCurrentHost)'" + Write-Verbose "AllUsersAllHosts = '$($PROFILE.AllUsersAllHosts)'" + return + } + + # Check if the location of this module file is in the PSModulePath + if (Test-InPSModulePath $ModuleBasePath) { + $profileContent = "`nImport-Module posh-git" + } + else { + $profileContent = "`nImport-Module '$ModuleBasePath\posh-git.psd1'" + } + + Add-Content -LiteralPath $profilePath -Value $profileContent -Encoding UTF8 -WhatIf:$WhatIf +} + +<# +.SYNOPSIS + Gets the file encoding of the specified file. +.DESCRIPTION + Gets the file encoding of the specified file. +.PARAMETER Path + Path to the file to check. The file must exist. +.EXAMPLE + PS C:\> Get-FileEncoding $profile + Get's the file encoding of the profile file. +.INPUTS + None. +.OUTPUTS + [System.String] +.NOTES + Adapted from http://www.west-wind.com/Weblog/posts/197245.aspx +#> +function Get-FileEncoding($Path) { + $bytes = [byte[]](Get-Content $Path -Encoding byte -ReadCount 4 -TotalCount 4) + + if (!$bytes) { return 'utf8' } + + switch -regex ('{0:x2}{1:x2}{2:x2}{3:x2}' -f $bytes[0],$bytes[1],$bytes[2],$bytes[3]) { + '^efbbbf' { return 'utf8' } + '^2b2f76' { return 'utf7' } + '^fffe' { return 'unicode' } + '^feff' { return 'bigendianunicode' } + '^0000feff' { return 'utf32' } + default { return 'ascii' } + } +} + +<# +.SYNOPSIS + Gets a StringComparison enum value appropriate for comparing paths on the OS platform. +.DESCRIPTION + Gets a StringComparison enum value appropriate for comparing paths on the OS platform. +.EXAMPLE + PS C:\> $pathStringComparison = Get-PathStringComparison +.INPUTS + None +.OUTPUTS + [System.StringComparison] +#> +function Get-PathStringComparison { + # File system paths are case-sensitive on Linux and case-insensitive on Windows and macOS + if (($PSVersionTable.PSVersion.Major -ge 6) -and $IsLinux) { + [System.StringComparison]::Ordinal + } + else { + [System.StringComparison]::OrdinalIgnoreCase + } +} + +function Get-PSModulePath { + $modulePaths = $Env:PSModulePath -split ';' + $modulePaths +} + +function Test-InPSModulePath { + param ( + [Parameter(Position=0, Mandatory=$true)] + [ValidateNotNull()] + [string] + $Path + ) + + $modulePaths = Get-PSModulePath + if (!$modulePaths) { return $false } + + $pathStringComparison = Get-PathStringComparison + $Path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path) + $inModulePath = @($modulePaths | Where-Object { $Path.StartsWith($_.TrimEnd([System.IO.Path]::DirectorySeparatorChar), $pathStringComparison) }).Count -gt 0 + + if ($inModulePath -and ('src' -eq (Split-Path $Path -Leaf))) { + Write-Warning 'posh-git repository structure is incompatible with %PSModulePath%.' + Write-Warning 'Importing with absolute path instead.' + return $false + } + + $inModulePath +} + +function Test-PoshGitImportedInScript { + param ( + [Parameter(Position=0)] + [string] + $Path + ) + + if (!$Path -or !(Test-Path -LiteralPath $Path)) { + return $false + } + + $match = (@(Get-Content $Path -ErrorAction SilentlyContinue) -match 'posh-git').Count -gt 0 + if ($match) { Write-Verbose "posh-git found in '$Path'" } + $match +} + +function dbg($Message, [Diagnostics.Stopwatch]$Stopwatch) { + if ($Stopwatch) { + Write-Verbose ('{0:00000}:{1}' -f $Stopwatch.ElapsedMilliseconds,$Message) -Verbose # -ForegroundColor Yellow + } +} diff --git a/src/en-US/about_posh-git.help.txt b/src/en-US/about_posh-git.help.txt index 7303418c9..b5dbcf95c 100644 --- a/src/en-US/about_posh-git.help.txt +++ b/src/en-US/about_posh-git.help.txt @@ -1,178 +1,178 @@ -TOPIC - posh-git - -SHORT DESCRIPTION - posh-git integrates Git and PowerShell providing tab completion of Git - commands, branch names, paths and more. It also provides Git status - summary information that can be displayed in the PowerShell prompt. - -LONG DESCRIPTION - posh-git integrates Git and PowerShell. Tab completion is supported for - Git subcommands, branch and remote names. Git also provides commands to - display colored Git status summary information. If you would like fine - grained control over how the Git status summary information is displayed - in your prompt function, you can get the raw status summary information - via the Get-GitStatus command. Then you can display the information in - your prompt however you would like. - - posh-git will install a prompt function if it detects the user does not - have their own, customized prompt. This prompt displays Git status summary - information when the current directory is located in a Git repository. - -GIT TAB COMPLETION - You can tab complete most common Git subcommands e.g.: - - C:\GitHub\posh-git> git ch --> git checkout - - You can also tab complete branch names and even remote names such as - origin and upstream. For instance, type the following inside of a Git - repo to see tab completion in action: - - C:\GitHub\posh-git> git fe or ma - - The above will expand to: - - C:\GitHub\posh-git> git fetch origin master - - And like tab completion in other parts of PowerShell, you can press tab - multiple times to cycle through all matches. For instance, type - "git ch" and press the tab key multiple times to cycle through "checkout", - "cherry" and "cherry-pick". - -POWERSHELL PROMPT - PowerShell generates its prompt by executing a function named "prompt", if - one exists. posh-git will install its prompt function if it detects the - user does not have their own, customized prompt function. This prompt - displays the current working directory followed by git status summary - information if the current directory is located in a Git repository, e.g.: - - C:\GitHub\posh-git [master ≡]> - - You can customize the posh-git prompt with the following settings: - - $GitPromptSettings.DefaultPromptPrefix - $GitPromptSettings.DefaultPromptSuffix - $GitPromptSettings.DefaultPromptDebugSuffix - $GitPromptSettings.DefaultPromptEnableTiming - $GitPromptSettings.DefaultPromptAbbreviateHomeDirectory - - For more information on customizing the posh-git prompt or creating your - own custom PowerShell prompt see: - - https://github.com/dahlbyk/posh-git/wiki/Customizing-Your-PowerShell-Prompt - -GIT STATUS SUMMARY - [{HEAD-name} S +A ~B -C !D | +E ~F -G !H W] - - * [ (BeforeText) - * {HEAD-name} is the current branch, or the SHA of a detached HEAD - * Cyan means the branch matches its remote - * Green means the branch is ahead of its remote (green light to push) - * Red means the branch is behind its remote - * Yellow means the branch is both ahead of and behind its remote - - * S represents the branch status in relation to remote (tracked origin) branch - * ≡ = Local branch is at the same commit level as the remote - branch (BranchIdenticalStatus). - * ↑ = Local branch is ahead of the remote branch by the specified - number of commits; a 'git push' is required to update the - remote branch (BranchAheadStatus). - * ↓ = Local branch is behind the remote branch by the specified - number of commits; a 'git pull' is required to update the - local branch (BranchBehindStatus). - * = Local branch is both ahead of the remote branch by the - specified number of commits () and behind by the - specified number of commits (); a rebase of the local - branch is required before pushing local changes to the - remote branch (BranchBehindAndAheadStatus). NOTE: this - status is only available if - $GitPromptSettings.BranchBehindAndAheadDisplay is set to - 'Compact'. - * × = The local branch is tracking a branch that is gone from the - remote (BranchGoneStatus). - - * ABCD represents the index | EFGH represents the working directory - * + = Added files - * ~ = Modified files - * - = Removed files - * ! = Conflicted files - * Index status is dark green and working directory status is dark red - reflecting the colors used by 'git status'. - - * W represents the status of the working directory - * ! = There are untracked changes (LocalStagedStatus) - * ~ = There are staged changes waiting to be committed (LocalWorkingStatus) - * None = There are no uncommitted or unstaged changes (LocalDefault) - * ] (AfterText) - - The (symbols) and surrounding text can be customized by the corresponding - properties of the global variable $GitPromptSettings. - - For example, a status summary of [master ≡ +0 ~2 -1 | +1 ~1 -0 !] - corresponds to the following 'git status': - - # On branch master - # - # Changes to be committed: - # (use "git reset HEAD ..." to unstage) - # - # modified: this-changed.txt - # modified: this-too.txt - # deleted: gone.ps1 - # - # Changed but not updated: - # (use "git add ..." to update what will be committed) - # (use "git checkout -- ..." to discard changes in working directory) - # - # modified: not-staged.ps1 - # - # Untracked files: - # (use "git add ..." to include in what will be committed) - # - # new.file - -USAGE AND CUSTOMIZATION - You need to import the posh-git module into your PowerShell session to - use it. Execute "Import-Module posh-git" to do this. After posh-git has - been imported, you can execute the command "Add-PoshGitToProfile" to have - your PowerShell profile updated to import posh-git whenever PowerShell - starts. - - When posh-git is imported it will provide a basic prompt function that - displays Git status summary information, unless you have your own, custom - prompt function. Prompt formatting, among other things, can be customized - using the global variables: $GitPromptSettings, $GitTabSettings and - $TortoiseGitSettings. To see the available settings, simply type the - variable name at the PowerShell prompt (after posh-git has been imported) - and press Enter. - - For more information on customizing the posh-git default prompt or creating - your own prompt fuction see: - - https://github.com/dahlbyk/posh-git/wiki/Customizing-Your-PowerShell-Prompt - - -PERFORMANCE - Displaying Git status in your prompt for a very large repository can - be prohibitively slow. Rather than turn off Git status entirely, you can - disable it on a repo-by-repo basis by adding individual repository paths - to $GitPromptSettings.RepositoriesInWhichToDisableFileStatus. - -PRIMARY COMMANDS - Get-GitStatus: - Returns information about the current Git repository as - well as the index and working directory. - - Write-GitStatus: - Writes directly to host the formatted text, as described above in the - GIT STATUS PROMPT section, when passed in the repository information - returned by Get-GitStatus e.g. Write-GitStatus (Get-GitStatus) - - Write-VcsStatus: - Gets the Git repository information and writes it formatted, as described - above in the GIT STATUS PROMPT section, directly to the host. - -BASED ON WORK BY: - Keith Dahlby, http://solutionizing.net/ - Mark Embling, http://www.markembling.info/ - Jeremy Skinner, http://www.jeremyskinner.co.uk/ +TOPIC + posh-git + +SHORT DESCRIPTION + posh-git integrates Git and PowerShell providing tab completion of Git + commands, branch names, paths and more. It also provides Git status + summary information that can be displayed in the PowerShell prompt. + +LONG DESCRIPTION + posh-git integrates Git and PowerShell. Tab completion is supported for + Git subcommands, branch and remote names. Git also provides commands to + display colored Git status summary information. If you would like fine + grained control over how the Git status summary information is displayed + in your prompt function, you can get the raw status summary information + via the Get-GitStatus command. Then you can display the information in + your prompt however you would like. + + posh-git will install a prompt function if it detects the user does not + have their own, customized prompt. This prompt displays Git status summary + information when the current directory is located in a Git repository. + +GIT TAB COMPLETION + You can tab complete most common Git subcommands e.g.: + + C:\GitHub\posh-git> git ch --> git checkout + + You can also tab complete branch names and even remote names such as + origin and upstream. For instance, type the following inside of a Git + repo to see tab completion in action: + + C:\GitHub\posh-git> git fe or ma + + The above will expand to: + + C:\GitHub\posh-git> git fetch origin master + + And like tab completion in other parts of PowerShell, you can press tab + multiple times to cycle through all matches. For instance, type + "git ch" and press the tab key multiple times to cycle through "checkout", + "cherry" and "cherry-pick". + +POWERSHELL PROMPT + PowerShell generates its prompt by executing a function named "prompt", if + one exists. posh-git will install its prompt function if it detects the + user does not have their own, customized prompt function. This prompt + displays the current working directory followed by git status summary + information if the current directory is located in a Git repository, e.g.: + + C:\GitHub\posh-git [master ≡]> + + You can customize the posh-git prompt with the following settings: + + $GitPromptSettings.DefaultPromptPrefix + $GitPromptSettings.DefaultPromptSuffix + $GitPromptSettings.DefaultPromptDebugSuffix + $GitPromptSettings.DefaultPromptEnableTiming + $GitPromptSettings.DefaultPromptAbbreviateHomeDirectory + + For more information on customizing the posh-git prompt or creating your + own custom PowerShell prompt see: + + https://github.com/dahlbyk/posh-git/wiki/Customizing-Your-PowerShell-Prompt + +GIT STATUS SUMMARY + [{HEAD-name} S +A ~B -C !D | +E ~F -G !H W] + + * [ (BeforeText) + * {HEAD-name} is the current branch, or the SHA of a detached HEAD + * Cyan means the branch matches its remote + * Green means the branch is ahead of its remote (green light to push) + * Red means the branch is behind its remote + * Yellow means the branch is both ahead of and behind its remote + + * S represents the branch status in relation to remote (tracked origin) branch + * ≡ = Local branch is at the same commit level as the remote + branch (BranchIdenticalStatus). + * ↑ = Local branch is ahead of the remote branch by the specified + number of commits; a 'git push' is required to update the + remote branch (BranchAheadStatus). + * ↓ = Local branch is behind the remote branch by the specified + number of commits; a 'git pull' is required to update the + local branch (BranchBehindStatus). + * = Local branch is both ahead of the remote branch by the + specified number of commits () and behind by the + specified number of commits (); a rebase of the local + branch is required before pushing local changes to the + remote branch (BranchBehindAndAheadStatus). NOTE: this + status is only available if + $GitPromptSettings.BranchBehindAndAheadDisplay is set to + 'Compact'. + * × = The local branch is tracking a branch that is gone from the + remote (BranchGoneStatus). + + * ABCD represents the index | EFGH represents the working directory + * + = Added files + * ~ = Modified files + * - = Removed files + * ! = Conflicted files + * Index status is dark green and working directory status is dark red + reflecting the colors used by 'git status'. + + * W represents the status of the working directory + * ! = There are untracked changes (LocalStagedStatus) + * ~ = There are staged changes waiting to be committed (LocalWorkingStatus) + * None = There are no uncommitted or unstaged changes (LocalDefault) + * ] (AfterText) + + The (symbols) and surrounding text can be customized by the corresponding + properties of the global variable $GitPromptSettings. + + For example, a status summary of [master ≡ +0 ~2 -1 | +1 ~1 -0 !] + corresponds to the following 'git status': + + # On branch master + # + # Changes to be committed: + # (use "git reset HEAD ..." to unstage) + # + # modified: this-changed.txt + # modified: this-too.txt + # deleted: gone.ps1 + # + # Changed but not updated: + # (use "git add ..." to update what will be committed) + # (use "git checkout -- ..." to discard changes in working directory) + # + # modified: not-staged.ps1 + # + # Untracked files: + # (use "git add ..." to include in what will be committed) + # + # new.file + +USAGE AND CUSTOMIZATION + You need to import the posh-git module into your PowerShell session to + use it. Execute "Import-Module posh-git" to do this. After posh-git has + been imported, you can execute the command "Add-PoshGitToProfile" to have + your PowerShell profile updated to import posh-git whenever PowerShell + starts. + + When posh-git is imported it will provide a basic prompt function that + displays Git status summary information, unless you have your own, custom + prompt function. Prompt formatting, among other things, can be customized + using the global variables: $GitPromptSettings, $GitTabSettings and + $TortoiseGitSettings. To see the available settings, simply type the + variable name at the PowerShell prompt (after posh-git has been imported) + and press Enter. + + For more information on customizing the posh-git default prompt or creating + your own prompt fuction see: + + https://github.com/dahlbyk/posh-git/wiki/Customizing-Your-PowerShell-Prompt + + +PERFORMANCE + Displaying Git status in your prompt for a very large repository can + be prohibitively slow. Rather than turn off Git status entirely, you can + disable it on a repo-by-repo basis by adding individual repository paths + to $GitPromptSettings.RepositoriesInWhichToDisableFileStatus. + +PRIMARY COMMANDS + Get-GitStatus: + Returns information about the current Git repository as + well as the index and working directory. + + Write-GitStatus: + Writes directly to host the formatted text, as described above in the + GIT STATUS PROMPT section, when passed in the repository information + returned by Get-GitStatus e.g. Write-GitStatus (Get-GitStatus) + + Write-VcsStatus: + Gets the Git repository information and writes it formatted, as described + above in the GIT STATUS PROMPT section, directly to the host. + +BASED ON WORK BY: + Keith Dahlby, http://solutionizing.net/ + Mark Embling, http://www.markembling.info/ + Jeremy Skinner, http://www.jeremyskinner.co.uk/ diff --git a/src/posh-git.psd1 b/src/posh-git.psd1 index d6b6399e0..8e277d942 100644 --- a/src/posh-git.psd1 +++ b/src/posh-git.psd1 @@ -1,74 +1,74 @@ -@{ - -# Script module or binary module file associated with this manifest. -ModuleToProcess = 'posh-git.psm1' - -# Version number of this module. -ModuleVersion = '0.7.0' - -# ID used to uniquely identify this module -GUID = '74c9fd30-734b-4c89-a8ae-7727ad21d1d5' - -# Author of this module -Author = 'Keith Dahlby and contributors' - -# Copyright statement for this module -Copyright = '(c) 2010-2017 Keith Dahlby and contributors' - -# Description of the functionality provided by this module -Description = 'Provides prompt with Git status summary information and tab completion for Git commands, parameters, remotes and branch names.' - -# Minimum version of the Windows PowerShell engine required by this module -PowerShellVersion = '2.0' - -# Functions to export from this module -FunctionsToExport = @( - 'Invoke-NullCoalescing', - 'Add-PoshGitToProfile', - 'Write-GitStatus', - 'Write-Prompt', - 'Write-VcsStatus', - 'Get-GitBranch', - 'Get-GitStatus', - 'Enable-GitColors', - 'Get-GitDirectory', - 'TabExpansion', - 'Get-AliasPattern', - 'Get-SshAgent', - 'Start-SshAgent', - 'Stop-SshAgent', - 'Add-SshKey', - 'Get-SshPath', - 'Update-AllBranches', - 'tgit' -) - -# Cmdlets to export from this module -CmdletsToExport = @() - -# Variables to export from this module -VariablesToExport = @() - -# Aliases to export from this module -AliasesToExport = @('??') - -# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. -PrivateData = @{ - - PSData = @{ - - # Tags applied to this module. These help with module discovery in online galleries. - Tags = @('git', 'tab-completion', 'tab-expansion') - - # A URL to the license for this module. - LicenseUri = 'https://github.com/dahlbyk/posh-git/blob/master/LICENSE.txt' - - # A URL to the main website for this project. - ProjectUri = 'https://github.com/dahlbyk/posh-git' - - } # End of PSData hashtable - -} # End of PrivateData hashtable - -} - +@{ + +# Script module or binary module file associated with this manifest. +ModuleToProcess = 'posh-git.psm1' + +# Version number of this module. +ModuleVersion = '0.7.0' + +# ID used to uniquely identify this module +GUID = '74c9fd30-734b-4c89-a8ae-7727ad21d1d5' + +# Author of this module +Author = 'Keith Dahlby and contributors' + +# Copyright statement for this module +Copyright = '(c) 2010-2017 Keith Dahlby and contributors' + +# Description of the functionality provided by this module +Description = 'Provides prompt with Git status summary information and tab completion for Git commands, parameters, remotes and branch names.' + +# Minimum version of the Windows PowerShell engine required by this module +PowerShellVersion = '2.0' + +# Functions to export from this module +FunctionsToExport = @( + 'Invoke-NullCoalescing', + 'Add-PoshGitToProfile', + 'Write-GitStatus', + 'Write-Prompt', + 'Write-VcsStatus', + 'Get-GitBranch', + 'Get-GitStatus', + 'Enable-GitColors', + 'Get-GitDirectory', + 'TabExpansion', + 'Get-AliasPattern', + 'Get-SshAgent', + 'Start-SshAgent', + 'Stop-SshAgent', + 'Add-SshKey', + 'Get-SshPath', + 'Update-AllBranches', + 'tgit' +) + +# Cmdlets to export from this module +CmdletsToExport = @() + +# Variables to export from this module +VariablesToExport = @() + +# Aliases to export from this module +AliasesToExport = @('??') + +# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. +PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + Tags = @('git', 'tab-completion', 'tab-expansion') + + # A URL to the license for this module. + LicenseUri = 'https://github.com/dahlbyk/posh-git/blob/master/LICENSE.txt' + + # A URL to the main website for this project. + ProjectUri = 'https://github.com/dahlbyk/posh-git' + + } # End of PSData hashtable + +} # End of PrivateData hashtable + +} + diff --git a/test/DefaultPrompt.Tests.ps1 b/test/DefaultPrompt.Tests.ps1 index 7ed08c881..e015cb7ac 100644 --- a/test/DefaultPrompt.Tests.ps1 +++ b/test/DefaultPrompt.Tests.ps1 @@ -1,95 +1,95 @@ -. $PSScriptRoot\Shared.ps1 - -Describe 'Default Prompt Tests' { - BeforeAll { - $prompt = Get-Item Function:\prompt - $OFS = '' - } - BeforeEach { - # Ensure these settings start out set to the default values - $GitPromptSettings.DefaultPromptPrefix = '' - $GitPromptSettings.DefaultPromptSuffix = '$(''>'' * ($nestedPromptLevel + 1)) ' - $GitPromptSettings.DefaultPromptDebugSuffix = ' [DBG]$(''>'' * ($nestedPromptLevel + 1)) ' - $GitPromptSettings.DefaultPromptAbbreviateHomeDirectory = $false - $GitPromptSettings.DefaultPromptEnableTiming = $false - } - - Context 'Prompt with no Git summary' { - It 'Returns the expected prompt string' { - Set-Location $env:windir -ErrorAction Stop - $res = [string](&$prompt 6>&1) - $res | Should BeExactly "$env:windir> " - } - It 'Returns the expected prompt string with changed DefaultPromptPrefix' { - Set-Location $Home -ErrorAction Stop - $GitPromptSettings.DefaultPromptPrefix = 'PS ' - $res = [string](&$prompt 6>&1) - $res | Should BeExactly "PS $Home> " - } - It 'Returns the expected prompt string with expanded DefaultPromptPrefix' { - Set-Location $Home -ErrorAction Stop - $GitPromptSettings.DefaultPromptPrefix = '[$(hostname)] ' - $res = [string](&$prompt 6>&1) - $res | Should BeExactly "[$(hostname)] $Home> " - } - It 'Returns the expected prompt string with changed DefaultPromptSuffix' { - Set-Location $Home -ErrorAction Stop - $GitPromptSettings.DefaultPromptSuffix = '`n> ' - $res = [string](&$prompt 6>&1) - $res | Should BeExactly "$Home`n> " - } - It 'Returns the expected prompt string with expanded DefaultPromptSuffix' { - Set-Location $Home -ErrorAction Stop - $GitPromptSettings.DefaultPromptSuffix = ' - $(6*7)> ' - $res = [string](&$prompt 6>&1) - $res | Should BeExactly "$Home - 42> " - } - It 'Returns the expected prompt string with DefaultPromptAbbreviateHomeDirectory enabled' { - Set-Location $Home -ErrorAction Stop - $GitPromptSettings.DefaultPromptAbbreviateHomeDirectory = $true - $res = [string](&$prompt 6>&1) - $res | Should BeExactly "~> " - } - It 'Returns the expected prompt string with prefix, suffix and abbrev home set' { - Set-Location $Home -ErrorAction Stop - $GitPromptSettings.DefaultPromptPrefix = '[$(hostname)] ' - $GitPromptSettings.DefaultPromptSuffix = ' - $(6*7)> ' - $GitPromptSettings.DefaultPromptAbbreviateHomeDirectory = $true - $res = [string](&$prompt 6>&1) - $res | Should BeExactly "[$(hostname)] ~ - 42> " - } - It 'Returns the expected prompt string with prompt timing enabled' { - Set-Location $Home -ErrorAction Stop - $GitPromptSettings.DefaultPromptEnableTiming = $true - $res = [string](&$prompt 6>&1) - $escapedHome = [regex]::Escape($Home) - $res | Should Match "$escapedHome \d+ms> " - } - } - - Context 'Prompt with Git summary' { - BeforeAll { - Set-Location $PSScriptRoot - } - - It 'Returns the expected prompt string with status' { - Mock git { - if ($args -contains 'rev-parse') { - $res = Invoke-Expression "git.exe $args" - return $res - } - return @' -## master -A test/Foo.Tests.ps1 - D test/Bar.Tests.ps1 - M test/Baz.Tests.ps1 - -'@ -split [System.Environment]::NewLine - } -ModuleName posh-git - - $res = [string](&$prompt 6>&1) - Assert-MockCalled git -ModuleName posh-git - $res | Should BeExactly "$PSScriptRoot [master +1 ~0 -0 | +0 ~1 -1 !]> " - } - } -} +. $PSScriptRoot\Shared.ps1 + +Describe 'Default Prompt Tests' { + BeforeAll { + $prompt = Get-Item Function:\prompt + $OFS = '' + } + BeforeEach { + # Ensure these settings start out set to the default values + $GitPromptSettings.DefaultPromptPrefix = '' + $GitPromptSettings.DefaultPromptSuffix = '$(''>'' * ($nestedPromptLevel + 1)) ' + $GitPromptSettings.DefaultPromptDebugSuffix = ' [DBG]$(''>'' * ($nestedPromptLevel + 1)) ' + $GitPromptSettings.DefaultPromptAbbreviateHomeDirectory = $false + $GitPromptSettings.DefaultPromptEnableTiming = $false + } + + Context 'Prompt with no Git summary' { + It 'Returns the expected prompt string' { + Set-Location $env:windir -ErrorAction Stop + $res = [string](&$prompt 6>&1) + $res | Should BeExactly "$env:windir> " + } + It 'Returns the expected prompt string with changed DefaultPromptPrefix' { + Set-Location $Home -ErrorAction Stop + $GitPromptSettings.DefaultPromptPrefix = 'PS ' + $res = [string](&$prompt 6>&1) + $res | Should BeExactly "PS $Home> " + } + It 'Returns the expected prompt string with expanded DefaultPromptPrefix' { + Set-Location $Home -ErrorAction Stop + $GitPromptSettings.DefaultPromptPrefix = '[$(hostname)] ' + $res = [string](&$prompt 6>&1) + $res | Should BeExactly "[$(hostname)] $Home> " + } + It 'Returns the expected prompt string with changed DefaultPromptSuffix' { + Set-Location $Home -ErrorAction Stop + $GitPromptSettings.DefaultPromptSuffix = '`n> ' + $res = [string](&$prompt 6>&1) + $res | Should BeExactly "$Home`n> " + } + It 'Returns the expected prompt string with expanded DefaultPromptSuffix' { + Set-Location $Home -ErrorAction Stop + $GitPromptSettings.DefaultPromptSuffix = ' - $(6*7)> ' + $res = [string](&$prompt 6>&1) + $res | Should BeExactly "$Home - 42> " + } + It 'Returns the expected prompt string with DefaultPromptAbbreviateHomeDirectory enabled' { + Set-Location $Home -ErrorAction Stop + $GitPromptSettings.DefaultPromptAbbreviateHomeDirectory = $true + $res = [string](&$prompt 6>&1) + $res | Should BeExactly "~> " + } + It 'Returns the expected prompt string with prefix, suffix and abbrev home set' { + Set-Location $Home -ErrorAction Stop + $GitPromptSettings.DefaultPromptPrefix = '[$(hostname)] ' + $GitPromptSettings.DefaultPromptSuffix = ' - $(6*7)> ' + $GitPromptSettings.DefaultPromptAbbreviateHomeDirectory = $true + $res = [string](&$prompt 6>&1) + $res | Should BeExactly "[$(hostname)] ~ - 42> " + } + It 'Returns the expected prompt string with prompt timing enabled' { + Set-Location $Home -ErrorAction Stop + $GitPromptSettings.DefaultPromptEnableTiming = $true + $res = [string](&$prompt 6>&1) + $escapedHome = [regex]::Escape($Home) + $res | Should Match "$escapedHome \d+ms> " + } + } + + Context 'Prompt with Git summary' { + BeforeAll { + Set-Location $PSScriptRoot + } + + It 'Returns the expected prompt string with status' { + Mock git { + if ($args -contains 'rev-parse') { + $res = Invoke-Expression "git.exe $args" + return $res + } + return @' +## master +A test/Foo.Tests.ps1 + D test/Bar.Tests.ps1 + M test/Baz.Tests.ps1 + +'@ -split [System.Environment]::NewLine + } -ModuleName posh-git + + $res = [string](&$prompt 6>&1) + Assert-MockCalled git -ModuleName posh-git + $res | Should BeExactly "$PSScriptRoot [master +1 ~0 -0 | +0 ~1 -1 !]> " + } + } +} diff --git a/test/Get-GitBranch.Tests.ps1 b/test/Get-GitBranch.Tests.ps1 index 3ee59888c..ee124536b 100644 --- a/test/Get-GitBranch.Tests.ps1 +++ b/test/Get-GitBranch.Tests.ps1 @@ -1,16 +1,16 @@ -. $PSScriptRoot\Shared.ps1 - -Describe 'Get-GitBranch Tests' { - Context 'Get-GitBranch GIT_DIR Tests' { - It 'Returns GIT_DIR! when in .git dir of the repo' { - $repoRoot = (Resolve-Path $PSScriptRoot\..).Path - Set-Location $repoRoot\.git -ErrorAction Stop - Get-GitBranch | Should BeExactly 'GIT_DIR!' - } - It 'Returns correct path when in a child folder of the .git dir of the repo' { - $repoRoot = (Resolve-Path $PSScriptRoot\..).Path - Set-Location $repoRoot\.git\hooks -ErrorAction Stop - Get-GitBranch | Should BeExactly 'GIT_DIR!' - } - } -} +. $PSScriptRoot\Shared.ps1 + +Describe 'Get-GitBranch Tests' { + Context 'Get-GitBranch GIT_DIR Tests' { + It 'Returns GIT_DIR! when in .git dir of the repo' { + $repoRoot = (Resolve-Path $PSScriptRoot\..).Path + Set-Location $repoRoot\.git -ErrorAction Stop + Get-GitBranch | Should BeExactly 'GIT_DIR!' + } + It 'Returns correct path when in a child folder of the .git dir of the repo' { + $repoRoot = (Resolve-Path $PSScriptRoot\..).Path + Set-Location $repoRoot\.git\hooks -ErrorAction Stop + Get-GitBranch | Should BeExactly 'GIT_DIR!' + } + } +} diff --git a/test/Get-GitDirectory.Tests.ps1 b/test/Get-GitDirectory.Tests.ps1 index 60ec9b700..69318f835 100644 --- a/test/Get-GitDirectory.Tests.ps1 +++ b/test/Get-GitDirectory.Tests.ps1 @@ -1,69 +1,69 @@ -. $PSScriptRoot\Shared.ps1 - -Describe 'Get-GitDiretory Tests' { - Context "Test normal repository" { - BeforeAll { - $origPath = Get-Location - } - AfterAll { - Set-Location $origPath - } - - It 'Returns $null for not a Git repo' { - Set-Location $env:windir - Get-GitDirectory | Should BeNullOrEmpty - } - It 'Returns $null for not a filesystem path' { - Set-Location Cert:\CurrentUser - Get-GitDirectory | Should BeNullOrEmpty - } - It 'Returns correct path when in the root of repo' { - $repoRoot = (Resolve-Path $PSScriptRoot\..).Path - Set-Location $repoRoot - Get-GitDirectory | Should BeExactly (MakeNativePath $repoRoot\.git) - } - It 'Returns correct path when under a child folder of the root of repo' { - $repoRoot = (Resolve-Path $PSScriptRoot\..).Path - Set-Location $PSScriptRoot - Get-GitDirectory | Should BeExactly (Join-Path $repoRoot .git) - } - } - - Context "Test bare repository" { - BeforeAll { - $origPath = Get-Location - $temp = [System.IO.Path]::GetTempPath() - $bareRepoName = "test.git" - $bareRepoPath = Join-Path $temp $bareRepoName - if (Test-Path $bareRepoPath) { - Remove-Item $bareRepoPath -Recurse -Force - } - git init --bare $bareRepoPath - } - AfterAll { - Set-Location $origPath - if (Test-Path $bareRepoPath) { - Remove-Item $bareRepoPath -Recurse -Force - } - } - - It 'Returns correct path when in the root of bare repo' { - Set-Location $bareRepoPath - Get-GitDirectory | Should BeExactly (MakeNativePath $bareRepoPath) - } - It 'Returns correct path when under a child folder of the root of bare repo' { - Set-Location $bareRepoPath\hooks -ErrorVariable Stop - MakeNativePath (Get-GitDirectory) | Should BeExactly $bareRepoPath - } - } - - Context "Test GIT_DIR environment variable" { - AfterAll { - Remove-Item Env:\GIT_DIR -ErrorAction SilentlyContinue - } - It 'Returns the value in GIT_DIR env var' { - $env:GIT_DIR = 'C:\xyzzy\posh-git\.git' - Get-GitDirectory | Should BeExactly $env:GIT_DIR - } - } -} +. $PSScriptRoot\Shared.ps1 + +Describe 'Get-GitDiretory Tests' { + Context "Test normal repository" { + BeforeAll { + $origPath = Get-Location + } + AfterAll { + Set-Location $origPath + } + + It 'Returns $null for not a Git repo' { + Set-Location $env:windir + Get-GitDirectory | Should BeNullOrEmpty + } + It 'Returns $null for not a filesystem path' { + Set-Location Cert:\CurrentUser + Get-GitDirectory | Should BeNullOrEmpty + } + It 'Returns correct path when in the root of repo' { + $repoRoot = (Resolve-Path $PSScriptRoot\..).Path + Set-Location $repoRoot + Get-GitDirectory | Should BeExactly (MakeNativePath $repoRoot\.git) + } + It 'Returns correct path when under a child folder of the root of repo' { + $repoRoot = (Resolve-Path $PSScriptRoot\..).Path + Set-Location $PSScriptRoot + Get-GitDirectory | Should BeExactly (Join-Path $repoRoot .git) + } + } + + Context "Test bare repository" { + BeforeAll { + $origPath = Get-Location + $temp = [System.IO.Path]::GetTempPath() + $bareRepoName = "test.git" + $bareRepoPath = Join-Path $temp $bareRepoName + if (Test-Path $bareRepoPath) { + Remove-Item $bareRepoPath -Recurse -Force + } + git init --bare $bareRepoPath + } + AfterAll { + Set-Location $origPath + if (Test-Path $bareRepoPath) { + Remove-Item $bareRepoPath -Recurse -Force + } + } + + It 'Returns correct path when in the root of bare repo' { + Set-Location $bareRepoPath + Get-GitDirectory | Should BeExactly (MakeNativePath $bareRepoPath) + } + It 'Returns correct path when under a child folder of the root of bare repo' { + Set-Location $bareRepoPath\hooks -ErrorVariable Stop + MakeNativePath (Get-GitDirectory) | Should BeExactly $bareRepoPath + } + } + + Context "Test GIT_DIR environment variable" { + AfterAll { + Remove-Item Env:\GIT_DIR -ErrorAction SilentlyContinue + } + It 'Returns the value in GIT_DIR env var' { + $env:GIT_DIR = 'C:\xyzzy\posh-git\.git' + Get-GitDirectory | Should BeExactly $env:GIT_DIR + } + } +} diff --git a/test/Get-GitStatus.Tests.ps1 b/test/Get-GitStatus.Tests.ps1 index c30349543..5bfc10529 100644 --- a/test/Get-GitStatus.Tests.ps1 +++ b/test/Get-GitStatus.Tests.ps1 @@ -1,414 +1,414 @@ -# For info on Pester mocking see - http://www.powershellmagazine.com/2014/09/30/pester-mock-and-testdrive/ -. $PSScriptRoot\Shared.ps1 - -Describe 'Get-GitStatus Tests' { - Context 'Get-GitStatus Working Directory Tests' { - BeforeAll { - Set-Location $PSScriptRoot - } - - It 'Returns the correct branch name' { - Mock git { - if ($args -contains 'rev-parse') { - $res = Invoke-Expression "git.exe $args" - return $res - } - return @' -## rkeithill/more-status-tests -'@ - } -ModuleName posh-git - - $status = Get-GitStatus - Assert-MockCalled git -ModuleName posh-git #-Exactly 1 - $status.Branch | Should Be "rkeithill/more-status-tests" - } - - - It 'Returns the correct number of added untracked working files' { - Mock git { - if ($args -contains 'rev-parse') { - $res = Invoke-Expression "git.exe $args" - return $res - } - return @' -## master -?? test/Foo.Tests.ps1 -?? test/Bar.Tests.ps1 -'@ -split [System.Environment]::NewLine - } -ModuleName posh-git - - $status = Get-GitStatus - Assert-MockCalled git -ModuleName posh-git #-Exactly 1 - $status.HasIndex | Should Be $false - $status.HasUntracked | Should Be $true - $status.HasWorking | Should Be $true - $status.Working.Added.Count | Should Be 2 - $status.Working.Deleted.Count | Should Be 0 - $status.Working.Modified.Count | Should Be 0 - $status.Working.Unmerged.Count | Should Be 0 - $status.Working.Added[0] | Should Be "test/Foo.Tests.ps1" - $status.Working.Added[1] | Should Be "test/Bar.Tests.ps1" - } - It 'Returns the correct number of added working files' { - Mock git { - if ($args -contains 'rev-parse') { - $res = Invoke-Expression "git.exe $args" - return $res - } - return @' -## master - A test/Foo.Tests.ps1 - A test/Bar.Tests.ps1 -'@ -split [System.Environment]::NewLine - } -ModuleName posh-git - - $status = Get-GitStatus - Assert-MockCalled git -ModuleName posh-git #-Exactly 1 - $status.HasIndex | Should Be $false - $status.HasUntracked | Should Be $true - $status.HasWorking | Should Be $true - $status.Working.Added.Count | Should Be 2 - $status.Working.Deleted.Count | Should Be 0 - $status.Working.Modified.Count | Should Be 0 - $status.Working.Unmerged.Count | Should Be 0 - $status.Working.Added[0] | Should Be "test/Foo.Tests.ps1" - $status.Working.Added[1] | Should Be "test/Bar.Tests.ps1" - } - It 'Returns the correct number of deleted working files' { - Mock git { - if ($args -contains 'rev-parse') { - $res = Invoke-Expression "git.exe $args" - return $res - } - return @' -## master - D test/Foo.Tests.ps1 - D test/Bar.Tests.ps1 -'@ -split [System.Environment]::NewLine - } -ModuleName posh-git - - $status = Get-GitStatus - Assert-MockCalled git -ModuleName posh-git #-Exactly 1 - $status.HasIndex | Should Be $false - $status.HasUntracked | Should Be $false - $status.HasWorking | Should Be $true - $status.Working.Added.Count | Should Be 0 - $status.Working.Deleted.Count | Should Be 2 - $status.Working.Modified.Count | Should Be 0 - $status.Working.Unmerged.Count | Should Be 0 - $status.Working.Deleted[0] | Should Be "test/Foo.Tests.ps1" - $status.Working.Deleted[1] | Should Be "test/Bar.Tests.ps1" - } - It 'Returns the correct number of modified working files' { - Mock git { - if ($args -contains 'rev-parse') { - $res = Invoke-Expression "git.exe $args" - return $res - } - return @' -## master - M test/Foo.Tests.ps1 - M test/Bar.Tests.ps1 -'@ -split [System.Environment]::NewLine - } -ModuleName posh-git - - $status = Get-GitStatus - Assert-MockCalled git -ModuleName posh-git #-Exactly 1 - $status.HasIndex | Should Be $false - $status.HasUntracked | Should Be $false - $status.HasWorking | Should Be $true - $status.Working.Added.Count | Should Be 0 - $status.Working.Deleted.Count | Should Be 0 - $status.Working.Modified.Count | Should Be 2 - $status.Working.Unmerged.Count | Should Be 0 - $status.Working.Modified[0] | Should Be "test/Foo.Tests.ps1" - $status.Working.Modified[1] | Should Be "test/Bar.Tests.ps1" - } - It 'Returns the correct number of unmerged working files' { - Mock git { - if ($args -contains 'rev-parse') { - $res = Invoke-Expression "git.exe $args" - return $res - } - return @' -## master - U test/Foo.Tests.ps1 - U test/Bar.Tests.ps1 -'@ -split [System.Environment]::NewLine - } -ModuleName posh-git - - $status = Get-GitStatus - Assert-MockCalled git -ModuleName posh-git #-Exactly 1 - $status.HasIndex | Should Be $false - $status.HasUntracked | Should Be $false - $status.HasWorking | Should Be $true - $status.Working.Added.Count | Should Be 0 - $status.Working.Deleted.Count | Should Be 0 - $status.Working.Modified.Count | Should Be 0 - $status.Working.Unmerged.Count | Should Be 2 - $status.Working.Unmerged[0] | Should Be "test/Foo.Tests.ps1" - $status.Working.Unmerged[1] | Should Be "test/Bar.Tests.ps1" - } - It 'Returns the correct number of mixed working files' { - Mock git { - if ($args -contains 'rev-parse') { - $res = Invoke-Expression "git.exe $args" - return $res - } - return @' -## master - ? test/Untracked.Tests.ps1 - A test/Added.Tests.ps1 - D test/Deleted.Tests.ps1 - M test/Modified.Tests.ps1 - U test/Unmerged.Tests.ps1 -'@ -split [System.Environment]::NewLine - } -ModuleName posh-git - - $status = Get-GitStatus - Assert-MockCalled git -ModuleName posh-git #-Exactly 1 - $status.HasIndex | Should Be $false - $status.HasUntracked | Should Be $true - $status.HasWorking | Should Be $true - $status.Working.Added.Count | Should Be 2 - $status.Working.Deleted.Count | Should Be 1 - $status.Working.Modified.Count | Should Be 1 - $status.Working.Unmerged.Count | Should Be 1 - $status.Working.Added[0] | Should Be "test/Untracked.Tests.ps1" - $status.Working.Added[1] | Should Be "test/Added.Tests.ps1" - $status.Working.Deleted[0] | Should Be "test/Deleted.Tests.ps1" - $status.Working.Modified[0] | Should Be "test/Modified.Tests.ps1" - $status.Working.Unmerged[0] | Should Be "test/Unmerged.Tests.ps1" - } - - It 'Returns the correct number of added index files' { - Mock git { - if ($args -contains 'rev-parse') { - $res = Invoke-Expression "git.exe $args" - return $res - } - return @' -## master -A test/Foo.Tests.ps1 -A test/Bar.Tests.ps1 -'@ -split [System.Environment]::NewLine - } -ModuleName posh-git - - $status = Get-GitStatus - Assert-MockCalled git -ModuleName posh-git #-Exactly 1 - $status.HasIndex | Should Be $true - $status.HasUntracked | Should Be $false - $status.HasWorking | Should Be $false - $status.Index.Added.Count | Should Be 2 - $status.Index.Deleted.Count | Should Be 0 - $status.Index.Modified.Count | Should Be 0 - $status.Index.Unmerged.Count | Should Be 0 - $status.Index.Added[0] | Should Be "test/Foo.Tests.ps1" - $status.Index.Added[1] | Should Be "test/Bar.Tests.ps1" - } - It 'Returns the correct number of deleted index files' { - Mock git { - if ($args -contains 'rev-parse') { - $res = Invoke-Expression "git.exe $args" - return $res - } - return @' -## master -D test/Foo.Tests.ps1 -D test/Bar.Tests.ps1 -'@ -split [System.Environment]::NewLine - } -ModuleName posh-git - - $status = Get-GitStatus - Assert-MockCalled git -ModuleName posh-git #-Exactly 1 - $status.HasIndex | Should Be $true - $status.HasUntracked | Should Be $false - $status.HasWorking | Should Be $false - $status.Index.Added.Count | Should Be 0 - $status.Index.Deleted.Count | Should Be 2 - $status.Index.Modified.Count | Should Be 0 - $status.Index.Unmerged.Count | Should Be 0 - $status.Index.Deleted[0] | Should Be "test/Foo.Tests.ps1" - $status.Index.Deleted[1] | Should Be "test/Bar.Tests.ps1" - } - It 'Returns the correct number of copied index files' { - Mock git { - if ($args -contains 'rev-parse') { - $res = Invoke-Expression "git.exe $args" - return $res - } - return @' -## master -C test/Foo.Tests.ps1 -C test/Bar.Tests.ps1 -'@ -split [System.Environment]::NewLine - } -ModuleName posh-git - - $status = Get-GitStatus - Assert-MockCalled git -ModuleName posh-git #-Exactly 1 - $status.HasIndex | Should Be $true - $status.HasUntracked | Should Be $false - $status.HasWorking | Should Be $false - $status.Index.Added.Count | Should Be 0 - $status.Index.Deleted.Count | Should Be 0 - $status.Index.Modified.Count | Should Be 2 - $status.Index.Unmerged.Count | Should Be 0 - $status.Index.Modified[0] | Should Be "test/Foo.Tests.ps1" - $status.Index.Modified[1] | Should Be "test/Bar.Tests.ps1" - } - It 'Returns the correct number of modified index files' { - Mock git { - if ($args -contains 'rev-parse') { - $res = Invoke-Expression "git.exe $args" - return $res - } - return @' -## master -M test/Foo.Tests.ps1 -M test/Bar.Tests.ps1 -'@ -split [System.Environment]::NewLine - } -ModuleName posh-git - - $status = Get-GitStatus - Assert-MockCalled git -ModuleName posh-git #-Exactly 1 - $status.HasIndex | Should Be $true - $status.HasUntracked | Should Be $false - $status.HasWorking | Should Be $false - $status.Index.Added.Count | Should Be 0 - $status.Index.Deleted.Count | Should Be 0 - $status.Index.Modified.Count | Should Be 2 - $status.Index.Unmerged.Count | Should Be 0 - $status.Index.Modified[0] | Should Be "test/Foo.Tests.ps1" - $status.Index.Modified[1] | Should Be "test/Bar.Tests.ps1" - } - It 'Returns the correct number of modified index files for a rename' { - Mock git { - if ($args -contains 'rev-parse') { - $res = Invoke-Expression "git.exe $args" - return $res - } - return @' -## master -R README.md -> README2.md -'@ -split [System.Environment]::NewLine - } -ModuleName posh-git - - $status = Get-GitStatus - Assert-MockCalled git -ModuleName posh-git #-Exactly 1 - $status.HasIndex | Should Be $true - $status.HasUntracked | Should Be $false - $status.HasWorking | Should Be $false - $status.Index.Added.Count | Should Be 0 - $status.Index.Deleted.Count | Should Be 0 - $status.Index.Modified.Count | Should Be 1 - $status.Index.Unmerged.Count | Should Be 0 - $status.Index.Modified[0] | Should Be "README.md" - } - It 'Returns the correct number of unmerged index files' { - Mock git { - if ($args -contains 'rev-parse') { - $res = Invoke-Expression "git.exe $args" - return $res - } - return @' -## master -U test/Foo.Tests.ps1 -U test/Bar.Tests.ps1 -'@ -split [System.Environment]::NewLine - } -ModuleName posh-git - - $status = Get-GitStatus - Assert-MockCalled git -ModuleName posh-git #-Exactly 1 - $status.HasIndex | Should Be $true - $status.HasUntracked | Should Be $false - $status.HasWorking | Should Be $false - $status.Index.Added.Count | Should Be 0 - $status.Index.Deleted.Count | Should Be 0 - $status.Index.Modified.Count | Should Be 0 - $status.Index.Unmerged.Count | Should Be 2 - $status.Index.Unmerged[0] | Should Be "test/Foo.Tests.ps1" - $status.Index.Unmerged[1] | Should Be "test/Bar.Tests.ps1" - } - It 'Returns the correct number of mixed index files' { - Mock git { - if ($args -contains 'rev-parse') { - $res = Invoke-Expression "git.exe $args" - return $res - } - return @' -## master -A test/Added.Tests.ps1 -D test/Deleted.Tests.ps1 -C test/Copied.Tests.ps1 -R README.md -> README2.md -M test/Modified.Tests.ps1 -U test/Unmerged.Tests.ps1 -'@ -split [System.Environment]::NewLine - } -ModuleName posh-git - - $status = Get-GitStatus - Assert-MockCalled git -ModuleName posh-git #-Exactly 1 - $status.HasIndex | Should Be $true - $status.HasUntracked | Should Be $false - $status.HasWorking | Should Be $false - $status.Index.Added.Count | Should Be 1 - $status.Index.Deleted.Count | Should Be 1 - $status.Index.Modified.Count | Should Be 3 - $status.Index.Unmerged.Count | Should Be 1 - $status.Index.Added[0] | Should Be "test/Added.Tests.ps1" - $status.Index.Deleted[0] | Should Be "test/Deleted.Tests.ps1" - $status.Index.Modified[0] | Should Be "test/Copied.Tests.ps1" - $status.Index.Modified[1] | Should Be "README.md" - $status.Index.Modified[2] | Should Be "test/Modified.Tests.ps1" - $status.Index.Unmerged[0] | Should Be "test/Unmerged.Tests.ps1" - } - - It 'Returns the correct number of mixed index and working files' { - Mock git { - if ($args -contains 'rev-parse') { - $res = Invoke-Expression "git.exe $args" - return $res - } - return @' -## master -A test/Added.Tests.ps1 -D test/Deleted.Tests.ps1 -C test/Copied.Tests.ps1 -R README.md -> README2.md -M test/Modified.Tests.ps1 -U test/Unmerged.Tests.ps1 - ? test/Untracked.Tests.ps1 - A test/Added.Tests.ps1 - D test/Deleted.Tests.ps1 - M test/Modified.Tests.ps1 - U test/Unmerged.Tests.ps1 -'@ -split [System.Environment]::NewLine - } -ModuleName posh-git - - $status = Get-GitStatus - Assert-MockCalled git -ModuleName posh-git #-Exactly 1 - $status.HasIndex | Should Be $true - $status.HasUntracked | Should Be $true - $status.HasWorking | Should Be $true - $status.Working.Added.Count | Should Be 2 - $status.Working.Deleted.Count | Should Be 1 - $status.Working.Modified.Count | Should Be 1 - $status.Working.Unmerged.Count | Should Be 1 - $status.Working.Added[0] | Should Be "test/Untracked.Tests.ps1" - $status.Working.Added[1] | Should Be "test/Added.Tests.ps1" - $status.Working.Deleted[0] | Should Be "test/Deleted.Tests.ps1" - $status.Working.Modified[0] | Should Be "test/Modified.Tests.ps1" - $status.Working.Unmerged[0] | Should Be "test/Unmerged.Tests.ps1" - $status.Index.Added.Count | Should Be 1 - $status.Index.Deleted.Count | Should Be 1 - $status.Index.Modified.Count | Should Be 3 - $status.Index.Unmerged.Count | Should Be 1 - $status.Index.Added[0] | Should Be "test/Added.Tests.ps1" - $status.Index.Deleted[0] | Should Be "test/Deleted.Tests.ps1" - $status.Index.Modified[0] | Should Be "test/Copied.Tests.ps1" - $status.Index.Modified[1] | Should Be "README.md" - $status.Index.Modified[2] | Should Be "test/Modified.Tests.ps1" - $status.Index.Unmerged[0] | Should Be "test/Unmerged.Tests.ps1" - } - } -} +# For info on Pester mocking see - http://www.powershellmagazine.com/2014/09/30/pester-mock-and-testdrive/ +. $PSScriptRoot\Shared.ps1 + +Describe 'Get-GitStatus Tests' { + Context 'Get-GitStatus Working Directory Tests' { + BeforeAll { + Set-Location $PSScriptRoot + } + + It 'Returns the correct branch name' { + Mock git { + if ($args -contains 'rev-parse') { + $res = Invoke-Expression "git.exe $args" + return $res + } + return @' +## rkeithill/more-status-tests +'@ + } -ModuleName posh-git + + $status = Get-GitStatus + Assert-MockCalled git -ModuleName posh-git #-Exactly 1 + $status.Branch | Should Be "rkeithill/more-status-tests" + } + + + It 'Returns the correct number of added untracked working files' { + Mock git { + if ($args -contains 'rev-parse') { + $res = Invoke-Expression "git.exe $args" + return $res + } + return @' +## master +?? test/Foo.Tests.ps1 +?? test/Bar.Tests.ps1 +'@ -split [System.Environment]::NewLine + } -ModuleName posh-git + + $status = Get-GitStatus + Assert-MockCalled git -ModuleName posh-git #-Exactly 1 + $status.HasIndex | Should Be $false + $status.HasUntracked | Should Be $true + $status.HasWorking | Should Be $true + $status.Working.Added.Count | Should Be 2 + $status.Working.Deleted.Count | Should Be 0 + $status.Working.Modified.Count | Should Be 0 + $status.Working.Unmerged.Count | Should Be 0 + $status.Working.Added[0] | Should Be "test/Foo.Tests.ps1" + $status.Working.Added[1] | Should Be "test/Bar.Tests.ps1" + } + It 'Returns the correct number of added working files' { + Mock git { + if ($args -contains 'rev-parse') { + $res = Invoke-Expression "git.exe $args" + return $res + } + return @' +## master + A test/Foo.Tests.ps1 + A test/Bar.Tests.ps1 +'@ -split [System.Environment]::NewLine + } -ModuleName posh-git + + $status = Get-GitStatus + Assert-MockCalled git -ModuleName posh-git #-Exactly 1 + $status.HasIndex | Should Be $false + $status.HasUntracked | Should Be $true + $status.HasWorking | Should Be $true + $status.Working.Added.Count | Should Be 2 + $status.Working.Deleted.Count | Should Be 0 + $status.Working.Modified.Count | Should Be 0 + $status.Working.Unmerged.Count | Should Be 0 + $status.Working.Added[0] | Should Be "test/Foo.Tests.ps1" + $status.Working.Added[1] | Should Be "test/Bar.Tests.ps1" + } + It 'Returns the correct number of deleted working files' { + Mock git { + if ($args -contains 'rev-parse') { + $res = Invoke-Expression "git.exe $args" + return $res + } + return @' +## master + D test/Foo.Tests.ps1 + D test/Bar.Tests.ps1 +'@ -split [System.Environment]::NewLine + } -ModuleName posh-git + + $status = Get-GitStatus + Assert-MockCalled git -ModuleName posh-git #-Exactly 1 + $status.HasIndex | Should Be $false + $status.HasUntracked | Should Be $false + $status.HasWorking | Should Be $true + $status.Working.Added.Count | Should Be 0 + $status.Working.Deleted.Count | Should Be 2 + $status.Working.Modified.Count | Should Be 0 + $status.Working.Unmerged.Count | Should Be 0 + $status.Working.Deleted[0] | Should Be "test/Foo.Tests.ps1" + $status.Working.Deleted[1] | Should Be "test/Bar.Tests.ps1" + } + It 'Returns the correct number of modified working files' { + Mock git { + if ($args -contains 'rev-parse') { + $res = Invoke-Expression "git.exe $args" + return $res + } + return @' +## master + M test/Foo.Tests.ps1 + M test/Bar.Tests.ps1 +'@ -split [System.Environment]::NewLine + } -ModuleName posh-git + + $status = Get-GitStatus + Assert-MockCalled git -ModuleName posh-git #-Exactly 1 + $status.HasIndex | Should Be $false + $status.HasUntracked | Should Be $false + $status.HasWorking | Should Be $true + $status.Working.Added.Count | Should Be 0 + $status.Working.Deleted.Count | Should Be 0 + $status.Working.Modified.Count | Should Be 2 + $status.Working.Unmerged.Count | Should Be 0 + $status.Working.Modified[0] | Should Be "test/Foo.Tests.ps1" + $status.Working.Modified[1] | Should Be "test/Bar.Tests.ps1" + } + It 'Returns the correct number of unmerged working files' { + Mock git { + if ($args -contains 'rev-parse') { + $res = Invoke-Expression "git.exe $args" + return $res + } + return @' +## master + U test/Foo.Tests.ps1 + U test/Bar.Tests.ps1 +'@ -split [System.Environment]::NewLine + } -ModuleName posh-git + + $status = Get-GitStatus + Assert-MockCalled git -ModuleName posh-git #-Exactly 1 + $status.HasIndex | Should Be $false + $status.HasUntracked | Should Be $false + $status.HasWorking | Should Be $true + $status.Working.Added.Count | Should Be 0 + $status.Working.Deleted.Count | Should Be 0 + $status.Working.Modified.Count | Should Be 0 + $status.Working.Unmerged.Count | Should Be 2 + $status.Working.Unmerged[0] | Should Be "test/Foo.Tests.ps1" + $status.Working.Unmerged[1] | Should Be "test/Bar.Tests.ps1" + } + It 'Returns the correct number of mixed working files' { + Mock git { + if ($args -contains 'rev-parse') { + $res = Invoke-Expression "git.exe $args" + return $res + } + return @' +## master + ? test/Untracked.Tests.ps1 + A test/Added.Tests.ps1 + D test/Deleted.Tests.ps1 + M test/Modified.Tests.ps1 + U test/Unmerged.Tests.ps1 +'@ -split [System.Environment]::NewLine + } -ModuleName posh-git + + $status = Get-GitStatus + Assert-MockCalled git -ModuleName posh-git #-Exactly 1 + $status.HasIndex | Should Be $false + $status.HasUntracked | Should Be $true + $status.HasWorking | Should Be $true + $status.Working.Added.Count | Should Be 2 + $status.Working.Deleted.Count | Should Be 1 + $status.Working.Modified.Count | Should Be 1 + $status.Working.Unmerged.Count | Should Be 1 + $status.Working.Added[0] | Should Be "test/Untracked.Tests.ps1" + $status.Working.Added[1] | Should Be "test/Added.Tests.ps1" + $status.Working.Deleted[0] | Should Be "test/Deleted.Tests.ps1" + $status.Working.Modified[0] | Should Be "test/Modified.Tests.ps1" + $status.Working.Unmerged[0] | Should Be "test/Unmerged.Tests.ps1" + } + + It 'Returns the correct number of added index files' { + Mock git { + if ($args -contains 'rev-parse') { + $res = Invoke-Expression "git.exe $args" + return $res + } + return @' +## master +A test/Foo.Tests.ps1 +A test/Bar.Tests.ps1 +'@ -split [System.Environment]::NewLine + } -ModuleName posh-git + + $status = Get-GitStatus + Assert-MockCalled git -ModuleName posh-git #-Exactly 1 + $status.HasIndex | Should Be $true + $status.HasUntracked | Should Be $false + $status.HasWorking | Should Be $false + $status.Index.Added.Count | Should Be 2 + $status.Index.Deleted.Count | Should Be 0 + $status.Index.Modified.Count | Should Be 0 + $status.Index.Unmerged.Count | Should Be 0 + $status.Index.Added[0] | Should Be "test/Foo.Tests.ps1" + $status.Index.Added[1] | Should Be "test/Bar.Tests.ps1" + } + It 'Returns the correct number of deleted index files' { + Mock git { + if ($args -contains 'rev-parse') { + $res = Invoke-Expression "git.exe $args" + return $res + } + return @' +## master +D test/Foo.Tests.ps1 +D test/Bar.Tests.ps1 +'@ -split [System.Environment]::NewLine + } -ModuleName posh-git + + $status = Get-GitStatus + Assert-MockCalled git -ModuleName posh-git #-Exactly 1 + $status.HasIndex | Should Be $true + $status.HasUntracked | Should Be $false + $status.HasWorking | Should Be $false + $status.Index.Added.Count | Should Be 0 + $status.Index.Deleted.Count | Should Be 2 + $status.Index.Modified.Count | Should Be 0 + $status.Index.Unmerged.Count | Should Be 0 + $status.Index.Deleted[0] | Should Be "test/Foo.Tests.ps1" + $status.Index.Deleted[1] | Should Be "test/Bar.Tests.ps1" + } + It 'Returns the correct number of copied index files' { + Mock git { + if ($args -contains 'rev-parse') { + $res = Invoke-Expression "git.exe $args" + return $res + } + return @' +## master +C test/Foo.Tests.ps1 +C test/Bar.Tests.ps1 +'@ -split [System.Environment]::NewLine + } -ModuleName posh-git + + $status = Get-GitStatus + Assert-MockCalled git -ModuleName posh-git #-Exactly 1 + $status.HasIndex | Should Be $true + $status.HasUntracked | Should Be $false + $status.HasWorking | Should Be $false + $status.Index.Added.Count | Should Be 0 + $status.Index.Deleted.Count | Should Be 0 + $status.Index.Modified.Count | Should Be 2 + $status.Index.Unmerged.Count | Should Be 0 + $status.Index.Modified[0] | Should Be "test/Foo.Tests.ps1" + $status.Index.Modified[1] | Should Be "test/Bar.Tests.ps1" + } + It 'Returns the correct number of modified index files' { + Mock git { + if ($args -contains 'rev-parse') { + $res = Invoke-Expression "git.exe $args" + return $res + } + return @' +## master +M test/Foo.Tests.ps1 +M test/Bar.Tests.ps1 +'@ -split [System.Environment]::NewLine + } -ModuleName posh-git + + $status = Get-GitStatus + Assert-MockCalled git -ModuleName posh-git #-Exactly 1 + $status.HasIndex | Should Be $true + $status.HasUntracked | Should Be $false + $status.HasWorking | Should Be $false + $status.Index.Added.Count | Should Be 0 + $status.Index.Deleted.Count | Should Be 0 + $status.Index.Modified.Count | Should Be 2 + $status.Index.Unmerged.Count | Should Be 0 + $status.Index.Modified[0] | Should Be "test/Foo.Tests.ps1" + $status.Index.Modified[1] | Should Be "test/Bar.Tests.ps1" + } + It 'Returns the correct number of modified index files for a rename' { + Mock git { + if ($args -contains 'rev-parse') { + $res = Invoke-Expression "git.exe $args" + return $res + } + return @' +## master +R README.md -> README2.md +'@ -split [System.Environment]::NewLine + } -ModuleName posh-git + + $status = Get-GitStatus + Assert-MockCalled git -ModuleName posh-git #-Exactly 1 + $status.HasIndex | Should Be $true + $status.HasUntracked | Should Be $false + $status.HasWorking | Should Be $false + $status.Index.Added.Count | Should Be 0 + $status.Index.Deleted.Count | Should Be 0 + $status.Index.Modified.Count | Should Be 1 + $status.Index.Unmerged.Count | Should Be 0 + $status.Index.Modified[0] | Should Be "README.md" + } + It 'Returns the correct number of unmerged index files' { + Mock git { + if ($args -contains 'rev-parse') { + $res = Invoke-Expression "git.exe $args" + return $res + } + return @' +## master +U test/Foo.Tests.ps1 +U test/Bar.Tests.ps1 +'@ -split [System.Environment]::NewLine + } -ModuleName posh-git + + $status = Get-GitStatus + Assert-MockCalled git -ModuleName posh-git #-Exactly 1 + $status.HasIndex | Should Be $true + $status.HasUntracked | Should Be $false + $status.HasWorking | Should Be $false + $status.Index.Added.Count | Should Be 0 + $status.Index.Deleted.Count | Should Be 0 + $status.Index.Modified.Count | Should Be 0 + $status.Index.Unmerged.Count | Should Be 2 + $status.Index.Unmerged[0] | Should Be "test/Foo.Tests.ps1" + $status.Index.Unmerged[1] | Should Be "test/Bar.Tests.ps1" + } + It 'Returns the correct number of mixed index files' { + Mock git { + if ($args -contains 'rev-parse') { + $res = Invoke-Expression "git.exe $args" + return $res + } + return @' +## master +A test/Added.Tests.ps1 +D test/Deleted.Tests.ps1 +C test/Copied.Tests.ps1 +R README.md -> README2.md +M test/Modified.Tests.ps1 +U test/Unmerged.Tests.ps1 +'@ -split [System.Environment]::NewLine + } -ModuleName posh-git + + $status = Get-GitStatus + Assert-MockCalled git -ModuleName posh-git #-Exactly 1 + $status.HasIndex | Should Be $true + $status.HasUntracked | Should Be $false + $status.HasWorking | Should Be $false + $status.Index.Added.Count | Should Be 1 + $status.Index.Deleted.Count | Should Be 1 + $status.Index.Modified.Count | Should Be 3 + $status.Index.Unmerged.Count | Should Be 1 + $status.Index.Added[0] | Should Be "test/Added.Tests.ps1" + $status.Index.Deleted[0] | Should Be "test/Deleted.Tests.ps1" + $status.Index.Modified[0] | Should Be "test/Copied.Tests.ps1" + $status.Index.Modified[1] | Should Be "README.md" + $status.Index.Modified[2] | Should Be "test/Modified.Tests.ps1" + $status.Index.Unmerged[0] | Should Be "test/Unmerged.Tests.ps1" + } + + It 'Returns the correct number of mixed index and working files' { + Mock git { + if ($args -contains 'rev-parse') { + $res = Invoke-Expression "git.exe $args" + return $res + } + return @' +## master +A test/Added.Tests.ps1 +D test/Deleted.Tests.ps1 +C test/Copied.Tests.ps1 +R README.md -> README2.md +M test/Modified.Tests.ps1 +U test/Unmerged.Tests.ps1 + ? test/Untracked.Tests.ps1 + A test/Added.Tests.ps1 + D test/Deleted.Tests.ps1 + M test/Modified.Tests.ps1 + U test/Unmerged.Tests.ps1 +'@ -split [System.Environment]::NewLine + } -ModuleName posh-git + + $status = Get-GitStatus + Assert-MockCalled git -ModuleName posh-git #-Exactly 1 + $status.HasIndex | Should Be $true + $status.HasUntracked | Should Be $true + $status.HasWorking | Should Be $true + $status.Working.Added.Count | Should Be 2 + $status.Working.Deleted.Count | Should Be 1 + $status.Working.Modified.Count | Should Be 1 + $status.Working.Unmerged.Count | Should Be 1 + $status.Working.Added[0] | Should Be "test/Untracked.Tests.ps1" + $status.Working.Added[1] | Should Be "test/Added.Tests.ps1" + $status.Working.Deleted[0] | Should Be "test/Deleted.Tests.ps1" + $status.Working.Modified[0] | Should Be "test/Modified.Tests.ps1" + $status.Working.Unmerged[0] | Should Be "test/Unmerged.Tests.ps1" + $status.Index.Added.Count | Should Be 1 + $status.Index.Deleted.Count | Should Be 1 + $status.Index.Modified.Count | Should Be 3 + $status.Index.Unmerged.Count | Should Be 1 + $status.Index.Added[0] | Should Be "test/Added.Tests.ps1" + $status.Index.Deleted[0] | Should Be "test/Deleted.Tests.ps1" + $status.Index.Modified[0] | Should Be "test/Copied.Tests.ps1" + $status.Index.Modified[1] | Should Be "README.md" + $status.Index.Modified[2] | Should Be "test/Modified.Tests.ps1" + $status.Index.Unmerged[0] | Should Be "test/Unmerged.Tests.ps1" + } + } +} diff --git a/test/GitParamTabExpansion.Tests.ps1 b/test/GitParamTabExpansion.Tests.ps1 index eb10e34b0..00c964186 100644 --- a/test/GitParamTabExpansion.Tests.ps1 +++ b/test/GitParamTabExpansion.Tests.ps1 @@ -1,46 +1,46 @@ -. $PSScriptRoot\Shared.ps1 - -Describe 'ParamsTabExpansion Tests' { - Context 'Push Parameters TabExpansion Tests' { - It 'Tab completes all long push parameters' { - $result = & $module GitTabExpansionInternal 'git push --' - $result -contains '--all' | Should Be $true - $result -contains '--delete' | Should Be $true - $result -contains '--dry-run' | Should Be $true - $result -contains '--exec=' | Should Be $true - $result -contains '--follow-tags' | Should Be $true - $result -contains '--force' | Should Be $true - $result -contains '--force-with-lease=' | Should Be $true - $result -contains '--mirror' | Should Be $true - $result -contains '--no-force-with-lease' | Should Be $true - $result -contains '--no-thin' | Should Be $true - $result -contains '--no-verify' | Should Be $true - $result -contains '--porcelain' | Should Be $true - $result -contains '--progress' | Should Be $true - $result -contains '--prune' | Should Be $true - $result -contains '--quiet' | Should Be $true - $result -contains '--receive-pack=' | Should Be $true - $result -contains '--recurse-submodules=' | Should Be $true - $result -contains '--repo=' | Should Be $true - $result -contains '--set-upstream' | Should Be $true - $result -contains '--tags' | Should Be $true - $result -contains '--thin' | Should Be $true - $result -contains '--verbose' | Should Be $true - $result -contains '--verify' | Should Be $true - } - It 'Tab completes all short push parameters' { - $result = & $module GitTabExpansionInternal 'git push -' - $result -contains '-f' | Should Be $true - $result -contains '-n' | Should Be $true - $result -contains '-q' | Should Be $true - $result -contains '-u' | Should Be $true - $result -contains '-v' | Should Be $true - } - It 'Tab completes push parameters values' { - $result = & $module GitTabExpansionInternal 'git push --recurse-submodules=' - $result -contains '--recurse-submodules=check' | Should Be $true - $result -contains '--recurse-submodules=on-demand' | Should Be $true - } - } -} - +. $PSScriptRoot\Shared.ps1 + +Describe 'ParamsTabExpansion Tests' { + Context 'Push Parameters TabExpansion Tests' { + It 'Tab completes all long push parameters' { + $result = & $module GitTabExpansionInternal 'git push --' + $result -contains '--all' | Should Be $true + $result -contains '--delete' | Should Be $true + $result -contains '--dry-run' | Should Be $true + $result -contains '--exec=' | Should Be $true + $result -contains '--follow-tags' | Should Be $true + $result -contains '--force' | Should Be $true + $result -contains '--force-with-lease=' | Should Be $true + $result -contains '--mirror' | Should Be $true + $result -contains '--no-force-with-lease' | Should Be $true + $result -contains '--no-thin' | Should Be $true + $result -contains '--no-verify' | Should Be $true + $result -contains '--porcelain' | Should Be $true + $result -contains '--progress' | Should Be $true + $result -contains '--prune' | Should Be $true + $result -contains '--quiet' | Should Be $true + $result -contains '--receive-pack=' | Should Be $true + $result -contains '--recurse-submodules=' | Should Be $true + $result -contains '--repo=' | Should Be $true + $result -contains '--set-upstream' | Should Be $true + $result -contains '--tags' | Should Be $true + $result -contains '--thin' | Should Be $true + $result -contains '--verbose' | Should Be $true + $result -contains '--verify' | Should Be $true + } + It 'Tab completes all short push parameters' { + $result = & $module GitTabExpansionInternal 'git push -' + $result -contains '-f' | Should Be $true + $result -contains '-n' | Should Be $true + $result -contains '-q' | Should Be $true + $result -contains '-u' | Should Be $true + $result -contains '-v' | Should Be $true + } + It 'Tab completes push parameters values' { + $result = & $module GitTabExpansionInternal 'git push --recurse-submodules=' + $result -contains '--recurse-submodules=check' | Should Be $true + $result -contains '--recurse-submodules=on-demand' | Should Be $true + } + } +} + diff --git a/test/ModuleManifest.Tests.ps1 b/test/ModuleManifest.Tests.ps1 index 4b7966213..61c61d188 100644 --- a/test/ModuleManifest.Tests.ps1 +++ b/test/ModuleManifest.Tests.ps1 @@ -1,8 +1,8 @@ -. $PSScriptRoot\Shared.ps1 - -Describe 'Module Manifest Tests' { - It 'Passes Test-ModuleManifest' { - Test-ModuleManifest -Path $moduleManifestPath | Should Not BeNullOrEmpty - $? | Should Be $true - } -} +. $PSScriptRoot\Shared.ps1 + +Describe 'Module Manifest Tests' { + It 'Passes Test-ModuleManifest' { + Test-ModuleManifest -Path $moduleManifestPath | Should Not BeNullOrEmpty + $? | Should Be $true + } +} diff --git a/test/Ssh.Tests.ps1 b/test/Ssh.Tests.ps1 index 2da448f84..a5391cf9a 100644 --- a/test/Ssh.Tests.ps1 +++ b/test/Ssh.Tests.ps1 @@ -1,24 +1,24 @@ -. $PSScriptRoot\Shared.ps1 - -Describe 'SSH Function Tests' { - Context 'Get-SshPath Tests' { - It 'Returns the correct default path' { - Get-SshPath | Should BeExactly (MakeNativePath $Home\.ssh\id_rsa) - } - It 'Returns the correct path for a given filename' { - $filename = 'xyzzy-eb2ff0a9-81ee-4983-b32d-530286600a51' - Get-SshPath $filename | Should BeExactly (MakeNativePath $Home\.ssh\$filename) - } - It 'Returns the correct path, given $Env:Home is not defined ($null)' { - $origEnvHome = $Env:HOME - try { - Remove-Item Env:\Home -ErrorAction SilentlyContinue - $Env:Home | Should BeNullOrEmpty - Get-SshPath | Should BeExactly (MakeNativePath $Home\.ssh\id_rsa) - } - finally { - Set-Item Env:\HOME -Value $origEnvHome - } - } - } -} +. $PSScriptRoot\Shared.ps1 + +Describe 'SSH Function Tests' { + Context 'Get-SshPath Tests' { + It 'Returns the correct default path' { + Get-SshPath | Should BeExactly (MakeNativePath $Home\.ssh\id_rsa) + } + It 'Returns the correct path for a given filename' { + $filename = 'xyzzy-eb2ff0a9-81ee-4983-b32d-530286600a51' + Get-SshPath $filename | Should BeExactly (MakeNativePath $Home\.ssh\$filename) + } + It 'Returns the correct path, given $Env:Home is not defined ($null)' { + $origEnvHome = $Env:HOME + try { + Remove-Item Env:\Home -ErrorAction SilentlyContinue + $Env:Home | Should BeNullOrEmpty + Get-SshPath | Should BeExactly (MakeNativePath $Home\.ssh\id_rsa) + } + finally { + Set-Item Env:\HOME -Value $origEnvHome + } + } + } +} diff --git a/test/TabExpansion.Tests.ps1 b/test/TabExpansion.Tests.ps1 index 56ee35f37..191550ab6 100644 --- a/test/TabExpansion.Tests.ps1 +++ b/test/TabExpansion.Tests.ps1 @@ -1,120 +1,120 @@ -. $PSScriptRoot\Shared.ps1 - -Describe 'TabExpansion Tests' { - Context 'Fetch/Push/Pull TabExpansion Tests' { - It 'Tab completes all remotes' { - $result = & $module GitTabExpansionInternal 'git push ' - $result | Should BeExactly (git remote) - } - It 'Tab completes all branches' { - $result = & $module GitTabExpansionInternal 'git push origin ' - $result -contains 'master' | Should Be $true - $result -contains 'origin/master' | Should Be $true - $result -contains 'origin/HEAD' | Should Be $true - } - It 'Tab completes all :branches' { - $result = & $module GitTabExpansionInternal 'git push origin :' - $result -contains ':master' | Should Be $true - } - It 'Tab completes matching remotes' { - $result = & $module GitTabExpansionInternal 'git push or' - $result | Should BeExactly 'origin' - } - It 'Tab completes matching branches' { - $result = & $module GitTabExpansionInternal 'git push origin ma' - $result | Should BeExactly 'master' - } - It 'Tab completes matching remote/branches' { - $result = & $module GitTabExpansionInternal 'git push origin origin/ma' - $result | Should BeExactly 'origin/master' - } - It 'Tab completes matching :branches' { - $result = & $module GitTabExpansionInternal 'git push origin :ma' - $result | Should BeExactly ':master' - } - It 'Tab completes matching ref:branches' { - $result = & $module GitTabExpansionInternal 'git push origin HEAD:ma' - $result | Should BeExactly 'HEAD:master' - } - It 'Tab completes matching +ref:branches' { - $result = & $module GitTabExpansionInternal 'git push origin +HEAD:ma' - $result | Should BeExactly '+HEAD:master' - } - It 'Tab completes matching remote with preceding parameters' { - $result = & $module GitTabExpansionInternal 'git push --follow-tags -u or' - $result | Should BeExactly 'origin' - } - It 'Tab completes all branches with preceding parameters' { - $result = & $module GitTabExpansionInternal 'git push --follow-tags -u origin ' - $result -contains 'master' | Should Be $true - $result -contains 'origin/master' | Should Be $true - $result -contains 'origin/HEAD' | Should Be $true - } - It 'Tab completes matching branch with preceding parameters' { - $result = & $module GitTabExpansionInternal 'git push --follow-tags -u origin ma' - $result | Should BeExactly 'master' - } - It 'Tab completes matching branch with intermixed parameters' { - $result = & $module GitTabExpansionInternal 'git push -u origin --follow-tags ma' - $result | Should BeExactly 'master' - $result = & $module GitTabExpansionInternal 'git push -u origin --follow-tags ma' - $result | Should BeExactly 'master' - } - It 'Tab completes matching ref:branch with intermixed parameters' { - $result = & $module GitTabExpansionInternal 'git push -u origin --follow-tags HEAD:ma' - $result | Should BeExactly 'HEAD:master' - $result = & $module GitTabExpansionInternal 'git push -u origin --follow-tags +HEAD:ma' - $result | Should BeExactly '+HEAD:master' - } - It 'Tab completes matching multiple push ref specs with intermixed parameters' { - $result = & $module GitTabExpansionInternal 'git push -u origin --follow-tags one :two three:four ma' - $result | Should BeExactly 'master' - $result = & $module GitTabExpansionInternal 'git push -u origin --follow-tags one :two three:four --crazy-param ma' - $result | Should BeExactly 'master' - $result = & $module GitTabExpansionInternal 'git push -u origin --follow-tags one :two three:four HEAD:ma' - $result | Should BeExactly 'HEAD:master' - $result = & $module GitTabExpansionInternal 'git push -u origin --follow-tags one :two three:four --crazy-param HEAD:ma' - $result | Should BeExactly 'HEAD:master' - - $result = & $module GitTabExpansionInternal 'git push -u origin --follow-tags one :two three:four +ma' - $result | Should BeExactly '+master' - $result = & $module GitTabExpansionInternal 'git push -u origin --follow-tags one :two three:four --crazy-param +ma' - $result | Should BeExactly '+master' - $result = & $module GitTabExpansionInternal 'git push -u origin --follow-tags one :two three:four +HEAD:ma' - $result | Should BeExactly '+HEAD:master' - $result = & $module GitTabExpansionInternal 'git push -u origin --follow-tags one :two three:four --crazy-param +HEAD:ma' - $result | Should BeExactly '+HEAD:master' - } - It 'Tab complete returns empty result for missing remote' { - $result = & $module GitTabExpansionInternal 'git push zy' - $result | Should BeNullOrEmpty - } - It 'Tab complete returns empty result for missing branch' { - $result = & $module GitTabExpansionInternal 'git push origin zy' - $result | Should BeNullOrEmpty - } - It 'Tab complete returns empty result for missing remotebranch' { - $result = & $module GitTabExpansionInternal 'git fetch origin/zy' - $result | Should BeNullOrEmpty - } - - It 'Tab completes branch names with - and -- in them' { - $branchName = 'branch--for-Pester-tests' - if (git branch --list -q $branchName) { - git branch -D $branchName - } - - git branch $branchName - try { - $result = & $module GitTabExpansionInternal 'git push origin branch-' - $result | Should BeExactly $branchName - - $result = & $module GitTabExpansionInternal 'git push --follow-tags -u origin ' - $result -contains $branchName | Should Be $true - } - finally { - git branch -D $branchName - } - } - } -} +. $PSScriptRoot\Shared.ps1 + +Describe 'TabExpansion Tests' { + Context 'Fetch/Push/Pull TabExpansion Tests' { + It 'Tab completes all remotes' { + $result = & $module GitTabExpansionInternal 'git push ' + $result | Should BeExactly (git remote) + } + It 'Tab completes all branches' { + $result = & $module GitTabExpansionInternal 'git push origin ' + $result -contains 'master' | Should Be $true + $result -contains 'origin/master' | Should Be $true + $result -contains 'origin/HEAD' | Should Be $true + } + It 'Tab completes all :branches' { + $result = & $module GitTabExpansionInternal 'git push origin :' + $result -contains ':master' | Should Be $true + } + It 'Tab completes matching remotes' { + $result = & $module GitTabExpansionInternal 'git push or' + $result | Should BeExactly 'origin' + } + It 'Tab completes matching branches' { + $result = & $module GitTabExpansionInternal 'git push origin ma' + $result | Should BeExactly 'master' + } + It 'Tab completes matching remote/branches' { + $result = & $module GitTabExpansionInternal 'git push origin origin/ma' + $result | Should BeExactly 'origin/master' + } + It 'Tab completes matching :branches' { + $result = & $module GitTabExpansionInternal 'git push origin :ma' + $result | Should BeExactly ':master' + } + It 'Tab completes matching ref:branches' { + $result = & $module GitTabExpansionInternal 'git push origin HEAD:ma' + $result | Should BeExactly 'HEAD:master' + } + It 'Tab completes matching +ref:branches' { + $result = & $module GitTabExpansionInternal 'git push origin +HEAD:ma' + $result | Should BeExactly '+HEAD:master' + } + It 'Tab completes matching remote with preceding parameters' { + $result = & $module GitTabExpansionInternal 'git push --follow-tags -u or' + $result | Should BeExactly 'origin' + } + It 'Tab completes all branches with preceding parameters' { + $result = & $module GitTabExpansionInternal 'git push --follow-tags -u origin ' + $result -contains 'master' | Should Be $true + $result -contains 'origin/master' | Should Be $true + $result -contains 'origin/HEAD' | Should Be $true + } + It 'Tab completes matching branch with preceding parameters' { + $result = & $module GitTabExpansionInternal 'git push --follow-tags -u origin ma' + $result | Should BeExactly 'master' + } + It 'Tab completes matching branch with intermixed parameters' { + $result = & $module GitTabExpansionInternal 'git push -u origin --follow-tags ma' + $result | Should BeExactly 'master' + $result = & $module GitTabExpansionInternal 'git push -u origin --follow-tags ma' + $result | Should BeExactly 'master' + } + It 'Tab completes matching ref:branch with intermixed parameters' { + $result = & $module GitTabExpansionInternal 'git push -u origin --follow-tags HEAD:ma' + $result | Should BeExactly 'HEAD:master' + $result = & $module GitTabExpansionInternal 'git push -u origin --follow-tags +HEAD:ma' + $result | Should BeExactly '+HEAD:master' + } + It 'Tab completes matching multiple push ref specs with intermixed parameters' { + $result = & $module GitTabExpansionInternal 'git push -u origin --follow-tags one :two three:four ma' + $result | Should BeExactly 'master' + $result = & $module GitTabExpansionInternal 'git push -u origin --follow-tags one :two three:four --crazy-param ma' + $result | Should BeExactly 'master' + $result = & $module GitTabExpansionInternal 'git push -u origin --follow-tags one :two three:four HEAD:ma' + $result | Should BeExactly 'HEAD:master' + $result = & $module GitTabExpansionInternal 'git push -u origin --follow-tags one :two three:four --crazy-param HEAD:ma' + $result | Should BeExactly 'HEAD:master' + + $result = & $module GitTabExpansionInternal 'git push -u origin --follow-tags one :two three:four +ma' + $result | Should BeExactly '+master' + $result = & $module GitTabExpansionInternal 'git push -u origin --follow-tags one :two three:four --crazy-param +ma' + $result | Should BeExactly '+master' + $result = & $module GitTabExpansionInternal 'git push -u origin --follow-tags one :two three:four +HEAD:ma' + $result | Should BeExactly '+HEAD:master' + $result = & $module GitTabExpansionInternal 'git push -u origin --follow-tags one :two three:four --crazy-param +HEAD:ma' + $result | Should BeExactly '+HEAD:master' + } + It 'Tab complete returns empty result for missing remote' { + $result = & $module GitTabExpansionInternal 'git push zy' + $result | Should BeNullOrEmpty + } + It 'Tab complete returns empty result for missing branch' { + $result = & $module GitTabExpansionInternal 'git push origin zy' + $result | Should BeNullOrEmpty + } + It 'Tab complete returns empty result for missing remotebranch' { + $result = & $module GitTabExpansionInternal 'git fetch origin/zy' + $result | Should BeNullOrEmpty + } + + It 'Tab completes branch names with - and -- in them' { + $branchName = 'branch--for-Pester-tests' + if (git branch --list -q $branchName) { + git branch -D $branchName + } + + git branch $branchName + try { + $result = & $module GitTabExpansionInternal 'git push origin branch-' + $result | Should BeExactly $branchName + + $result = & $module GitTabExpansionInternal 'git push --follow-tags -u origin ' + $result -contains $branchName | Should Be $true + } + finally { + git branch -D $branchName + } + } + } +} diff --git a/test/Utils.Tests.ps1 b/test/Utils.Tests.ps1 index 2004b4e62..d2c0964f8 100644 --- a/test/Utils.Tests.ps1 +++ b/test/Utils.Tests.ps1 @@ -1,156 +1,156 @@ -. $PSScriptRoot\Shared.ps1 -. $modulePath\Utils.ps1 - -Describe 'Utils Function Tests' { - Context 'Add-PoshGitToProfile Tests' { - BeforeAll { - $newLine = [System.Environment]::NewLine - } - BeforeEach { - $profilePath = [System.IO.Path]::GetTempFileName() - } - AfterEach { - Remove-Item $profilePath -ErrorAction SilentlyContinue - } - It 'Creates profile file if it does not exist that imports absolute path' { - Mock Get-PSModulePath { - return @() - } - Remove-Item -LiteralPath $profilePath - Test-Path -LiteralPath $profilePath | Should Be $false - - Add-PoshGitToProfile $profilePath - - Test-Path -LiteralPath $profilePath | Should Be $true - Get-FileEncoding $profilePath | Should Be 'utf8' - $content = Get-Content $profilePath - $content.Count | Should Be 2 - @($content)[1] | Should BeExactly "Import-Module '$modulePath\posh-git.psd1'" - } - It 'Creates profile file if it does not exist that imports from module path' { - $parentDir = Split-Path $profilePath -Parent - Mock Get-PSModulePath { - return @( - 'C:\Users\Keith\Documents\WindowsPowerShell\Modules', - 'C:\Program Files\WindowsPowerShell\Modules', - 'C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\', - "$parentDir") - } - - Remove-Item -LiteralPath $profilePath - Test-Path -LiteralPath $profilePath | Should Be $false - - Add-PoshGitToProfile $profilePath $parentDir - - Test-Path -LiteralPath $profilePath | Should Be $true - Get-FileEncoding $profilePath | Should Be 'utf8' - $content = Get-Content $profilePath - $content.Count | Should Be 2 - @($content)[1] | Should BeExactly "Import-Module posh-git" - } - It 'Does not modify profile that already refers to posh-git' { - $profileContent = @' -Import-Module PSCX -Import-Module posh-git -'@ - Set-Content $profilePath -Value $profileContent -Encoding Ascii - - $output = Add-PoshGitToProfile $profilePath 3>&1 - - $output[1] | Should Match 'posh-git appears' - Get-FileEncoding $profilePath | Should Be 'ascii' - $content = Get-Content $profilePath - $content.Count | Should Be 2 - $content -join $newLine | Should BeExactly $profileContent - } - It 'Adds import from PSModulePath on existing (Unicode) profile file correctly' { - $profileContent = @' -Import-Module PSCX - -New-Alias pscore C:\Users\Keith\GitHub\rkeithhill\PowerShell\src\powershell-win-core\bin\Debug\netcoreapp1.1\win10-x64\powershell.exe -'@ - Set-Content $profilePath -Value $profileContent -Encoding Unicode - - Add-PoshGitToProfile $profilePath (Split-Path $profilePath -Parent) - - Test-Path -LiteralPath $profilePath | Should Be $true - Get-FileEncoding $profilePath | Should Be 'unicode' - $content = Get-Content $profilePath - $content.Count | Should Be 5 - $profileContent += "${newLine}${newLine}Import-Module posh-git" - $content -join $newLine | Should BeExactly $profileContent - } - } - - Context 'Test-PoshGitImportedInScript Tests' { - BeforeEach { - $profilePath = [System.IO.Path]::GetTempFileName() - } - AfterEach { - Remove-Item $profilePath -ErrorAction SilentlyContinue - } - It 'Detects Import-Module posh-git in profile script' { - $profileContent = "Import-Module posh-git" - Set-Content $profilePath -Value $profileContent -Encoding Unicode - Test-PoshGitImportedInScript $profilePath | Should Be $true - } - It 'Detects chocolatey installed line in profile script' { - $profileContent = ". 'C:\tools\poshgit\dahlbyk-posh-git-18d600a\profile.example.ps1" - Set-Content $profilePath -Value $profileContent -Encoding Unicode - Test-PoshGitImportedInScript $profilePath | Should Be $true - } - It 'Returns false when one-line profile script does not import posh-git' { - $profileContent = "# Test" - Set-Content $profilePath -Value $profileContent -Encoding Unicode - Test-PoshGitImportedInScript $profilePath | Should Be $false - } - It 'Returns false when profile script does not import posh-git' { - $profileContent = "Import-Module Pscx`nImport-Module platyPS`nImport-Module Plaster" - Set-Content $profilePath -Value $profileContent -Encoding Unicode - Test-PoshGitImportedInScript $profilePath | Should Be $false - } - } - - Context 'Test-InPSModulePath Tests' { - It 'Returns false for install not under any PSModulePaths' { - Mock Get-PSModulePath { } - $path = "C:\Users\Keith\Documents\WindowsPowerShell\Modules\posh-git\0.7.0\" - Test-InPSModulePath $path | Should Be $false - Assert-MockCalled Get-PSModulePath - } - It 'Returns true for install under single PSModulePath' { - Mock Get-PSModulePath { - return 'C:\Users\Keith\Documents\WindowsPowerShell\Modules\posh-git\' - } - $path = "C:\Users\Keith\Documents\WindowsPowerShell\Modules\posh-git\0.7.0" - Test-InPSModulePath $path | Should Be $true - Assert-MockCalled Get-PSModulePath - } - It 'Returns true for install under multiple PSModulePaths' { - Mock Get-PSModulePath { - return 'C:\Users\Keith\Documents\WindowsPowerShell\Modules\posh-git\0.7.0\', - 'C:\Users\Keith\Documents\WindowsPowerShell\Modules\posh-git\0.6.1.20160330\' - } - $path = "C:\Users\Keith\Documents\WindowsPowerShell\Modules\posh-git\0.7.0" - Test-InPSModulePath $path | Should Be $true - Assert-MockCalled Get-PSModulePath - } - It 'Returns false when current posh-git module location is not under PSModulePaths' { - Mock Get-PSModulePath { - return 'C:\Users\Keith\Documents\WindowsPowerShell\Modules\posh-git\0.7.0\', - 'C:\Users\Keith\Documents\WindowsPowerShell\Modules\posh-git\0.6.1.20160330\' - } - $path = "C:\tools\posh-git\dahlbyk-posh-git-18d600a" - Test-InPSModulePath $path | Should Be $false - Assert-MockCalled Get-PSModulePath - } - It 'Returns false when current posh-git module location is under PSModulePath, but in a src directory' { - Mock Get-PSModulePath { - return 'C:\GitHub' - } - $path = "C:\GitHub\posh-git\src" - Test-InPSModulePath $path | Should Be $false - Assert-MockCalled Get-PSModulePath - } - } -} +. $PSScriptRoot\Shared.ps1 +. $modulePath\Utils.ps1 + +Describe 'Utils Function Tests' { + Context 'Add-PoshGitToProfile Tests' { + BeforeAll { + $newLine = [System.Environment]::NewLine + } + BeforeEach { + $profilePath = [System.IO.Path]::GetTempFileName() + } + AfterEach { + Remove-Item $profilePath -ErrorAction SilentlyContinue + } + It 'Creates profile file if it does not exist that imports absolute path' { + Mock Get-PSModulePath { + return @() + } + Remove-Item -LiteralPath $profilePath + Test-Path -LiteralPath $profilePath | Should Be $false + + Add-PoshGitToProfile $profilePath + + Test-Path -LiteralPath $profilePath | Should Be $true + Get-FileEncoding $profilePath | Should Be 'utf8' + $content = Get-Content $profilePath + $content.Count | Should Be 2 + @($content)[1] | Should BeExactly "Import-Module '$modulePath\posh-git.psd1'" + } + It 'Creates profile file if it does not exist that imports from module path' { + $parentDir = Split-Path $profilePath -Parent + Mock Get-PSModulePath { + return @( + 'C:\Users\Keith\Documents\WindowsPowerShell\Modules', + 'C:\Program Files\WindowsPowerShell\Modules', + 'C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\', + "$parentDir") + } + + Remove-Item -LiteralPath $profilePath + Test-Path -LiteralPath $profilePath | Should Be $false + + Add-PoshGitToProfile $profilePath $parentDir + + Test-Path -LiteralPath $profilePath | Should Be $true + Get-FileEncoding $profilePath | Should Be 'utf8' + $content = Get-Content $profilePath + $content.Count | Should Be 2 + @($content)[1] | Should BeExactly "Import-Module posh-git" + } + It 'Does not modify profile that already refers to posh-git' { + $profileContent = @' +Import-Module PSCX +Import-Module posh-git +'@ + Set-Content $profilePath -Value $profileContent -Encoding Ascii + + $output = Add-PoshGitToProfile $profilePath 3>&1 + + $output[1] | Should Match 'posh-git appears' + Get-FileEncoding $profilePath | Should Be 'ascii' + $content = Get-Content $profilePath + $content.Count | Should Be 2 + $content -join $newLine | Should BeExactly $profileContent + } + It 'Adds import from PSModulePath on existing (Unicode) profile file correctly' { + $profileContent = @' +Import-Module PSCX + +New-Alias pscore C:\Users\Keith\GitHub\rkeithhill\PowerShell\src\powershell-win-core\bin\Debug\netcoreapp1.1\win10-x64\powershell.exe +'@ + Set-Content $profilePath -Value $profileContent -Encoding Unicode + + Add-PoshGitToProfile $profilePath (Split-Path $profilePath -Parent) + + Test-Path -LiteralPath $profilePath | Should Be $true + Get-FileEncoding $profilePath | Should Be 'unicode' + $content = Get-Content $profilePath + $content.Count | Should Be 5 + $profileContent += "${newLine}${newLine}Import-Module posh-git" + $content -join $newLine | Should BeExactly $profileContent + } + } + + Context 'Test-PoshGitImportedInScript Tests' { + BeforeEach { + $profilePath = [System.IO.Path]::GetTempFileName() + } + AfterEach { + Remove-Item $profilePath -ErrorAction SilentlyContinue + } + It 'Detects Import-Module posh-git in profile script' { + $profileContent = "Import-Module posh-git" + Set-Content $profilePath -Value $profileContent -Encoding Unicode + Test-PoshGitImportedInScript $profilePath | Should Be $true + } + It 'Detects chocolatey installed line in profile script' { + $profileContent = ". 'C:\tools\poshgit\dahlbyk-posh-git-18d600a\profile.example.ps1" + Set-Content $profilePath -Value $profileContent -Encoding Unicode + Test-PoshGitImportedInScript $profilePath | Should Be $true + } + It 'Returns false when one-line profile script does not import posh-git' { + $profileContent = "# Test" + Set-Content $profilePath -Value $profileContent -Encoding Unicode + Test-PoshGitImportedInScript $profilePath | Should Be $false + } + It 'Returns false when profile script does not import posh-git' { + $profileContent = "Import-Module Pscx`nImport-Module platyPS`nImport-Module Plaster" + Set-Content $profilePath -Value $profileContent -Encoding Unicode + Test-PoshGitImportedInScript $profilePath | Should Be $false + } + } + + Context 'Test-InPSModulePath Tests' { + It 'Returns false for install not under any PSModulePaths' { + Mock Get-PSModulePath { } + $path = "C:\Users\Keith\Documents\WindowsPowerShell\Modules\posh-git\0.7.0\" + Test-InPSModulePath $path | Should Be $false + Assert-MockCalled Get-PSModulePath + } + It 'Returns true for install under single PSModulePath' { + Mock Get-PSModulePath { + return 'C:\Users\Keith\Documents\WindowsPowerShell\Modules\posh-git\' + } + $path = "C:\Users\Keith\Documents\WindowsPowerShell\Modules\posh-git\0.7.0" + Test-InPSModulePath $path | Should Be $true + Assert-MockCalled Get-PSModulePath + } + It 'Returns true for install under multiple PSModulePaths' { + Mock Get-PSModulePath { + return 'C:\Users\Keith\Documents\WindowsPowerShell\Modules\posh-git\0.7.0\', + 'C:\Users\Keith\Documents\WindowsPowerShell\Modules\posh-git\0.6.1.20160330\' + } + $path = "C:\Users\Keith\Documents\WindowsPowerShell\Modules\posh-git\0.7.0" + Test-InPSModulePath $path | Should Be $true + Assert-MockCalled Get-PSModulePath + } + It 'Returns false when current posh-git module location is not under PSModulePaths' { + Mock Get-PSModulePath { + return 'C:\Users\Keith\Documents\WindowsPowerShell\Modules\posh-git\0.7.0\', + 'C:\Users\Keith\Documents\WindowsPowerShell\Modules\posh-git\0.6.1.20160330\' + } + $path = "C:\tools\posh-git\dahlbyk-posh-git-18d600a" + Test-InPSModulePath $path | Should Be $false + Assert-MockCalled Get-PSModulePath + } + It 'Returns false when current posh-git module location is under PSModulePath, but in a src directory' { + Mock Get-PSModulePath { + return 'C:\GitHub' + } + $path = "C:\GitHub\posh-git\src" + Test-InPSModulePath $path | Should Be $false + Assert-MockCalled Get-PSModulePath + } + } +} diff --git a/test/git-help.txt b/test/git-help.txt index 6211766b4..ef0610816 100644 --- a/test/git-help.txt +++ b/test/git-help.txt @@ -1,42 +1,42 @@ -usage: git [--version] [--help] [-C ] [-c name=value] - [--exec-path[=]] [--html-path] [--man-path] [--info-path] - [-p | --paginate | --no-pager] [--no-replace-objects] [--bare] - [--git-dir=] [--work-tree=] [--namespace=] - [] - -These are common Git commands used in various situations: - -start a working area (see also: git help tutorial) - clone Clone a repository into a new directory - init Create an empty Git repository or reinitialize an existing one - -work on the current change (see also: git help everyday) - add Add file contents to the index - mv Move or rename a file, a directory, or a symlink - reset Reset current HEAD to the specified state - rm Remove files from the working tree and from the index - -examine the history and state (see also: git help revisions) - bisect Use binary search to find the commit that introduced a bug - grep Print lines matching a pattern - log Show commit logs - show Show various types of objects - status Show the working tree status - -grow, mark and tweak your common history - branch List, create, or delete branches - checkout Switch branches or restore working tree files - commit Record changes to the repository - diff Show changes between commits, commit and working tree, etc - merge Join two or more development histories together - rebase Reapply commits on top of another base tip - tag Create, list, delete or verify a tag object signed with GPG - -collaborate (see also: git help workflows) - fetch Download objects and refs from another repository - pull Fetch from and integrate with another repository or a local branch - push Update remote refs along with associated objects - -'git help -a' and 'git help -g' list available subcommands and some -concept guides. See 'git help ' or 'git help ' -to read about a specific subcommand or concept. +usage: git [--version] [--help] [-C ] [-c name=value] + [--exec-path[=]] [--html-path] [--man-path] [--info-path] + [-p | --paginate | --no-pager] [--no-replace-objects] [--bare] + [--git-dir=] [--work-tree=] [--namespace=] + [] + +These are common Git commands used in various situations: + +start a working area (see also: git help tutorial) + clone Clone a repository into a new directory + init Create an empty Git repository or reinitialize an existing one + +work on the current change (see also: git help everyday) + add Add file contents to the index + mv Move or rename a file, a directory, or a symlink + reset Reset current HEAD to the specified state + rm Remove files from the working tree and from the index + +examine the history and state (see also: git help revisions) + bisect Use binary search to find the commit that introduced a bug + grep Print lines matching a pattern + log Show commit logs + show Show various types of objects + status Show the working tree status + +grow, mark and tweak your common history + branch List, create, or delete branches + checkout Switch branches or restore working tree files + commit Record changes to the repository + diff Show changes between commits, commit and working tree, etc + merge Join two or more development histories together + rebase Reapply commits on top of another base tip + tag Create, list, delete or verify a tag object signed with GPG + +collaborate (see also: git help workflows) + fetch Download objects and refs from another repository + pull Fetch from and integrate with another repository or a local branch + push Update remote refs along with associated objects + +'git help -a' and 'git help -g' list available subcommands and some +concept guides. See 'git help ' or 'git help ' +to read about a specific subcommand or concept. diff --git a/test/testDebugHarness.ps1 b/test/testDebugHarness.ps1 index 43e933d25..56b32b1aa 100644 --- a/test/testDebugHarness.ps1 +++ b/test/testDebugHarness.ps1 @@ -1,2 +1,2 @@ - -Invoke-Pester $PSScriptRoot + +Invoke-Pester $PSScriptRoot