diff --git a/images/dark/icon-add.svg b/images/dark/icon-add.svg
index 3475c1e..5b11d1f 100644
--- a/images/dark/icon-add.svg
+++ b/images/dark/icon-add.svg
@@ -1 +1,5 @@
-Layer 1
\ No newline at end of file
+
+
+
+
+
\ No newline at end of file
diff --git a/images/dark/icon-branch.svg b/images/dark/icon-branch.svg
new file mode 100644
index 0000000..41efea1
--- /dev/null
+++ b/images/dark/icon-branch.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/images/dark/icon-commit.svg b/images/dark/icon-commit.svg
index 2e6dc1c..ecd6949 100644
--- a/images/dark/icon-commit.svg
+++ b/images/dark/icon-commit.svg
@@ -1,4 +1,4 @@
-
-
+
+
\ No newline at end of file
diff --git a/images/dark/icon-download.svg b/images/dark/icon-download.svg
new file mode 100644
index 0000000..12ad689
--- /dev/null
+++ b/images/dark/icon-download.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/images/dark/icon-history.svg b/images/dark/icon-history.svg
new file mode 100644
index 0000000..a31288d
--- /dev/null
+++ b/images/dark/icon-history.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/images/dark/icon-refresh.svg b/images/dark/icon-refresh.svg
index c916dd2..4a3e34c 100644
--- a/images/dark/icon-refresh.svg
+++ b/images/dark/icon-refresh.svg
@@ -1,5 +1,4 @@
-
-
-
+
+
\ No newline at end of file
diff --git a/images/dark/icon-remote.svg b/images/dark/icon-remote.svg
new file mode 100644
index 0000000..bf72ab2
--- /dev/null
+++ b/images/dark/icon-remote.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/images/dark/icon-repo.svg b/images/dark/icon-repo.svg
new file mode 100644
index 0000000..b1f8746
--- /dev/null
+++ b/images/dark/icon-repo.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/images/dark/icon-search.svg b/images/dark/icon-search.svg
new file mode 100644
index 0000000..25db6f2
--- /dev/null
+++ b/images/dark/icon-search.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/images/dark/icon-stash.svg b/images/dark/icon-stash.svg
new file mode 100644
index 0000000..47b80ce
--- /dev/null
+++ b/images/dark/icon-stash.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/images/dark/icon-upload.svg b/images/dark/icon-upload.svg
new file mode 100644
index 0000000..7520647
--- /dev/null
+++ b/images/dark/icon-upload.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/images/light/icon-add.svg b/images/light/icon-add.svg
index bdecdb0..20a5a3e 100644
--- a/images/light/icon-add.svg
+++ b/images/light/icon-add.svg
@@ -1 +1,5 @@
-Layer 1
\ No newline at end of file
+
+
+
+
+
\ No newline at end of file
diff --git a/images/light/icon-branch.svg b/images/light/icon-branch.svg
new file mode 100644
index 0000000..c33314e
--- /dev/null
+++ b/images/light/icon-branch.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/images/light/icon-commit.svg b/images/light/icon-commit.svg
index 4724a89..82bf174 100644
--- a/images/light/icon-commit.svg
+++ b/images/light/icon-commit.svg
@@ -1,4 +1,4 @@
-
-
+
+
\ No newline at end of file
diff --git a/images/light/icon-download.svg b/images/light/icon-download.svg
new file mode 100644
index 0000000..f271a00
--- /dev/null
+++ b/images/light/icon-download.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/images/light/icon-history.svg b/images/light/icon-history.svg
new file mode 100644
index 0000000..2851e5c
--- /dev/null
+++ b/images/light/icon-history.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/images/light/icon-refresh.svg b/images/light/icon-refresh.svg
index e11b8f9..f6674b3 100644
--- a/images/light/icon-refresh.svg
+++ b/images/light/icon-refresh.svg
@@ -1,5 +1,4 @@
-
-
-
+
+
\ No newline at end of file
diff --git a/images/light/icon-remote.svg b/images/light/icon-remote.svg
new file mode 100644
index 0000000..da57739
--- /dev/null
+++ b/images/light/icon-remote.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/images/light/icon-repo.svg b/images/light/icon-repo.svg
new file mode 100644
index 0000000..4e2368e
--- /dev/null
+++ b/images/light/icon-repo.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/images/light/icon-search.svg b/images/light/icon-search.svg
new file mode 100644
index 0000000..a9ac03c
--- /dev/null
+++ b/images/light/icon-search.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/images/light/icon-stash.svg b/images/light/icon-stash.svg
new file mode 100644
index 0000000..2172d4f
--- /dev/null
+++ b/images/light/icon-stash.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/images/light/icon-upload.svg b/images/light/icon-upload.svg
new file mode 100644
index 0000000..a0bb4a8
--- /dev/null
+++ b/images/light/icon-upload.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/package.json b/package.json
index dee7193..949ed56 100644
--- a/package.json
+++ b/package.json
@@ -413,20 +413,34 @@
"default": null,
"description": "Specifies how all absolute dates will be formatted by default\nSee https://momentjs.com/docs/#/displaying/format/ for valid formats"
},
- "gitlens.fileHistoryExplorer.commitFormat": {
+ "gitlens.gitExplorer.view": {
+ "type": "string",
+ "default": "repository",
+ "enum": [
+ "history",
+ "repository"
+ ],
+ "description": "Specifies the starting view (mode) of the `GitLens` custom view\n `history` - shows the commit history of the active file\n `repository` - shows a repository explorer"
+ },
+ "gitlens.gitExplorer.commitFormat": {
"type": "string",
"default": "${message} \u00a0\u2022\u00a0 ${authorAgo} \u00a0\u2022\u00a0 ${id}",
- "description": "Specifies the format of committed changes in the `Git File History` explorer\nAvailable tokens\n ${id} - commit id\n ${author} - commit author\n ${message} - commit message\n ${ago} - relative commit date (e.g. 1 day ago)\n ${date} - formatted commit date (format specified by `gitlens.statusBar.dateFormat`)\n ${authorAgo} - commit author, relative commit date\nSee https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting"
+ "description": "Specifies the format of committed changes in the `GitLens` custom view\nAvailable tokens\n ${id} - commit id\n ${author} - commit author\n ${message} - commit message\n ${ago} - relative commit date (e.g. 1 day ago)\n ${date} - formatted commit date (format specified by `gitlens.statusBar.dateFormat`)\n ${authorAgo} - commit author, relative commit date\nSee https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting"
+ },
+ "gitlens.gitExplorer.commitFileFormat": {
+ "type": "string",
+ "default": "${filePath}",
+ "description": "Specifies the format of a committed file in the `GitLens` custom view\nAvailable tokens\n ${file} - file name\n ${filePath} - file name and path\n ${path} - file path"
},
- "gitlens.stashExplorer.stashFormat": {
+ "gitlens.gitExplorer.stashFormat": {
"type": "string",
"default": "${message}",
- "description": "Specifies the format of stashed changes in the `Git Stashes` explorer\nAvailable tokens\n ${id} - commit id\n ${author} - commit author\n ${message} - commit message\n ${ago} - relative commit date (e.g. 1 day ago)\n ${date} - formatted commit date (format specified by `gitlens.statusBar.dateFormat`)\n ${authorAgo} - commit author, relative commit date\nSee https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting"
+ "description": "Specifies the format of stashed changes in the `GitLens` custom view\nAvailable tokens\n ${id} - commit id\n ${author} - commit author\n ${message} - commit message\n ${ago} - relative commit date (e.g. 1 day ago)\n ${date} - formatted commit date (format specified by `gitlens.statusBar.dateFormat`)\n ${authorAgo} - commit author, relative commit date\nSee https://github.com/eamodio/vscode-gitlens/wiki/Advanced-Formatting for advanced formatting"
},
- "gitlens.stashExplorer.stashFileFormat": {
+ "gitlens.gitExplorer.stashFileFormat": {
"type": "string",
"default": "${filePath}",
- "description": "Specifies the format of a stashed file in the `Git Stashes` explorer\nAvailable tokens\n ${file} - file name\n ${filePath} - file name and path\n ${path} - file path"
+ "description": "Specifies the format of a stashed file in the `GitLens` custom view\nAvailable tokens\n ${file} - file name\n ${filePath} - file name and path\n ${path} - file path"
},
"gitlens.statusBar.enabled": {
"type": "boolean",
@@ -772,17 +786,17 @@
},
{
"command": "gitlens.diffWithNext",
- "title": "Compare File with Next Commit",
+ "title": "Compare File with Next Revision",
"category": "GitLens"
},
{
"command": "gitlens.diffWithPrevious",
- "title": "Compare File with Previous",
+ "title": "Compare File with Previous Revision",
"category": "GitLens"
},
{
"command": "gitlens.diffLineWithPrevious",
- "title": "Compare Line Commit with Previous",
+ "title": "Compare Line Revision with Previous",
"category": "GitLens"
},
{
@@ -792,12 +806,12 @@
},
{
"command": "gitlens.diffWithWorking",
- "title": "Compare File with Working Tree",
+ "title": "Compare File with Working Revision",
"category": "GitLens"
},
{
"command": "gitlens.diffLineWithWorking",
- "title": "Compare Line Commit with Working Tree",
+ "title": "Compare Line Revision with Working",
"category": "GitLens"
},
{
@@ -864,7 +878,11 @@
{
"command": "gitlens.showCommitSearch",
"title": "Search Commits",
- "category": "GitLens"
+ "category": "GitLens",
+ "icon": {
+ "dark": "images/dark/icon-search.svg",
+ "light": "images/light/icon-search.svg"
+ }
},
{
"command": "gitlens.showFileHistory",
@@ -985,66 +1003,56 @@
}
},
{
- "command": "gitlens.fileHistoryExplorer.refresh",
- "title": "Refresh",
+ "command": "gitlens.gitExplorer.switchToHistoryView",
+ "title": "Switch to History View",
"category": "GitLens",
"icon": {
- "dark": "images/dark/icon-refresh.svg",
- "light": "images/light/icon-refresh.svg"
+ "dark": "images/dark/icon-history.svg",
+ "light": "images/light/icon-history.svg"
}
},
{
- "command": "gitlens.fileHistoryExplorer.openChanges",
- "title": "Open Changes",
- "category": "GitLens"
+ "command": "gitlens.gitExplorer.switchToRepositoryView",
+ "title": "Switch to Repository View",
+ "category": "GitLens",
+ "icon": {
+ "dark": "images/dark/icon-repo.svg",
+ "light": "images/light/icon-repo.svg"
+ }
},
{
- "command": "gitlens.fileHistoryExplorer.openFile",
- "title": "Open File",
+ "command": "gitlens.gitExplorer.openChanges",
+ "title": "Open Changes",
"category": "GitLens"
},
{
- "command": "gitlens.fileHistoryExplorer.openFileRevision",
- "title": "Open File Revision",
+ "command": "gitlens.gitExplorer.openChangesWithWorking",
+ "title": "Open Changes with Working Tree",
"category": "GitLens"
},
{
- "command": "gitlens.fileHistoryExplorer.openFileInRemote",
- "title": "Open File in Remote",
+ "command": "gitlens.gitExplorer.openFile",
+ "title": "Open File",
"category": "GitLens"
},
{
- "command": "gitlens.fileHistoryExplorer.openFileRevisionInRemote",
- "title": "Open File Revision in Remote",
+ "command": "gitlens.gitExplorer.openFileRevision",
+ "title": "Open Revision",
"category": "GitLens"
},
{
- "command": "gitlens.stashExplorer.refresh",
- "title": "Refresh",
- "category": "GitLens",
- "icon": {
- "dark": "images/dark/icon-refresh.svg",
- "light": "images/light/icon-refresh.svg"
- }
- },
- {
- "command": "gitlens.stashExplorer.openChanges",
- "title": "Open Changes",
+ "command": "gitlens.gitExplorer.openFileRevisionInRemote",
+ "title": "Open Revision in Remote",
"category": "GitLens"
},
{
- "command": "gitlens.stashExplorer.openFile",
- "title": "Open File",
+ "command": "gitlens.gitExplorer.openChangedFiles",
+ "title": "Open Files",
"category": "GitLens"
},
{
- "command": "gitlens.stashExplorer.openStashedFile",
- "title": "Open Stashed File",
- "category": "GitLens"
- },
- {
- "command": "gitlens.stashExplorer.openFileInRemote",
- "title": "Open File in Remote",
+ "command": "gitlens.gitExplorer.openChangedFileRevisions",
+ "title": "Open Revisions",
"category": "GitLens"
}
],
@@ -1203,47 +1211,39 @@
"when": "false"
},
{
- "command": "gitlens.fileHistoryExplorer.refresh",
- "when": "false"
- },
- {
- "command": "gitlens.fileHistoryExplorer.openChanges",
- "when": "false"
+ "command": "gitlens.gitExplorer.switchToHistoryView",
+ "when": "gitlens:gitExplorer:view == repository"
},
{
- "command": "gitlens.fileHistoryExplorer.openFile",
- "when": "false"
+ "command": "gitlens.gitExplorer.switchToRepositoryView",
+ "when": "gitlens:gitExplorer:view == history"
},
{
- "command": "gitlens.fileHistoryExplorer.openFileRevision",
+ "command": "gitlens.gitExplorer.openChanges",
"when": "false"
},
{
- "command": "gitlens.fileHistoryExplorer.openFileInRemote",
+ "command": "gitlens.gitExplorer.openChangesWithWorking",
"when": "false"
},
{
- "command": "gitlens.fileHistoryExplorer.openFileRevisionInRemote",
+ "command": "gitlens.gitExplorer.openFile",
"when": "false"
},
{
- "command": "gitlens.stashExplorer.refresh",
+ "command": "gitlens.gitExplorer.openFileRevision",
"when": "false"
},
{
- "command": "gitlens.stashExplorer.openChanges",
+ "command": "gitlens.gitExplorer.openFileRevisionInRemote",
"when": "false"
},
{
- "command": "gitlens.stashExplorer.openFile",
+ "command": "gitlens.gitExplorer.openChangedFiles",
"when": "false"
},
{
- "command": "gitlens.stashExplorer.openStashedFile",
- "when": "false"
- },
- {
- "command": "gitlens.stashExplorer.openFileInRemote",
+ "command": "gitlens.gitExplorer.openChangedFileRevisions",
"when": "false"
}
],
@@ -1432,116 +1432,156 @@
],
"view/title": [
{
- "command": "gitlens.gitExplorer.refresh",
+ "command": "gitlens.showCommitSearch",
"when": "gitlens:enabled && view == gitlens.gitExplorer",
- "group": "navigation"
+ "group": "navigation@1"
},
{
- "command": "gitlens.fileHistoryExplorer.refresh",
- "when": "gitlens:enabled && view == gitlens.fileHistoryExplorer",
- "group": "navigation"
+ "command": "gitlens.gitExplorer.switchToHistoryView",
+ "when": "gitlens:enabled && view == gitlens.gitExplorer && gitlens:gitExplorer:view == repository",
+ "group": "navigation@2"
},
{
- "command": "gitlens.stashSave",
- "when": "gitlens:enabled && view == gitlens.stashExplorer",
- "group": "navigation@1"
+ "command": "gitlens.gitExplorer.switchToRepositoryView",
+ "when": "gitlens:enabled && view == gitlens.gitExplorer && gitlens:gitExplorer:view == history",
+ "group": "navigation@3"
},
{
- "command": "gitlens.stashExplorer.refresh",
- "when": "gitlens:enabled && view == gitlens.stashExplorer",
- "group": "navigation@2"
+ "command": "gitlens.gitExplorer.refresh",
+ "when": "gitlens:enabled && view == gitlens.gitExplorer",
+ "group": "navigation@4"
}
],
"view/item/context": [
{
- "command": "gitlens.openCommitInRemote",
- "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == commit",
+ "command": "gitlens.openBranchInRemote",
+ "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:branch-history",
"group": "1_gitlens@1"
},
{
- "command": "gitlens.openFileInRemote",
- "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == commit-file",
+ "command": "gitlens.openCommitInRemote",
+ "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit",
"group": "1_gitlens@1"
},
{
- "command": "gitlens.diffWithPrevious",
- "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == commit-file",
+ "command": "gitlens.copyShaToClipboard",
+ "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit",
"group": "2_gitlens@1"
},
{
- "command": "gitlens.diffWithWorking",
- "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == commit-file",
+ "command": "gitlens.copyMessageToClipboard",
+ "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit",
"group": "2_gitlens@2"
},
{
- "command": "gitlens.fileHistoryExplorer.openChanges",
- "when": "gitlens:enabled && view == gitlens.fileHistoryExplorer && viewItem == commit-file",
+ "command": "gitlens.gitExplorer.openChangedFiles",
+ "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit",
+ "group": "3_gitlens@1"
+ },
+ {
+ "command": "gitlens.gitExplorer.openChangedFileRevisions",
+ "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit",
+ "group": "3_gitlens@2"
+ },
+ {
+ "command": "gitlens.showQuickCommitDetails",
+ "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit",
+ "group": "4_gitlens@1"
+ },
+ {
+ "command": "gitlens.gitExplorer.openChanges",
+ "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit-file",
"group": "1_gitlens@1"
},
{
- "command": "gitlens.diffWithWorking",
- "when": "gitlens:enabled && view == gitlens.fileHistoryExplorer && viewItem == commit-file",
+ "command": "gitlens.gitExplorer.openChangesWithWorking",
+ "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit-file",
"group": "1_gitlens@2"
},
{
- "command": "gitlens.fileHistoryExplorer.openFile",
- "when": "gitlens:enabled && view == gitlens.fileHistoryExplorer && viewItem == commit-file",
+ "command": "gitlens.gitExplorer.openFile",
+ "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit-file",
"group": "2_gitlens@1"
},
{
- "command": "gitlens.fileHistoryExplorer.openFileRevision",
- "when": "gitlens:enabled && view == gitlens.fileHistoryExplorer && viewItem == commit-file",
+ "command": "gitlens.gitExplorer.openFileRevision",
+ "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit-file",
"group": "2_gitlens@2"
},
{
- "command": "gitlens.fileHistoryExplorer.openFileInRemote",
- "when": "gitlens:enabled && view == gitlens.fileHistoryExplorer && viewItem == commit-file",
- "group": "2_gitlens@3"
+ "command": "gitlens.openFileInRemote",
+ "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit-file",
+ "group": "3_gitlens@1"
},
{
- "command": "gitlens.fileHistoryExplorer.openFileRevisionInRemote",
- "when": "gitlens:enabled && view == gitlens.fileHistoryExplorer && viewItem == commit-file",
- "group": "2_gitlens@4"
+ "command": "gitlens.gitExplorer.openFileRevisionInRemote",
+ "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit-file",
+ "group": "3_gitlens@2"
},
{
- "command": "gitlens.showQuickCommitDetails",
- "when": "gitlens:enabled && view == gitlens.fileHistoryExplorer && viewItem == commit-file",
- "group": "3_gitlens@1"
+ "command": "gitlens.showQuickFileHistory",
+ "when": "gitlens:isTracked && view == gitlens.gitExplorer && viewItem == gitlens:commit-file && gitlens:gitExplorer:view == repository",
+ "group": "4_gitlens@1"
+ },
+ {
+ "command": "gitlens.showQuickCommitFileDetails",
+ "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:commit-file",
+ "group": "4_gitlens@2"
},
{
"command": "gitlens.stashApply",
- "when": "gitlens:enabled && view == gitlens.stashExplorer && viewItem == stash-commit",
- "group": "3_gitlens@1"
+ "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash",
+ "group": "1_gitlens@1"
},
{
"command": "gitlens.stashDelete",
- "when": "gitlens:enabled && view == gitlens.stashExplorer && viewItem == stash-commit",
+ "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash",
+ "group": "1_gitlens@2"
+ },
+ {
+ "command": "gitlens.copyMessageToClipboard",
+ "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash",
+ "group": "2_gitlens@1"
+ },
+ {
+ "command": "gitlens.gitExplorer.openChangedFiles",
+ "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash",
"group": "3_gitlens@1"
},
{
- "command": "gitlens.stashExplorer.openChanges",
- "when": "gitlens:enabled && view == gitlens.stashExplorer && viewItem == commit-file",
+ "command": "gitlens.gitExplorer.openChangedFileRevisions",
+ "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash",
+ "group": "3_gitlens@2"
+ },
+ {
+ "command": "gitlens.gitExplorer.openChanges",
+ "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash-file",
"group": "1_gitlens@1"
},
{
- "command": "gitlens.stashExplorer.openFile",
- "when": "gitlens:enabled && view == gitlens.stashExplorer && viewItem == commit-file",
+ "command": "gitlens.gitExplorer.openChangesWithWorking",
+ "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash-file",
"group": "1_gitlens@2"
},
{
- "command": "gitlens.stashExplorer.openStashedFile",
- "when": "gitlens:enabled && view == gitlens.stashExplorer && viewItem == commit-file",
- "group": "1_gitlens@3"
+ "command": "gitlens.gitExplorer.openFile",
+ "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash-file",
+ "group": "2_gitlens@1"
},
{
- "command": "gitlens.stashExplorer.openFileInRemote",
- "when": "gitlens:enabled && view == gitlens.stashExplorer && viewItem == commit-file",
- "group": "1_gitlens@4"
+ "command": "gitlens.gitExplorer.openFileRevision",
+ "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash-file",
+ "group": "2_gitlens@2"
},
{
- "command": "gitlens.diffWithWorking",
- "when": "gitlens:enabled && view == gitlens.stashExplorer && viewItem == commit-file",
- "group": "2_gitlens@2"
+ "command": "gitlens.openFileInRemote",
+ "when": "gitlens:enabled && view == gitlens.gitExplorer && viewItem == gitlens:stash-file",
+ "group": "3_gitlens@1"
+ },
+ {
+ "command": "gitlens.showQuickFileHistory",
+ "when": "gitlens:isTracked && view == gitlens.gitExplorer && viewItem == gitlens:stash-file",
+ "group": "4_gitlens@1"
}
]
},
@@ -1640,13 +1680,8 @@
"views": {
"explorer": [
{
- "id": "gitlens.fileHistoryExplorer",
- "name": "Git File History",
- "when": "gitlens:enabled && config.gitlens.insiders"
- },
- {
- "id": "gitlens.stashExplorer",
- "name": "Git Stashes",
+ "id": "gitlens.gitExplorer",
+ "name": "GitLens",
"when": "gitlens:enabled"
}
]
diff --git a/src/commands/common.ts b/src/commands/common.ts
index 5b0a95a..2ef0cad 100644
--- a/src/commands/common.ts
+++ b/src/commands/common.ts
@@ -1,6 +1,7 @@
'use strict';
import { commands, Disposable, SourceControlResourceGroup, SourceControlResourceState, TextDocumentShowOptions, TextEditor, TextEditorEdit, Uri, window, workspace } from 'vscode';
import { ExplorerNode } from '../views/explorerNodes';
+import { GitBranch, GitCommit } from '../gitService';
import { Logger } from '../logger';
import { Telemetry } from '../telemetry';
@@ -125,6 +126,18 @@ export interface CommandViewContext extends CommandBaseContext {
node: ExplorerNode;
}
+export function isCommandViewContextWithBranch(context: CommandContext): context is CommandViewContext & { node: (ExplorerNode & { branch: GitBranch }) } {
+ return context.type === 'view' && (context.node as any).branch && (context.node as any).branch instanceof GitBranch;
+}
+
+interface ICommandViewContextWithCommit extends CommandViewContext {
+ node: (ExplorerNode & { commit: T });
+}
+
+export function isCommandViewContextWithCommit(context: CommandContext): context is ICommandViewContextWithCommit {
+ return context.type === 'view' && (context.node as any).commit && (context.node as any).commit instanceof GitCommit;
+}
+
export type CommandContext = CommandScmGroupsContext | CommandScmStatesContext | CommandUnknownContext | CommandUriContext | CommandViewContext;
function isScmResourceGroup(group: any): group is SourceControlResourceGroup {
diff --git a/src/commands/copyMessageToClipboard.ts b/src/commands/copyMessageToClipboard.ts
index 141c607..e18e734 100644
--- a/src/commands/copyMessageToClipboard.ts
+++ b/src/commands/copyMessageToClipboard.ts
@@ -1,7 +1,7 @@
'use strict';
import { Iterables } from '../system';
import { TextEditor, Uri, window } from 'vscode';
-import { ActiveEditorCommand, Commands, getCommandUri } from './common';
+import { ActiveEditorCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithCommit } from './common';
import { GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { copy } from 'copy-paste';
@@ -17,6 +17,16 @@ export class CopyMessageToClipboardCommand extends ActiveEditorCommand {
super(Commands.CopyMessageToClipboard);
}
+ protected async preExecute(context: CommandContext, args: CopyMessageToClipboardCommandArgs = {}): Promise {
+ if (isCommandViewContextWithCommit(context)) {
+ args = { ...args };
+ args.sha = context.node.commit.sha;
+ return this.execute(context.editor, context.node.commit.uri, args);
+ }
+
+ return this.execute(context.editor, context.uri, args);
+ }
+
async execute(editor?: TextEditor, uri?: Uri, args: CopyMessageToClipboardCommandArgs = {}): Promise {
uri = getCommandUri(uri, editor);
@@ -64,7 +74,7 @@ export class CopyMessageToClipboardCommand extends ActiveEditorCommand {
// Get the full commit message -- since blame only returns the summary
const commit = await this.git.getLogCommit(gitUri.repoPath, gitUri.fsPath, args.sha);
- if (!commit) return undefined;
+ if (commit === undefined) return undefined;
args.message = commit.message;
}
diff --git a/src/commands/copyShaToClipboard.ts b/src/commands/copyShaToClipboard.ts
index a47aba7..d226ff5 100644
--- a/src/commands/copyShaToClipboard.ts
+++ b/src/commands/copyShaToClipboard.ts
@@ -1,7 +1,7 @@
'use strict';
import { Iterables } from '../system';
import { TextEditor, Uri, window } from 'vscode';
-import { ActiveEditorCommand, Commands, getCommandUri } from './common';
+import { ActiveEditorCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithCommit } from './common';
import { GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { copy } from 'copy-paste';
@@ -16,6 +16,16 @@ export class CopyShaToClipboardCommand extends ActiveEditorCommand {
super(Commands.CopyShaToClipboard);
}
+ protected async preExecute(context: CommandContext, args: CopyShaToClipboardCommandArgs = {}): Promise {
+ if (isCommandViewContextWithCommit(context)) {
+ args = { ...args };
+ args.sha = context.node.commit.sha;
+ return this.execute(context.editor, context.node.commit.uri, args);
+ }
+
+ return this.execute(context.editor, context.uri, args);
+ }
+
async execute(editor?: TextEditor, uri?: Uri, args: CopyShaToClipboardCommandArgs = {}): Promise {
uri = getCommandUri(uri, editor);
@@ -45,7 +55,7 @@ export class CopyShaToClipboardCommand extends ActiveEditorCommand {
try {
const blame = await this.git.getBlameForLine(gitUri, blameline);
- if (!blame) return undefined;
+ if (blame === undefined) return undefined;
args.sha = blame.commit.sha;
}
diff --git a/src/commands/diffWithPrevious.ts b/src/commands/diffWithPrevious.ts
index c7ea727..564587e 100644
--- a/src/commands/diffWithPrevious.ts
+++ b/src/commands/diffWithPrevious.ts
@@ -2,7 +2,7 @@
import { Iterables } from '../system';
import { commands, Range, TextDocumentShowOptions, TextEditor, Uri, window } from 'vscode';
import { ActiveEditorCommand, Commands, getCommandUri } from './common';
-import { BuiltInCommands, GlyphChars } from '../constants';
+import { BuiltInCommands, FakeSha, GlyphChars } from '../constants';
import { DiffWithWorkingCommandArgs } from './diffWithWorking';
import { GitCommit, GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
@@ -14,6 +14,10 @@ export interface DiffWithPreviousCommandArgs {
line?: number;
range?: Range;
showOptions?: TextDocumentShowOptions;
+
+ allowMissingPrevious?: boolean;
+ leftTitlePrefix?: string;
+ rightTitlePrefix?: string;
}
export class DiffWithPreviousCommand extends ActiveEditorCommand {
@@ -51,12 +55,12 @@ export class DiffWithPreviousCommand extends ActiveEditorCommand {
}
}
- if (args.commit.previousSha === undefined) return Messages.showCommitHasNoPreviousCommitWarningMessage(args.commit);
+ if (args.commit.previousSha === undefined && !args.allowMissingPrevious) return Messages.showCommitHasNoPreviousCommitWarningMessage(args.commit);
try {
const [rhs, lhs] = await Promise.all([
this.git.getVersionedFile(args.commit.repoPath, args.commit.uri.fsPath, args.commit.sha),
- this.git.getVersionedFile(args.commit.repoPath, args.commit.previousUri.fsPath, args.commit.previousSha)
+ this.git.getVersionedFile(args.commit.repoPath, args.commit.previousUri.fsPath, args.commit.previousSha === undefined ? FakeSha : args.commit.previousSha)
]);
if (args.line !== undefined && args.line !== 0) {
@@ -69,7 +73,9 @@ export class DiffWithPreviousCommand extends ActiveEditorCommand {
await commands.executeCommand(BuiltInCommands.Diff,
Uri.file(lhs),
Uri.file(rhs),
- `${path.basename(args.commit.previousUri.fsPath)} (${args.commit.previousShortSha}) ${GlyphChars.ArrowLeftRight} ${path.basename(args.commit.uri.fsPath)} (${args.commit.shortSha})`,
+ args.commit.previousShortSha === undefined
+ ? `${path.basename(args.commit.uri.fsPath)} (${args.rightTitlePrefix || ''}${args.commit.shortSha})`
+ : `${path.basename(args.commit.previousUri.fsPath)} (${args.leftTitlePrefix || ''}${args.commit.previousShortSha}) ${GlyphChars.ArrowLeftRight} ${path.basename(args.commit.uri.fsPath)} (${args.rightTitlePrefix || ''}${args.commit.shortSha})`,
args.showOptions);
}
catch (ex) {
diff --git a/src/commands/openBranchInRemote.ts b/src/commands/openBranchInRemote.ts
index 98b6834..8d4486e 100644
--- a/src/commands/openBranchInRemote.ts
+++ b/src/commands/openBranchInRemote.ts
@@ -1,7 +1,7 @@
'use strict';
import { Arrays } from '../system';
import { commands, TextEditor, Uri, window } from 'vscode';
-import { ActiveEditorCommand, Commands, getCommandUri } from './common';
+import { ActiveEditorCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithBranch } from './common';
import { GlyphChars } from '../constants';
import { GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
@@ -18,6 +18,16 @@ export class OpenBranchInRemoteCommand extends ActiveEditorCommand {
super(Commands.OpenBranchInRemote);
}
+ protected async preExecute(context: CommandContext, args: OpenBranchInRemoteCommandArgs = {}): Promise {
+ if (isCommandViewContextWithBranch(context)) {
+ args = { ...args };
+ args.branch = context.node.branch.name;
+ return this.execute(context.editor, context.uri, args);
+ }
+
+ return this.execute(context.editor, context.uri, args);
+ }
+
async execute(editor?: TextEditor, uri?: Uri, args: OpenBranchInRemoteCommandArgs = {}) {
uri = getCommandUri(uri, editor);
diff --git a/src/commands/openCommitInRemote.ts b/src/commands/openCommitInRemote.ts
index 707b005..efae8b9 100644
--- a/src/commands/openCommitInRemote.ts
+++ b/src/commands/openCommitInRemote.ts
@@ -1,12 +1,11 @@
'use strict';
import { Arrays } from '../system';
import { commands, TextEditor, Uri, window } from 'vscode';
-import { ActiveEditorCommand, CommandContext, Commands, getCommandUri } from './common';
+import { ActiveEditorCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithCommit } from './common';
import { GitBlameCommit, GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { Messages } from '../messages';
import { OpenInRemoteCommandArgs } from './openInRemote';
-import { CommitNode } from '../views/explorerNodes';
export interface OpenCommitInRemoteCommandArgs {
sha?: string;
@@ -19,7 +18,7 @@ export class OpenCommitInRemoteCommand extends ActiveEditorCommand {
}
protected async preExecute(context: CommandContext, args: OpenCommitInRemoteCommandArgs = {}): Promise {
- if (context.type === 'view' && context.node instanceof CommitNode) {
+ if (isCommandViewContextWithCommit(context)) {
args = { ...args };
args.sha = context.node.commit.sha;
return this.execute(context.editor, context.node.commit.uri, args);
diff --git a/src/commands/openFileInRemote.ts b/src/commands/openFileInRemote.ts
index 03624b1..0d2ad06 100644
--- a/src/commands/openFileInRemote.ts
+++ b/src/commands/openFileInRemote.ts
@@ -1,7 +1,7 @@
'use strict';
import { Arrays } from '../system';
import { commands, Range, TextEditor, Uri, window } from 'vscode';
-import { ActiveEditorCommand, Commands, getCommandUri } from './common';
+import { ActiveEditorCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithCommit } from './common';
import { GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { OpenInRemoteCommandArgs } from './openInRemote';
@@ -16,6 +16,16 @@ export class OpenFileInRemoteCommand extends ActiveEditorCommand {
super(Commands.OpenFileInRemote);
}
+ protected async preExecute(context: CommandContext, args: OpenFileInRemoteCommandArgs = {}): Promise {
+ if (isCommandViewContextWithCommit(context)) {
+ args = { ...args };
+ args.range = false;
+ return this.execute(context.editor, context.node.commit.uri, args);
+ }
+
+ return this.execute(context.editor, context.uri, args);
+ }
+
async execute(editor?: TextEditor, uri?: Uri, args: OpenFileInRemoteCommandArgs = { range: true }) {
uri = getCommandUri(uri, editor);
if (uri === undefined) return undefined;
diff --git a/src/commands/openInRemote.ts b/src/commands/openInRemote.ts
index 8c279fe..a5bc29a 100644
--- a/src/commands/openInRemote.ts
+++ b/src/commands/openInRemote.ts
@@ -28,6 +28,7 @@ export class OpenInRemoteCommand extends ActiveEditorCommand {
try {
if (args.remotes.length === 1) {
+ this.ensureRemoteBranchName(args);
const command = new OpenRemoteCommandQuickPickItem(args.remotes[0], args.resource);
return command.execute();
}
@@ -35,16 +36,7 @@ export class OpenInRemoteCommand extends ActiveEditorCommand {
let placeHolder = '';
switch (args.resource.type) {
case 'branch':
- // Check to see if the remote is in the branch
- const index = args.resource.branch.indexOf('/');
- if (index >= 0) {
- const remoteName = args.resource.branch.substring(0, index);
- const remote = args.remotes.find(r => r.name === remoteName);
- if (remote !== undefined) {
- args.resource.branch = args.resource.branch.substring(index + 1);
- args.remotes = [remote];
- }
- }
+ this.ensureRemoteBranchName(args);
placeHolder = `open ${args.resource.branch} branch in${GlyphChars.Ellipsis}`;
break;
@@ -54,6 +46,10 @@ export class OpenInRemoteCommand extends ActiveEditorCommand {
break;
case 'file':
+ placeHolder = `open ${args.resource.fileName} in${GlyphChars.Ellipsis}`;
+ break;
+
+ case 'revision':
if (args.resource.commit !== undefined && args.resource.commit instanceof GitLogCommit) {
if (args.resource.commit.status === 'D') {
args.resource.sha = args.resource.commit.previousSha;
@@ -71,10 +67,6 @@ export class OpenInRemoteCommand extends ActiveEditorCommand {
placeHolder = `open ${args.resource.fileName}${shaSuffix} in${GlyphChars.Ellipsis}`;
}
break;
-
- case 'working-file':
- placeHolder = `open ${args.resource.fileName} in${GlyphChars.Ellipsis}`;
- break;
}
if (args.remotes.length === 1) {
@@ -93,4 +85,19 @@ export class OpenInRemoteCommand extends ActiveEditorCommand {
return window.showErrorMessage(`Unable to open in remote provider. See output channel for more details`);
}
}
+
+ private ensureRemoteBranchName(args: OpenInRemoteCommandArgs) {
+ if (args.remotes === undefined || args.resource === undefined || args.resource.type !== 'branch') return;
+
+ // Check to see if the remote is in the branch
+ const index = args.resource.branch.indexOf('/');
+ if (index >= 0) {
+ const remoteName = args.resource.branch.substring(0, index);
+ const remote = args.remotes.find(r => r.name === remoteName);
+ if (remote !== undefined) {
+ args.resource.branch = args.resource.branch.substring(index + 1);
+ args.remotes = [remote];
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/src/commands/showCommitSearch.ts b/src/commands/showCommitSearch.ts
index 8ded360..68f6f6a 100644
--- a/src/commands/showCommitSearch.ts
+++ b/src/commands/showCommitSearch.ts
@@ -8,7 +8,6 @@ import { Logger } from '../logger';
import { Messages } from '../messages';
import { CommandQuickPickItem, CommitsQuickPick } from '../quickPicks';
import { ShowQuickCommitDetailsCommandArgs } from './showQuickCommitDetails';
-import { paste } from 'copy-paste';
const searchByRegex = /^([@:#])/;
const searchByMap = new Map([
@@ -49,12 +48,6 @@ export class ShowCommitSearchCommand extends ActiveEditorCachedCommand {
args.search = `#${blameLine.commit.shortSha}`;
}
}
-
- if (!args.search) {
- args.search = await new Promise((resolve, reject) => {
- paste((err: Error, content: string) => resolve(err ? '' : content));
- });
- }
}
}
catch (ex) {
diff --git a/src/commands/showFileHistory.ts b/src/commands/showFileHistory.ts
index 19df42b..9b898b0 100644
--- a/src/commands/showFileHistory.ts
+++ b/src/commands/showFileHistory.ts
@@ -2,7 +2,7 @@
import { commands, Position, Range, TextEditor, TextEditorEdit, Uri, window } from 'vscode';
import { Commands, EditorCommand, getCommandUri } from './common';
import { BuiltInCommands } from '../constants';
-import { GitExplorer } from '../views/gitExplorer';
+// import { GitExplorer } from '../views/gitExplorer';
import { GitService, GitUri } from '../gitService';
import { Messages } from '../messages';
import { Logger } from '../logger';
@@ -15,7 +15,7 @@ export interface ShowFileHistoryCommandArgs {
export class ShowFileHistoryCommand extends EditorCommand {
- constructor(private git: GitService, private explorer?: GitExplorer) {
+ constructor(private git: GitService) {
super(Commands.ShowFileHistory);
}
@@ -33,10 +33,10 @@ export class ShowFileHistoryCommand extends EditorCommand {
const gitUri = await GitUri.fromUri(uri, this.git);
try {
- if (this.explorer !== undefined) {
- this.explorer.addHistory(gitUri);
- return undefined;
- }
+ // if (this.explorer !== undefined) {
+ // this.explorer.addHistory(gitUri);
+ // return undefined;
+ // }
const locations = await this.git.getLogLocations(gitUri, args.sha, args.line);
if (locations === undefined) return Messages.showFileNotUnderSourceControlWarningMessage('Unable to show file history');
diff --git a/src/commands/showQuickCommitDetails.ts b/src/commands/showQuickCommitDetails.ts
index 2015a53..bc2e67f 100644
--- a/src/commands/showQuickCommitDetails.ts
+++ b/src/commands/showQuickCommitDetails.ts
@@ -1,9 +1,8 @@
'use strict';
import { Strings } from '../system';
import { commands, TextEditor, Uri, window } from 'vscode';
-import { ActiveEditorCachedCommand, CommandContext, Commands, getCommandUri } from './common';
+import { ActiveEditorCachedCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithCommit } from './common';
import { GlyphChars } from '../constants';
-import { CommitNode } from '../views/explorerNodes';
import { GitCommit, GitLog, GitLogCommit, GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
import { CommandQuickPickItem, CommitDetailsQuickPick, CommitWithFileStatusQuickPickItem } from '../quickPicks';
@@ -27,7 +26,7 @@ export class ShowQuickCommitDetailsCommand extends ActiveEditorCachedCommand {
protected async preExecute(context: CommandContext, ...args: any[]): Promise {
if (context.type === 'view') {
- if (context.node instanceof CommitNode) {
+ if (isCommandViewContextWithCommit(context)) {
args = [{ sha: context.node.uri.sha, commit: context.node.commit }];
}
else {
diff --git a/src/commands/showQuickCommitFileDetails.ts b/src/commands/showQuickCommitFileDetails.ts
index 1261c78..f2fb799 100644
--- a/src/commands/showQuickCommitFileDetails.ts
+++ b/src/commands/showQuickCommitFileDetails.ts
@@ -1,7 +1,7 @@
'use strict';
import { Strings } from '../system';
import { TextEditor, Uri, window } from 'vscode';
-import { ActiveEditorCachedCommand, Commands, getCommandUri } from './common';
+import { ActiveEditorCachedCommand, CommandContext, Commands, getCommandUri, isCommandViewContextWithCommit } from './common';
import { GlyphChars } from '../constants';
import { GitCommit, GitLog, GitLogCommit, GitService, GitUri } from '../gitService';
import { Logger } from '../logger';
@@ -24,6 +24,18 @@ export class ShowQuickCommitFileDetailsCommand extends ActiveEditorCachedCommand
super(Commands.ShowQuickCommitFileDetails);
}
+ protected async preExecute(context: CommandContext, ...args: any[]): Promise {
+ if (context.type === 'view') {
+ if (isCommandViewContextWithCommit(context)) {
+ args = [{ sha: context.node.uri.sha, commit: context.node.commit }];
+ }
+ else {
+ args = [{ sha: context.node.uri.sha }];
+ }
+ }
+ return this.execute(context.editor, context.uri, ...args);
+ }
+
async execute(editor?: TextEditor, uri?: Uri, args: ShowQuickCommitFileDetailsCommandArgs = {}) {
uri = getCommandUri(uri, editor);
if (uri === undefined) return undefined;
diff --git a/src/commands/stashApply.ts b/src/commands/stashApply.ts
index 007355a..39ce5d8 100644
--- a/src/commands/stashApply.ts
+++ b/src/commands/stashApply.ts
@@ -1,13 +1,11 @@
'use strict';
import { Strings } from '../system';
import { MessageItem, window } from 'vscode';
-import { GitService, GitStashCommit } from '../gitService';
-import { Command, CommandContext, Commands } from './common';
+import { Command, CommandContext, Commands, isCommandViewContextWithCommit } from './common';
import { GlyphChars } from '../constants';
-import { CommitQuickPickItem, StashListQuickPick } from '../quickPicks';
+import { GitService, GitStashCommit } from '../gitService';
import { Logger } from '../logger';
-import { CommandQuickPickItem } from '../quickPicks';
-import { StashCommitNode } from '../views/stashCommitNode';
+import { CommandQuickPickItem, CommitQuickPickItem, StashListQuickPick } from '../quickPicks';
export interface StashApplyCommandArgs {
confirm?: boolean;
@@ -24,12 +22,9 @@ export class StashApplyCommand extends Command {
}
protected async preExecute(context: CommandContext, args: StashApplyCommandArgs = { confirm: true, deleteAfter: false }) {
- if (context.type === 'view' && context.node instanceof StashCommitNode) {
+ if (isCommandViewContextWithCommit(context)) {
args = { ...args };
-
- const stash = context.node.commit;
- args.stashItem = { stashName: stash.stashName, message: stash.message };
-
+ args.stashItem = { stashName: context.node.commit.stashName, message: context.node.commit.message };
return this.execute(args);
}
diff --git a/src/commands/stashDelete.ts b/src/commands/stashDelete.ts
index bc45153..9d447c3 100644
--- a/src/commands/stashDelete.ts
+++ b/src/commands/stashDelete.ts
@@ -1,11 +1,10 @@
'use strict';
import { MessageItem, window } from 'vscode';
-import { Command, CommandContext, Commands } from './common';
+import { Command, CommandContext, Commands, isCommandViewContextWithCommit } from './common';
import { GlyphChars } from '../constants';
-import { GitService } from '../gitService';
+import { GitService, GitStashCommit } from '../gitService';
import { Logger } from '../logger';
import { CommandQuickPickItem } from '../quickPicks';
-import { StashCommitNode } from '../views/stashCommitNode';
export interface StashDeleteCommandArgs {
confirm?: boolean;
@@ -21,12 +20,9 @@ export class StashDeleteCommand extends Command {
}
protected async preExecute(context: CommandContext, args: StashDeleteCommandArgs = { confirm: true }) {
- if (context.type === 'view' && context.node instanceof StashCommitNode) {
+ if (isCommandViewContextWithCommit(context)) {
args = { ...args };
-
- const stash = context.node.commit;
- args.stashItem = { stashName: stash.stashName, message: stash.message };
-
+ args.stashItem = { stashName: context.node.commit.stashName, message: context.node.commit.message };
return this.execute(args);
}
diff --git a/src/configuration.ts b/src/configuration.ts
index fee3e17..c2b17fc 100644
--- a/src/configuration.ts
+++ b/src/configuration.ts
@@ -2,6 +2,7 @@
import { FileAnnotationType } from './annotations/annotationController';
import { Commands } from './commands';
import { LineAnnotationType } from './currentLineController';
+import { GitExplorerView } from './views/gitExplorer';
import { OutputLevel } from './logger';
export { ExtensionKey } from './constants';
@@ -296,19 +297,10 @@ export interface IConfig {
defaultDateFormat: string | null;
- fileHistoryExplorer: {
- commitFormat: string;
- // commitFileFormat: string;
- // dateFormat: string | null;
- };
-
gitExplorer: {
+ view: GitExplorerView;
commitFormat: string;
commitFileFormat: string;
- // dateFormat: string | null;
- };
-
- stashExplorer: {
stashFormat: string;
stashFileFormat: string;
// dateFormat: string | null;
diff --git a/src/constants.ts b/src/constants.ts
index 83690f5..1c48be5 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -8,6 +8,8 @@ export const QualifiedExtensionId = `eamodio.${ExtensionId}`;
export const ApplicationInsightsKey = 'a9c302f8-6483-4d01-b92c-c159c799c679';
+export const FakeSha = 'ffffffffffffffffffffffffffffffffffffffff';
+
export type BuiltInCommands = 'cursorMove' |
'editor.action.showReferences' |
'editor.action.toggleRenderWhitespace' |
@@ -40,23 +42,25 @@ export const BuiltInCommands = {
};
export type CommandContext =
+ 'gitlens:annotationStatus' |
'gitlens:canToggleCodeLens' |
'gitlens:enabled' |
'gitlens:hasRemotes' |
+ 'gitlens:gitExplorer:view' |
'gitlens:isBlameable' |
'gitlens:isRepository' |
'gitlens:isTracked' |
- 'gitlens:key' |
- 'gitlens:annotationStatus';
+ 'gitlens:key';
export const CommandContext = {
+ AnnotationStatus: 'gitlens:annotationStatus' as CommandContext,
CanToggleCodeLens: 'gitlens:canToggleCodeLens' as CommandContext,
Enabled: 'gitlens:enabled' as CommandContext,
+ GitExplorerView: 'gitlens:gitExplorer:view' as CommandContext,
HasRemotes: 'gitlens:hasRemotes' as CommandContext,
IsBlameable: 'gitlens:isBlameable' as CommandContext,
IsRepository: 'gitlens:isRepository' as CommandContext,
IsTracked: 'gitlens:isTracked' as CommandContext,
- Key: 'gitlens:key' as CommandContext,
- AnnotationStatus: 'gitlens:annotationStatus' as CommandContext
+ Key: 'gitlens:key' as CommandContext
};
export function setCommandContext(key: CommandContext | string, value: any) {
@@ -77,6 +81,7 @@ export type GlyphChars = '\u21a9' |
'\u2194' |
'\u21e8' |
'\u2191' |
+ '\u2713' |
'\u2014' |
'\u2022' |
'\u2026' |
@@ -90,6 +95,7 @@ export const GlyphChars = {
ArrowLeftRight: '\u2194' as GlyphChars,
ArrowRightHollow: '\u21e8' as GlyphChars,
ArrowUp: '\u2191' as GlyphChars,
+ Check: '\u2713' as GlyphChars,
Dash: '\u2014' as GlyphChars,
Dot: '\u2022' as GlyphChars,
Ellipsis: '\u2026' as GlyphChars,
diff --git a/src/currentLineController.ts b/src/currentLineController.ts
index 838cd01..52f1e2d 100644
--- a/src/currentLineController.ts
+++ b/src/currentLineController.ts
@@ -476,11 +476,11 @@ export class CurrentLineController extends Disposable {
break;
case StatusBarCommand.DiffWithPrevious:
this._statusBarItem.command = Commands.DiffLineWithPrevious;
- this._statusBarItem.tooltip = 'Compare Line Commit with Previous';
+ this._statusBarItem.tooltip = 'Compare Line Revision with Previous';
break;
case StatusBarCommand.DiffWithWorking:
this._statusBarItem.command = Commands.DiffLineWithWorking;
- this._statusBarItem.tooltip = 'Compare Line Commit with Working Tree';
+ this._statusBarItem.tooltip = 'Compare Line Revision with Working';
break;
case StatusBarCommand.ToggleCodeLens:
this._statusBarItem.tooltip = 'Toggle Git CodeLens';
diff --git a/src/extension.ts b/src/extension.ts
index f3f92eb..5164786 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -20,9 +20,7 @@ import { ApplicationInsightsKey, CommandContext, ExtensionKey, QualifiedExtensio
import { CodeLensController } from './codeLensController';
import { CurrentLineController, LineAnnotationType } from './currentLineController';
import { GitContentProvider } from './gitContentProvider';
-// import { GitExplorer } from './views/gitExplorer';
-import { FileHistoryExplorer } from './views/fileHistoryExplorer';
-import { StashExplorer } from './views/stashExplorer';
+import { GitExplorer } from './views/gitExplorer';
import { GitRevisionCodeLensProvider } from './gitRevisionCodeLensProvider';
import { GitContextTracker, GitService } from './gitService';
import { Keyboard } from './keyboard';
@@ -94,11 +92,7 @@ export async function activate(context: ExtensionContext) {
context.subscriptions.push(new Keyboard());
- // const explorer = new GitExplorer(context, git);
- // context.subscriptions.push(window.registerTreeDataProvider('gitlens.gitExplorer', explorer));
-
- context.subscriptions.push(window.registerTreeDataProvider('gitlens.fileHistoryExplorer', new FileHistoryExplorer(context, git)));
- context.subscriptions.push(window.registerTreeDataProvider('gitlens.stashExplorer', new StashExplorer(context, git)));
+ context.subscriptions.push(window.registerTreeDataProvider('gitlens.gitExplorer', new GitExplorer(context, git)));
context.subscriptions.push(commands.registerTextEditorCommand('gitlens.computingFileAnnotations', () => { }));
diff --git a/src/git/git.ts b/src/git/git.ts
index e22d7d9..98ef823 100644
--- a/src/git/git.ts
+++ b/src/git/git.ts
@@ -31,7 +31,9 @@ const GitWarnings = [
/Not a git repository/,
/is outside repository/,
/no such path/,
- /does not have any commits/
+ /does not have any commits/,
+ /Path \'.*?\' does not exist in/,
+ /Path \'.*?\' exists on disk, but not in/
];
async function gitCommand(options: { cwd: string, encoding?: string }, ...args: any[]) {
diff --git a/src/git/remotes/provider.ts b/src/git/remotes/provider.ts
index b4a48f8..c17f5a1 100644
--- a/src/git/remotes/provider.ts
+++ b/src/git/remotes/provider.ts
@@ -3,12 +3,13 @@ import { commands, Range, Uri } from 'vscode';
import { BuiltInCommands } from '../../constants';
import { GitLogCommit } from '../../gitService';
-export type RemoteResourceType = 'branch' | 'commit' | 'file' | 'repo' | 'working-file';
-export type RemoteResource = { type: 'branch', branch: string } |
+export type RemoteResourceType = 'branch' | 'commit' | 'file' | 'repo' | 'revision';
+export type RemoteResource =
+ { type: 'branch', branch: string } |
{ type: 'commit', sha: string } |
- { type: 'file', branch?: string, commit?: GitLogCommit, fileName: string, range?: Range, sha?: string } |
+ { type: 'file', branch?: string, fileName: string, range?: Range } |
{ type: 'repo' } |
- { type: 'working-file', branch?: string, fileName: string, range?: Range };
+ { type: 'revision', branch?: string, commit?: GitLogCommit, fileName: string, range?: Range, sha?: string };
export function getNameFromRemoteResource(resource: RemoteResource) {
switch (resource.type) {
@@ -16,7 +17,7 @@ export function getNameFromRemoteResource(resource: RemoteResource) {
case 'commit': return 'Commit';
case 'file': return 'File';
case 'repo': return 'Repository';
- case 'working-file': return 'Working File';
+ case 'revision': return 'Revision';
default: return '';
}
}
@@ -43,16 +44,11 @@ export abstract class RemoteProvider {
open(resource: RemoteResource): Promise<{} | undefined> {
switch (resource.type) {
- case 'branch':
- return this.openBranch(resource.branch);
- case 'commit':
- return this.openCommit(resource.sha);
- case 'file':
- return this.openFile(resource.fileName, resource.branch, resource.sha, resource.range);
- case 'repo':
- return this.openRepo();
- case 'working-file':
- return this.openFile(resource.fileName, resource.branch, undefined, resource.range);
+ case 'branch': return this.openBranch(resource.branch);
+ case 'commit': return this.openCommit(resource.sha);
+ case 'file': return this.openFile(resource.fileName, resource.branch, undefined, resource.range);
+ case 'repo': return this.openRepo();
+ case 'revision': return this.openFile(resource.fileName, resource.branch, resource.sha, resource.range);
}
}
diff --git a/src/gitContentProvider.ts b/src/gitContentProvider.ts
index 19ae9ea..1853f87 100644
--- a/src/gitContentProvider.ts
+++ b/src/gitContentProvider.ts
@@ -1,5 +1,5 @@
'use strict';
-import { ExtensionContext, TextDocumentContentProvider, Uri, window } from 'vscode';
+import { CancellationToken, ExtensionContext, TextDocumentContentProvider, Uri, window } from 'vscode';
import { DocumentSchemes } from './constants';
import { GitService } from './gitService';
import { Logger } from './logger';
@@ -11,7 +11,7 @@ export class GitContentProvider implements TextDocumentContentProvider {
constructor(context: ExtensionContext, private git: GitService) { }
- async provideTextDocumentContent(uri: Uri): Promise {
+ async provideTextDocumentContent(uri: Uri, token: CancellationToken): Promise {
const data = GitService.fromGitContentUri(uri);
const fileName = data.originalFileName || data.fileName;
try {
@@ -23,8 +23,8 @@ export class GitContentProvider implements TextDocumentContentProvider {
}
catch (ex) {
Logger.error(ex, 'GitContentProvider', 'getVersionedFileText');
- await window.showErrorMessage(`Unable to show Git revision ${data.sha.substring(0, 8)} of '${path.relative(data.repoPath, fileName)}'`);
- return '';
+ window.showErrorMessage(`Unable to show Git revision ${data.sha.substring(0, 8)} of '${path.relative(data.repoPath, fileName)}'`);
+ return undefined;
}
}
}
\ No newline at end of file
diff --git a/src/gitRevisionCodeLensProvider.ts b/src/gitRevisionCodeLensProvider.ts
index 6658b4c..0bbd3af 100644
--- a/src/gitRevisionCodeLensProvider.ts
+++ b/src/gitRevisionCodeLensProvider.ts
@@ -32,13 +32,12 @@ export class GitRevisionCodeLensProvider implements CodeLensProvider {
const lenses: CodeLens[] = [];
const commit = await this.git.getLogCommit(gitUri.repoPath, gitUri.fsPath, gitUri.sha, { firstIfMissing: true, previous: true });
- if (!commit) return lenses;
-
- lenses.push(new GitDiffWithWorkingCodeLens(this.git, commit.uri.fsPath, commit, new Range(0, 0, 0, 1)));
+ if (commit === undefined) return lenses;
if (commit.previousSha) {
- lenses.push(new GitDiffWithPreviousCodeLens(this.git, commit.previousUri.fsPath, commit, new Range(0, 1, 0, 2)));
+ lenses.push(new GitDiffWithPreviousCodeLens(this.git, commit.previousUri.fsPath, commit, new Range(0, 0, 0, 1)));
}
+ lenses.push(new GitDiffWithWorkingCodeLens(this.git, commit.uri.fsPath, commit, new Range(0, 1, 0, 2)));
return lenses;
}
@@ -51,7 +50,7 @@ export class GitRevisionCodeLensProvider implements CodeLensProvider {
_resolveDiffWithWorkingTreeCodeLens(lens: GitDiffWithWorkingCodeLens, token: CancellationToken): Thenable {
lens.command = {
- title: `Compare ${lens.commit.shortSha} with Working Tree`,
+ title: `Compare Revision (${lens.commit.shortSha}) with Working`,
command: Commands.DiffWithWorking,
arguments: [
Uri.file(lens.fileName),
@@ -66,7 +65,7 @@ export class GitRevisionCodeLensProvider implements CodeLensProvider {
_resolveGitDiffWithPreviousCodeLens(lens: GitDiffWithPreviousCodeLens, token: CancellationToken): Thenable {
lens.command = {
- title: `Compare ${lens.commit.shortSha} with Previous ${lens.commit.previousShortSha}`,
+ title: `Compare Revision (${lens.commit.shortSha}) with Previous (${lens.commit.previousShortSha})`,
command: Commands.DiffWithPrevious,
arguments: [
Uri.file(lens.fileName),
diff --git a/src/quickPicks/commitDetails.ts b/src/quickPicks/commitDetails.ts
index 722dd1b..ea0b38a 100644
--- a/src/quickPicks/commitDetails.ts
+++ b/src/quickPicks/commitDetails.ts
@@ -69,28 +69,31 @@ export class CommitWithFileStatusQuickPickItem extends OpenFileCommandQuickPickI
export class OpenCommitFilesCommandQuickPickItem extends OpenFilesCommandQuickPickItem {
- constructor(commit: GitLogCommit, item?: QuickPickItem) {
- const uris = commit.fileStatuses.map(s => (s.status === 'D')
- ? GitService.toGitContentUri(commit.previousSha!, commit.previousShortSha!, s.fileName, commit.repoPath, s.originalFileName)
- : GitService.toGitContentUri(commit.sha, commit.shortSha, s.fileName, commit.repoPath, s.originalFileName));
+ constructor(commit: GitLogCommit, versioned: boolean = false, item?: QuickPickItem) {
+ const repoPath = commit.repoPath;
+ const uris = commit.fileStatuses
+ .filter(s => s.status !== 'D')
+ .map(s => GitUri.fromFileStatus(s, repoPath));
super(uris, item || {
label: `$(file-symlink-file) Open Changed Files`,
- description: `${Strings.pad(GlyphChars.Dash, 2, 3)} in ${GlyphChars.Space}$(git-commit) ${commit.shortSha}`
- // detail: `Opens all of the changed files in $(git-commit) ${commit.shortSha}`
+ description: ''
+ // detail: `Opens all of the changed file in the working tree`
});
}
}
-export class OpenCommitWorkingTreeFilesCommandQuickPickItem extends OpenFilesCommandQuickPickItem {
+export class OpenCommitFileRevisionsCommandQuickPickItem extends OpenFilesCommandQuickPickItem {
+
+ constructor(commit: GitLogCommit, item?: QuickPickItem) {
+ const uris = commit.fileStatuses
+ .filter(s => s.status !== 'D')
+ .map(s => GitService.toGitContentUri(commit.sha, commit.shortSha, s.fileName, commit.repoPath, s.originalFileName));
- constructor(commit: GitLogCommit, versioned: boolean = false, item?: QuickPickItem) {
- const repoPath = commit.repoPath;
- const uris = commit.fileStatuses.filter(_ => _.status !== 'D').map(_ => GitUri.fromFileStatus(_, repoPath));
super(uris, item || {
- label: `$(file-symlink-file) Open Changed Working Files`,
- description: ''
- // detail: `Opens all of the changed file in the working tree`
+ label: `$(file-symlink-file) Open Changed Revisions`,
+ description: `${Strings.pad(GlyphChars.Dash, 2, 3)} in ${GlyphChars.Space}$(git-commit) ${commit.shortSha}`
+ // detail: `Opens all of the changed files in $(git-commit) ${commit.shortSha}`
});
}
}
@@ -196,8 +199,8 @@ export class CommitDetailsQuickPick {
} as ShowQuickCommitDetailsCommandArgs
]));
- items.push(new OpenCommitFilesCommandQuickPickItem(commit));
- items.push(new OpenCommitWorkingTreeFilesCommandQuickPickItem(commit));
+ items.push(new OpenCommitFilesCommandQuickPickItem(commit));
+ items.push(new OpenCommitFileRevisionsCommandQuickPickItem(commit));
if (goBackCommand) {
items.splice(0, 0, goBackCommand);
diff --git a/src/quickPicks/commitFileDetails.ts b/src/quickPicks/commitFileDetails.ts
index 95ed618..91e4c47 100644
--- a/src/quickPicks/commitFileDetails.ts
+++ b/src/quickPicks/commitFileDetails.ts
@@ -13,6 +13,17 @@ import * as path from 'path';
export class OpenCommitFileCommandQuickPickItem extends OpenFileCommandQuickPickItem {
constructor(commit: GitLogCommit, item?: QuickPickItem) {
+ const uri = Uri.file(path.resolve(commit.repoPath, commit.fileName));
+ super(uri, item || {
+ label: `$(file-symlink-file) Open File`,
+ description: `${Strings.pad(GlyphChars.Dash, 2, 3)} ${path.basename(commit.fileName)}`
+ });
+ }
+}
+
+export class OpenCommitFileRevisionCommandQuickPickItem extends OpenFileCommandQuickPickItem {
+
+ constructor(commit: GitLogCommit, item?: QuickPickItem) {
let description: string;
let uri: Uri;
if (commit.status === 'D') {
@@ -24,23 +35,12 @@ export class OpenCommitFileCommandQuickPickItem extends OpenFileCommandQuickPick
description = `${Strings.pad(GlyphChars.Dash, 2, 3)} ${path.basename(commit.fileName)} in ${GlyphChars.Space}$(git-commit) ${commit.shortSha}`;
}
super(uri, item || {
- label: `$(file-symlink-file) Open File`,
+ label: `$(file-symlink-file) Open Revision`,
description: description
});
}
}
-export class OpenCommitWorkingTreeFileCommandQuickPickItem extends OpenFileCommandQuickPickItem {
-
- constructor(commit: GitLogCommit, item?: QuickPickItem) {
- const uri = Uri.file(path.resolve(commit.repoPath, commit.fileName));
- super(uri, item || {
- label: `$(file-symlink-file) Open Working File`,
- description: `${Strings.pad(GlyphChars.Dash, 2, 3)} ${path.basename(commit.fileName)}`
- });
- }
-}
-
export class CommitFileDetailsQuickPick {
static async show(git: GitService, commit: GitLogCommit, uri: Uri, goBackCommand?: CommandQuickPickItem, currentCommand?: CommandQuickPickItem, fileLog?: GitLog): Promise {
@@ -74,7 +74,7 @@ export class CommitFileDetailsQuickPick {
if (commit.previousSha) {
items.push(new CommandQuickPickItem({
- label: `$(git-compare) Compare File with Previous`,
+ label: `$(git-compare) Compare File with Previous Revision`,
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(git-commit) ${commit.previousShortSha} ${GlyphChars.Space} $(git-compare) ${GlyphChars.Space} $(git-commit) ${commit.shortSha}`
}, Commands.DiffWithPrevious, [
commit.uri,
@@ -87,7 +87,7 @@ export class CommitFileDetailsQuickPick {
if (commit.workingFileName) {
items.push(new CommandQuickPickItem({
- label: `$(git-compare) Compare File with Working Tree`,
+ label: `$(git-compare) Compare File with Working Revision`,
description: `${Strings.pad(GlyphChars.Dash, 2, 3)} $(git-commit) ${commit.shortSha} ${GlyphChars.Space} $(git-compare) ${GlyphChars.Space} $(file-text) ${workingName}`
}, Commands.DiffWithWorking, [
Uri.file(path.resolve(commit.repoPath, commit.workingFileName)),
@@ -120,28 +120,28 @@ export class CommitFileDetailsQuickPick {
]));
}
- items.push(new OpenCommitFileCommandQuickPickItem(commit));
if (commit.workingFileName && commit.status !== 'D') {
- items.push(new OpenCommitWorkingTreeFileCommandQuickPickItem(commit));
+ items.push(new OpenCommitFileCommandQuickPickItem(commit));
}
+ items.push(new OpenCommitFileRevisionCommandQuickPickItem(commit));
const remotes = Arrays.uniqueBy(await git.getRemotes(commit.repoPath), _ => _.url, _ => !!_.provider);
if (remotes.length) {
- if (!stash) {
- items.push(new OpenRemotesCommandQuickPickItem(remotes, {
- type: 'file',
- fileName: commit.fileName,
- commit
- } as RemoteResource, currentCommand));
- }
if (commit.workingFileName && commit.status !== 'D') {
const branch = await git.getBranch(commit.repoPath || git.repoPath) as GitBranch;
items.push(new OpenRemotesCommandQuickPickItem(remotes, {
- type: 'working-file',
+ type: 'file',
fileName: commit.workingFileName,
branch: branch.name
} as RemoteResource, currentCommand));
}
+ if (!stash) {
+ items.push(new OpenRemotesCommandQuickPickItem(remotes, {
+ type: 'revision',
+ fileName: commit.fileName,
+ commit
+ } as RemoteResource, currentCommand));
+ }
}
if (commit.workingFileName) {
diff --git a/src/quickPicks/fileHistory.ts b/src/quickPicks/fileHistory.ts
index 709c9c1..d2d5216 100644
--- a/src/quickPicks/fileHistory.ts
+++ b/src/quickPicks/fileHistory.ts
@@ -139,7 +139,7 @@ export class FileHistoryQuickPick {
const remotes = Arrays.uniqueBy(await git.getRemotes(uri.repoPath!), _ => _.url, _ => !!_.provider);
if (remotes.length) {
items.splice(index++, 0, new OpenRemotesCommandQuickPickItem(remotes, {
- type: 'file',
+ type: 'revision',
branch: branch!.name,
fileName: uri.getRelativePath(),
sha: uri.sha
diff --git a/src/quickPicks/remotes.ts b/src/quickPicks/remotes.ts
index 01464b4..7777b74 100644
--- a/src/quickPicks/remotes.ts
+++ b/src/quickPicks/remotes.ts
@@ -44,6 +44,14 @@ export class OpenRemotesCommandQuickPickItem extends CommandQuickPickItem {
break;
case 'file':
+ description = `$(file-text) ${path.basename(resource.fileName)}`;
+ break;
+
+ case 'repo':
+ description = `$(repo) Repository`;
+ break;
+
+ case 'revision':
if (resource.commit !== undefined && resource.commit instanceof GitLogCommit) {
if (resource.commit.status === 'D') {
resource.sha = resource.commit.previousSha;
@@ -59,14 +67,6 @@ export class OpenRemotesCommandQuickPickItem extends CommandQuickPickItem {
description = `$(file-text) ${path.basename(resource.fileName)}${shortFileSha ? ` in ${GlyphChars.Space}$(git-commit) ${shortFileSha}` : ''}`;
}
break;
-
- case 'repo':
- description = `$(repo) Repository`;
- break;
-
- case 'working-file':
- description = `$(file-text) ${path.basename(resource.fileName)}`;
- break;
}
const remote = remotes[0];
diff --git a/src/views/branchHistoryNode.ts b/src/views/branchHistoryNode.ts
index 58f4aec..266bd66 100644
--- a/src/views/branchHistoryNode.ts
+++ b/src/views/branchHistoryNode.ts
@@ -4,26 +4,35 @@ import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { CommitNode } from './commitNode';
import { GlyphChars } from '../constants';
import { ExplorerNode, ResourceType } from './explorerNode';
-import { GitBranch, GitService, GitUri } from '../gitService';
+import { GitBranch, GitRemote, GitService, GitUri } from '../gitService';
export class BranchHistoryNode extends ExplorerNode {
- readonly resourceType: ResourceType = 'branch-history';
+ readonly resourceType: ResourceType = 'gitlens:branch-history';
- constructor(public readonly branch: GitBranch, uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
+ constructor(public readonly branch: GitBranch, private readonly remote: GitRemote | undefined, uri: GitUri, private readonly template: string, protected readonly context: ExtensionContext, protected readonly git: GitService) {
super(uri);
}
- async getChildren(): Promise {
+ async getChildren(): Promise {
const log = await this.git.getLogForRepo(this.uri.repoPath!, this.branch.name);
if (log === undefined) return [];
- return [...Iterables.map(log.commits.values(), c => new CommitNode(c, this.git.config.gitExplorer.commitFormat, this.context, this.git))];
+ return [...Iterables.map(log.commits.values(), c => new CommitNode(c, this.template, this.context, this.git))];
}
- getTreeItem(): TreeItem {
- const item = new TreeItem(`${this.branch.name}${this.branch.current ? ` ${GlyphChars.Dash} current` : ''}`, TreeItemCollapsibleState.Collapsed);
+ async getTreeItem(): Promise {
+ const name = this.remote !== undefined
+ ? this.branch.name.substring(this.remote.name.length + 1)
+ : this.branch.name;
+ const item = new TreeItem(`${name}${this.branch!.current ? ` ${GlyphChars.Space} ${GlyphChars.Check}` : ''}`, TreeItemCollapsibleState.Collapsed);
item.contextValue = this.resourceType;
+
+ item.iconPath = {
+ dark: this.context.asAbsolutePath('images/dark/icon-branch.svg'),
+ light: this.context.asAbsolutePath('images/light/icon-branch.svg')
+ };
+
return item;
}
}
diff --git a/src/views/branchesNode.ts b/src/views/branchesNode.ts
index 4cc3e87..897668f 100644
--- a/src/views/branchesNode.ts
+++ b/src/views/branchesNode.ts
@@ -7,22 +7,29 @@ import { GitService, GitUri } from '../gitService';
export class BranchesNode extends ExplorerNode {
- readonly resourceType: ResourceType = 'branches';
+ readonly resourceType: ResourceType = 'gitlens:branches';
constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
super(uri);
}
- async getChildren(): Promise {
+ async getChildren(): Promise {
const branches = await this.git.getBranches(this.uri.repoPath!);
if (branches === undefined) return [];
- return [...Iterables.filterMap(branches.sort(_ => _.current ? 0 : 1), b => b.remote ? undefined : new BranchHistoryNode(b, this.uri, this.context, this.git))];
+ branches.sort((a, b) => (a.current ? -1 : 1) - (b.current ? -1 : 1) || a.name.localeCompare(b.name));
+ return [...Iterables.filterMap(branches, b => b.remote ? undefined : new BranchHistoryNode(b, undefined, this.uri, this.git.config.gitExplorer.commitFormat, this.context, this.git))];
}
getTreeItem(): TreeItem {
- const item = new TreeItem(`Branches`, TreeItemCollapsibleState.Collapsed);
+ const item = new TreeItem(`Branches`, TreeItemCollapsibleState.Expanded);
item.contextValue = this.resourceType;
+
+ item.iconPath = {
+ dark: this.context.asAbsolutePath('images/dark/icon-branch.svg'),
+ light: this.context.asAbsolutePath('images/light/icon-branch.svg')
+ };
+
return item;
}
}
diff --git a/src/views/commitFileNode.ts b/src/views/commitFileNode.ts
index ddfba5f..137e3c6 100644
--- a/src/views/commitFileNode.ts
+++ b/src/views/commitFileNode.ts
@@ -7,9 +7,9 @@ import * as path from 'path';
export class CommitFileNode extends ExplorerNode {
- readonly resourceType: ResourceType = 'commit-file';
+ readonly resourceType: ResourceType = 'gitlens:commit-file';
- constructor(public readonly status: IGitStatusFile, public commit: GitCommit, private template: string, protected readonly context: ExtensionContext, protected readonly git: GitService) {
+ constructor(public readonly status: IGitStatusFile, public commit: GitCommit, protected readonly context: ExtensionContext, protected readonly git: GitService) {
super(new GitUri(Uri.file(path.resolve(commit.repoPath, status.fileName)), { repoPath: commit.repoPath, fileName: status.fileName, sha: commit.sha }));
}
@@ -25,7 +25,7 @@ export class CommitFileNode extends ExplorerNode {
}
}
- const item = new TreeItem(StatusFileFormatter.fromTemplate(this.template, this.status), TreeItemCollapsibleState.None);
+ const item = new TreeItem(StatusFileFormatter.fromTemplate(this.git.config.gitExplorer.commitFileFormat, this.status), TreeItemCollapsibleState.None);
item.contextValue = this.resourceType;
const icon = getGitStatusIcon(this.status.status);
@@ -40,8 +40,18 @@ export class CommitFileNode extends ExplorerNode {
}
getCommand(): Command | undefined {
+ let allowMissingPrevious = false;
+ let prefix = undefined;
+ if (this.status.status === 'A') {
+ allowMissingPrevious = true;
+ prefix = 'added in ';
+ }
+ else if (this.status.status === 'D') {
+ prefix = 'deleted in ';
+ }
+
return {
- title: 'Compare File with Previous',
+ title: 'Compare File with Previous Revision',
command: Commands.DiffWithPrevious,
arguments: [
GitUri.fromFileStatus(this.status, this.commit.repoPath),
@@ -51,7 +61,9 @@ export class CommitFileNode extends ExplorerNode {
showOptions: {
preserveFocus: true,
preview: true
- }
+ },
+ allowMissingPrevious: allowMissingPrevious,
+ rightTitlePrefix: prefix
} as DiffWithPreviousCommandArgs
]
};
diff --git a/src/views/commitNode.ts b/src/views/commitNode.ts
index 510b8a9..6c5b578 100644
--- a/src/views/commitNode.ts
+++ b/src/views/commitNode.ts
@@ -4,13 +4,13 @@ import { Command, ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'v
import { Commands, DiffWithPreviousCommandArgs } from '../commands';
import { CommitFileNode } from './commitFileNode';
import { ExplorerNode, ResourceType } from './explorerNode';
-import { CommitFormatter, GitCommit, GitService, GitUri } from '../gitService';
+import { CommitFormatter, GitLogCommit, GitService, GitUri } from '../gitService';
export class CommitNode extends ExplorerNode {
- readonly resourceType: ResourceType = 'commit';
+ readonly resourceType: ResourceType = 'gitlens:commit';
- constructor(public readonly commit: GitCommit, private template: string, protected readonly context: ExtensionContext, protected readonly git: GitService) {
+ constructor(public readonly commit: GitLogCommit, private readonly template: string, protected readonly context: ExtensionContext, protected readonly git: GitService) {
super(new GitUri(commit.uri, commit));
}
@@ -23,7 +23,7 @@ export class CommitNode extends ExplorerNode {
const commit = Iterables.first(log.commits.values());
if (commit === undefined) return [];
- return [...Iterables.map(commit.fileStatuses, s => new CommitFileNode(s, commit, this.git.config.gitExplorer.commitFileFormat, this.context, this.git))];
+ return [...Iterables.map(commit.fileStatuses, s => new CommitFileNode(s, commit, this.context, this.git))];
}
getTreeItem(): TreeItem {
@@ -31,7 +31,8 @@ export class CommitNode extends ExplorerNode {
if (this.commit.type === 'file') {
item.collapsibleState = TreeItemCollapsibleState.None;
item.command = this.getCommand();
- item.contextValue = 'commit-file';
+ const resourceType: ResourceType = 'gitlens:commit-file';
+ item.contextValue = resourceType;
}
else {
item.collapsibleState = TreeItemCollapsibleState.Collapsed;
@@ -47,8 +48,19 @@ export class CommitNode extends ExplorerNode {
}
getCommand(): Command | undefined {
+ let allowMissingPrevious = false;
+ let prefix = undefined;
+ const status = this.commit.fileStatuses[0];
+ if (status.status === 'A') {
+ allowMissingPrevious = true;
+ prefix = 'added in ';
+ }
+ else if (status.status === 'D') {
+ prefix = 'deleted in ';
+ }
+
return {
- title: 'Compare File with Previous',
+ title: 'Compare File with Previous Revision',
command: Commands.DiffWithPrevious,
arguments: [
new GitUri(this.uri, this.commit),
@@ -58,7 +70,9 @@ export class CommitNode extends ExplorerNode {
showOptions: {
preserveFocus: true,
preview: true
- }
+ },
+ allowMissingPrevious: allowMissingPrevious,
+ rightTitlePrefix: prefix
} as DiffWithPreviousCommandArgs
]
};
diff --git a/src/views/explorerNode.ts b/src/views/explorerNode.ts
index 8a958f0..abad231 100644
--- a/src/views/explorerNode.ts
+++ b/src/views/explorerNode.ts
@@ -2,7 +2,22 @@
import { Command, Event, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { GitUri } from '../gitService';
-export declare type ResourceType = 'text' | 'status' | 'branches' | 'repository' | 'branch-history' | 'file-history' | 'stash-history' | 'commit' | 'stash-commit' | 'commit-file';
+export declare type ResourceType =
+ 'gitlens:branches' |
+ 'gitlens:branch-history' |
+ 'gitlens:commit' |
+ 'gitlens:commit-file' |
+ 'gitlens:file-history' |
+ 'gitlens:history' |
+ 'gitlens:message' |
+ 'gitlens:remote' |
+ 'gitlens:remotes' |
+ 'gitlens:repository' |
+ 'gitlens:stash' |
+ 'gitlens:stash-file' |
+ 'gitlens:stashes' |
+ 'gitlens:status' |
+ 'gitlens:status-upstream';
export abstract class ExplorerNode {
@@ -22,11 +37,11 @@ export abstract class ExplorerNode {
refresh?(): void;
}
-export class TextExplorerNode extends ExplorerNode {
+export class MessageNode extends ExplorerNode {
- readonly resourceType: ResourceType = 'text';
+ readonly resourceType: ResourceType = 'gitlens:message';
- constructor(private text: string) {
+ constructor(private message: string) {
super(new GitUri());
}
@@ -35,7 +50,7 @@ export class TextExplorerNode extends ExplorerNode {
}
getTreeItem(): TreeItem | Promise {
- const item = new TreeItem(this.text, TreeItemCollapsibleState.None);
+ const item = new TreeItem(this.message, TreeItemCollapsibleState.None);
item.contextValue = this.resourceType;
return item;
}
diff --git a/src/views/explorerNodes.ts b/src/views/explorerNodes.ts
index e715c08..e08134e 100644
--- a/src/views/explorerNodes.ts
+++ b/src/views/explorerNodes.ts
@@ -6,7 +6,12 @@ export * from './branchHistoryNode';
export * from './commitFileNode';
export * from './commitNode';
export * from './fileHistoryNode';
+export * from './historyNode';
+export * from './remoteNode';
+export * from './remotesNode';
export * from './repositoryNode';
-export * from './stashCommitNode';
+export * from './stashFileNode';
export * from './stashNode';
-export * from './statusNode';
\ No newline at end of file
+export * from './stashesNode';
+export * from './statusNode';
+export * from './statusUpstreamNode';
\ No newline at end of file
diff --git a/src/views/fileHistoryExplorer.ts b/src/views/fileHistoryExplorer.ts
deleted file mode 100644
index 1487757..0000000
--- a/src/views/fileHistoryExplorer.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-'use strict';
-// import { Arrays } from '../system';
-import { commands, Event, EventEmitter, ExtensionContext, TextEditor, TreeDataProvider, TreeItem, Uri, window } from 'vscode';
-import { Commands, DiffWithPreviousCommandArgs, openEditor, OpenFileInRemoteCommandArgs } from '../commands';
-import { UriComparer } from '../comparers';
-import { CommitNode, ExplorerNode, FileHistoryNode, TextExplorerNode } from './explorerNodes';
-import { GitService, GitUri } from '../gitService';
-
-export * from './explorerNodes';
-
-export class FileHistoryExplorer implements TreeDataProvider {
-
- private _node?: ExplorerNode;
-
- private _onDidChangeTreeData = new EventEmitter();
- public get onDidChangeTreeData(): Event {
- return this._onDidChangeTreeData.event;
- }
-
- constructor(private context: ExtensionContext, private git: GitService) {
- commands.registerCommand('gitlens.fileHistoryExplorer.refresh', this.refresh, this);
- commands.registerCommand('gitlens.fileHistoryExplorer.openChanges', this.openChanges, this);
- commands.registerCommand('gitlens.fileHistoryExplorer.openFile', this.openFile, this);
- commands.registerCommand('gitlens.fileHistoryExplorer.openFileRevision', this.openFileRevision, this);
- commands.registerCommand('gitlens.fileHistoryExplorer.openFileInRemote', this.openFileInRemote, this);
- commands.registerCommand('gitlens.fileHistoryExplorer.openFileRevisionInRemote', this.openFileRevisionInRemote, this);
-
- context.subscriptions.push(window.onDidChangeActiveTextEditor(this.onActiveEditorChanged, this));
-
- this._node = this.getRootNode(window.activeTextEditor);
- }
-
- async getTreeItem(node: ExplorerNode): Promise {
- return node.getTreeItem();
- }
-
- async getChildren(node?: ExplorerNode): Promise {
- if (this._node === undefined) return [new TextExplorerNode('No active file')];
- if (node === undefined) return this._node.getChildren();
- return node.getChildren();
- }
-
- private getRootNode(editor: TextEditor | undefined): ExplorerNode | undefined {
- if (window.visibleTextEditors.length === 0) return undefined;
- if (editor === undefined) return this._node;
-
- const uri = this.git.getGitUriForFile(editor.document.uri) || new GitUri(editor.document.uri, { repoPath: this.git.repoPath, fileName: editor.document.uri.fsPath });
- if (UriComparer.equals(uri, this._node && this._node.uri)) return this._node;
-
- return new FileHistoryNode(uri, this.context, this.git);
- }
-
- private onActiveEditorChanged(editor: TextEditor | undefined) {
- const node = this.getRootNode(editor);
- if (node === this._node) return;
-
- this.refresh();
- }
-
- refresh(node?: ExplorerNode) {
- this._node = node || this.getRootNode(window.activeTextEditor);
- this._onDidChangeTreeData.fire();
- }
-
- private openChanges(node: CommitNode) {
- const command = node.getCommand();
- if (command === undefined || command.arguments === undefined) return;
-
- const [uri, args] = command.arguments as [Uri, DiffWithPreviousCommandArgs];
- args.showOptions!.preview = false;
- return commands.executeCommand(command.command, uri, args);
- }
-
- private openFile(node: CommitNode) {
- return openEditor(node.uri, { preserveFocus: true, preview: false });
- }
-
- private openFileRevision(node: CommitNode) {
- return openEditor(GitService.toGitContentUri(node.uri), { preserveFocus: true, preview: false });
- }
-
- private async openFileInRemote(node: CommitNode) {
- return commands.executeCommand(Commands.OpenFileInRemote, node.commit.uri, { range: false } as OpenFileInRemoteCommandArgs);
- }
-
- private async openFileRevisionInRemote(node: CommitNode) {
- return commands.executeCommand(Commands.OpenFileInRemote, new GitUri(node.commit.uri, node.commit), { range: false } as OpenFileInRemoteCommandArgs);
- }
-}
\ No newline at end of file
diff --git a/src/views/fileHistoryNode.ts b/src/views/fileHistoryNode.ts
index f55a42d..8497d59 100644
--- a/src/views/fileHistoryNode.ts
+++ b/src/views/fileHistoryNode.ts
@@ -2,13 +2,12 @@
import { Iterables } from '../system';
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { CommitNode } from './commitNode';
-import { ExplorerNode, ResourceType, TextExplorerNode } from './explorerNode';
+import { ExplorerNode, MessageNode, ResourceType } from './explorerNode';
import { GitService, GitUri } from '../gitService';
export class FileHistoryNode extends ExplorerNode {
- static readonly rootType: ResourceType = 'file-history';
- readonly resourceType: ResourceType = 'file-history';
+ readonly resourceType: ResourceType = 'gitlens:file-history';
constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
super(uri);
@@ -16,14 +15,20 @@ export class FileHistoryNode extends ExplorerNode {
async getChildren(): Promise {
const log = await this.git.getLogForFile(this.uri.repoPath, this.uri.fsPath, this.uri.sha);
- if (log === undefined) return [new TextExplorerNode('No file history')];
+ if (log === undefined) return [new MessageNode('No file history')];
- return [...Iterables.map(log.commits.values(), c => new CommitNode(c, this.git.config.fileHistoryExplorer.commitFormat, this.context, this.git))];
+ return [...Iterables.map(log.commits.values(), c => new CommitNode(c, this.git.config.gitExplorer.commitFormat, this.context, this.git))];
}
getTreeItem(): TreeItem {
- const item = new TreeItem(`History of ${this.uri.getFormattedPath()}`, TreeItemCollapsibleState.Expanded);
+ const item = new TreeItem(`${this.uri.getFormattedPath()}`, TreeItemCollapsibleState.Expanded);
item.contextValue = this.resourceType;
+
+ item.iconPath = {
+ dark: this.context.asAbsolutePath('images/dark/icon-history.svg'),
+ light: this.context.asAbsolutePath('images/light/icon-history.svg')
+ };
+
return item;
}
}
\ No newline at end of file
diff --git a/src/views/gitExplorer.ts b/src/views/gitExplorer.ts
index 2ebdceb..f405471 100644
--- a/src/views/gitExplorer.ts
+++ b/src/views/gitExplorer.ts
@@ -1,73 +1,162 @@
'use strict';
-// import { Functions } from '../system';
-import { commands, Event, EventEmitter, ExtensionContext, TreeDataProvider, TreeItem, Uri } from 'vscode';
+import { Functions } from '../system';
+import { commands, Event, EventEmitter, ExtensionContext, TextDocumentShowOptions, TextEditor, TreeDataProvider, TreeItem, Uri, window } from 'vscode';
+import { Commands, DiffWithPreviousCommandArgs, DiffWithWorkingCommandArgs, openEditor, OpenFileInRemoteCommandArgs } from '../commands';
import { UriComparer } from '../comparers';
-import { ExplorerNode, FileHistoryNode, RepositoryNode, ResourceType, StashNode } from './explorerNodes';
+import { CommandContext, setCommandContext } from '../constants';
+import { CommitFileNode, CommitNode, ExplorerNode, HistoryNode, MessageNode, RepositoryNode, StashNode } from './explorerNodes';
import { GitService, GitUri } from '../gitService';
export * from './explorerNodes';
+export type GitExplorerView =
+ 'history' |
+ 'repository';
+export const GitExplorerView = {
+ History: 'history' as GitExplorerView,
+ Repository: 'repository' as GitExplorerView
+};
+
+export interface OpenFileRevisionCommandArgs {
+ uri?: Uri;
+ showOptions?: TextDocumentShowOptions;
+}
+
export class GitExplorer implements TreeDataProvider {
- // private _refreshDebounced: () => void;
+ private _root?: ExplorerNode;
+ private _view: GitExplorerView = GitExplorerView.Repository;
private _onDidChangeTreeData = new EventEmitter();
public get onDidChangeTreeData(): Event {
return this._onDidChangeTreeData.event;
}
- private _roots: ExplorerNode[] = [];
+ constructor(private readonly context: ExtensionContext, private readonly git: GitService) {
+ commands.registerCommand('gitlens.gitExplorer.switchToHistoryView', () => this.switchTo(GitExplorerView.History), this);
+ commands.registerCommand('gitlens.gitExplorer.switchToRepositoryView', () => this.switchTo(GitExplorerView.Repository), this);
+ commands.registerCommand('gitlens.gitExplorer.refresh', this.refresh, this);
+ commands.registerCommand('gitlens.gitExplorer.openChanges', this.openChanges, this);
+ commands.registerCommand('gitlens.gitExplorer.openChangesWithWorking', this.openChangesWithWorking, this);
+ commands.registerCommand('gitlens.gitExplorer.openFile', this.openFile, this);
+ commands.registerCommand('gitlens.gitExplorer.openFileRevision', this.openFileRevision, this);
+ commands.registerCommand('gitlens.gitExplorer.openFileRevisionInRemote', this.openFileRevisionInRemote, this);
+ commands.registerCommand('gitlens.gitExplorer.openChangedFiles', this.openChangedFiles, this);
+ commands.registerCommand('gitlens.gitExplorer.openChangedFileRevisions', this.openChangedFileRevisions, this);
+
+ const fn = Functions.debounce(this.onActiveEditorChanged, 500);
+ context.subscriptions.push(window.onDidChangeActiveTextEditor(fn, this));
+
+ this._view = this.git.config.gitExplorer.view;
+ setCommandContext(CommandContext.GitExplorerView, this._view);
+ this._root = this.getRootNode();
+ }
- constructor(private context: ExtensionContext, private git: GitService) {
- commands.registerCommand('gitlens.gitExplorer.refresh', () => this.refresh());
+ async getTreeItem(node: ExplorerNode): Promise {
+ return node.getTreeItem();
+ }
+
+ async getChildren(node?: ExplorerNode): Promise {
+ if (this._root === undefined) {
+ if (this._view === GitExplorerView.History) return [new MessageNode('No active file; no history to show')];
+ return [];
+ }
- // this._refreshDebounced = Functions.debounce(this.refresh.bind(this), 250);
+ if (node === undefined) return this._root.getChildren();
+ return node.getChildren();
+ }
- // const editor = window.activeTextEditor;
+ private getRootNode(editor?: TextEditor): ExplorerNode | undefined {
+ const uri = new GitUri(Uri.file(this.git.repoPath), { repoPath: this.git.repoPath, fileName: this.git.repoPath });
- // const uri = (editor !== undefined && editor.document !== undefined)
- // ? new GitUri(editor.document.uri, { repoPath: git.repoPath, fileName: editor.document.uri.fsPath })
- // : new GitUri(Uri.file(git.repoPath), { repoPath: git.repoPath, fileName: git.repoPath });
+ switch (this._view) {
+ case GitExplorerView.History: return this.getHistoryNode(editor || window.activeTextEditor);
+ case GitExplorerView.Repository: return new RepositoryNode(uri, this.context, this.git);
+ }
- const uri = new GitUri(Uri.file(git.repoPath), { repoPath: git.repoPath, fileName: git.repoPath });
- this._roots.push(new RepositoryNode(uri, context, git));
+ return undefined;
}
- async getTreeItem(node: ExplorerNode): Promise {
- // if (node.onDidChangeTreeData !== undefined) {
- // node.onDidChangeTreeData(() => setTimeout(this._refreshDebounced, 1));
- // }
- return node.getTreeItem();
+ private getHistoryNode(editor: TextEditor | undefined): ExplorerNode | undefined {
+ if (window.visibleTextEditors.length === 0) return undefined;
+ if (editor === undefined) return this._root;
+
+ const uri = this.git.getGitUriForFile(editor.document.uri) || new GitUri(editor.document.uri, { repoPath: this.git.repoPath, fileName: editor.document.uri.fsPath });
+ if (UriComparer.equals(uri, this._root && this._root.uri)) return this._root;
+
+ return new HistoryNode(uri, this.context, this.git);
}
- async getChildren(node?: ExplorerNode): Promise {
- if (this._roots.length === 0) return [];
- if (node === undefined) return this._roots;
+ private onActiveEditorChanged(editor: TextEditor | undefined) {
+ if (this._view !== GitExplorerView.History) return;
+ const root = this.getRootNode(editor);
+ if (root === this._root) return;
- return node.getChildren();
+ this.refresh(root);
}
- addHistory(uri: GitUri) {
- this._add(uri, FileHistoryNode);
+ refresh(root?: ExplorerNode) {
+ this._root = root || this.getRootNode();
+ this._onDidChangeTreeData.fire();
+ }
+
+ switchTo(view: GitExplorerView) {
+ if (this._view === view) return;
+
+ this._view = view;
+ setCommandContext(CommandContext.GitExplorerView, this._view);
+
+ this._root = undefined;
+ this.refresh();
+ }
+
+ private openChanges(node: CommitNode | StashNode) {
+ const command = node.getCommand();
+ if (command === undefined || command.arguments === undefined) return;
+
+ const [uri, args] = command.arguments as [Uri, DiffWithPreviousCommandArgs];
+ args.showOptions!.preview = false;
+ return commands.executeCommand(command.command, uri, args);
}
- addStash(uri: GitUri) {
- this._add(uri, StashNode);
+ private openChangesWithWorking(node: CommitNode | StashNode) {
+ const args: DiffWithWorkingCommandArgs = {
+ commit: node.commit,
+ showOptions: {
+ preserveFocus: true,
+ preview: false
+
+ }
+ };
+ return commands.executeCommand(Commands.DiffWithWorking, new GitUri(node.commit.uri, node.commit), args);
+ }
+
+ private openFile(node: CommitNode | StashNode) {
+ return openEditor(node.uri, { preserveFocus: true, preview: false });
+ }
+
+ private openFileRevision(node: CommitNode | StashNode | CommitFileNode, options: OpenFileRevisionCommandArgs = { showOptions: { preserveFocus: true, preview: false } }) {
+ return openEditor(options.uri || GitService.toGitContentUri(node.uri), options.showOptions || { preserveFocus: true, preview: false });
}
- private _add(uri: GitUri, type: { new (uri: GitUri, context: ExtensionContext, git: GitService): T, rootType: ResourceType }) {
- if (!this._roots.some(_ => _.resourceType === type.rootType && UriComparer.equals(uri, _.uri))) {
- this._roots.push(new type(uri, this.context, this.git));
+ private async openChangedFiles(node: CommitNode | StashNode, options: TextDocumentShowOptions = { preserveFocus: false, preview: false }) {
+ const repoPath = node.commit.repoPath;
+ const uris = node.commit.fileStatuses.filter(s => s.status !== 'D').map(s => GitUri.fromFileStatus(s, repoPath));
+ for (const uri of uris) {
+ await openEditor(uri, options);
}
- this.refresh();
}
- clear() {
- this._roots = [];
- this.refresh();
+ private async openChangedFileRevisions(node: CommitNode | StashNode, options: TextDocumentShowOptions = { preserveFocus: false, preview: false }) {
+ const uris = node.commit.fileStatuses
+ .filter(s => s.status !== 'D')
+ .map(s => GitService.toGitContentUri(node.commit.sha, node.commit.shortSha, s.fileName, node.commit.repoPath, s.originalFileName));
+ for (const uri of uris) {
+ await openEditor(uri, options);
+ }
}
- refresh() {
- this._onDidChangeTreeData.fire();
+ private async openFileRevisionInRemote(node: CommitNode | StashNode) {
+ return commands.executeCommand(Commands.OpenFileInRemote, new GitUri(node.commit.uri, node.commit), { range: false } as OpenFileInRemoteCommandArgs);
}
}
\ No newline at end of file
diff --git a/src/views/historyNode.ts b/src/views/historyNode.ts
new file mode 100644
index 0000000..96acc31
--- /dev/null
+++ b/src/views/historyNode.ts
@@ -0,0 +1,30 @@
+'use strict';
+import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
+import { ExplorerNode, ResourceType } from './explorerNode';
+import { FileHistoryNode } from './fileHistoryNode';
+import { GitService, GitUri } from '../gitService';
+
+export class HistoryNode extends ExplorerNode {
+
+ readonly resourceType: ResourceType = 'gitlens:history';
+
+ constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
+ super(uri);
+ }
+
+ async getChildren(): Promise {
+ return [new FileHistoryNode(this.uri, this.context, this.git)];
+ }
+
+ getTreeItem(): TreeItem {
+ const item = new TreeItem(`${this.uri.getFormattedPath()}`, TreeItemCollapsibleState.Expanded);
+ item.contextValue = this.resourceType;
+
+ item.iconPath = {
+ dark: this.context.asAbsolutePath('images/dark/icon-history.svg'),
+ light: this.context.asAbsolutePath('images/light/icon-history.svg')
+ };
+
+ return item;
+ }
+}
\ No newline at end of file
diff --git a/src/views/remoteNode.ts b/src/views/remoteNode.ts
new file mode 100644
index 0000000..c19ba24
--- /dev/null
+++ b/src/views/remoteNode.ts
@@ -0,0 +1,35 @@
+'use strict';
+import { Iterables } from '../system';
+import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
+import { BranchHistoryNode } from './branchHistoryNode';
+import { ExplorerNode, ResourceType } from './explorerNode';
+import { GitRemote, GitService, GitUri } from '../gitService';
+
+export class RemoteNode extends ExplorerNode {
+
+ readonly resourceType: ResourceType = 'gitlens:remote';
+
+ constructor(public readonly remote: GitRemote, uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
+ super(uri);
+ }
+
+ async getChildren(): Promise {
+ const branches = await this.git.getBranches(this.uri.repoPath!);
+ if (branches === undefined) return [];
+
+ branches.sort((a, b) => a.name.localeCompare(b.name));
+ return [...Iterables.filterMap(branches, b => !b.remote || !b.name.startsWith(this.remote.name) ? undefined : new BranchHistoryNode(b, this.remote, this.uri, this.git.config.gitExplorer.commitFormat, this.context, this.git))];
+ }
+
+ getTreeItem(): TreeItem {
+ const item = new TreeItem(this.remote.name, TreeItemCollapsibleState.Collapsed);
+ item.contextValue = this.resourceType;
+
+ // item.iconPath = {
+ // dark: this.context.asAbsolutePath('images/dark/icon-remote.svg'),
+ // light: this.context.asAbsolutePath('images/light/icon-remote.svg')
+ // };
+
+ return item;
+ }
+ }
diff --git a/src/views/remotesNode.ts b/src/views/remotesNode.ts
new file mode 100644
index 0000000..be7852b
--- /dev/null
+++ b/src/views/remotesNode.ts
@@ -0,0 +1,35 @@
+'use strict';
+import { Arrays, Iterables } from '../system';
+import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
+import { ExplorerNode, ResourceType } from './explorerNode';
+import { GitService, GitUri } from '../gitService';
+import { RemoteNode } from './remoteNode';
+
+export class RemotesNode extends ExplorerNode {
+
+ readonly resourceType: ResourceType = 'gitlens:remotes';
+
+ constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
+ super(uri);
+ }
+
+ async getChildren(): Promise {
+ const remotes = Arrays.uniqueBy(await this.git.getRemotes(this.uri.repoPath!), r => r.url, r => !!r.provider);
+ if (remotes === undefined) return [];
+
+ remotes.sort((a, b) => a.name.localeCompare(b.name));
+ return [...Iterables.map(remotes, r => new RemoteNode(r, this.uri, this.context, this.git))];
+ }
+
+ getTreeItem(): TreeItem {
+ const item = new TreeItem(`Remotes`, TreeItemCollapsibleState.Collapsed);
+ item.contextValue = this.resourceType;
+
+ item.iconPath = {
+ dark: this.context.asAbsolutePath('images/dark/icon-remote.svg'),
+ light: this.context.asAbsolutePath('images/light/icon-remote.svg')
+ };
+
+ return item;
+ }
+ }
diff --git a/src/views/repositoryNode.ts b/src/views/repositoryNode.ts
index 5d6cff5..d69519e 100644
--- a/src/views/repositoryNode.ts
+++ b/src/views/repositoryNode.ts
@@ -4,12 +4,13 @@ import { BranchesNode } from './branchesNode';
import { GlyphChars } from '../constants';
import { ExplorerNode, ResourceType } from './explorerNode';
import { GitService, GitUri } from '../gitService';
-// import { StatusNode } from './statusNode';
+import { RemotesNode } from './remotesNode';
+import { StatusNode } from './statusNode';
+import { StashesNode } from './stashesNode';
export class RepositoryNode extends ExplorerNode {
- static readonly rootType: ResourceType = 'repository';
- readonly resourceType: ResourceType = 'repository';
+ readonly resourceType: ResourceType = 'gitlens:repository';
constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
super(uri);
@@ -17,8 +18,10 @@ export class RepositoryNode extends ExplorerNode {
async getChildren(): Promise {
return [
- // new StatusNode(this.uri, this.context, this.git),
- new BranchesNode(this.uri, this.context, this.git)
+ new StatusNode(this.uri, this.context, this.git),
+ new BranchesNode(this.uri, this.context, this.git),
+ new RemotesNode(this.uri, this.context, this.git),
+ new StashesNode(this.uri, this.context, this.git)
];
}
diff --git a/src/views/stashCommitNode.ts b/src/views/stashCommitNode.ts
deleted file mode 100644
index 5b5655c..0000000
--- a/src/views/stashCommitNode.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-'use strict';
-import { Event, EventEmitter, ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
-import { CommitFileNode } from './commitFileNode';
-import { ExplorerNode, ResourceType } from './explorerNode';
-import { CommitFormatter, GitService, GitStashCommit, GitUri } from '../gitService';
-
-export class StashCommitNode extends ExplorerNode {
-
- readonly resourceType: ResourceType = 'stash-commit';
-
- private _onDidChangeTreeData = new EventEmitter();
- public get onDidChangeTreeData(): Event {
- return this._onDidChangeTreeData.event;
- }
-
- constructor(public readonly commit: GitStashCommit, protected readonly context: ExtensionContext, protected readonly git: GitService) {
- super(new GitUri(commit.uri, commit));
- }
-
- async getChildren(): Promise {
- return Promise.resolve((this.commit as GitStashCommit).fileStatuses.map(_ => new CommitFileNode(_, this.commit, this.git.config.stashExplorer.stashFileFormat, this.context, this.git)));
- }
-
- getTreeItem(): TreeItem {
- const label = CommitFormatter.fromTemplate(this.git.config.stashExplorer.stashFormat, this.commit, this.git.config.defaultDateFormat);
-
- const item = new TreeItem(label, TreeItemCollapsibleState.Collapsed);
- item.contextValue = this.resourceType;
- // item.command = {
- // title: 'Show Stash Details',
- // command: Commands.ShowQuickCommitDetails,
- // arguments: [
- // new GitUri(commit.uri, commit),
- // {
- // commit: this.commit,
- // sha: this.commit.sha
- // } as ShowQuickCommitDetailsCommandArgs
- // ]
- // };
- return item;
- }
-
- refresh() {
- this._onDidChangeTreeData.fire();
- }
-}
\ No newline at end of file
diff --git a/src/views/stashExplorer.ts b/src/views/stashExplorer.ts
deleted file mode 100644
index ab067ed..0000000
--- a/src/views/stashExplorer.ts
+++ /dev/null
@@ -1,75 +0,0 @@
-'use strict';
-// import { Functions } from '../system';
-import { commands, Event, EventEmitter, ExtensionContext, TreeDataProvider, TreeItem, Uri } from 'vscode';
-import { Commands, DiffWithPreviousCommandArgs, openEditor, OpenFileInRemoteCommandArgs } from '../commands';
-import { ExplorerNode, StashCommitNode, StashNode } from './explorerNodes';
-import { GitService, GitUri } from '../gitService';
-
-export * from './explorerNodes';
-
-export class StashExplorer implements TreeDataProvider {
-
- private _node: ExplorerNode;
-
- private _onDidChangeTreeData = new EventEmitter();
- public get onDidChangeTreeData(): Event {
- return this._onDidChangeTreeData.event;
- }
-
- constructor(private context: ExtensionContext, private git: GitService) {
- commands.registerCommand('gitlens.stashExplorer.refresh', this.refresh, this);
- commands.registerCommand('gitlens.stashExplorer.openChanges', this.openChanges, this);
- commands.registerCommand('gitlens.stashExplorer.openFile', this.openFile, this);
- commands.registerCommand('gitlens.stashExplorer.openStashedFile', this.openStashedFile, this);
- commands.registerCommand('gitlens.stashExplorer.openFileInRemote', this.openFileInRemote, this);
-
- context.subscriptions.push(this.git.onDidChangeRepo(this.onRepoChanged, this));
-
- this._node = this.getRootNode();
- }
-
- async getTreeItem(node: ExplorerNode): Promise {
- return node.getTreeItem();
- }
-
- async getChildren(node?: ExplorerNode): Promise {
- if (node === undefined) return this._node.getChildren();
- return node.getChildren();
- }
-
- private getRootNode(): ExplorerNode {
- const uri = new GitUri(Uri.file(this.git.repoPath), { repoPath: this.git.repoPath, fileName: this.git.repoPath });
- return new StashNode(uri, this.context, this.git);
- }
-
- private onRepoChanged(reasons: ('stash' | 'unknown')[]) {
- if (!reasons.includes('stash')) return;
-
- this.refresh();
- }
-
- refresh() {
- this._onDidChangeTreeData.fire();
- }
-
- private openChanges(node: StashCommitNode) {
- const command = node.getCommand();
- if (command === undefined || command.arguments === undefined) return;
-
- const [uri, args] = command.arguments as [Uri, DiffWithPreviousCommandArgs];
- args.showOptions!.preview = false;
- return commands.executeCommand(command.command, uri, args);
- }
-
- private openFile(node: StashCommitNode) {
- return openEditor(node.uri, { preserveFocus: true, preview: false });
- }
-
- private openStashedFile(node: StashCommitNode) {
- return openEditor(GitService.toGitContentUri(node.uri), { preserveFocus: true, preview: false });
- }
-
- private openFileInRemote(node: StashCommitNode) {
- return commands.executeCommand(Commands.OpenFileInRemote, node.commit.uri, { range: false } as OpenFileInRemoteCommandArgs);
- }
-}
\ No newline at end of file
diff --git a/src/views/stashFileNode.ts b/src/views/stashFileNode.ts
new file mode 100644
index 0000000..16dbbd5
--- /dev/null
+++ b/src/views/stashFileNode.ts
@@ -0,0 +1,14 @@
+'use strict';
+import { ExtensionContext } from 'vscode';
+import { ResourceType } from './explorerNode';
+import { GitService, GitStashCommit, IGitStatusFile } from '../gitService';
+import { CommitFileNode } from './commitFileNode';
+
+export class StashFileNode extends CommitFileNode {
+
+ readonly resourceType: ResourceType = 'gitlens:stash-file';
+
+ constructor(readonly status: IGitStatusFile, readonly commit: GitStashCommit, readonly context: ExtensionContext, readonly git: GitService) {
+ super(status, commit, context, git);
+ }
+}
\ No newline at end of file
diff --git a/src/views/stashNode.ts b/src/views/stashNode.ts
index 4cc83b3..9fc5e64 100644
--- a/src/views/stashNode.ts
+++ b/src/views/stashNode.ts
@@ -1,29 +1,35 @@
'use strict';
-import { Iterables } from '../system';
-import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
-import { ExplorerNode, ResourceType, TextExplorerNode } from './explorerNode';
-import { GitService, GitUri } from '../gitService';
-import { StashCommitNode } from './stashCommitNode';
+import { Event, EventEmitter, ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
+import { ExplorerNode, ResourceType } from './explorerNode';
+import { CommitFormatter, GitService, GitStashCommit, GitUri } from '../gitService';
+import { StashFileNode } from './stashFileNode';
export class StashNode extends ExplorerNode {
- static readonly rootType: ResourceType = 'stash-history';
- readonly resourceType: ResourceType = 'stash-history';
+ readonly resourceType: ResourceType = 'gitlens:stash';
- constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
- super(uri);
- }
+ private _onDidChangeTreeData = new EventEmitter();
+ public get onDidChangeTreeData(): Event {
+ return this._onDidChangeTreeData.event;
+ }
- async getChildren(): Promise {
- const stash = await this.git.getStashList(this.uri.repoPath!);
- if (stash === undefined) return [new TextExplorerNode('No stashed changes')];
+ constructor(public readonly commit: GitStashCommit, protected readonly context: ExtensionContext, protected readonly git: GitService) {
+ super(new GitUri(commit.uri, commit));
+ }
- return [...Iterables.map(stash.commits.values(), c => new StashCommitNode(c, this.context, this.git))];
+ async getChildren(): Promise {
+ return Promise.resolve((this.commit as GitStashCommit).fileStatuses.map(s => new StashFileNode(s, this.commit, this.context, this.git)));
}
getTreeItem(): TreeItem {
- const item = new TreeItem(`Stashed Changes`, TreeItemCollapsibleState.Collapsed);
+ const label = CommitFormatter.fromTemplate(this.git.config.gitExplorer.stashFormat, this.commit, this.git.config.defaultDateFormat);
+
+ const item = new TreeItem(label, TreeItemCollapsibleState.Collapsed);
item.contextValue = this.resourceType;
return item;
}
+
+ refresh() {
+ this._onDidChangeTreeData.fire();
+ }
}
\ No newline at end of file
diff --git a/src/views/stashesNode.ts b/src/views/stashesNode.ts
new file mode 100644
index 0000000..42f1035
--- /dev/null
+++ b/src/views/stashesNode.ts
@@ -0,0 +1,34 @@
+'use strict';
+import { Iterables } from '../system';
+import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
+import { ExplorerNode, MessageNode, ResourceType } from './explorerNode';
+import { GitService, GitUri } from '../gitService';
+import { StashNode } from './stashNode';
+
+export class StashesNode extends ExplorerNode {
+
+ readonly resourceType: ResourceType = 'gitlens:stashes';
+
+ constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
+ super(uri);
+ }
+
+ async getChildren(): Promise {
+ const stash = await this.git.getStashList(this.uri.repoPath!);
+ if (stash === undefined) return [new MessageNode('No stashed changes')];
+
+ return [...Iterables.map(stash.commits.values(), c => new StashNode(c, this.context, this.git))];
+ }
+
+ getTreeItem(): TreeItem {
+ const item = new TreeItem(`Stashes`, TreeItemCollapsibleState.Collapsed);
+ item.contextValue = this.resourceType;
+
+ item.iconPath = {
+ dark: this.context.asAbsolutePath('images/dark/icon-stash.svg'),
+ light: this.context.asAbsolutePath('images/light/icon-stash.svg')
+ };
+
+ return item;
+ }
+}
\ No newline at end of file
diff --git a/src/views/statusNode.ts b/src/views/statusNode.ts
index e614b87..b00fd23 100644
--- a/src/views/statusNode.ts
+++ b/src/views/statusNode.ts
@@ -1,34 +1,60 @@
-import { Strings } from '../system';
import { ExtensionContext, TreeItem, TreeItemCollapsibleState } from 'vscode';
-import { GlyphChars } from '../constants';
import { ExplorerNode, ResourceType } from './explorerNode';
import { GitService, GitUri } from '../gitService';
+import { StatusUpstreamNode } from './statusUpstreamNode';
export class StatusNode extends ExplorerNode {
- readonly resourceType: ResourceType = 'status';
+ readonly resourceType: ResourceType = 'gitlens:status';
constructor(uri: GitUri, protected readonly context: ExtensionContext, protected readonly git: GitService) {
super(uri);
}
async getChildren(): Promise {
- return [];
- // const status = await this.git.getStatusForRepo(this.uri.repoPath!);
- // if (status === undefined) return [];
+ const status = await this.git.getStatusForRepo(this.uri.repoPath!);
+ if (status === undefined) return [];
+
+ const children = [];
+
+ if (status.state.behind) {
+ children.push(new StatusUpstreamNode(status, 'behind', this.git.config.gitExplorer.commitFormat, this.context, this.git));
+ }
+
+ if (status.state.ahead) {
+ children.push(new StatusUpstreamNode(status, 'ahead', this.git.config.gitExplorer.commitFormat, this.context, this.git));
+ }
- // return [...Iterables.map(status.files, b => new CommitFile(b, this.uri, this.context, this.git))];
+ return children;
}
async getTreeItem(): Promise {
const status = await this.git.getStatusForRepo(this.uri.repoPath!);
- let suffix = '';
- if (status !== undefined) {
- suffix = ` ${GlyphChars.Dash} ${GlyphChars.ArrowUp} ${status.state.ahead} ${GlyphChars.ArrowDown} ${status.state.behind} ${Strings.pad(GlyphChars.Dot, 1, 1)} ${status.branch} ${GlyphChars.ArrowLeftRight} ${status.upstream}`;
+ if (status === undefined) return new TreeItem('No repo status');
+
+ let hasChildren = false;
+ let label = '';
+ if (status.upstream) {
+ if (!status.state.ahead && !status.state.behind) {
+ label = `${status.branch} is up-to-date with ${status.upstream}`;
+ }
+ else {
+ label = `${status.branch} is not up-to-date with ${status.upstream}`;
+ hasChildren = true;
+ }
+ }
+ else {
+ label = `${status.branch} is up-to-date`;
}
- const item = new TreeItem(`Status${suffix}`, TreeItemCollapsibleState.Collapsed);
+ const item = new TreeItem(label, hasChildren ? TreeItemCollapsibleState.Expanded : TreeItemCollapsibleState.None);
item.contextValue = this.resourceType;
+
+ item.iconPath = {
+ dark: this.context.asAbsolutePath('images/dark/icon-repo.svg'),
+ light: this.context.asAbsolutePath('images/light/icon-repo.svg')
+ };
+
return item;
}
}
\ No newline at end of file
diff --git a/src/views/statusUpstreamNode.ts b/src/views/statusUpstreamNode.ts
new file mode 100644
index 0000000..1f2a230
--- /dev/null
+++ b/src/views/statusUpstreamNode.ts
@@ -0,0 +1,53 @@
+'use strict';
+import { Iterables } from '../system';
+import { ExtensionContext, TreeItem, TreeItemCollapsibleState, Uri } from 'vscode';
+import { ExplorerNode, ResourceType } from './explorerNode';
+import { GitService, GitStatus, GitUri } from '../gitService';
+import { CommitNode } from './commitNode';
+
+export class StatusUpstreamNode extends ExplorerNode {
+
+ readonly resourceType: ResourceType = 'gitlens:status-upstream';
+
+ constructor(public readonly status: GitStatus, public readonly direction: 'ahead' | 'behind', private readonly template: string, protected readonly context: ExtensionContext, protected readonly git: GitService) {
+ super(new GitUri(Uri.file(status.repoPath), { repoPath: status.repoPath, fileName: status.repoPath }));
+ }
+
+ async getChildren(): Promise {
+ const range = this.direction === 'ahead'
+ ? `${this.status.upstream}..${this.status.branch}`
+ : `${this.status.branch}..${this.status.upstream}`;
+ let log = await this.git.getLogForRepo(this.uri.repoPath!, range);
+ if (log === undefined) return [];
+
+ if (this.direction !== 'ahead') return [...Iterables.map(log.commits.values(), c => new CommitNode(c, this.template, this.context, this.git))];
+
+ // Since the last commit when we are looking 'ahead' can have no previous (because of the range given) -- look it up
+ const commits = Array.from(log.commits.values());
+ const commit = commits[commits.length - 1];
+ if (commit.previousSha === undefined) {
+ log = await this.git.getLogForRepo(this.uri.repoPath!, commit.sha, 2);
+ if (log !== undefined) {
+ commits[commits.length - 1] = Iterables.first(log.commits.values());
+ }
+ }
+
+ return [...Iterables.map(commits, c => new CommitNode(c, this.template, this.context, this.git))];
+}
+
+ async getTreeItem(): Promise {
+ const label = this.direction === 'ahead'
+ ? `${this.status.state.ahead} commit${this.status.state.ahead > 1 ? 's' : ''} ahead` // of ${this.status.upstream}`
+ : `${this.status.state.behind} commit${this.status.state.behind > 1 ? 's' : ''} behind`; // ${this.status.upstream}`;
+
+ const item = new TreeItem(label, TreeItemCollapsibleState.Collapsed);
+ item.contextValue = this.resourceType;
+
+ item.iconPath = {
+ dark: this.context.asAbsolutePath(`images/dark/icon-${this.direction === 'ahead' ? 'upload' : 'download'}.svg`),
+ light: this.context.asAbsolutePath(`images/light/icon-${this.direction === 'ahead' ? 'upload' : 'download'}.svg`)
+ };
+
+ return item;
+ }
+}
\ No newline at end of file