diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index a180d0516..c17e1dc1c 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -10,6 +10,7 @@ Have you read GitHub for Unity's Code of Conduct? By filing an Issue, you are ex
- Include the log file in the PR.
- On Windows, the extension log file is at `%LOCALAPPDATA%\GitHubUnity\github-unity.log`
- On macOS, the extension log file is at `~/Library/Logs/GitHubUnity/github-unity.log`
+ - On linux, the extension log file is at `~/.local/share/GitHubUnity/github-unity.log`
### Description
diff --git a/.gitignore b/.gitignore
index 67d4e53bc..7fc38bf13 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,5 @@ _NCrunch_GitHub.Unity
.DS_Store
build/
TestResult.xml
-submodules/
*.stackdump
*.lastcodeanalysissucceeded
\ No newline at end of file
diff --git a/.gitmodules b/.gitmodules
index d9e14a3e9..8ecda521f 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,6 @@
[submodule "script"]
path = script
url = git@github.com:github-for-unity/UnityBuildScripts
+[submodule "submodules/packaging"]
+ path = submodules/packaging
+ url = https://github.com/github-for-unity/packaging
diff --git a/GitHub.Unity.sln b/GitHub.Unity.sln
index 0197b47fc..0707a70fa 100644
--- a/GitHub.Unity.sln
+++ b/GitHub.Unity.sln
@@ -5,8 +5,12 @@ VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitHub.Unity", "src\UnityExtension\Assets\Editor\GitHub.Unity\GitHub.Unity.csproj", "{ADD7A18B-DD2A-4C22-A2C1-488964EFF30A}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitHub.Unity.45", "src\UnityExtension\Assets\Editor\GitHub.Unity\GitHub.Unity.45.csproj", "{ADD7A18B-DD2A-4C22-A2C1-488964EFF30B}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitHub.Api", "src\GitHub.Api\GitHub.Api.csproj", "{B389ADAF-62CC-486E-85B4-2D8B078DF763}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitHub.Api.45", "src\GitHub.Api\GitHub.Api.45.csproj", "{B389ADAF-62CC-486E-85B4-2D8B078DF76B}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitHub.Logging", "src\GitHub.Logging\GitHub.Logging.csproj", "{BB6A8EDA-15D8-471B-A6ED-EE551E0B3BA0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CopyLibrariesToDevelopmentFolder", "src\packaging\CopyLibrariesToDevelopmentFolder\CopyLibrariesToDevelopmentFolder.csproj", "{44257C81-EE4A-4817-9AF4-A26C02AA6DD4}"
@@ -31,6 +35,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestWebServer", "src\tests\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityTests", "src\UnityExtension\Assets\Editor\UnityTests\UnityTests.csproj", "{462CDBD4-0DDA-4854-1B13-CFDACBFB66F5}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExtensionLoader", "src\UnityExtension\Assets\Editor\GitHub.Unity\ExtensionLoader\ExtensionLoader.csproj", "{6B0EAB30-511A-44C1-87FE-D9AB7E34D115}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityShim", "src\UnityShim\UnityShim.csproj", "{F94F8AE1-C171-4A83-89E8-6557CA91A188}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -46,6 +54,13 @@ Global
{ADD7A18B-DD2A-4C22-A2C1-488964EFF30A}.dev|Any CPU.Build.0 = dev|Any CPU
{ADD7A18B-DD2A-4C22-A2C1-488964EFF30A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ADD7A18B-DD2A-4C22-A2C1-488964EFF30A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {ADD7A18B-DD2A-4C22-A2C1-488964EFF30B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {ADD7A18B-DD2A-4C22-A2C1-488964EFF30B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {ADD7A18B-DD2A-4C22-A2C1-488964EFF30B}.DebugNoUnity|Any CPU.ActiveCfg = Debug|Any CPU
+ {ADD7A18B-DD2A-4C22-A2C1-488964EFF30B}.dev|Any CPU.ActiveCfg = dev|Any CPU
+ {ADD7A18B-DD2A-4C22-A2C1-488964EFF30B}.dev|Any CPU.Build.0 = dev|Any CPU
+ {ADD7A18B-DD2A-4C22-A2C1-488964EFF30B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {ADD7A18B-DD2A-4C22-A2C1-488964EFF30B}.Release|Any CPU.Build.0 = Release|Any CPU
{B389ADAF-62CC-486E-85B4-2D8B078DF763}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B389ADAF-62CC-486E-85B4-2D8B078DF763}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B389ADAF-62CC-486E-85B4-2D8B078DF763}.DebugNoUnity|Any CPU.ActiveCfg = Debug|Any CPU
@@ -54,6 +69,14 @@ Global
{B389ADAF-62CC-486E-85B4-2D8B078DF763}.dev|Any CPU.Build.0 = dev|Any CPU
{B389ADAF-62CC-486E-85B4-2D8B078DF763}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B389ADAF-62CC-486E-85B4-2D8B078DF763}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B389ADAF-62CC-486E-85B4-2D8B078DF76B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B389ADAF-62CC-486E-85B4-2D8B078DF76B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B389ADAF-62CC-486E-85B4-2D8B078DF76B}.DebugNoUnity|Any CPU.ActiveCfg = Debug|Any CPU
+ {B389ADAF-62CC-486E-85B4-2D8B078DF76B}.DebugNoUnity|Any CPU.Build.0 = Debug|Any CPU
+ {B389ADAF-62CC-486E-85B4-2D8B078DF76B}.dev|Any CPU.ActiveCfg = dev|Any CPU
+ {B389ADAF-62CC-486E-85B4-2D8B078DF76B}.dev|Any CPU.Build.0 = dev|Any CPU
+ {B389ADAF-62CC-486E-85B4-2D8B078DF76B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B389ADAF-62CC-486E-85B4-2D8B078DF76B}.Release|Any CPU.Build.0 = Release|Any CPU
{BB6A8EDA-15D8-471B-A6ED-EE551E0B3BA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BB6A8EDA-15D8-471B-A6ED-EE551E0B3BA0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BB6A8EDA-15D8-471B-A6ED-EE551E0B3BA0}.DebugNoUnity|Any CPU.ActiveCfg = Debug|Any CPU
@@ -132,6 +155,22 @@ Global
{462CDBD4-0DDA-4854-1B13-CFDACBFB66F5}.dev|Any CPU.Build.0 = Debug|Any CPU
{462CDBD4-0DDA-4854-1B13-CFDACBFB66F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{462CDBD4-0DDA-4854-1B13-CFDACBFB66F5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6B0EAB30-511A-44C1-87FE-D9AB7E34D115}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6B0EAB30-511A-44C1-87FE-D9AB7E34D115}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6B0EAB30-511A-44C1-87FE-D9AB7E34D115}.DebugNoUnity|Any CPU.ActiveCfg = Debug|Any CPU
+ {6B0EAB30-511A-44C1-87FE-D9AB7E34D115}.DebugNoUnity|Any CPU.Build.0 = Debug|Any CPU
+ {6B0EAB30-511A-44C1-87FE-D9AB7E34D115}.dev|Any CPU.ActiveCfg = dev|Any CPU
+ {6B0EAB30-511A-44C1-87FE-D9AB7E34D115}.dev|Any CPU.Build.0 = dev|Any CPU
+ {6B0EAB30-511A-44C1-87FE-D9AB7E34D115}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6B0EAB30-511A-44C1-87FE-D9AB7E34D115}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F94F8AE1-C171-4A83-89E8-6557CA91A188}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F94F8AE1-C171-4A83-89E8-6557CA91A188}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F94F8AE1-C171-4A83-89E8-6557CA91A188}.DebugNoUnity|Any CPU.ActiveCfg = Debug|Any CPU
+ {F94F8AE1-C171-4A83-89E8-6557CA91A188}.DebugNoUnity|Any CPU.Build.0 = Debug|Any CPU
+ {F94F8AE1-C171-4A83-89E8-6557CA91A188}.dev|Any CPU.ActiveCfg = dev|Any CPU
+ {F94F8AE1-C171-4A83-89E8-6557CA91A188}.dev|Any CPU.Build.0 = dev|Any CPU
+ {F94F8AE1-C171-4A83-89E8-6557CA91A188}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F94F8AE1-C171-4A83-89E8-6557CA91A188}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -147,4 +186,7 @@ Global
{3DD3451C-30FA-4294-A3A9-1E080342F867} = {D17F1B4C-42DC-4E78-BCEF-9F239A084C4D}
{462CDBD4-0DDA-4854-1B13-CFDACBFB66F5} = {D17F1B4C-42DC-4E78-BCEF-9F239A084C4D}
EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {66BD4D50-3779-4912-9596-2C838BF24911}
+ EndGlobalSection
EndGlobal
diff --git a/GitHub.Unity.sln.DotSettings b/GitHub.Unity.sln.DotSettings
index 31c5e56f1..2166c735a 100644
--- a/GitHub.Unity.sln.DotSettings
+++ b/GitHub.Unity.sln.DotSettings
@@ -22,8 +22,11 @@
END_OF_LINE
1
1
+ False
+ False
False
True
+ NEVER
False
True
False
@@ -339,8 +342,13 @@
SSH
<Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
<Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ True
+ True
+ True
+ True
True
True
+ True
True
True
True
diff --git a/LICENSE b/LICENSE
index 9f06ebe84..a306528ee 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2016-2018 GitHub
+Copyright (c) 2016-2019 GitHub
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 236bdd910..1cd67b951 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,16 @@
# [GitHub for Unity](https://unity.github.com)
-The GitHub for Unity extension brings [Git](https://git-scm.com/) and GitHub into [Unity](https://unity3d.com/), integrating source control into your work with friendly and accessible tools and workflows.
+## NOTICE OF DEPRECATION
-You can reach the team right here by opening a [new issue](https://github.com/github-for-unity/Unity/issues/new), or by joining one of the chats below. You can also email us at unity@github.com, or tweet at [@GitHubUnity](https://twitter.com/GitHubUnity)
+This project is dead y'all! Remove GitHub for Unity from your project, then go to https://github.com/spoiledcat/git-for-unity and install Git for Unity from the instructions there.
-[](https://ci.appveyor.com/project/github-windows/unity)
+# What is it
+
+The GitHub for Unity extension brings [Git](https://git-scm.com/) and GitHub into [Unity](https://unity3d.com/), integrating source control into your work with friendly and accessible tools and workflows.
-[](https://discord.gg/5zH8hVx)
-[](https://www.twitch.tv/sh4na)
+You can reach the team right here by opening a [new issue](https://github.com/github-for-unity/Unity/issues/new). You can also tweet at [@GitHubUnity](https://twitter.com/GitHubUnity)
+[](https://ci.appveyor.com/project/github-windows/unity)
## Notices
@@ -16,169 +18,19 @@ Please refer to the [list of known issues](https://github.com/github-for-unity/U
From version 0.19 onwards, the location of the plugin has moved to `Assets/Plugins/GitHub`. If you have version 0.18 or lower, you need to delete the `Assets/Editor/GitHub` folder before you install newer versions. You should exit Unity and delete the folder from Explorer/Finder, as Unity will not unload native libraries while it's running. Also, remember to update your `.gitignore` file.
-#### Table Of Contents
-
-[Installing GitHub for Unity](#installing-github-for-unity)
- * [Requirements](#requirements)
- * [Git on macOS](#git-on-macos)
- * [Git on Windows](#git-on-windows)
- * [Installation](#installation)
- * [Log files](#log-files)
- * [Windows](#windows)
- * [macOS](#macos)
-
-[Building and Contributing](#building-and-contributing)
-
-[Quick Guide to GitHub for Unity](#quick-guide-to-github-for-unity)
- * [Opening the GitHub window](#opening-the-github-window)
- * [Initialize Repository](#initialize-repository)
- * [Authentication](#authentication)
- * [Publish a new repository](#publish-a-new-repository)
- * [Commiting your work - Changes tab](#commiting-your-work---changes-tab)
- * [Pushing/pulling your work - History tab](#pushingpulling-your-work---history-tab)
- * [Branches tab](#branches-tab)
- * [Settings tab](#settings-tab)
-
-[More Resources](#more-resources)
-
-[License](#license)
-
-## Installing GitHub for Unity
-
-### Requirements
-
-- Unity 5.4 or higher
- - There's currently an blocker issue opened for 5.3 support, so we know it doesn't run there. Personal edition is fine.
-- Git and Git LFS 2.x
-
-#### Git on macOS
-
-The current release has limited macOS support. macOS users will need to install the latest [Git](https://git-scm.com/downloads) and [Git LFS](https://git-lfs.github.com/) manually, and make sure these are on the path. You can configure the Git location in the Settings tab on the GitHub window.
-
-The easiest way of installing git and git lfs is to install [Homebrew](https://brew.sh/) and then do `brew install git git-lfs`.
-
-Make sure a Git user and email address are set in the `~/.gitconfig` file before you initialize a repository for the first time. You can set these values by opening your `~/.gitconfig` file and adding the following section, if it doesn't exist yet:
-
-```
-[user]
- name = Your Name
- email = Your Email
-```
-
-#### Git on Windows
-
-The GitHub for Unity extension ships with a bundle of Git and Git LFS, to ensure that you have the correct version. These will be installed into `%LOCALAPPDATA%\GitHubUnity` when the extension runs for the first time.
-
-Make sure a Git user and email address are set in the `%HOME%\.gitconfig` file before you initialize a repository for the first time. You can set these values by opening your `%HOME%\.gitconfig` file and adding the following section, if it doesn't exist yet:
-
-```
-[user]
- name = Your Name
- email = Your Email
-```
-
-Once the extension is installed, you can open a command line with the same Git and Git LFS version that the extension uses by going to `Window` -> `GitHub Command Line` in Unity.
-
-### Installation
-
-This extensions needs to be installed (and updated) for each Unity project that you want to version control.
-First step is to download the latest package from [the releases page](https://github.com/github-for-unity/Unity/releases);
-it will be saved as a file with the extension `.unitypackage`.
-To install it, open Unity, then open the project you want to version control, and then double click on the downloaded package.
-Alternatively, import the package by clicking Assets, Import Package, Custom Package, then select the downloaded package.
-
-#### Log files
-
-##### macOS
-
-The extension log file can be found at `~/Library/Logs/GitHubUnity/github-unity.log`
-
-##### Windows
-
-The extension log file can be found at `%LOCALAPPDATA%\GitHubUnity\github-unity.log`
-
## Building and Contributing
-The [CONTRIBUTING.md](CONTRIBUTING.md) document will help you get setup and familiar with the source. The [documentation](docs/) folder also contains more resources relevant to the project.
-
Please read the [How to Build](docs/contributing/how-to-build.md) document for information on how to build GitHub for Unity.
-If you're looking for something to work on, check out the [up-for-grabs](https://github.com/github-for-unity/Unity/issues?q=is%3Aopen+is%3Aissue+label%3Aup-for-grabs) label.
-
-
-## I have a problem with GitHub for Unity
-
-First, please search the [open issues](https://github.com/github-for-unity/Unity/issues?q=is%3Aopen)
-and [closed issues](https://github.com/github-for-unity/Unity/issues?q=is%3Aclosed)
-to see if your issue hasn't already been reported (it may also be fixed).
-
-If you can't find an issue that matches what you're seeing, open a [new issue](https://github.com/github-for-unity/Unity/issues/new)
-and fill out the template to provide us with enough information to investigate
-further.
-
-## Quick Guide to GitHub for Unity
-
-### Opening the GitHub window
-
-You can access the GitHub window by going to Windows -> GitHub. The window opens by default next to the Inspector window.
-
-### Initialize Repository
-
-
-
-If the current Unity project is not in a Git repository, the GitHub for Unity extension will offer to initialize the repository for you. This will:
-
-- Initialize a git repository at the Unity project root via `git init`
-- Initialize git-lfs via `git lfs install`
-- Set up a `.gitignore` file at the Unity project root.
-- Set up a `.gitattributes` file at the Unity project root with a large list of known binary filetypes (images, audio, etc) that should be tracked by LFS
-- Configure the project to serialize meta files as text
-- Create an initial commit with the `.gitignore` and `.gitattributes` file.
-
-### Authentication
-
-To set up credentials in Git so you can push and pull, you can sign in to GitHub by going to `Window` -> `GitHub` -> `Account` -> `Sign in`. You only have to sign in successfully once, your credentials will remain on the system for all Git operations in Unity and outside of it. If you've already signed in once but the Account dropdown still says `Sign in`, ignore it, it's a bug.
-
-
-
-### Publish a new repository
-
-1. Go to [github.com](https://github.com) and create a new empty repository - do not add a license, readme or other files during the creation process.
-2. Copy the **https** URL shown in the creation page
-3. In Unity, go to `Windows` -> `GitHub` -> `Settings` and paste the url into the `Remote` textbox.
-3. Click `Save repository`.
-4. Go to the `History` tab and click `Push`.
-
-### Commiting your work - Changes tab
-
-You can see which files have been changed and commit them through the Changes tab. `.meta` files will show up in relation to their files on the tree, so you can select a file for comitting and automatically have their `.meta`
-
-
-
-### Pushing/pulling your work - History tab
-
-The history tab includes a `Push` button to push your work to the server. Make sure you have a remote url configured in the `Settings` tab so that you can push and pull your work.
-
-To receive updates from the server by clicking on the `Pull` button. You cannot pull if you have local changes, so commit your changes before pulling.
-
-
-
-### Branches tab
-
-
-
-### Settings tab
-
-You can configure your user data in the Settings tab, along with the path to the Git installation.
+The [CONTRIBUTING.md](CONTRIBUTING.md) document will help you get setup and familiar with the source. The [documentation](docs/) folder also contains more resources relevant to the project.
-Locked files will appear in a list in the Settings tab. You can see who has locked a file and release file locks after you've pushed your work.
+If you're looking for something to work on, check out the [up-for-grabs](https://github.com/github-for-unity/Unity/issues?q=is%3Aopen+is%3Aissue+label%3Aup-for-grabs) label.
-
+## How to use
-## More Resources
+The [quick guide to GitHub for Unity](docs/using/quick-guide.md)
-See [unity.github.com](https://unity.github.com) for more product-oriented
-information about GitHub for Unity.
+More [in-depth information](docs/readme.md)
## License
@@ -188,6 +40,6 @@ The MIT license grant is not for GitHub's trademarks, which include the logo
designs. GitHub reserves all trademark and copyright rights in and to all
GitHub trademarks. GitHub's logos include, for instance, the stylized
Invertocat designs that include "logo" in the file title in the following
-folder: [IconsAndLogos](https://github.com/github-for-unity/Unity/tree/master/src/UnityExtension/Assets/Editor/GitHub.Unity/IconsAndLogos).
+folder: [IconsAndLogos](src/UnityExtension/Assets/Editor/GitHub.Unity/IconsAndLogos).
Copyright 2015 - 2018 GitHub, Inc.
diff --git a/appveyor.yml b/appveyor.yml
index 3736c7189..bfb3e1b77 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,35 +1,28 @@
-version: '{build}.{branch}'
environment:
GHFU_KEY:
secure: KFcQA1VOCEMGUgy2dxH8G5O7C9DsAtQrnc6LakFpd9BRFtNnt2E8RSadPoJwQ9gztWaLS8vQLdU7cV5Ivt01LOnPI2kU1fQd2SHtKwJFve8ppvK/yZ/luhiXvIdGeEAiXQyuc1WUwuECoJVA6n7/uQKr1Q+eHniMitHuFpyQ7OqnwF6f+4TeBS6D78fKd3QoeP4XDdxCjNWPNmLv7BvWFMm5CuTK9aAWhOy8em/nVIED8qt36uHnncsDn+DH7uunj+VwmhVS+4yhuKHHz5naiUAHHIziZ4wBW6Q8rcf7xYEeISjlfxJ6TXs4Wwp406AO+n3v1DZaxSXwvoxplfopeGyb6imJfbwdTU/MHf2uj9wXobR8UhDarcrugVW+J3bqZyvkg20HfSe80gfQUBlK5OdMAp58dhWkddvJSO5TnzqmLlo/60gZxjheIbjdLaavSKcM4xOALXQlBbJJgVQrB/F6tYf7pRK3BlS8VoakyOGjJRzsSNdssSVrLVW3rwANORbH6Z1ZYvQw2ObP6/EBMceer0+JV4y9zB5q9C68erlr1NtJB0xKUp9/7I5GQj4lJ+pDsaFdsj40SyyD4yazSZf/3VIhZi/rQTJm0Ft0ifTZGSxnNTOVMZ5fsoJmUL0bn75Xt9q59cKYzK041HtEzRSElnBeuTf+Sm/MLLV28P1sonwntMhcYQ5ZPIuGmKa8jAJ0kxPXyT56MPJpwbNrbCOw2t9hXg5QbYv/+0RcoRoJ7P/5OY3M6mj8Emtu8N0SFKD6lfv9KNLFAyBuF6Ml7RyOs5RRogIdEapegTY7jwGH7igibrP0lt6HYshM7hKQrRYm2saokBV9TfgV9qnPyGs5/zyTTUGW4y0LavxiXl/vQIpxwCg4liCf1Dgw4Zrxvh40bziKGc3X06RJcysJ8cskOi6gB8eK8VTbAy6/Ufgv+pyjIns1iJxmdVbl8MrlgPXmOephdRPYZJDiFd4ynw0Slm1mqbzPWHdQ/mtMGxNRcysIOPKzKaKvK7Syor5SNtv4HU097dyjVQyW9krHaX/DSnx++dMDCIZJEDYxFw7LmTvl6AFWU4C3HM7+26cHBuBMBYS1PwcRijG+hwsHiXIomVuVglcxp5HC2eFbtBF9h1g030/tCeIBhZIVdSVO78319CsL1+aVtI5WjeQglH1OcTS42OF1Nb/4EjaN4I/w6yRJU5dmK7Q+rHQ+7NnPT4n5flQV6oe1XNbLen0raDuGos6v+aaoOQ50HlCSMMBJ3liapVXIAQ+Z/XM/cNZZa1TB6/C363Hjrts6Uq3IjKXmomhA33je+Wl6mTZqBucXUJs76p+ZgKubWvfzK/e6tORJggAgFNoa9Y56r3t7J8UdUolt301I1cCVz9CvYMUsWTmjKTR5SpssbbcuRcFUhgkHrhsSq0rBef/dfn5VEP8sateiqbpMne5iWO4Wc0Qx2/fJfx2zf9oqO3MihAyYDwPI7JCrmccJY9cHV89YlrytZuh87aBQ8d+T5ELdeG3bbYGYbKJ/yOGxMo8cRSomWUp2809Ea/lgu6WjyY1SdEjh20fwONOTRd/AA3h3XIMU7NY999YOEy1zj3yeP+awozDWQU8GSojA/ceLU5HT0U/RT2XJgkzUFb/u8wanNG6InPqvZy01bLR8JZmqXZl/4XgNpEsbzwPxjzuZJP6O4f2Usq1ZmHUfwlouXLWuHTv5/DPYJ9kO91wjzAH46IsoadmQkRDomHYCDPRCnYoS2zBkmBNukCgCKWsSwD5Msw/tpgIorMi5AAFhIOeWt/7tcKZ5nslbbnmZFtDkOBriPEiOjrAziRqFNAdBjecMrckRQrlkYjkpJG/pXZXYq219/8Sy1/HdNrWTCdq7nc947Fvq41CfumT4c2TqhVc/oflJ9SaxIl2A1Vtbw5LkmQKL08vOitsyZgRupcbqLcSYGo2dG+ks0gK5o2rvNp1nyN3ADh7JFmtD7/og55zlsAj7wP6rLEZ44h+dk7+Sh96WoCPfzSJnchg2vsydTpK6sG3Cp5qjEk0Tps88nX0SPvEPxwEpBL08+XIlg5OVxiTIYI0NSeEjAxwip2ptgaeI4c0yPB5SahMArEw/8YeEflWhiDyjoG4Bw0O1v9fRSSYiFhcYwrkr6yBK81hx6uH6DzqDqtKOxwJ3kKhapfwZXStmeOt4AwiSUHO8TiX1t0i7Jqwl7mRduz3LfmqGCeEsNxnLuhc6MPeEva8LO8ILCEcVz8bHlwUMWqabdZRm9UtbWtZp/u8ffPSBbgNFna2kKFr/F7dmXiv18CpNHOGxb/rSdmIaov1nXJR7XUyKPRO548PHE6iNxuNjWjcuFw1L0IUCNEeVUs7tvNHUTYOXRvfXNm3DbhjFnGix2JVCB2xz6QhDV4Hh6y0/rJl0b2dW25iM5HZdwCBGwgGM+9HyD7r+OBiRn+rd996c81+JsWL4jsa//16uwcbEpsF3tAB7b0by4qHbeZ+Gs3M06Sje4UVpLgKQVHSd/hfo4M70v3APhyz0WFBhLLZyouz0OdazKZ4W+HGBcunAPw/sYdMYZLe4ZmA6B+wxtSzojNKFaCFWoh3S5vLClZTraj7Mhh02PPsY0fmo15ceHBwKjMfGZ0pXt8uiPL29ECUstxSLVnPv6M4uXPJa7k+0lvj7XdB7aJ/LzexPAa/Z1+hsr2sO9An5qPnKM5Tp5zj9Xq2T7WBiDObYLxYZX5ez32jKfSYgv3cpIo5HnhKB3rZL3Alp6iJ2NFsDiB6pIUc2YQ3UU8wiMU90ifA83ORttzRDdLCuH1lYCHPk8rcVqeydgNrI4pRVrdIah3wm6hHc7YjSSnjIOhcl286iVtYgn10RUKxcs//ElgoGm0IkefKRy2WcDDL+10ZifpSWxRu0yrpwlxd0uHCAhrkOEnvaamn+0TSu/6s9VxoUyn9ZJhY7Jgnb6Z9Qxi4C+u2vXf6lOQvzl4AawnD9DW+w2L6hr2njGhvgjj2VLIHM/GIOV/OaYW97AiW0NBuEGDyBiuj8TxIUL7IuVj+QZVfyUzZHHL0c0Hy4jlQ+sh2nFzOAGWVZwEdAvLl9JCCs46iA9DHtBSrHxit7lytyspp7q8TYfE1lA0pIwkx20E3t+4CNdUQAr/IJaZJxhdfKAyW3UipP4LdRbweyYHZYFkoN0gEDMrzE0yB7XFNw5ddm/+o8KIuSUl44UVFcp2j0KPfuXadx7Pz1aa5HKpVUdc5CfJOjqgPJFn/MQU702YdUaV0qD+EHDOiVv313gUHdy9kpieQ3s2LDSh0qBkPdxLAdYXKLP24Mj3V+A2lyHU1WtLrIEVP37eCAFSYPf6Lz6TW4zrEBpHF4nwlE8M+0jQ/oB4lINxnkCa3YKYLFMiZ3dAmqGzVElesgymmB21xvdfrHgB1Z5OtQqYT8PPAw6llujXv6Pj9CqDGGS4U8UeW5GCFi/qyV6+hdg2IUsWtSzkbLJ5n8cfafEYeRBRgzK/B6qlTmoOrRl+bzmjVCJX29P+38KCpu7srnSQ+T0fR6t0OWyHGfC/39iMzATnhpiIXdnngVV9Cypgod5we44C2Rb4Or/nr5mdEidElIIthDiD7GHPNSeMXrdxs+ow76rh42DiY7x0L0SMRWyUEz0seL1JdBCdNn/7LuSn4CVpggqZD8anf9n+IUjrJtqQ+AvaogfuxM65byhGK4iVIijrogfBHb4nGywXxeEKe03JJ8nOWWN2ndyNhMW1dfNGraHvAt7DWL+/tp4qKCA89VFaZjwsqINANF1VVwh96SB6qT4tlKJjaPD3YpawT6Jfs+cg3pMj36FIPzHoNd/r+LwCBZ0WiA5xZiO0DX6WhwTfJVStsz4i9VXElCmWF2dpf5kTEC0T62Y1VCc++M1cTfwX34mdHPvdsm1Vi1qpqz4HTez8ateFukyj1FIN7++eYWoBJBoclhb3y/VUFwepORi84pz1fXUSSl8Fpg2U7NRyj+gcM5v/VAC1FGR4CJVpODIdROF7mCrLTbPzLn8Fv7EJHgHKNeU/sIT13+5V/UJSZPAxWcaUKhRWWuShSVb/1U13LjiWkHvmuH7SVLHbJDO5C5lA589rz4weTMd1OSymPuNB/xj2d2YrJUwqB3olsaxwm8w/bs2ot4GF4HFAdx3l0ESiR8jkBNAvr6vwRcXv+7nfXRpx2Mo5QU2YaunbqZxibmtNCQZBH8ZpQyUZOek4A5qDh6HW2VyJqKXeE8u1fbtOzB9xDYxgTrlVFhCw==
-clone_script:
-- ps: >-
- if(-not $env:appveyor_pull_request_number) {
- git lfs clone -q -n --branch=$env:appveyor_repo_branch https://github.com/$env:appveyor_repo_name.git $env:appveyor_build_folder
- git checkout -qf $env:appveyor_repo_commit
- } else {
- git lfs clone -q -n https://github.com/$env:appveyor_repo_name.git $env:appveyor_build_folder
- git fetch -q origin +refs/pull/$env:appveyor_pull_request_number/merge:
- git lfs fetch origin FETCH_HEAD
- git checkout -qf FETCH_HEAD
- }
-
- Set-Location $env:appveyor_build_folder
+ matrix:
+ - node_version: '8'
install:
- ps: >-
+ $full_build = Test-Path env:GHFU_KEY
+
+ $package = $full_build
+
git submodule sync
git submodule init
- $full_build = Test-Path env:GHFU_KEY
-
if ($full_build) {
+ $env:BUILD_TYPE="full"
$fileContent = "-----BEGIN RSA PRIVATE KEY-----`n"
$fileContent += $env:GHFU_KEY.Replace(' ', "`n")
$fileContent += "`n-----END RSA PRIVATE KEY-----`n"
Set-Content c:\users\appveyor\.ssh\id_rsa $fileContent
+ Install-Product node $env:node_version
} else {
+ $env:BUILD_TYPE="partial"
git submodule deinit script
$destdir = Join-Path $env:appveyor_build_folder 'lib'
$destfile = Join-Path $destdir 'deps.zip'
@@ -43,6 +36,24 @@ install:
nuget restore GitHub.Unity.sln
+ Set-Location $env:appveyor_build_folder
+
+ $version = Get-Content "$($env:appveyor_build_folder)\common\SolutionInfo.cs" | %{ $regex = "const string GitHubForUnityVersion = `"([^`"]*)`""; if ($_ -match $regex) { $matches[1] } }
+
+ $env:package_version="$($version).$($env:APPVEYOR_BUILD_NUMBER)"
+
+ Update-AppveyorBuild -Version $env:package_version
+
+ $message = "Building "
+
+ if ($package) { $message += "and packaging "}
+
+ if ($full_build) { $message += "(full build)" } else { $message += "(partial build)" }
+
+ $message += " version " + $env:package_version + " "
+
+ Write-Host $message
+
assembly_info:
patch: false
file: common\SolutionInfo.cs
@@ -60,10 +71,37 @@ test:
categories:
except:
- DoNotRunOnAppVeyor
-artifacts:
-- path: unity\PackageProject
- type: zip
- name: github-for-unity-packageproject
-- path: build\*.log
-on_failure:
- - ps: Get-ChildItem build\*.log | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
+on_success:
+- ps: |
+ if ($package) {
+ $rootdir=$env:appveyor_build_folder
+ Set-Location $rootdir
+ $sourcedir="$rootdir\unity\PackageProject"
+ $packagename="github-for-unity-$($env:package_version)"
+ $packagefile="$rootdir\$($packagename).unitypackage"
+ $commitfile="$sourcedir\commit"
+ $zipfile="$rootdir\PackageProject-$($env:package_version).zip"
+
+ # generate mdb files
+ Write-Output "Generating mdb files"
+ Get-ChildItem -Recurse "$($sourcedir)\*.pdb" | foreach { $_.fullname.substring(0, $_.fullname.length - $_.extension.length) } | foreach { Write-Output "Generating $($_).mdb"; & 'lib\pdb2mdb.exe' "$($_).dll" }
+
+ # generate unitypackage
+ Write-Output "Generating $packagefile"
+ submodules\packaging\unitypackage\run.ps1 -PathToPackage:$sourcedir -OutputFolder:$rootdir -PackageName:$packagename
+
+ # save commit
+ Add-Content $commitfile $appveyor_repo_commit
+
+ Write-Output "Zipping $sourcedir to $zipfile"
+ 7z a $zipfile $sourcedir
+
+ Write-Output "Uploading $zipfile"
+ Push-AppveyorArtifact $zipfile -DeploymentName source
+ Push-AppveyorArtifact $packagefile -DeploymentName package
+ Push-AppveyorArtifact "$($packagefile).md5" -DeploymentName package
+ }
+on_finish:
+- ps: |
+ Set-Location $env:appveyor_build_folder
+ Get-ChildItem $env:appveyor_build_folder\build\*.log | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name -DeploymentName logs }
diff --git a/common/SolutionInfo.cs b/common/SolutionInfo.cs
index 62edf1036..eb95db6a0 100644
--- a/common/SolutionInfo.cs
+++ b/common/SolutionInfo.cs
@@ -11,7 +11,7 @@
[assembly: AssemblyInformationalVersion(System.AssemblyVersionInformation.Version)]
[assembly: ComVisible(false)]
[assembly: AssemblyCompany("GitHub, Inc.")]
-[assembly: AssemblyCopyright("Copyright GitHub, Inc. 2017-2018")]
+[assembly: AssemblyCopyright("Copyright GitHub, Inc. 2016-2019")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -31,9 +31,10 @@
namespace System
{
internal static class AssemblyVersionInformation {
- // this is for the AssemblyVersion and AssemblyVersion attributes, which can't handle alphanumerics
- internal const string VersionForAssembly = "1.0.0";
- // Actual real version
- internal const string Version = "1.0.0rc5";
+ private const string GitHubForUnityVersion = "1.4.0";
+ internal const string VersionForAssembly = GitHubForUnityVersion;
+
+ // If this is an alpha, beta or other pre-release, mark it as such as shown below
+ internal const string Version = GitHubForUnityVersion; // GitHubForUnityVersion + "-beta1"
}
}
diff --git a/common/packaging.targets b/common/packaging.targets
index 77f866e20..a5ddd41a5 100644
--- a/common/packaging.targets
+++ b/common/packaging.targets
@@ -7,8 +7,9 @@
@@ -22,16 +23,16 @@
+ Condition="!$([System.String]::Copy('%(Filename)').Contains('deleteme')) and !$([System.String]::Copy('%(Extension)').Contains('xml'))" />
-
+
-
+
diff --git a/common/properties.props b/common/properties.props
index 977f217b3..8316329ed 100644
--- a/common/properties.props
+++ b/common/properties.props
@@ -3,7 +3,7 @@
Internal
- ENABLE_METRICS
+ ENABLE_METRICS
$(BuildDefs);ENABLE_MONO
$(SolutionDir)script\lib\
diff --git a/create-octorun-zip.sh b/create-octorun-zip.sh
new file mode 100755
index 000000000..4eb568d33
--- /dev/null
+++ b/create-octorun-zip.sh
@@ -0,0 +1,3 @@
+#!/bin/sh -eu
+DIR=$(pwd)
+submodules/packaging/octorun/run.sh --path $DIR/octorun --out $DIR/src/GitHub.Api/Resources --source $DIR/src/GitHub.Api/Installer
diff --git a/create-unitypackage.sh b/create-unitypackage.sh
new file mode 100755
index 000000000..333f7cb90
--- /dev/null
+++ b/create-unitypackage.sh
@@ -0,0 +1,9 @@
+#!/bin/sh -eu
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+
+version=$(sed -En 's,.*GitHubForUnityVersion = "(.*)".*,\1,p' common/SolutionInfo.cs)
+commitcount=$(git rev-list --count HEAD)
+commit=$(git log -n1 --pretty=format:%h)
+version="${version}.${commitcount}-${commit}"
+
+$DIR/submodules/packaging/unitypackage/run.sh --path $DIR/unity/PackageProject --out $DIR --file github-for-unity-$version
diff --git a/docs/contributing/how-to-build.md b/docs/contributing/how-to-build.md
index db494a224..13ef8c2fa 100644
--- a/docs/contributing/how-to-build.md
+++ b/docs/contributing/how-to-build.md
@@ -13,7 +13,7 @@ This repository is LFS-enabled. To clone it, you should use a git client that su
### MacOS
-- Mono 4.x required.
+- [Mono 4.x](https://download.mono-project.com/archive/4.8.1/macos-10-universal/) required. You can install it via brew with `brew tap shana/mono && brew install mono@4.8`
- Mono 5.x will not work
- `UnityEngine.dll` and `UnityEditor.dll`.
- If you've installed Unity in the default location of `/Applications/Unity`, the build will be able to reference these DLLs automatically. Otherwise, you'll need to copy these DLLs from `[Unity installation path]/Unity.app/Contents/Managed` into the `lib` directory in order for the build to work
@@ -35,12 +35,25 @@ git submodule deinit script
### Important pre-build steps
-To be able to authenticate in GitHub for Unity, you'll need to:
+The build needs to reference `UnityEngine.dll` and `UnityEditor.dll`. These DLLs are included with Unity. If you've installed Unity in the default location, the build will be able to find them automatically. If not, copy these DLLs from `[your Unity installation path]\Unity\Editor\Data\Managed` into the `lib` directory in order for the build to work.
+
+#### Developer OAuth app
+
+Because GitHub for Unity uses OAuth web application flow to interact with the GitHub API and perform actions on behalf of a user, it needs to be bundled with a Client ID and Secret.
+
+For external contributors, we have bundled a developer OAuth application in the source so that you can complete the sign in flow locally without needing to configure your own application.
+
+These are listed in `src/GitHub.Api/Application/ApplicationInfo.cs`
+
+DO NOT TRUST THIS CLIENT ID AND SECRET! THIS IS ONLY FOR TESTING PURPOSES!!
+
+The limitation with this developer application is that this will not work with GitHub Enterprise. You will see sign-in will fail on the OAuth callback due to the credentials not being present there.
+
+To provide your own Client ID and Client Secret:
- [Register a new developer application](https://github.com/settings/developers) in your profile.
- Copy [common/ApplicationInfo_Local.cs-example](../../common/ApplicationInfo_Local.cs-example) to `common/ApplicationInfo_Local.cs` and fill out the clientId/clientSecret fields for your application.
-The build needs to reference `UnityEngine.dll` and `UnityEditor.dll`. These DLLs are included with Unity. If you've installed Unity in the default location, the build will be able to find them automatically. If not, copy these DLLs from `[your Unity installation path]\Unity\Editor\Data\Managed` into the `lib` directory in order for the build to work.
### Visual Studio
@@ -56,13 +69,12 @@ Once you've built the solution for the first time, you can open `src/UnityExtens
The build also creates a Unity test project called `GitHubExtension` inside a directory called `github-unity-test` next to your local clone. For instance, if the repository is located at `c:\Projects\Unity` the test project will be at `c:\Projects\github-unity-test\GitHubExtension`. You can use this project to test binary builds of the extension in a clean environment (all needed DLLs will be copied to it every time you build).
-Note: some files might be locked by Unity if have one of the build output projects open when you compile from VS or the command line. This is expected and shouldn't cause issues with your builds.
+Note: some files might be locked by Unity if have one of the build output projects open when you compile from VS or the command line. This is expected and shouldn't cause issues with your builds.
## Solution organization
The `GitHub.Unity.sln` solution includes several projects:
-- dotnet-httpclient35 and octokit: external dependencies for threading and github api support, respectively. These are the submodules.
- packaging: empty projects with build rules that copy DLLs to various locations for testing
- Tests: unit and integration test projects
- GitHub.Logging: A logging helper library
diff --git a/docs/readme.md b/docs/readme.md
index ae4be69a9..d73e8e840 100644
--- a/docs/readme.md
+++ b/docs/readme.md
@@ -30,6 +30,8 @@ Details about how the team is organizing and shipping GitHub for Unity:
## Using
+[Quick Guide](using/quick-guide.md)
+
These documents contain more details on how to use the GitHub for Unity plugin:
-- **[Installing and Updating the GitHub for Unity package](https://github.com/github-for-unity/Unity/blob/master/docs/using/how-to-install-and-update.md)**
-- **[Getting Started with the GitHub for Unity package](https://github.com/github-for-unity/Unity/blob/master/docs/using/getting-started.md)**
+- **[Installing and Updating the GitHub for Unity package](using/how-to-install-and-update.md)**
+- **[Getting Started with the GitHub for Unity package](using/getting-started.md)**
diff --git a/docs/using/authenticating-to-github.md b/docs/using/authenticating-to-github.md
new file mode 100644
index 000000000..203a3473d
--- /dev/null
+++ b/docs/using/authenticating-to-github.md
@@ -0,0 +1,37 @@
+# Authenticating to GitHub
+
+## How to sign in to GitHub
+
+1. Open the **GitHub** window by going to the top level **Window** menu and selecting **GitHub**, as shown below.
+
+
+
+1. Click the **Sign in** button at the top right of the window.
+
+
+
+1. In the **Authenticate** dialog, enter your username or email and password
+
+
+
+ If your account requires Two Factor Authentication, you will be prompted for your auth code.
+
+
+
+You will need to create a GitHub account before you can sign in, if you don't have one already.
+
+- For more information on creating a GitHub account, see "[Signing up for a new GitHub account](https://help.github.com/articles/signing-up-for-a-new-github-account/)".
+
+### Personal access tokens
+
+If the sign in operation above fails, you can manually create a personal access token and use it as your password.
+
+The scopes for the personal access token are: `user`, `repo`.
+- *user* scope: Grants access to the user profile data. We currently use this to display your avatar and check whether your plans lets you publish private repositories.
+- *repo* scope: Grants read/write access to code, commit statuses, invitations, collaborators, adding team memberships, and deployment statuses for public and private repositories and organizations. This is needed for all git network operations (push, pull, fetch), and for getting information about the repository you're currently working on.
+
+***Note:*** *Some older versions of the plugin ask for `gist` and `write:public_key`.*
+
+For more information on creating personal access tokens, see "[Creating a personal access token for the command line](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line).
+
+For more information on authenticating with SAML single sign-on, see "[About authentication with SAML single sign-on](https://help.github.com/articles/about-authentication-with-saml-single-sign-on)."
diff --git a/docs/using/getting-started.md b/docs/using/getting-started.md
index 11f2b008f..4c5892a96 100644
--- a/docs/using/getting-started.md
+++ b/docs/using/getting-started.md
@@ -19,8 +19,10 @@ And you should see the GitHub spinner:
- History: A history of commits with title, time stamp, and commit author
- Branches: A list of local and remote branches with the ability to create new branches, switch branches, or checkout remote branches
- Settings: your git configuration (pulled from your local git credentials if they have been previously set), your repository configuration (you can manually put the URL to any remote repository here instead of using the Publish button to publish to GitHub), a list of locked files, your git installation details, and general settings to help us better help you if you get stuck
-4. You can
-# Connecting to an Existing Repository
-
-# Connecting to an Existing Repository that already has the GitHub for Unity package
+# Cloning an Existing Repository
+GitHub for Unity does not have the functionality to clone projects (yet!).
+1. Clone the repository (either through command line or with GitHub Desktop https://desktop.github.com/).
+2. Open the project in Unity.
+3. Install GitHub for Unity if it is not already installed.
+4. The GitHub plugin should load with all functionality enabled.
\ No newline at end of file
diff --git a/docs/using/how-to-install-and-update.md b/docs/using/how-to-install-and-update.md
index 5f357cf79..fb9dd93d8 100644
--- a/docs/using/how-to-install-and-update.md
+++ b/docs/using/how-to-install-and-update.md
@@ -43,4 +43,9 @@ Once you've downloaded the package file, you can quickly install it within Unity
# Updating the GitHub for Unity Package
-_COMING SOON_
+
+- If you are running Unity and wish to update GitHub for Unity (unless explicitly stated), be sure that the files in `x64` and `x86` are not selected.
+
+ 
+
+- Otherwise, it's best to stop Unity and delete GitHub for Unity from your project. Startup Unity and run the package installer like normal. Allowing it to restore everything.
diff --git a/docs/using/images/branches-initial-view.png b/docs/using/images/branches-initial-view.png
new file mode 100644
index 000000000..811bcf724
--- /dev/null
+++ b/docs/using/images/branches-initial-view.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:558821e67ea26955434da0485785e9240b6c15c44a3790b642fdcd37382e56a2
+size 59798
diff --git a/docs/using/images/changes-view.png b/docs/using/images/changes-view.png
new file mode 100644
index 000000000..23000f8ef
--- /dev/null
+++ b/docs/using/images/changes-view.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2ac4637a222a06e6ab57f23082b8e8b2f5cb9e35b9b99776c112ddcebb923dec
+size 61918
diff --git a/docs/using/images/confirm-pull-changes.png b/docs/using/images/confirm-pull-changes.png
new file mode 100644
index 000000000..722bbd639
--- /dev/null
+++ b/docs/using/images/confirm-pull-changes.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4b0e1aa1d8d86cb0264c9ca569a5c8e71b0aba73a2eb6611c06ec47670037d7d
+size 21478
diff --git a/docs/using/images/confirm-push-changes.png b/docs/using/images/confirm-push-changes.png
new file mode 100644
index 000000000..ff1ca1cd0
--- /dev/null
+++ b/docs/using/images/confirm-push-changes.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9e2e136027b03609f344bfde0c777d8e915544a5e9920f1268e4cc3abc5b1481
+size 18676
diff --git a/docs/using/images/confirm-revert.png b/docs/using/images/confirm-revert.png
new file mode 100644
index 000000000..cbf5d1566
--- /dev/null
+++ b/docs/using/images/confirm-revert.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ba990ce12403bfb53fb387ca3f3e2c447b21814ed6cd7e6f88d968463a5253a8
+size 21973
diff --git a/docs/using/images/create-new-branch-view.png b/docs/using/images/create-new-branch-view.png
new file mode 100644
index 000000000..34c69d1ea
--- /dev/null
+++ b/docs/using/images/create-new-branch-view.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f0239a51f611501dc63c828de206f35d412493871f65e712983666e67dbfc231
+size 62046
diff --git a/docs/using/images/delete-dialog.png b/docs/using/images/delete-dialog.png
new file mode 100644
index 000000000..574489f7c
--- /dev/null
+++ b/docs/using/images/delete-dialog.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:99d7cd8ef4957614cc844a08a632159157295414dc4675c1394f3209e30ffa58
+size 76315
diff --git a/docs/using/images/github-authenticate.png b/docs/using/images/github-authenticate.png
new file mode 100644
index 000000000..188121d97
--- /dev/null
+++ b/docs/using/images/github-authenticate.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:31ca0f7c4fc5c737a3db2da6eeb3deaa862d27e2cf3d1aa78fa14555b7cac750
+size 5927
diff --git a/docs/using/images/github-menu-item.png b/docs/using/images/github-menu-item.png
new file mode 100644
index 000000000..44f3f9458
--- /dev/null
+++ b/docs/using/images/github-menu-item.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c423766a70230c245f4bcdb1e33d9e76f267bf789876ecf9050016f9c8671735
+size 19651
diff --git a/docs/using/images/github-sign-in-button.png b/docs/using/images/github-sign-in-button.png
new file mode 100644
index 000000000..cb6132c24
--- /dev/null
+++ b/docs/using/images/github-sign-in-button.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:79bea09964bcf1964d4d1e1fb9712fe9fc67dc8ffe11f936534c9f2be63fd777
+size 9318
diff --git a/docs/using/images/github-two-factor.png b/docs/using/images/github-two-factor.png
new file mode 100644
index 000000000..7a3286616
--- /dev/null
+++ b/docs/using/images/github-two-factor.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:aba2174f780ea52a7cf7bbfd5144527bb7b29c3ce6a3200b6b6f8c69b2d6fb48
+size 9665
diff --git a/docs/using/images/locked-scene.png b/docs/using/images/locked-scene.png
new file mode 100644
index 000000000..7a839141d
--- /dev/null
+++ b/docs/using/images/locked-scene.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bbc246b18cbd173c19f392fe7ab85bffb8a97a7616ef99e7e37496e307924e49
+size 51983
diff --git a/docs/using/images/locks-view-right-click.png b/docs/using/images/locks-view-right-click.png
new file mode 100644
index 000000000..0ee14312c
--- /dev/null
+++ b/docs/using/images/locks-view-right-click.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:590dae09b3c029e48c3703d6c85a6774b062ebea46f11c2c062d97fedcb436df
+size 27251
diff --git a/docs/using/images/locks-view.png b/docs/using/images/locks-view.png
new file mode 100644
index 000000000..e8790d74e
--- /dev/null
+++ b/docs/using/images/locks-view.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:118f2b2e1bb82b598107eaa73db9a077374ff4f9601e47677eca7296353b1bc3
+size 23274
diff --git a/docs/using/images/name-branch.png b/docs/using/images/name-branch.png
new file mode 100644
index 000000000..9926c06db
--- /dev/null
+++ b/docs/using/images/name-branch.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:08232fa5cfd41a41c8091197ac27f433fdfa865c4996388dd0c731ac75176442
+size 59599
diff --git a/docs/using/images/new-branch-created.png b/docs/using/images/new-branch-created.png
new file mode 100644
index 000000000..2a37a9aeb
--- /dev/null
+++ b/docs/using/images/new-branch-created.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8543da247ee23364c15abec87e4e3de3e62b85a470e7d3d090ade18fb321072c
+size 64045
diff --git a/docs/using/images/post-commit-view.png b/docs/using/images/post-commit-view.png
new file mode 100644
index 000000000..81fc7d135
--- /dev/null
+++ b/docs/using/images/post-commit-view.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:91e26123236bdd3e692ca7d30ff0d6477311fe739d8008ba19fd543ee3506176
+size 21345
diff --git a/docs/using/images/post-push-history-view.png b/docs/using/images/post-push-history-view.png
new file mode 100644
index 000000000..0d2ed767a
--- /dev/null
+++ b/docs/using/images/post-push-history-view.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c0c2d5ccaddc88d3268e0d57f02229525240ecc71bded951e23171456560268b
+size 22485
diff --git a/docs/using/images/pull-view.png b/docs/using/images/pull-view.png
new file mode 100644
index 000000000..842ff9660
--- /dev/null
+++ b/docs/using/images/pull-view.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:feb83a46ebdd34ff46db3a38e8829f5adec5b10824bf69a8c56c9ba6fbd603ff
+size 25054
diff --git a/docs/using/images/push-view.png b/docs/using/images/push-view.png
new file mode 100644
index 000000000..42bef651a
--- /dev/null
+++ b/docs/using/images/push-view.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ab87bae0916af3681b7528891a359c77597fa3ea57f13eb6924f1cbcf2f2ab7d
+size 14581
diff --git a/docs/using/images/release-lock.png b/docs/using/images/release-lock.png
new file mode 100644
index 000000000..545b81c8a
--- /dev/null
+++ b/docs/using/images/release-lock.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0f6e05a714d270016d1f8ad4e018a895ed2b023fe59bd34a28df8d315c065649
+size 140913
diff --git a/docs/using/images/request-lock.png b/docs/using/images/request-lock.png
new file mode 100644
index 000000000..b877e1d8f
--- /dev/null
+++ b/docs/using/images/request-lock.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3f25877141bed2dcbdc4324edcbd802b6f216771e80de704f20dc99a3fdc718b
+size 136078
diff --git a/docs/using/images/revert-commit.png b/docs/using/images/revert-commit.png
new file mode 100644
index 000000000..daad75c8c
--- /dev/null
+++ b/docs/using/images/revert-commit.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b7711f428f3e04ea056c47b5d43109f3941761f4c286360d3616e0598d2d7b75
+size 26119
diff --git a/docs/using/images/revert.png b/docs/using/images/revert.png
new file mode 100644
index 000000000..326071a28
--- /dev/null
+++ b/docs/using/images/revert.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dea7b36eec8e2d99e1906b6cbc9cd22f52d2db09cc1a0cf34563585720b6649d
+size 25141
diff --git a/docs/using/images/success-pull-changes.png b/docs/using/images/success-pull-changes.png
new file mode 100644
index 000000000..a246da405
--- /dev/null
+++ b/docs/using/images/success-pull-changes.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e0407ab83ca311f319ff7feac0dac84b862935e2b50353ff82c067bdadf80e16
+size 16888
diff --git a/docs/using/images/success-push-changes.png b/docs/using/images/success-push-changes.png
new file mode 100644
index 000000000..8fba1f506
--- /dev/null
+++ b/docs/using/images/success-push-changes.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cfd290cb1e52b0acb5503642a3aaef164bf343735729325484e25d8b99293d7e
+size 13262
diff --git a/docs/using/images/switch-confirmation.png b/docs/using/images/switch-confirmation.png
new file mode 100644
index 000000000..92d623d6c
--- /dev/null
+++ b/docs/using/images/switch-confirmation.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4ed80f5cf2fab5fd256dd7cd093ad505c966edb95bdc5a15c3f1ace042bec7f6
+size 73947
diff --git a/docs/using/images/switch-or-delete.png b/docs/using/images/switch-or-delete.png
new file mode 100644
index 000000000..deab6badf
--- /dev/null
+++ b/docs/using/images/switch-or-delete.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8c0d171b2e8f7e212f2446b416235c6232c57c9b4927cfa41891bffa1940ae9d
+size 73828
diff --git a/docs/using/images/switched-branches.png b/docs/using/images/switched-branches.png
new file mode 100644
index 000000000..c185d25c5
--- /dev/null
+++ b/docs/using/images/switched-branches.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:542806a8dcc012cdfadadaf9cdab3cf4871bbbbee2c8d73834946df4ed249aec
+size 64281
diff --git a/docs/using/locking-files.md b/docs/using/locking-files.md
new file mode 100644
index 000000000..f90c8e7a3
--- /dev/null
+++ b/docs/using/locking-files.md
@@ -0,0 +1,30 @@
+# Locking files
+
+## Request locks
+
+From the Project tab, right-click on a file to open the context menu and select `Request Lock`.
+
+
+An additional way to lock a file is by selecting it and going to `Assets` -> `Request Lock`.
+
+## View locks
+
+After requesting a lock, a lock icon appears in the bottom right-hand corner of the file.
+
+
+A list of all locked files will appear in the **Locks** view in the GitHub tab.
+
+
+## Release locks
+
+There are three ways to release locks:
+
+1. From the Project tab, right-click on the locked file to open the context menu and select the option to `Release Lock`.
+
+
+2. From the GitHub tab under the **Locks** view, right-click to open the context menu and select to `Release Lock`.
+
+
+3. Select the file to unlock and go to select the menu option `Assets` -> `Release Lock`.
+
+Note: There are also two options for how to release a lock on a file. Always choose the `Release Lock` option first. The `Release Lock (forced)` option can be used to remove someone else's lock.
diff --git a/docs/using/managing-branches.md b/docs/using/managing-branches.md
new file mode 100644
index 000000000..13fa9c1bd
--- /dev/null
+++ b/docs/using/managing-branches.md
@@ -0,0 +1,38 @@
+# Managing branches
+
+Initial **Branches** view
+
+
+
+## Create branch
+
+1. From the **Branches** view, click on `master` under local branches to enable the `New Branch` button and be able to create a new branch from master.
+2. Click on `New Branch`.
+
+
+3. Enter a name for the branch and click `Create`.
+
+
+4. The new branch will be created from master.
+
+
+## Checkout branch
+
+1. Right-click on a local branch and select `Switch` or double-click on the branch to switch to it.
+
+
+2. A dialog will appear asking `Switch branch to 'branch name'?`. Select `Switch`.
+
+
+The branch will be checked out.
+
+
+## Delete branches
+
+1. Click on the branch name to be deleted and the `Delete` button becomes enabled.
+2. Right-click on a local branch and select `Delete` or click the `Delete` button above the Local branches list.
+
+
+3. A dialog appears asking `Are you sure you want to delete the branch: 'branch name'?`. Select `Delete`.
+
+The branch will be deleted.
diff --git a/docs/using/quick-guide.md b/docs/using/quick-guide.md
new file mode 100644
index 000000000..2011ee40f
--- /dev/null
+++ b/docs/using/quick-guide.md
@@ -0,0 +1,164 @@
+# Quick Guide
+
+## More resources
+
+These documents contain more details on how to use the GitHub for Unity plugin:
+- **[Installing and Updating the GitHub for Unity package](https://github.com/github-for-unity/Unity/blob/master/docs/using/how-to-install-and-update.md)**
+- **[Getting Started with the GitHub for Unity package](https://github.com/github-for-unity/Unity/blob/master/docs/using/getting-started.md)**
+- **[Authenticating to GitHub](https://github.com/github-for-unity/Unity/blob/master/docs/using/authenticating-to-github.md)**
+- **[Managing Branches](https://github.com/github-for-unity/Unity/blob/master/docs/using/managing-branches.md)**
+- **[Locking Files](https://github.com/github-for-unity/Unity/blob/master/docs/using/locking-files.md)**
+- **[Working with Changes](https://github.com/github-for-unity/Unity/blob/master/docs/using/working-with-changes.md)**
+- **[Using the Api](https://github.com/github-for-unity/Unity/blob/master/docs/using/using-the-api.md)**
+
+## Table of Contents
+
+[Installing GitHub for Unity](#installing-github-for-unity)
+
+- [Requirements](#requirements)
+ - [Git on macOS](#git-on-macos)
+ - [Git on Windows](#git-on-windows)
+- [Installation](#installation)
+- [Log files](#log-files)
+ - [Windows](#windows)
+ - [macOS](#macos)
+
+[Quick Guide to GitHub for Unity](#quick-guide-to-github-for-unity)
+
+- [Opening the GitHub window](#opening-the-github-window)
+- [Initialize Repository](#initialize-repository)
+- [Authentication](#authentication)
+- [Publish a new repository](#publish-a-new-repository)
+- [Commiting your work - Changes tab](#commiting-your-work---changes-tab)
+- [Pushing/pulling your work - History tab](#pushingpulling-your-work---history-tab)
+- [Branches tab](#branches-tab)
+- [Settings tab](#settings-tab)
+
+## Installing GitHub for Unity
+
+### Requirements
+
+- Unity 5.4 or higher
+ - There's currently a blocker issue opened for 5.3 support, so we know it doesn't run there. Personal edition is fine.
+- Git and Git LFS 2.x
+
+#### Git on macOS
+
+The current release has limited macOS support. macOS users will need to install the latest [Git](https://git-scm.com/downloads) and [Git LFS](https://git-lfs.github.com/) manually, and make sure these are on the path. You can configure the Git location in the `Settings` tab on the GitHub window.
+
+The easiest way of installing git and git lfs is to install [Homebrew](https://brew.sh/) and then do `brew install git git-lfs`.
+
+Make sure a Git user and email address are set in the `~/.gitconfig` file before you initialize a repository for the first time. You can set these values by opening your `~/.gitconfig` file and adding the following section, if it doesn't exist yet:
+
+```
+[user]
+ name = Your Name
+ email = Your Email
+```
+
+#### Git on Windows
+
+The GitHub for Unity extension ships with a bundle of Git and Git LFS, to ensure that you have the correct version. These will be installed into `%LOCALAPPDATA%\GitHubUnity` when the extension runs for the first time.
+
+Make sure a Git user and email address are set in the `%HOME%\.gitconfig` file before you initialize a repository for the first time. You can set these values by opening your `%HOME%\.gitconfig` file and adding the following section, if it doesn't exist yet:
+
+```
+[user]
+ name = Your Name
+ email = Your Email
+```
+
+Once the extension is installed, you can open a command line with the same Git and Git LFS version that the extension uses by going to `Window` -> `GitHub Command Line` in Unity.
+
+### Installation
+
+This extensions needs to be installed (and updated) for each Unity project that you want to version control.
+First step is to download the latest package from [the releases page](https://github.com/github-for-unity/Unity/releases); it will be saved as a file with the extension `.unitypackage`.
+To install it, open Unity, then open the project you want to version control, and then double click on the downloaded package.
+Alternatively, import the package by clicking `Assets`, `Import Package`, `Custom Package`, then select the downloaded package.
+
+#### Log files
+
+##### macOS
+
+The extension log file can be found at `~/Library/Logs/GitHubUnity/github-unity.log`
+
+##### Windows
+
+The extension log file can be found at `%LOCALAPPDATA%\GitHubUnity\github-unity.log`
+
+## I have a problem with GitHub for Unity
+
+First, please search the [open issues](https://github.com/github-for-unity/Unity/issues?q=is%3Aopen)
+and [closed issues](https://github.com/github-for-unity/Unity/issues?q=is%3Aclosed)
+to see if your issue hasn't already been reported (it may also be fixed).
+
+If you can't find an issue that matches what you're seeing, open a [new issue](https://github.com/github-for-unity/Unity/issues/new)
+and fill out the template to provide us with enough information to investigate
+further.
+
+## Quick Guide to GitHub for Unity
+
+### Opening the GitHub window
+
+You can access the GitHub window by going to `Windows` -> `GitHub`. The window opens by default next to the Inspector window.
+
+### Initialize Repository
+
+
+
+If the current Unity project is not in a Git repository, the GitHub for Unity extension will offer to initialize the repository for you. This will:
+
+- Initialize a git repository at the Unity project root via `git init`
+- Initialize git-lfs via `git lfs install`
+- Set up a `.gitignore` file at the Unity project root.
+- Set up a `.gitattributes` file at the Unity project root with a large list of known binary filetypes (images, audio, etc) that should be tracked by LFS
+- Configure the project to serialize meta files as text
+- Create an initial commit with the `.gitignore` and `.gitattributes` file.
+
+### Authentication
+
+To set up credentials in Git so you can push and pull, you can sign in to GitHub by going to `Window` -> `GitHub` -> `Account` -> `Sign in`. You only have to sign in successfully once, your credentials will remain on the system for all Git operations in Unity and outside of it. If you've already signed in once but the Account dropdown still says `Sign in`, ignore it, it's a bug.
+
+
+
+For more information on Authentication: - **[Authenticating to GitHub](https://github.com/github-for-unity/Unity/blob/master/docs/using/authenticating-to-github.md)**
+
+### Publish a new repository
+
+1. Go to [github.com](https://github.com) and create a new empty repository - do not add a license, readme or other files during the creation process.
+2. Copy the **https** URL shown in the creation page
+3. In Unity, go to `Windows` -> `GitHub` -> `Settings` and paste the url into the `Remote` textbox.
+4. Click `Save repository`.
+5. Go to the `History` tab and click `Push`.
+
+### Commiting your work - Changes tab
+
+You can see which files have been changed and commit them through the `Changes` tab. `.meta` files will show up in relation to their files on the tree, so you can select a file for comitting and automatically have their `.meta`
+
+
+
+For more information on working with changes: - **[Working with Changes](https://github.com/github-for-unity/Unity/blob/master/docs/using/working-with-changes.md#commit-changes)**
+
+### Pushing/pulling your work - History tab
+
+The history tab includes a `Push` button to push your work to the server. Make sure you have a remote url configured in the `Settings` tab so that you can push and pull your work.
+
+To receive updates from the server by clicking on the `Pull` button. You cannot pull if you have local changes, so commit your changes before pulling.
+
+
+
+
+For more information on working with changes: - **[Working with Changes](https://github.com/github-for-unity/Unity/blob/master/docs/using/working-with-changes.md#pulling-changes)**
+
+### Branches tab
+
+
+
+### Settings tab
+
+You can configure your user data in the `Settings` tab, along with the path to the Git installation.
+
+Locked files will appear in a list in the Settings tab. You can see who has locked a file and release file locks after you've pushed your work.
+
+
diff --git a/docs/using/using-the-api.md b/docs/using/using-the-api.md
new file mode 100644
index 000000000..ab75cab55
--- /dev/null
+++ b/docs/using/using-the-api.md
@@ -0,0 +1,75 @@
+# Using the API
+
+GitHub for Unity provides access to a git client to help users create their own tools to assist in their workflow.
+
+Users can separate the user interface from the API by removing `GitHub.Unity.dll`. All other libraries are required by the API.
+
+## Creating an instance of `GitClient`
+```cs
+var defaultEnvironment = new DefaultEnvironment();
+defaultEnvironment.Initialize(null, NPath.Default, NPath.Default, NPath.Default, Application.dataPath.ToNPath());
+
+var processEnvironment = new ProcessEnvironment(defaultEnvironment);
+var processManager = new ProcessManager(defaultEnvironment, processEnvironment, TaskManager.Instance.Token);
+
+var gitClient = new GitClient(defaultEnvironment, processManager, TaskManager.Instance.Token);
+```
+
+## Full Example
+This example creates a window that has a single button which commits all changes.
+```cs
+using System;
+using System.Globalization;
+using GitHub.Unity;
+using UnityEditor;
+using UnityEngine;
+
+public class CustomGitEditor : EditorWindow
+{
+ [MenuItem("Window/Custom Git")]
+ public static void ShowWindow()
+ {
+ EditorWindow.GetWindow(typeof(CustomGitEditor));
+ }
+
+ [NonSerialized] private GitClient gitClient;
+
+ public void OnEnable()
+ {
+ InitGitClient();
+ }
+
+ private void InitGitClient()
+ {
+ if (gitClient != null) return;
+
+ Debug.Log("Init GitClient");
+
+ var defaultEnvironment = new DefaultEnvironment();
+ defaultEnvironment.Initialize(null, NPath.Default, NPath.Default,
+ NPath.Default, Application.dataPath.ToNPath());
+
+ var processEnvironment = new ProcessEnvironment(defaultEnvironment);
+ var processManager = new ProcessManager(defaultEnvironment, processEnvironment, TaskManager.Instance.Token);
+
+ gitClient = new GitClient(defaultEnvironment, processManager, TaskManager.Instance.Token);
+ }
+
+ void OnGUI()
+ {
+ GUILayout.Label("Custom Git Window", EditorStyles.boldLabel);
+
+ if (GUILayout.Button("Commit Stuff"))
+ {
+ var message = DateTime.Now.ToString(CultureInfo.InvariantCulture);
+ var body = string.Empty;
+
+ gitClient.AddAll()
+ .Then(gitClient.Commit(message, body))
+ .Start();
+ }
+ }
+}
+```
+
+
diff --git a/docs/using/working-with-changes.md b/docs/using/working-with-changes.md
new file mode 100644
index 000000000..228e4bd3f
--- /dev/null
+++ b/docs/using/working-with-changes.md
@@ -0,0 +1,53 @@
+# Working with changes
+
+## Commit changes
+
+All changes made to a repository will show up under the **Changes** view.
+
+1. Select the changes to be committed. Can choose the All/None options, or select directories or files individually.
+2. Enter a Commit summary which describes the purpose of the commit. An optional Commit description can also be entered.
+3. Click the button `Commit to [branch name]`.
+
+
+The commit will not be shown under the **History** view. On the top bar the button `Push (1)` indicates that there is 1 commit to push.
+
+
+
+## Push changes to GitHub
+
+1. Click `Push` once ready to push a commit to GitHub.
+
+
+2. A dialog will appear asking `Would you like to push changes to remote 'branch name'?` Select `Push`.
+
+
+3. Another dialog will appear when the push to GitHub is complete saying `Branch pushed`. Select `ok`.
+
+
+## Revert changes
+
+1. From the **History** view, right-click on a commit in the commit list. A `Revert` option will appear.
+2. Click `Revert`.
+
+
+3. A dialog will appear asking `Are you sure you want to revert the following commit: "commit message"?`. Select `Revert`.
+
+
+4. A new commit appears titled `Revert "commit summary"` and the view indicates that there is 1 commit to push.
+
+
+5. Follow the steps to push the reverted commit to GitHub.
+
+## Pulling changes
+
+1. Click the `Fetch` button to get all the latest branches and tags for the repository. The `Pull` button will then show the number of commits to pull from GitHub.
+2. Click `Pull`.
+
+
+3. A dialog will appear asking `Would you like to pull changes from remote 'branch name'?`. Select `Pull`.
+
+
+4. Another dialog appears saying `Local branch is up to date with 'branch name'`. Select `ok`.
+
+
+
diff --git a/lib/ICSharpCode.NRefactory.dll b/lib/ICSharpCode.NRefactory.dll
deleted file mode 100644
index f11688c6c..000000000
--- a/lib/ICSharpCode.NRefactory.dll
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:2e28e35172498877c7e4a33253b0c5df172bce5655b2ab933a922d6777ae5e6d
-size 528384
diff --git a/lib/ICSharpCode.SharpZipLib.dll b/lib/ICSharpCode.SharpZipLib.dll
deleted file mode 100644
index 108abfd2a..000000000
--- a/lib/ICSharpCode.SharpZipLib.dll
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:fbace48694fbfff69ff99ff7aefbde67965f7723092df9c13ff537d2a319f410
-size 192000
diff --git a/lib/pdb2mdb.exe b/lib/pdb2mdb.exe
new file mode 100644
index 000000000..72547bf3c
--- /dev/null
+++ b/lib/pdb2mdb.exe
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a72425f98bdb5923704946c4010485d471ce9e35611264f394637fb185424619
+size 369664
diff --git a/nuget.config b/nuget.config
index 1a5bd9cf3..bad252e01 100644
--- a/nuget.config
+++ b/nuget.config
@@ -1,7 +1,7 @@
-
+
diff --git a/octorun/bin/octorun-meta b/octorun/bin/octorun-meta
new file mode 100644
index 000000000..1d2d3a7c9
--- /dev/null
+++ b/octorun/bin/octorun-meta
@@ -0,0 +1,3 @@
+#!/usr/bin/env node
+
+require('../src/bin/app-meta.js');
diff --git a/octorun/bin/octorun-token b/octorun/bin/octorun-token
new file mode 100644
index 000000000..3ea7b1500
--- /dev/null
+++ b/octorun/bin/octorun-token
@@ -0,0 +1,3 @@
+#!/usr/bin/env node
+
+require('../src/bin/app-token.js');
diff --git a/octorun/src/api.js b/octorun/src/api.js
index fcaf96c50..f7a672e39 100644
--- a/octorun/src/api.js
+++ b/octorun/src/api.js
@@ -1,17 +1,17 @@
var config = require("./configuration");
var octokitWrapper = require("./octokit");
-function ApiWrapper() {
- this.octokit = octokitWrapper.createOctokit();
-
- if (!config.user || !config.token) {
- throw "user and/or token missing";
- }
-
+function ApiWrapper(host) {
if (!config.appName) {
throw "appName missing";
}
+ if (!config.token) {
+ throw "token missing";
+ }
+
+ this.octokit = octokitWrapper.createOctokit(config.appName, host);
+
this.octokit.authenticate({
type: "oauth",
token: config.token
diff --git a/octorun/src/authentication.js b/octorun/src/authentication.js
index 43a2de5bb..4147522cd 100644
--- a/octorun/src/authentication.js
+++ b/octorun/src/authentication.js
@@ -1,12 +1,11 @@
-var endOfLine = require('os').EOL;
var config = require("./configuration");
var octokitWrapper = require("./octokit");
var twoFactorRegex = new RegExp("must specify two-factor authentication otp code", "gi");
-var scopes = ["user", "repo", "gist", "write:public_key"];
+var scopes = ["user", "repo"];
-var handleAuthentication = function (username, password, onSuccess, onFailure, twoFactor) {
+var handleAuthentication = function (username, password, onSuccess, onFailure, twoFactor, host) {
if (!config.clientId || !config.clientSecret) {
throw "clientId and/or clientSecret missing";
}
@@ -15,7 +14,7 @@ var handleAuthentication = function (username, password, onSuccess, onFailure, t
throw "appName missing";
}
- var octokit = octokitWrapper.createOctokit();
+ var octokit = octokitWrapper.createOctokit(config.appName, host);
octokit.authenticate({
type: "basic",
@@ -27,7 +26,6 @@ var handleAuthentication = function (username, password, onSuccess, onFailure, t
if (twoFactor) {
headers = {
"X-GitHub-OTP": twoFactor,
- "user-agent": config.appName
};
}
diff --git a/octorun/src/bin/app-login.js b/octorun/src/bin/app-login.js
index f3484c31b..4577bac81 100644
--- a/octorun/src/bin/app-login.js
+++ b/octorun/src/bin/app-login.js
@@ -1,12 +1,12 @@
var commander = require("commander");
var package = require('../../package.json');
var authentication = require('../authentication');
-var endOfLine = require('os').EOL;
var output = require('../output');
commander
.version(package.version)
.option('-t, --twoFactor')
+ .option('-h, --host ')
.parse(process.argv);
var handleAuthentication = function (username, password, twoFactor) {
@@ -19,7 +19,7 @@ var handleAuthentication = function (username, password, twoFactor) {
}
}, function (error) {
output.error(error);
- }, twoFactor);
+ }, twoFactor, commander.host);
}
var encoding = 'utf-8';
diff --git a/octorun/src/bin/app-meta.js b/octorun/src/bin/app-meta.js
new file mode 100644
index 000000000..ecbc36e66
--- /dev/null
+++ b/octorun/src/bin/app-meta.js
@@ -0,0 +1,52 @@
+var commander = require('commander');
+var package = require('../../package.json');
+var output = require('../output');
+
+commander
+ .version(package.version)
+ .option('-h, --host ')
+ .parse(process.argv);
+
+var host = commander.host;
+var port = 443;
+var scheme = 'https';
+
+if (host) {
+ var https = require(scheme);
+ var options = {
+ protocol: scheme + ':',
+ hostname: host,
+ port: port,
+ path: '/api/v3/meta',
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+ };
+
+ var req = https.request(options, function (res) {
+ var success = res.statusCode == 200;
+
+ if(!success) {
+ output.error(res.statusCode);
+ } else {
+ res.on('data', function (d) {
+ output.custom("success", d, true);
+ });
+
+ res.on('end', function (d) {
+ process.exit();
+ });
+ }
+ });
+
+ req.on('error', function (error) {
+ output.error(error);
+ });
+
+ req.end();
+}
+else {
+ commander.help();
+ process.exit(-1);
+}
\ No newline at end of file
diff --git a/octorun/src/bin/app-organizations.js b/octorun/src/bin/app-organizations.js
index 480289aa1..1a67e181f 100644
--- a/octorun/src/bin/app-organizations.js
+++ b/octorun/src/bin/app-organizations.js
@@ -1,16 +1,15 @@
var commander = require("commander");
var package = require('../../package.json');
var ApiWrapper = require('../api');
-var endOfLine = require('os').EOL;
var output = require('../output');
commander
.version(package.version)
+ .option('-h, --host ')
.parse(process.argv);
try {
-
- var apiWrapper = new ApiWrapper();
+ var apiWrapper = new ApiWrapper(commander.host);
apiWrapper.getOrgs(function (error, result) {
if (error) {
output.error(error);
diff --git a/octorun/src/bin/app-publish.js b/octorun/src/bin/app-publish.js
index 5fe602390..62305763e 100644
--- a/octorun/src/bin/app-publish.js
+++ b/octorun/src/bin/app-publish.js
@@ -1,7 +1,6 @@
var commander = require("commander");
var package = require('../../package.json')
var ApiWrapper = require('../api')
-var endOfLine = require('os').EOL;
var output = require('../output');
commander
@@ -10,6 +9,7 @@ commander
.option('-d, --description ')
.option('-o, --organization ')
.option('-p, --private')
+ .option('-h, --host ')
.parse(process.argv);
if(!commander.repository)
@@ -24,7 +24,7 @@ if (commander.private) {
}
try {
- var apiWrapper = new ApiWrapper();
+ var apiWrapper = new ApiWrapper(commander.host);
apiWrapper.publish(commander.repository, commander.description, private, commander.organization,
function (error, result) {
diff --git a/octorun/src/bin/app-token.js b/octorun/src/bin/app-token.js
new file mode 100644
index 000000000..5811be43e
--- /dev/null
+++ b/octorun/src/bin/app-token.js
@@ -0,0 +1,66 @@
+
+var commander = require('commander');
+var package = require('../../package.json');
+var output = require('../output');
+var config = require("../configuration");
+var querystring = require('querystring');
+
+commander
+ .version(package.version)
+ .option('-h, --host ')
+ .parse(process.argv);
+
+var host = commander.host;
+var port = 443;
+var scheme = 'https';
+
+var valid = host && config.clientId && config.clientSecret && config.token;
+if (valid) {
+ var https = require(scheme);
+
+ var postData = querystring.stringify({
+ client_id: config.clientId,
+ client_secret: config.clientSecret,
+ code: config.token
+ });
+
+ var options = {
+ protocol: scheme + ':',
+ hostname: host,
+ port: port,
+ path: '/login/oauth/access_token',
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ 'Content-Length': postData.length
+ }
+ };
+
+ var req = https.request(options, function (res) {
+ var success = res.statusCode == 200;
+
+ if(!success) {
+ output.error(res.statusCode);
+ } else {
+ res.on('data', function (d) {
+ output.custom("success", d, true);
+ });
+
+ res.on('end', function (d) {
+ process.exit();
+ });
+ }
+ });
+
+ req.on('error', function (error) {
+ output.error(error);
+ });
+
+ req.write(postData);
+
+ req.end();
+}
+else {
+ commander.help();
+ process.exit(-1);
+}
\ No newline at end of file
diff --git a/octorun/src/bin/app-usage.js b/octorun/src/bin/app-usage.js
index 9f6811d15..0fb32b691 100644
--- a/octorun/src/bin/app-usage.js
+++ b/octorun/src/bin/app-usage.js
@@ -1,9 +1,7 @@
-var commander = require("commander");
-var package = require('../../package.json')
-var config = require("../configuration");
-var endOfLine = require('os').EOL;
+var commander = require('commander');
+var package = require('../../package.json');
+var config = require('../configuration');
var fs = require('fs');
-var util = require('util');
var output = require('../output');
commander
@@ -46,9 +44,6 @@ if (fileContents && host) {
'Content-Type': 'application/json'
}
};
- if (config.token) {
- options.headers['Authorization'] = 'token ' + config.token;
- }
var req = https.request(options, function (res) {
var success = res.statusCode == 200;
diff --git a/octorun/src/bin/app-validate.js b/octorun/src/bin/app-validate.js
index 8ba643021..294fbcbdc 100644
--- a/octorun/src/bin/app-validate.js
+++ b/octorun/src/bin/app-validate.js
@@ -1,15 +1,15 @@
var commander = require("commander");
var package = require('../../package.json');
-var endOfLine = require('os').EOL;
var ApiWrapper = require('../api');
var output = require('../output');
commander
.version(package.version)
+ .option('-h, --host ')
.parse(process.argv);
try {
- var apiWrapper = new ApiWrapper();
+ var apiWrapper = new ApiWrapper(commander.host);
apiWrapper.verifyUser(function (error, result) {
if (error) {
diff --git a/octorun/src/bin/app.js b/octorun/src/bin/app.js
index e40d738b2..095292965 100644
--- a/octorun/src/bin/app.js
+++ b/octorun/src/bin/app.js
@@ -9,4 +9,6 @@ commander
.command('organizations', 'Get Organizations')
.command('publish', 'Publish')
.command('usage', 'Usage')
+ .command('token', 'Create OAuth Token')
+ .command('meta', 'Get Server Meta Data')
.parse(process.argv);
\ No newline at end of file
diff --git a/octorun/src/configuration.js b/octorun/src/configuration.js
index f9462acde..0fe3906fd 100644
--- a/octorun/src/configuration.js
+++ b/octorun/src/configuration.js
@@ -3,13 +3,11 @@ require("dotenv").config({silent: true});
var clientId = process.env.OCTOKIT_CLIENT_ID;
var clientSecret = process.env.OCTOKIT_CLIENT_SECRET;
var appName = process.env.OCTOKIT_USER_AGENT;
-var user = process.env.OCTORUN_USER;
var token = process.env.OCTORUN_TOKEN;
module.exports = {
clientId: clientId,
clientSecret: clientSecret,
appName: appName,
- user: user,
token: token
};
\ No newline at end of file
diff --git a/octorun/src/octokit.js b/octorun/src/octokit.js
index 1cf90b1ac..b0ab0a42f 100644
--- a/octorun/src/octokit.js
+++ b/octorun/src/octokit.js
@@ -1,19 +1,20 @@
var Octokit = require('octokit-rest-for-node-v0.12');
-var createOctokit = function () {
- return Octokit({
+var createOctokit = function (appName, host) {
+ var octokitConfiguration = {
timeout: 0,
requestMedia: 'application/vnd.github.v3+json',
headers: {
- 'user-agent': 'octokit/rest.js v1.2.3'
+ 'user-agent': appName
}
+ };
- // change for custom GitHub Enterprise URL
- //host: 'api.github.com',
- //pathPrefix: '',
- //protocol: 'https',
- //port: 443
- });
+ if (host) {
+ octokitConfiguration.host = host;
+ octokitConfiguration.pathPrefix = 'api/v3';
+ }
+
+ return Octokit(octokitConfiguration);
};
module.exports = { createOctokit: createOctokit };
\ No newline at end of file
diff --git a/octorun/version b/octorun/version
index b2dfd9b3b..998379c47 100644
--- a/octorun/version
+++ b/octorun/version
@@ -1 +1 @@
-b4b80eb4ac
\ No newline at end of file
+902910f48
\ No newline at end of file
diff --git a/package.cmd b/package.cmd
index e25ddbd41..a47264d8f 100644
--- a/package.cmd
+++ b/package.cmd
@@ -47,7 +47,7 @@ if not exist "%Unity%" (
del /Q unity\PackageProject\Assets\Plugins\GitHub\Editor\*.pdb.meta
del /Q unity\PackageProject\Assets\Plugins\GitHub\Editor\*.xml
- for /f tokens^=^2^ usebackq^ delims^=^" %%G in (`find "const string Version" common\SolutionInfo.cs`) do call :Package %%G
+ for /f tokens^=^2^ usebackq^ delims^=^" %%G in (`find "const string GitHubForUnityVersion" common\SolutionInfo.cs`) do call :Package %%G
goto End
diff --git a/package.sh b/package.sh
index ca09bd5e5..8dc3c93dc 100755
--- a/package.sh
+++ b/package.sh
@@ -58,7 +58,7 @@ rm -f unity/PackageProject/Assets/Plugins/GitHub/Editor/*.pdb
rm -f unity/PackageProject/Assets/Plugins/GitHub/Editor/*.pdb.meta
rm -f unity/PackageProject/Assets/Plugins/GitHub/Editor/*.xml
-Version=`sed -En 's,.*Version = "(.*)".*,\1,p' common/SolutionInfo.cs`
+Version=`sed -En 's,.*GitHubForUnityVersion = "(.*)".*,\1,p' common/SolutionInfo.cs`
commitcount=`git rev-list --count HEAD`
commit=`git log -n1 --pretty=format:%h`
Version="${Version}.${commitcount}-${commit}"
diff --git a/script b/script
index 259dba7e8..d373977da 160000
--- a/script
+++ b/script
@@ -1 +1 @@
-Subproject commit 259dba7e8375a96a935b51e2169fe029cd70c039
+Subproject commit d373977da73bdf7f9170e778638c80e5b49ca3b3
diff --git a/src/.gitignore b/src/.gitignore
index f8cebfc72..a7abc4e4a 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -312,4 +312,5 @@ sysinfo.txt
# Builds
*.apk
*.unitypackage
-UnityExtension/**/manifest.json
\ No newline at end of file
+UnityExtension/**/manifest.json
+tests/IntegrationTests/IOTestsRepo/
\ No newline at end of file
diff --git a/src/GitHub.Api/Application/ApiClient.cs b/src/GitHub.Api/Application/ApiClient.cs
index 73055228b..97a26797a 100644
--- a/src/GitHub.Api/Application/ApiClient.cs
+++ b/src/GitHub.Api/Application/ApiClient.cs
@@ -1,38 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Net;
using GitHub.Logging;
using System.Runtime.Serialization;
using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading;
+using GitHub.Unity.Json;
namespace GitHub.Unity
{
- class ApiClient : IApiClient
+ public class ApiClient : IApiClient
{
private static readonly ILogging logger = LogHelper.GetLogger();
+ private static readonly Regex httpStatusErrorRegex = new Regex("(?<=[a-z])([A-Z])", RegexOptions.Compiled);
+ private static readonly Regex accessTokenRegex = new Regex("access_token=(.*?)&", RegexOptions.Compiled);
+
public HostAddress HostAddress { get; }
- public UriString OriginalUrl { get; }
private readonly IKeychain keychain;
private readonly IProcessManager processManager;
private readonly ITaskManager taskManager;
- private readonly NPath nodeJsExecutablePath;
- private readonly NPath octorunScriptPath;
private readonly ILoginManager loginManager;
+ private readonly IEnvironment environment;
+ private IKeychainAdapter keychainAdapter;
+ private Connection connection;
- public ApiClient(UriString hostUrl, IKeychain keychain, IProcessManager processManager, ITaskManager taskManager, NPath nodeJsExecutablePath, NPath octorunScriptPath)
+ public ApiClient(IKeychain keychain, IProcessManager processManager, ITaskManager taskManager,
+ IEnvironment environment, UriString host = null)
{
- Guard.ArgumentNotNull(hostUrl, nameof(hostUrl));
Guard.ArgumentNotNull(keychain, nameof(keychain));
- HostAddress = HostAddress.Create(hostUrl);
- OriginalUrl = hostUrl;
+ host = host == null
+ ? UriString.ToUriString(HostAddress.GitHubDotComHostAddress.WebUri)
+ : new UriString(host.ToRepositoryUri().GetComponents(UriComponents.SchemeAndServer, UriFormat.SafeUnescaped));
+
+ HostAddress = HostAddress.Create(host);
+
this.keychain = keychain;
this.processManager = processManager;
this.taskManager = taskManager;
- this.nodeJsExecutablePath = nodeJsExecutablePath;
- this.octorunScriptPath = octorunScriptPath;
- loginManager = new LoginManager(keychain, processManager, taskManager, nodeJsExecutablePath, octorunScriptPath);
+ this.environment = environment;
+ loginManager = new LoginManager(keychain, processManager, taskManager, environment);
}
public ITask Logout(UriString host)
@@ -40,16 +50,24 @@ public ITask Logout(UriString host)
return loginManager.Logout(host);
}
- public void CreateRepository(string name, string description, bool isPrivate, Action callback, string organization = null)
+ public void CreateRepository(string name, string description, bool isPrivate,
+ Action callback, string organization = null)
{
Guard.ArgumentNotNull(callback, "callback");
new FuncTask(taskManager.Token, () =>
{
- var user = GetCurrentUser();
- var keychainAdapter = keychain.Connect(OriginalUrl);
+ EnsureValidCredentials();
+
+ var command = new StringBuilder("publish");
+
+ if (!HostAddress.IsGitHubDotCom())
+ {
+ command.Append(" -h ");
+ command.Append(HostAddress.ApiUri.Host);
+ }
- var command = new StringBuilder("publish -r \"");
+ command.Append(" -r \"");
command.Append(name);
command.Append("\"");
@@ -72,8 +90,9 @@ public void CreateRepository(string name, string description, bool isPrivate, Ac
command.Append(" -p");
}
- var octorunTask = new OctorunTask(taskManager.Token, nodeJsExecutablePath, octorunScriptPath, command.ToString(),
- user: user.Login, userToken: keychainAdapter.Credential.Token)
+ var adapter = EnsureKeychainAdapter();
+
+ var octorunTask = new OctorunTask(taskManager.Token, environment, command.ToString(), adapter.Credential.Token)
.Configure(processManager);
var ret = octorunTask.RunSynchronously();
@@ -101,16 +120,85 @@ public void CreateRepository(string name, string description, bool isPrivate, Ac
.Start();
}
+ public void GetEnterpriseServerMeta(Action onSuccess, Action onError = null)
+ {
+ Guard.ArgumentNotNull(onSuccess, nameof(onSuccess));
+ new FuncTask(taskManager.Token, () =>
+ {
+ var octorunTask = new OctorunTask(taskManager.Token, environment, "meta -h " + HostAddress.ApiUri.Host)
+ .Configure(processManager);
+
+ var ret = octorunTask.RunSynchronously();
+ if (ret.IsSuccess)
+ {
+ var deserializeObject = SimpleJson.DeserializeObject>(ret.Output[0]);
+
+ return new GitHubHostMeta
+ {
+ InstalledVersion = (string)deserializeObject["installed_version"],
+ GithubServicesSha = (string)deserializeObject["github_services_sha"],
+ VerifiablePasswordAuthentication = (bool)deserializeObject["verifiable_password_authentication"]
+ };
+ }
+
+ var message = ret.GetApiErrorMessage();
+
+ logger.Trace("Message: {0}", message);
+
+ if (message != null)
+ {
+ if (message.Contains("ETIMEDOUT", StringComparison.InvariantCulture))
+ {
+ message = "Connection timed out.";
+ }
+ else if (message.Contains("ECONNREFUSED", StringComparison.InvariantCulture))
+ {
+ message = "Connection refused.";
+ }
+ else if (message.Contains("ENOTFOUND", StringComparison.InvariantCulture))
+ {
+ message = "Address not found.";
+ }
+ else
+ {
+ int httpStatusCode;
+ if (int.TryParse(message, out httpStatusCode))
+ {
+ var httpStatus = ((HttpStatusCode)httpStatusCode).ToString();
+ message = httpStatusErrorRegex.Replace(httpStatus, " $1");
+ }
+ }
+ }
+ else
+ {
+ message = "Error getting server meta";
+ }
+
+ throw new ApiClientException(message);
+ })
+ .FinallyInUI((success, ex, meta) =>
+ {
+ if (success)
+ onSuccess(meta);
+ else
+ {
+ logger.Error(ex, "Error getting server meta");
+ onError?.Invoke(ex);
+ }
+ })
+ .Start();
+ }
+
public void GetOrganizations(Action onSuccess, Action onError = null)
{
Guard.ArgumentNotNull(onSuccess, nameof(onSuccess));
new FuncTask(taskManager.Token, () =>
{
- var user = GetCurrentUser();
- var keychainAdapter = keychain.Connect(OriginalUrl);
+ var adapter = EnsureKeychainAdapter();
- var octorunTask = new OctorunTask(taskManager.Token, nodeJsExecutablePath, octorunScriptPath, "organizations",
- user: user.Login, userToken: keychainAdapter.Credential.Token)
+ var command = HostAddress.IsGitHubDotCom() ? "organizations" : "organizations -h " + HostAddress.ApiUri.Host;
+ var octorunTask = new OctorunTask(taskManager.Token, environment,
+ command, adapter.Credential.Token)
.Configure(processManager);
var ret = octorunTask.RunSynchronously();
@@ -143,6 +231,17 @@ public void GetOrganizations(Action onSuccess, Action
.Start();
}
+ private IKeychainAdapter EnsureKeychainAdapter()
+ {
+ var adapter = KeychainAdapter;
+ if (adapter.Credential == null)
+ {
+ throw new ApiClientException("No Credentials found");
+ }
+
+ return adapter;
+ }
+
public void GetCurrentUser(Action onSuccess, Action onError = null)
{
Guard.ArgumentNotNull(onSuccess, nameof(onSuccess));
@@ -157,13 +256,72 @@ public void GetCurrentUser(Action onSuccess, Action onErr
.Start();
}
+ public void LoginWithToken(string token, Action result)
+ {
+ Guard.ArgumentNotNull(token, "token");
+ Guard.ArgumentNotNull(result, "result");
+
+ new FuncTask(taskManager.Token,
+ () => loginManager.LoginWithToken(HostAddress.WebUri.Host, token))
+ .FinallyInUI((success, ex, res) =>
+ {
+ if (!success)
+ {
+ logger.Warning(ex);
+ result(false);
+ return;
+ }
+
+ result(res);
+ })
+ .Start();
+ }
+
+ public void CreateOAuthToken(string code, Action result)
+ {
+ var command = "token -h " + HostAddress.WebUri.Host;
+ var octorunTask = new OctorunTask(taskManager.Token, environment, command, code)
+ .Configure(processManager);
+
+ octorunTask
+ .Then((b, octorunResult) =>
+ {
+ if (b && octorunResult.IsSuccess)
+ {
+ var first = octorunResult.Output.FirstOrDefault();
+ if (first == null)
+ {
+ result(false, "Error validating token.");
+ return;
+ }
+
+ var match = accessTokenRegex.Match(first);
+ if (match.Success)
+ {
+ var token = match.Groups[1].Value;
+ LoginWithToken(token, b1 => result(b1, "Error validating token."));
+ }
+ else
+ {
+ result(false, octorunResult.Output.FirstOrDefault());
+ }
+ }
+ else
+ {
+ result(false, octorunResult.Output.FirstOrDefault());
+ }
+ })
+ .Catch(exception => result(false, exception.ToString()))
+ .Start();
+ }
+
public void Login(string username, string password, Action need2faCode, Action result)
{
Guard.ArgumentNotNull(need2faCode, "need2faCode");
Guard.ArgumentNotNull(result, "result");
new FuncTask(taskManager.Token,
- () => loginManager.Login(OriginalUrl, username, password))
+ () => loginManager.Login(HostAddress.WebUri.Host, username, password))
.FinallyInUI((success, ex, res) =>
{
if (!success)
@@ -206,51 +364,76 @@ public void ContinueLogin(LoginResult loginResult, string code)
.Start();
}
- private GitHubUser GetCurrentUser()
+ public void EnsureValidCredentials()
{
- //TODO: ONE_USER_LOGIN This assumes we only support one login
- var keychainConnection = keychain.Connections.FirstOrDefault();
- if (keychainConnection == null)
- throw new KeychainEmptyException();
-
- var keychainAdapter = GetValidatedKeychainAdapter(keychainConnection);
+ GetCurrentUser();
+ }
+ public GitHubUser GetCurrentUser()
+ {
// we can't trust that the system keychain has the username filled out correctly.
// if it doesn't, we need to grab the username from the server and check it
// unfortunately this means that things will be slower when the keychain doesn't have all the info
- if (keychainConnection.User == null || keychainAdapter.Credential.Username != keychainConnection.Username)
+ if (Connection.User == null || KeychainAdapter.Credential.Username != Connection.Username)
{
- keychainConnection.User = GetValidatedGitHubUser(keychainConnection, keychainAdapter);
+ Connection.User = GetValidatedGitHubUser();
}
- return keychainConnection.User;
+
+ return Connection.User;
}
- private IKeychainAdapter GetValidatedKeychainAdapter(Connection keychainConnection)
+ private Connection Connection
{
- var keychainAdapter = keychain.Load(keychainConnection.Host);
- if (keychainAdapter == null)
- throw new KeychainEmptyException();
-
- if (string.IsNullOrEmpty(keychainAdapter.Credential?.Username))
+ get
{
- logger.Warning("LoadKeychainInternal: Username is empty");
- throw new TokenUsernameMismatchException(keychainConnection.Username);
+ if (connection == null)
+ {
+ connection = keychain.Connections.FirstOrDefault(x => x.Host.ToUriString().Host == HostAddress.WebUri.Host);
+ }
+
+ return connection;
}
+ }
- if (keychainAdapter.Credential.Username != keychainConnection.Username)
+ private IKeychainAdapter KeychainAdapter
+ {
+ get
{
- logger.Warning("LoadKeychainInternal: Token username does not match");
- }
+ if (keychainAdapter == null)
+ {
+ if (Connection == null)
+ throw new KeychainEmptyException();
- return keychainAdapter;
+ var loadedKeychainAdapter = keychain.LoadFromSystem(Connection.Host);
+ if (loadedKeychainAdapter == null)
+ throw new KeychainEmptyException();
+
+ if (string.IsNullOrEmpty(loadedKeychainAdapter.Credential?.Username))
+ {
+ logger.Warning("LoadKeychainInternal: Username is empty");
+ throw new TokenUsernameMismatchException(connection.Username);
+ }
+
+ if (loadedKeychainAdapter.Credential.Username != connection.Username)
+ {
+ logger.Warning("LoadKeychainInternal: Token username does not match");
+ }
+
+ keychainAdapter = loadedKeychainAdapter;
+ }
+
+ return keychainAdapter;
+ }
}
- private GitHubUser GetValidatedGitHubUser(Connection keychainConnection, IKeychainAdapter keychainAdapter)
+ private GitHubUser GetValidatedGitHubUser()
{
try
{
- var octorunTask = new OctorunTask(taskManager.Token, nodeJsExecutablePath, octorunScriptPath, "validate",
- user: keychainConnection.Username, userToken: keychainAdapter.Credential.Token)
+ var adapter = EnsureKeychainAdapter();
+
+ var command = HostAddress.IsGitHubDotCom() ? "validate" : "validate -h " + HostAddress.ApiUri.Host;
+ var octorunTask = new OctorunTask(taskManager.Token, environment, command, adapter.Credential.Token)
.Configure(processManager);
var ret = octorunTask.RunSynchronously();
@@ -258,10 +441,10 @@ private GitHubUser GetValidatedGitHubUser(Connection keychainConnection, IKeycha
{
var login = ret.Output[1];
- if (login != keychainConnection.Username)
+ if (!string.Equals(login, Connection.Username, StringComparison.InvariantCultureIgnoreCase))
{
logger.Trace("LoadKeychainInternal: Api username does not match");
- throw new TokenUsernameMismatchException(keychainConnection.Username, login);
+ throw new TokenUsernameMismatchException(Connection.Username, login);
}
return new GitHubUser
@@ -286,13 +469,20 @@ private GitHubUser GetValidatedGitHubUser(Connection keychainConnection, IKeycha
}
}
- class GitHubUser
+ public class GitHubHostMeta
+ {
+ public bool VerifiablePasswordAuthentication { get; set; }
+ public string GithubServicesSha { get; set; }
+ public string InstalledVersion { get; set; }
+ }
+
+ public class GitHubUser
{
public string Name { get; set; }
public string Login { get; set; }
}
- class GitHubRepository
+ public class GitHubRepository
{
public string Name { get; set; }
public string CloneUrl { get; set; }
@@ -315,7 +505,7 @@ protected ApiClientException(SerializationInfo info, StreamingContext context) :
}
[Serializable]
- class TokenUsernameMismatchException : ApiClientException
+ public class TokenUsernameMismatchException : ApiClientException
{
public string CachedUsername { get; }
public string CurrentUsername { get; }
@@ -330,7 +520,7 @@ protected TokenUsernameMismatchException(SerializationInfo info, StreamingContex
}
[Serializable]
- class KeychainEmptyException : ApiClientException
+ public class KeychainEmptyException : ApiClientException
{
public KeychainEmptyException()
{
diff --git a/src/GitHub.Api/Application/ApplicationInfo.cs b/src/GitHub.Api/Application/ApplicationInfo.cs
index f70bf4241..f15f7d482 100644
--- a/src/GitHub.Api/Application/ApplicationInfo.cs
+++ b/src/GitHub.Api/Application/ApplicationInfo.cs
@@ -6,15 +6,32 @@ static partial class ApplicationInfo
#if DEBUG
public const string ApplicationName = "GitHub for Unity Debug";
public const string ApplicationProvider = "GitHub";
+ public const string ApplicationSafeName = "GitHubUnity-dev";
#else
public const string ApplicationName = "GitHubUnity";
public const string ApplicationProvider = "GitHub";
-#endif
public const string ApplicationSafeName = "GitHubUnity";
+#endif
public const string ApplicationDescription = "GitHub for Unity";
+#if DEBUG
+/*
+ For external contributors, we have bundled a developer OAuth application
+ called `GitHub for Unity (dev)` so that you can complete the sign in flow
+ locally without needing to configure your own application.
+ This is for testing only and it is (obviously) public, proceed with caution.
+
+ For a release build, you should create a new oauth application on github.com,
+ copy the `common/ApplicationInfo_Local.cs-example`
+ template to `common/ApplicationInfo_Local.cs` and fill out the `myClientId` and
+ `myClientSecret` fields for your oauth app.
+ */
+ internal static string ClientId { get; private set; } = "924a97f36926f535e72c";
+ internal static string ClientSecret { get; private set; } = "b4fa550b7f8e38034c6b1339084fa125eebb6155";
+#else
internal static string ClientId { get; private set; } = "";
internal static string ClientSecret { get; private set; } = "";
+#endif
public static string Version { get { return System.AssemblyVersionInformation.Version; } }
diff --git a/src/GitHub.Api/Application/ApplicationManagerBase.cs b/src/GitHub.Api/Application/ApplicationManagerBase.cs
index e009573d2..5d258087e 100644
--- a/src/GitHub.Api/Application/ApplicationManagerBase.cs
+++ b/src/GitHub.Api/Application/ApplicationManagerBase.cs
@@ -7,7 +7,7 @@
namespace GitHub.Unity
{
- class ApplicationManagerBase : IApplicationManager
+ public class ApplicationManagerBase : IApplicationManager
{
protected static ILogging Logger { get; } = LogHelper.GetLogger();
@@ -48,14 +48,13 @@ protected void Initialize()
ApplicationConfiguration.GitTimeout = UserSettings.Get(Constants.GitTimeoutKey, ApplicationConfiguration.GitTimeout);
Platform.Initialize(ProcessManager, TaskManager);
progress.OnProgress += progressReporter.UpdateProgress;
- UsageTracker = new UsageTracker(TaskManager, GitClient, ProcessManager, UserSettings, Environment, InstanceId.ToString());
+ UsageTracker = new UsageTracker(TaskManager, GitClient, ProcessManager, UserSettings, Environment, Platform.Keychain, InstanceId.ToString());
#if ENABLE_METRICS
var metricsService = new MetricsService(ProcessManager,
TaskManager,
- Environment.FileSystem,
- Environment.NodeJsExecutablePath,
- Environment.OctorunScriptPath);
+ Platform.Keychain,
+ Environment);
UsageTracker.MetricsService = metricsService;
#endif
}
@@ -138,7 +137,7 @@ public void Run()
RestartRepository();
}
- progress.UpdateProgress(100, 100, "Initialization failed");
+ progress.UpdateProgress(100, 100, "Initialized");
}
catch (Exception ex)
{
@@ -191,7 +190,7 @@ public void SetupGit(GitInstaller.GitInstallationState state)
{
if (Environment.RepositoryPath.IsInitialized)
{
- ConfigureMergeSettings();
+ UpdateMergeSettings();
GitClient.LfsInstall()
.Catch(e =>
@@ -281,23 +280,56 @@ public void InitializeRepository()
thread.Start();
}
- private void ConfigureMergeSettings()
+ private void ConfigureMergeSettings(string keyName = null)
{
var unityYamlMergeExec =
Environment.UnityApplicationContents.Combine("Tools", "UnityYAMLMerge" + Environment.ExecutableExtension);
- var yamlMergeCommand = Environment.IsWindows
- ? $@"'{unityYamlMergeExec}' merge -p ""$BASE"" ""$REMOTE"" ""$LOCAL"" ""$MERGED"""
- : $@"'{unityYamlMergeExec}' merge -p '$BASE' '$REMOTE' '$LOCAL' '$MERGED'";
- GitClient.SetConfig("merge.unityyamlmerge.cmd", yamlMergeCommand, GitConfigSource.Local).Catch(e => {
- Logger.Error(e, "Error setting merge.unityyamlmerge.cmd");
+ var yamlMergeCommand = $"'{unityYamlMergeExec}' merge -h -p --force %O %B %A %A";
+
+ keyName = keyName ?? "unityyamlmerge";
+
+ GitClient.SetConfig($"merge.{keyName}.name", "Unity SmartMerge (UnityYamlMerge)", GitConfigSource.Local).Catch(e => {
+ Logger.Error(e, "Error setting merge." + keyName + ".name");
+ return true;
+ }).RunSynchronously();
+
+ GitClient.SetConfig($"merge.{keyName}.driver", yamlMergeCommand, GitConfigSource.Local).Catch(e => {
+ Logger.Error(e, "Error setting merge." + keyName + ".driver");
+ return true;
+ }).RunSynchronously();
+
+ GitClient.SetConfig($"merge.{keyName}.recursive", "binary", GitConfigSource.Local).Catch(e => {
+ Logger.Error(e, "Error setting merge." + keyName + ".recursive");
+ return true;
+ }).RunSynchronously();
+ }
+
+ private void UpdateMergeSettings()
+ {
+ var gitAttributesPath = Environment.RepositoryPath.Combine(".gitattributes");
+ if (gitAttributesPath.FileExists())
+ {
+ var readAllText = gitAttributesPath.ReadAllText();
+ var containsLegacyUnityYamlMergeError = readAllText.Contains("unityamlmerge");
+
+ if (containsLegacyUnityYamlMergeError)
+ {
+ ConfigureMergeSettings("unityamlmerge");
+ }
+ }
+
+ GitClient.UnSetConfig("merge.unityyamlmerge.cmd", GitConfigSource.Local).Catch(e => {
+ Logger.Error(e, "Error removing merge.unityyamlmerge.cmd");
return true;
}).RunSynchronously();
- GitClient.SetConfig("merge.unityyamlmerge.trustExitCode", "false", GitConfigSource.Local).Catch(e => {
- Logger.Error(e, "Error setting merge.unityyamlmerge.trustExitCode");
+ GitClient.UnSetConfig("merge.unityyamlmerge.trustExitCode", GitConfigSource.Local).Catch(e => {
+ Logger.Error(e, "Error removing merge.unityyamlmerge.trustExitCode");
return true;
}).RunSynchronously();
+
+ ConfigureMergeSettings();
}
public void RestartRepository()
@@ -319,6 +351,8 @@ protected virtual void InitializeUI() {}
protected virtual void InitializationComplete() {}
private bool disposed = false;
+ private IOAuthCallbackManager oAuthCallbackManager;
+
protected virtual void Dispose(bool disposing)
{
if (disposing)
@@ -357,6 +391,20 @@ public void Dispose()
public ISettings SystemSettings { get { return Environment.SystemSettings; } }
public ISettings UserSettings { get { return Environment.UserSettings; } }
public IUsageTracker UsageTracker { get; protected set; }
+
+ public IOAuthCallbackManager OAuthCallbackManager
+ {
+ get
+ {
+ if (oAuthCallbackManager == null)
+ {
+ oAuthCallbackManager = new OAuthCallbackManager();
+ }
+
+ return oAuthCallbackManager;
+ }
+ }
+
public bool IsBusy { get { return isBusy; } }
protected TaskScheduler UIScheduler { get; private set; }
protected SynchronizationContext SynchronizationContext { get; private set; }
diff --git a/src/GitHub.Api/Application/IApiClient.cs b/src/GitHub.Api/Application/IApiClient.cs
index 650595ce2..09c79611f 100644
--- a/src/GitHub.Api/Application/IApiClient.cs
+++ b/src/GitHub.Api/Application/IApiClient.cs
@@ -2,16 +2,18 @@
namespace GitHub.Unity
{
- interface IApiClient
+ public interface IApiClient
{
HostAddress HostAddress { get; }
- UriString OriginalUrl { get; }
void CreateRepository(string name, string description, bool isPrivate,
Action callback, string organization = null);
void GetOrganizations(Action onSuccess, Action onError = null);
void Login(string username, string password, Action need2faCode, Action result);
void ContinueLogin(LoginResult loginResult, string code);
+ void LoginWithToken(string token, Action result);
ITask Logout(UriString host);
void GetCurrentUser(Action onSuccess, Action onError = null);
+ void GetEnterpriseServerMeta(Action onSuccess, Action onError = null);
+ void CreateOAuthToken(string code, Action result);
}
}
diff --git a/src/GitHub.Api/Application/IApplicationManager.cs b/src/GitHub.Api/Application/IApplicationManager.cs
index 9b8b5f638..ab82a27d3 100644
--- a/src/GitHub.Api/Application/IApplicationManager.cs
+++ b/src/GitHub.Api/Application/IApplicationManager.cs
@@ -16,6 +16,7 @@ public interface IApplicationManager : IDisposable
ITaskManager TaskManager { get; }
IGitClient GitClient { get; }
IUsageTracker UsageTracker { get; }
+ IOAuthCallbackManager OAuthCallbackManager { get; }
bool IsBusy { get; }
void Run();
void InitializeRepository();
@@ -23,4 +24,4 @@ public interface IApplicationManager : IDisposable
void SetupGit(GitInstaller.GitInstallationState state);
void RestartRepository();
}
-}
\ No newline at end of file
+}
diff --git a/src/GitHub.Api/Application/Organization.cs b/src/GitHub.Api/Application/Organization.cs
index e78849dd6..8deea7d99 100644
--- a/src/GitHub.Api/Application/Organization.cs
+++ b/src/GitHub.Api/Application/Organization.cs
@@ -1,8 +1,8 @@
namespace GitHub.Unity
{
- class Organization
+ public class Organization
{
public string Name { get; set; }
public string Login { get; set; }
}
-}
\ No newline at end of file
+}
diff --git a/src/GitHub.Api/Authentication/Credential.cs b/src/GitHub.Api/Authentication/Credential.cs
index 2e31f9838..86e76c458 100644
--- a/src/GitHub.Api/Authentication/Credential.cs
+++ b/src/GitHub.Api/Authentication/Credential.cs
@@ -16,7 +16,7 @@ public Credential(UriString host, string username, string token)
this.Token = token;
}
- public void UpdateToken(string token, string username)
+ public void Update(string token, string username)
{
this.Token = token;
this.Username = username;
diff --git a/src/GitHub.Api/Authentication/ICredentialManager.cs b/src/GitHub.Api/Authentication/ICredentialManager.cs
index 68bf53eb6..94aef5a97 100644
--- a/src/GitHub.Api/Authentication/ICredentialManager.cs
+++ b/src/GitHub.Api/Authentication/ICredentialManager.cs
@@ -8,7 +8,7 @@ public interface ICredential : IDisposable
UriString Host { get; }
string Username { get; }
string Token { get; }
- void UpdateToken(string token, string username);
+ void Update(string token, string username);
}
public interface ICredentialManager
@@ -17,6 +17,5 @@ public interface ICredentialManager
void Save(ICredential cred);
void Delete(UriString host);
bool HasCredentials();
- ICredential CachedCredentials { get; }
}
}
diff --git a/src/GitHub.Api/Authentication/IKeychain.cs b/src/GitHub.Api/Authentication/IKeychain.cs
index 92d5dc524..4a14e1e2f 100644
--- a/src/GitHub.Api/Authentication/IKeychain.cs
+++ b/src/GitHub.Api/Authentication/IKeychain.cs
@@ -6,15 +6,13 @@ namespace GitHub.Unity
public interface IKeychain
{
IKeychainAdapter Connect(UriString host);
- IKeychainAdapter Load(UriString host);
+ IKeychainAdapter LoadFromSystem(UriString host);
void Clear(UriString host, bool deleteFromCredentialManager);
- void Save(UriString host);
- void SetCredentials(ICredential credential);
+ void SaveToSystem(UriString host);
void Initialize();
Connection[] Connections { get; }
IList Hosts { get; }
bool HasKeys { get; }
- void SetToken(UriString host, string token, string username);
event Action ConnectionsChanged;
}
diff --git a/src/GitHub.Api/Authentication/ILoginManager.cs b/src/GitHub.Api/Authentication/ILoginManager.cs
index 66d982ae0..c78112c0e 100644
--- a/src/GitHub.Api/Authentication/ILoginManager.cs
+++ b/src/GitHub.Api/Authentication/ILoginManager.cs
@@ -8,9 +8,9 @@ namespace GitHub.Unity
interface ILoginManager
{
///
- /// Attempts to log into a GitHub server.
+ /// Attempts to log into a GitHub server with a username and password.
///
- ///
+ /// The host.
/// The username.
/// The password.
/// The logged in user.
@@ -18,6 +18,7 @@ interface ILoginManager
/// The login authorization failed.
///
LoginResultData Login(UriString host, string username, string password);
+
LoginResultData ContinueLogin(LoginResultData loginResultData, string twofacode);
///
@@ -26,5 +27,13 @@ interface ILoginManager
/// The address of the server.
///
ITask Logout(UriString hostAddress);
+
+ ///
+ /// Attempts to log into a GitHub server with a token.
+ ///
+ /// The host.
+ /// The token.
+ ///
+ bool LoginWithToken(UriString host, string token);
}
}
diff --git a/src/GitHub.Api/Authentication/Keychain.cs b/src/GitHub.Api/Authentication/Keychain.cs
index 992cc26b3..c71d45cbe 100644
--- a/src/GitHub.Api/Authentication/Keychain.cs
+++ b/src/GitHub.Api/Authentication/Keychain.cs
@@ -67,7 +67,7 @@ public bool Equals(Connection other)
}
}
- class Keychain : IKeychain
+ public class Keychain : IKeychain
{
const string ConnectionFile = "connections.json";
@@ -95,19 +95,16 @@ public Keychain(IEnvironment environment, ICredentialManager credentialManager)
public IKeychainAdapter Connect(UriString host)
{
Guard.ArgumentNotNull(host, nameof(host));
-
return FindOrCreateAdapter(host);
}
- public IKeychainAdapter Load(UriString host)
+ public IKeychainAdapter LoadFromSystem(UriString host)
{
Guard.ArgumentNotNull(host, nameof(host));
- var keychainAdapter = FindOrCreateAdapter(host);
- var connection = GetConnection(host);
-
- var keychainItem = credentialManager.Load(host);
- if (keychainItem == null)
+ var keychainAdapter = Connect(host) as KeychainAdapter;
+ var credential = credentialManager.Load(host);
+ if (credential == null)
{
logger.Warning("Cannot load host from Credential Manager; removing from cache");
Clear(host, false);
@@ -115,12 +112,18 @@ public IKeychainAdapter Load(UriString host)
}
else
{
- if (keychainItem.Username != connection.Username)
+ keychainAdapter.Set(credential);
+ var connection = GetConnection(host);
+ if (connection.Username == null)
{
- logger.Warning("Keychain Username:\"{0}\" does not match cached Username:\"{1}\"; Hopefully it works", keychainItem.Username, connection.Username);
+ connection.Username = credential.Username;
+ SaveConnectionsToDisk();
}
- keychainAdapter.Set(keychainItem);
+ if (credential.Username != connection.Username)
+ {
+ logger.Warning("Keychain Username:\"{0}\" does not match cached Username:\"{1}\"; Hopefully it works", credential.Username, connection.Username);
+ }
}
return keychainAdapter;
}
@@ -151,7 +154,7 @@ public void Clear(UriString host, bool deleteFromCredentialManager)
RemoveCredential(host, deleteFromCredentialManager);
}
- public void Save(UriString host)
+ public void SaveToSystem(UriString host)
{
Guard.ArgumentNotNull(host, nameof(host));
@@ -159,24 +162,6 @@ public void Save(UriString host)
AddConnection(new Connection(host, keychainAdapter.Credential.Username));
}
- public void SetCredentials(ICredential credential)
- {
- Guard.ArgumentNotNull(credential, nameof(credential));
-
- var keychainAdapter = GetKeychainAdapter(credential.Host);
- keychainAdapter.Set(credential);
- }
-
- public void SetToken(UriString host, string token, string username)
- {
- Guard.ArgumentNotNull(host, nameof(host));
- Guard.ArgumentNotNull(token, nameof(token));
- Guard.ArgumentNotNull(username, nameof(username));
-
- var keychainAdapter = GetKeychainAdapter(host);
- keychainAdapter.UpdateToken(token, username);
- }
-
private void LoadConnectionsFromDisk()
{
if (cachePath.FileExists())
@@ -262,11 +247,11 @@ private void RemoveCredential(UriString host, bool deleteFromCredentialManager)
private Connection GetConnection(UriString host)
{
if (!connections.ContainsKey(host))
- throw new ArgumentException($"{host} is not found", nameof(host));
+ return AddConnection(new Connection(host, null));
return connections[host];
}
- private void AddConnection(Connection connection)
+ private Connection AddConnection(Connection connection)
{
// create new connection in the connection cache for this host
if (connections.ContainsKey(connection.Host))
@@ -274,6 +259,7 @@ private void AddConnection(Connection connection)
else
connections.Add(connection.Host, connection);
SaveConnectionsToDisk();
+ return connection;
}
private void RemoveConnection(UriString host)
diff --git a/src/GitHub.Api/Authentication/KeychainAdapter.cs b/src/GitHub.Api/Authentication/KeychainAdapter.cs
index abbe9895e..7a0c5d3aa 100644
--- a/src/GitHub.Api/Authentication/KeychainAdapter.cs
+++ b/src/GitHub.Api/Authentication/KeychainAdapter.cs
@@ -1,6 +1,6 @@
namespace GitHub.Unity
{
- class KeychainAdapter : IKeychainAdapter
+ public class KeychainAdapter : IKeychainAdapter
{
public ICredential Credential { get; private set; }
@@ -9,9 +9,9 @@ public void Set(ICredential credential)
Credential = credential;
}
- public void UpdateToken(string token, string username)
+ public void Update(string token, string username)
{
- Credential.UpdateToken(token, username);
+ Credential.Update(token, username);
}
public void Clear()
@@ -23,5 +23,8 @@ public void Clear()
public interface IKeychainAdapter
{
ICredential Credential { get; }
+ void Set(ICredential credential);
+ void Update(string token, string username);
+ void Clear();
}
}
diff --git a/src/GitHub.Api/Authentication/LoginManager.cs b/src/GitHub.Api/Authentication/LoginManager.cs
index 44a337a54..6f892b659 100644
--- a/src/GitHub.Api/Authentication/LoginManager.cs
+++ b/src/GitHub.Api/Authentication/LoginManager.cs
@@ -1,4 +1,5 @@
using System;
+using System.Text;
using GitHub.Logging;
namespace GitHub.Unity
@@ -15,15 +16,14 @@ public enum LoginResultCodes
///
/// Provides services for logging into a GitHub server.
///
- class LoginManager : ILoginManager
+ public class LoginManager : ILoginManager
{
private readonly ILogging logger = LogHelper.GetLogger();
private readonly IKeychain keychain;
private readonly IProcessManager processManager;
private readonly ITaskManager taskManager;
- private readonly NPath? nodeJsExecutablePath;
- private readonly NPath? octorunScript;
+ private readonly IEnvironment environment;
///
/// Initializes a new instance of the class.
@@ -35,15 +35,39 @@ class LoginManager : ILoginManager
///
public LoginManager(
IKeychain keychain, IProcessManager processManager, ITaskManager taskManager,
- NPath? nodeJsExecutablePath = null, NPath? octorunScript = null)
+ IEnvironment environment)
{
Guard.ArgumentNotNull(keychain, nameof(keychain));
this.keychain = keychain;
this.processManager = processManager;
this.taskManager = taskManager;
- this.nodeJsExecutablePath = nodeJsExecutablePath;
- this.octorunScript = octorunScript;
+ this.environment = environment;
+ }
+
+ public bool LoginWithToken(UriString host, string token)
+ {
+ Guard.ArgumentNotNull(host, nameof(host));
+ Guard.ArgumentNotNullOrWhiteSpace(token, nameof(token));
+
+ var keychainAdapter = keychain.Connect(host);
+ keychainAdapter.Set(new Credential(host, "[token]", token));
+
+ try
+ {
+ var username = RetrieveUsername(token, host);
+ keychainAdapter.Update(token, username);
+ keychain.SaveToSystem(host);
+
+ return true;
+ }
+ catch (Exception e)
+ {
+ logger.Warning(e, "Login Exception");
+
+ keychain.Clear(host, false);
+ return false;
+ }
}
///
@@ -58,8 +82,8 @@ public LoginResultData Login(
// Start by saving the username and password, these will be used by the `IGitHubClient`
// until an authorization token has been created and acquired:
- keychain.Connect(host);
- keychain.SetCredentials(new Credential(host, username, password));
+ var keychainAdapter = keychain.Connect(host);
+ keychainAdapter.Set(new Credential(host, username, password));
try
{
@@ -71,16 +95,13 @@ public LoginResultData Login(
throw new InvalidOperationException("Returned token is null or empty");
}
- if (loginResultData.Code == LoginResultCodes.Success)
- {
- username = RetrieveUsername(loginResultData, username);
- }
-
- keychain.SetToken(host, loginResultData.Token, username);
+ keychainAdapter.Update(loginResultData.Token, username);
if (loginResultData.Code == LoginResultCodes.Success)
{
- keychain.Save(host);
+ username = RetrieveUsername(loginResultData.Token, host);
+ keychainAdapter.Update(loginResultData.Token, username);
+ keychain.SaveToSystem(host);
}
return loginResultData;
@@ -101,6 +122,9 @@ public LoginResultData ContinueLogin(LoginResultData loginResultData, string two
{
var host = loginResultData.Host;
var keychainAdapter = keychain.Connect(host);
+ if (keychainAdapter.Credential == null) {
+ return new LoginResultData(LoginResultCodes.Failed, Localization.LoginFailed, host);
+ }
var username = keychainAdapter.Credential.Username;
var password = keychainAdapter.Credential.Token;
try
@@ -114,9 +138,10 @@ public LoginResultData ContinueLogin(LoginResultData loginResultData, string two
throw new InvalidOperationException("Returned token is null or empty");
}
- username = RetrieveUsername(loginResultData, username);
- keychain.SetToken(host, loginResultData.Token, username);
- keychain.Save(host);
+ keychainAdapter.Update(loginResultData.Token, username);
+ username = RetrieveUsername(loginResultData.Token, host);
+ keychainAdapter.Update(loginResultData.Token, username);
+ keychain.SaveToSystem(host);
return loginResultData;
}
@@ -146,22 +171,23 @@ private LoginResultData TryLogin(
string code = null
)
{
- if (!nodeJsExecutablePath.HasValue)
+ var hasTwoFactorCode = code != null;
+
+ var command = new StringBuilder("login");
+
+ if (hasTwoFactorCode)
{
- throw new InvalidOperationException("nodeJsExecutablePath must be set");
+ command.Append(" --twoFactor");
}
- if (!octorunScript.HasValue)
+ if (!HostAddress.IsGitHubDotCom(host))
{
- throw new InvalidOperationException("octorunScript must be set");
+ command.Append(" -h ");
+ command.Append(host.Host);
}
- var hasTwoFactorCode = code != null;
-
- var arguments = hasTwoFactorCode ? "login --twoFactor" : "login";
- var loginTask = new OctorunTask(taskManager.Token, nodeJsExecutablePath.Value, octorunScript.Value,
- arguments, ApplicationInfo.ClientId, ApplicationInfo.ClientSecret);
- loginTask.Configure(processManager, workingDirectory: octorunScript.Value.Parent.Parent, withInput: true);
+ var loginTask = new OctorunTask(taskManager.Token, environment, command.ToString());
+ loginTask.Configure(processManager, withInput: true);
loginTask.OnStartProcess += proc =>
{
proc.StandardInput.WriteLine(username);
@@ -191,15 +217,11 @@ private LoginResultData TryLogin(
return new LoginResultData(LoginResultCodes.Failed, ret.GetApiErrorMessage() ?? "Failed.", host);
}
- private string RetrieveUsername(LoginResultData loginResultData, string username)
+ private string RetrieveUsername(string token, UriString host)
{
- if (!username.Contains("@"))
- {
- return username;
- }
-
- var octorunTask = new OctorunTask(taskManager.Token, nodeJsExecutablePath.Value, octorunScript.Value, "validate",
- user: username, userToken: loginResultData.Token).Configure(processManager);
+ var command = HostAddress.IsGitHubDotCom(host) ? "validate" : "validate -h " + host.Host;
+ var octorunTask = new OctorunTask(taskManager.Token, environment, command, token)
+ .Configure(processManager);
var validateResult = octorunTask.RunSynchronously();
if (!validateResult.IsSuccess)
@@ -211,7 +233,7 @@ private string RetrieveUsername(LoginResultData loginResultData, string username
}
}
- class LoginResultData
+ public class LoginResultData
{
public LoginResultCodes Code;
public string Message;
diff --git a/src/GitHub.Api/Authentication/OAuthCallbackManager.cs b/src/GitHub.Api/Authentication/OAuthCallbackManager.cs
new file mode 100644
index 000000000..b5b75b094
--- /dev/null
+++ b/src/GitHub.Api/Authentication/OAuthCallbackManager.cs
@@ -0,0 +1,106 @@
+using System;
+using System.IO;
+using System.Net;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Web;
+using GitHub.Logging;
+
+namespace GitHub.Unity
+{
+ public interface IOAuthCallbackManager
+ {
+ event Action OnCallback;
+ bool IsRunning { get; }
+ void Start();
+ void Stop();
+ }
+
+ public class OAuthCallbackManager : IOAuthCallbackManager
+ {
+ const int CallbackPort = 42424;
+ public static readonly Uri CallbackUrl = new Uri($"http://localhost:{CallbackPort}/callback");
+
+ private static readonly ILogging logger = LogHelper.GetLogger();
+ private static readonly object _lock = new object();
+
+
+ private readonly CancellationTokenSource cancelSource;
+
+ private HttpListener httpListener;
+ public bool IsRunning { get; private set; }
+
+ public event Action OnCallback;
+
+ public OAuthCallbackManager()
+ {
+ cancelSource = new CancellationTokenSource();
+ }
+
+ public void Start()
+ {
+ if (!IsRunning)
+ {
+ lock(_lock)
+ {
+ if (!IsRunning)
+ {
+ logger.Trace("Starting");
+
+ httpListener = new HttpListener();
+ httpListener.Prefixes.Add(CallbackUrl.AbsoluteUri + "/");
+ httpListener.Start();
+ Task.Factory.StartNew(Listen, cancelSource.Token);
+ IsRunning = true;
+ }
+ }
+ }
+ }
+
+ public void Stop()
+ {
+ logger.Trace("Stopping");
+ cancelSource.Cancel();
+ }
+
+ private void Listen()
+ {
+ try
+ {
+ using (httpListener)
+ {
+ using (cancelSource.Token.Register(httpListener.Stop))
+ {
+ while (true)
+ {
+ var context = httpListener.GetContext();
+ var queryParts = HttpUtility.ParseQueryString(context.Request.Url.Query);
+
+ var state = queryParts["state"];
+ var code = queryParts["code"];
+
+ logger.Trace("OnCallback: {0}", state);
+ if (OnCallback != null)
+ {
+ OnCallback(state, code);
+ }
+
+ context.Response.StatusCode = 200;
+ context.Response.Close();
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ logger.Trace(ex.Message);
+ }
+ finally
+ {
+ IsRunning = false;
+ httpListener = null;
+ }
+ }
+ }
+}
diff --git a/src/GitHub.Api/Cache/CacheContainer.cs b/src/GitHub.Api/Cache/CacheContainer.cs
index 40f1802fb..2c07ff867 100644
--- a/src/GitHub.Api/Cache/CacheContainer.cs
+++ b/src/GitHub.Api/Cache/CacheContainer.cs
@@ -90,6 +90,7 @@ public void Dispose()
public IBranchCache BranchCache { get { return (IBranchCache)caches[CacheType.Branches].Value; } }
public IGitLogCache GitLogCache { get { return (IGitLogCache)caches[CacheType.GitLog].Value; } }
+ public IGitFileLogCache GitFileLogCache { get { return (IGitFileLogCache)caches[CacheType.GitFileLog].Value; } }
public IGitAheadBehindCache GitTrackingStatusCache { get { return (IGitAheadBehindCache)caches[CacheType.GitAheadBehind].Value; } }
public IGitStatusCache GitStatusEntriesCache { get { return (IGitStatusCache)caches[CacheType.GitStatus].Value; } }
public IGitLocksCache GitLocksCache { get { return (IGitLocksCache)caches[CacheType.GitLocks].Value; } }
diff --git a/src/GitHub.Api/Cache/CacheInterfaces.cs b/src/GitHub.Api/Cache/CacheInterfaces.cs
index 35487451e..ae815beac 100644
--- a/src/GitHub.Api/Cache/CacheInterfaces.cs
+++ b/src/GitHub.Api/Cache/CacheInterfaces.cs
@@ -9,6 +9,7 @@ public enum CacheType
RepositoryInfo,
Branches,
GitLog,
+ GitFileLog,
GitAheadBehind,
GitStatus,
GitLocks,
@@ -22,6 +23,7 @@ public interface ICacheContainer : IDisposable
IBranchCache BranchCache { get; }
IGitLogCache GitLogCache { get; }
+ IGitFileLogCache GitFileLogCache { get; }
IGitAheadBehindCache GitTrackingStatusCache { get; }
IGitStatusCache GitStatusEntriesCache { get; }
IGitLocksCache GitLocksCache { get; }
@@ -40,6 +42,7 @@ public interface IManagedCache
bool ValidateData();
void InvalidateData();
+ void ResetInvalidation();
DateTimeOffset LastUpdatedAt { get; }
CacheType CacheType { get; }
@@ -91,7 +94,7 @@ public interface IBranchCache : IManagedCache
ILocalConfigBranchDictionary LocalConfigBranches { get; }
IRemoteConfigBranchDictionary RemoteConfigBranches { get; }
IConfigRemoteDictionary ConfigRemotes { get; }
-
+
void SetRemotes(Dictionary remoteConfigs, Dictionary> configBranches, GitRemote[] gitRemotes, GitBranch[] gitBranches);
void SetLocals(Dictionary configBranches, GitBranch[] gitBranches);
}
@@ -114,6 +117,11 @@ public interface IGitLogCache : IManagedCache
List Log { get; set; }
}
+ public interface IGitFileLogCache : IManagedCache
+ {
+ GitFileLog FileLog { get; set; }
+ }
+
public interface ICanUpdate
{
void UpdateData(T data);
diff --git a/src/GitHub.Api/Events/RepositoryWatcher.cs b/src/GitHub.Api/Events/RepositoryWatcher.cs
index 582790303..a9c852fc6 100644
--- a/src/GitHub.Api/Events/RepositoryWatcher.cs
+++ b/src/GitHub.Api/Events/RepositoryWatcher.cs
@@ -8,7 +8,7 @@
namespace GitHub.Unity
{
- interface IRepositoryWatcher : IDisposable
+ public interface IRepositoryWatcher : IDisposable
{
void Start();
void Stop();
@@ -23,13 +23,14 @@ interface IRepositoryWatcher : IDisposable
int CheckAndProcessEvents();
}
- class RepositoryWatcher : IRepositoryWatcher
+ public class RepositoryWatcher : IRepositoryWatcher
{
private readonly RepositoryPathConfiguration paths;
private readonly CancellationToken cancellationToken;
private readonly NPath[] ignoredPaths;
private readonly ManualResetEventSlim pauseEvent;
private NativeInterface nativeInterface;
+ private NativeInterface worktreeNativeInterface;
private bool running;
private int lastCountOfProcessedEvents = 0;
private bool processingEvents;
@@ -64,6 +65,11 @@ public void Initialize()
try
{
nativeInterface = new NativeInterface(pathsRepositoryPath);
+
+ if (paths.IsWorktree)
+ {
+ worktreeNativeInterface = new NativeInterface(paths.WorktreeDotGitPath);
+ }
}
catch (Exception ex)
{
@@ -80,6 +86,18 @@ public void Start()
}
Logger.Trace("Watching Path: \"{0}\"", paths.RepositoryPath.ToString());
+
+ if (paths.IsWorktree)
+ {
+ if (worktreeNativeInterface == null)
+ {
+ Logger.Warning("Worktree NativeInterface is null");
+ throw new InvalidOperationException("Worktree NativeInterface is null");
+ }
+
+ Logger.Trace("Watching Additional Path for Worktree: \"{0}\"", paths.WorktreeDotGitPath);
+ }
+
running = true;
pauseEvent.Reset();
Task.Factory.StartNew(WatcherLoop, cancellationToken, TaskCreationOptions.None, TaskScheduler.Default);
@@ -131,6 +149,15 @@ public int CheckAndProcessEvents()
processedEventCount = ProcessEvents(fileEvents);
}
+ if (worktreeNativeInterface != null)
+ {
+ fileEvents = worktreeNativeInterface.GetEvents();
+ if (fileEvents.Length > 0)
+ {
+ processedEventCount = processedEventCount + ProcessEvents(fileEvents);
+ }
+ }
+
lastCountOfProcessedEvents = processedEventCount;
processingEvents = false;
signalProcessingEventsDone.Set();
@@ -158,7 +185,7 @@ private int ProcessEvents(Event[] fileEvents)
var fileA = eventDirectory.Combine(fileEvent.FileA);
// handling events in .git/*
- if (fileA.IsChildOf(paths.DotGitPath))
+ if (fileA.IsChildOf(paths.DotGitPath) || (paths.WorktreeDotGitPath.IsInitialized && fileA.IsChildOf(paths.WorktreeDotGitPath)))
{
if (!events.Contains(EventType.ConfigChanged) && fileA.Equals(paths.DotGitConfig))
{
diff --git a/src/GitHub.Api/Git/FailureSeverity.cs b/src/GitHub.Api/Git/FailureSeverity.cs
index fdaa58345..3d34f95ef 100644
--- a/src/GitHub.Api/Git/FailureSeverity.cs
+++ b/src/GitHub.Api/Git/FailureSeverity.cs
@@ -1,8 +1,8 @@
namespace GitHub.Unity
{
- enum FailureSeverity
+ public enum FailureSeverity
{
Moderate,
Critical
};
-}
\ No newline at end of file
+}
diff --git a/src/GitHub.Api/Git/GitBranch.cs b/src/GitHub.Api/Git/GitBranch.cs
index 857212810..ecb6fede9 100644
--- a/src/GitHub.Api/Git/GitBranch.cs
+++ b/src/GitHub.Api/Git/GitBranch.cs
@@ -10,12 +10,12 @@ public struct GitBranch
public string name;
public string tracking;
- public GitBranch(string name, string tracking)
+ public GitBranch(string name, string tracking = null)
{
Guard.ArgumentNotNullOrWhiteSpace(name, "name");
this.name = name;
- this.tracking = tracking;
+ this.tracking = tracking ?? string.Empty;
}
public override int GetHashCode()
@@ -64,7 +64,7 @@ public bool Equals(GitBranch other)
public override string ToString()
{
- return $"{Name} Tracking? {Tracking}";
+ return $"{Name} Tracking? {Tracking ?? "[NULL]"}";
}
}
-}
\ No newline at end of file
+}
diff --git a/src/GitHub.Api/Git/GitClient.cs b/src/GitHub.Api/Git/GitClient.cs
index 8dc75d6b6..6aca3146b 100644
--- a/src/GitHub.Api/Git/GitClient.cs
+++ b/src/GitHub.Api/Git/GitClient.cs
@@ -2,48 +2,310 @@
using System;
using System.Collections.Generic;
using System.Threading;
+using GitHub.Unity.Git.Tasks;
using static GitHub.Unity.GitInstaller;
namespace GitHub.Unity
{
+ ///
+ /// Client that provides access to git functionality
+ ///
public interface IGitClient
{
+ ///
+ /// Executes `git init` to initialize a git repo.
+ ///
+ /// A custom output processor instance
+ /// String output of git command
ITask Init(IOutputProcessor processor = null);
+
+ ///
+ /// Executes `git lfs install` to install LFS hooks.
+ ///
+ /// A custom output processor instance
+ /// String output of git command
ITask LfsInstall(IOutputProcessor processor = null);
+
+ ///
+ /// Executes `git rev-list` to determine the ahead/behind status between two refs.
+ ///
+ /// Ref to compare
+ /// Ref to compare against
+ /// A custom output processor instance
+ /// output
ITask AheadBehindStatus(string gitRef, string otherRef, IOutputProcessor processor = null);
+
+ ///
+ /// Executes `git status` to determine the working directory status.
+ ///
+ /// A custom output processor instance
+ /// output
ITask Status(IOutputProcessor processor = null);
+
+ ///
+ /// Executes `git config get` to get a configuration value.
+ ///
+ /// The configuration key to get
+ /// The config source (unspecified, local,user,global) to use
+ /// A custom output processor instance
+ /// String output of git command
ITask GetConfig(string key, GitConfigSource configSource, IOutputProcessor processor = null);
+
+ ///
+ /// Executes `git config set` to set a configuration value.
+ ///
+ /// The configuration key to set
+ /// The value to set
+ /// The config source (unspecified, local,user,global) to use
+ /// A custom output processor instance
+ /// String output of git command
ITask SetConfig(string key, string value, GitConfigSource configSource, IOutputProcessor processor = null);
+
+ ///
+ /// Executes `git config --unset` to remove a configuration value.
+ ///
+ /// The configuration key to remove
+ /// The config source (unspecified, local,user,global) to use
+ /// A custom output processor instance
+ /// String output of git command
+ ITask UnSetConfig(string key, GitConfigSource configSource, IOutputProcessor processor = null);
+
+ ///
+ /// Executes two `git config get` commands to get the git user and email.
+ ///
+ /// output
ITask GetConfigUserAndEmail();
+
+ ///
+ /// Executes `git lfs locks` to get a list of lfs locks from the git lfs server.
+ ///
+ ///
+ /// A custom output processor instance
+ /// of output
ITask> ListLocks(bool local, BaseOutputListProcessor processor = null);
+
+ ///
+ /// Executes `git pull` to perform a pull operation.
+ ///
+ /// The remote to pull from
+ /// The branch to pull
+ /// A custom output processor instance
+ /// String output of git command
ITask Pull(string remote, string branch, IOutputProcessor processor = null);
+
+ ///
+ /// Executes `git push` to perform a push operation.
+ ///
+ /// The remote to push to
+ /// The branch to push
+ /// A custom output processor instance
+ /// String output of git command
ITask Push(string remote, string branch, IOutputProcessor processor = null);
+
+ ///
+ /// Executes `git revert` to perform a revert operation.
+ ///
+ /// The changeset to revert
+ /// A custom output processor instance
+ /// String output of git command
ITask Revert(string changeset, IOutputProcessor processor = null);
+
+ ///
+ /// Executes `git fetch` to perform a fetch operation.
+ ///
+ /// The remote to fetch from
+ /// A custom output processor instance
+ /// String output of git command
ITask Fetch(string remote, IOutputProcessor processor = null);
+
+ ///
+ /// Executes `git checkout` to switch branches.
+ ///
+ /// The branch to checkout
+ /// A custom output processor instance
+ /// String output of git command
ITask SwitchBranch(string branch, IOutputProcessor processor = null);
+
+ ///
+ /// Executes `git branch -d` to delete a branch.
+ ///
+ /// The branch to delete
+ /// The flag to indicate the branch should be deleted even if not merged
+ /// A custom output processor instance
+ /// String output of git command
ITask DeleteBranch(string branch, bool deleteUnmerged = false, IOutputProcessor processor = null);
+
+ ///
+ /// Executes `git branch` to create a branch.
+ ///
+ /// The name of branch to create
+ /// The name of branch to create from
+ /// A custom output processor instance
+ /// String output of git command
ITask CreateBranch(string branch, string baseBranch, IOutputProcessor processor = null);
+
+ ///
+ /// Executes `git remote add` to add a git remote.
+ ///
+ /// The remote to add
+ /// The url of the remote
+ /// A custom output processor instance
+ /// String output of git command
ITask RemoteAdd(string remote, string url, IOutputProcessor processor = null);
+
+ ///
+ /// Executes `git remote rm` to remove a git remote.
+ ///
+ /// The remote to remove
+ /// A custom output processor instance
+ /// String output of git command
ITask RemoteRemove(string remote, IOutputProcessor processor = null);
+
+ ///
+ /// Executes `git remote set-url` to change the url of a git remote.
+ ///
+ /// The remote to change
+ /// The url to change to
+ /// A custom output processor instance
+ /// String output of git command
ITask RemoteChange(string remote, string url, IOutputProcessor processor = null);
+
+ ///
+ /// Executes `git commit` to perform a commit operation.
+ ///
+ /// The commit message summary
+ /// The commit message body
+ /// A custom output processor instance
+ /// String output of git command
ITask Commit(string message, string body, IOutputProcessor processor = null);
+
+ ///
+ /// Executes at least one `git add` command to add the list of files to the git index.
+ ///
+ /// The file to add
+ /// A custom output processor instance
+ /// String output of git command
ITask Add(IList files, IOutputProcessor processor = null);
+
+ ///
+ /// Executes `git add -A` to add all files to the git index.
+ ///
+ /// A custom output processor instance
+ /// String output of git command
ITask AddAll(IOutputProcessor processor = null);
+
+ ///
+ /// Executes at least one `git checkout` command to discard changes to the list of files.
+ ///
+ /// The files to discard
+ /// A custom output processor instance
+ /// String output of git command
ITask Discard(IList files, IOutputProcessor processor = null);
+
+ ///
+ /// Executes `git checkout -- .` to discard all changes in the working directory.
+ ///
+ /// A custom output processor instance
+ /// String output of git command
ITask DiscardAll(IOutputProcessor processor = null);
+
+ ///
+ /// Executes at least one `git checkout` command to checkout files at the given changeset
+ ///
+ /// The md5 of the changeset
+ /// The files to check out
+ /// A custom output processor instance
+ /// String output of git command
+ ITask CheckoutVersion(string changeset, IList files, IOutputProcessor processor = null);
+
+ ///
+ /// Executes at least one `git reset HEAD` command to remove files from the git index.
+ ///
+ /// The files to remove
+ /// A custom output processor instance
+ /// String output of git command
ITask Remove(IList files, IOutputProcessor processor = null);
+
+ ///
+ /// Executes at least one `git add` command to add the list of files to the git index. Followed by a `git commit` command to commit the changes.
+ ///
+ /// The files to add and commit
+ /// The commit message summary
+ /// The commit message body
+ /// A custom output processor instance
+ /// String output of git command
ITask AddAndCommit(IList files, string message, string body, IOutputProcessor processor = null);
+
+ ///
+ /// Executes `git lfs lock` to lock a file.
+ ///
+ /// The file to lock
+ /// A custom output processor instance
+ /// String output of git command
ITask Lock(NPath file, IOutputProcessor processor = null);
+
+ ///
+ /// Executes `git lfs unlock` to unlock a file.
+ ///
+ /// The file to unlock
+ /// If force should be used
+ /// A custom output processor instance
+ /// String output of git command
ITask Unlock(NPath file, bool force, IOutputProcessor processor = null);
+
+ ///
+ /// Executes `git log` to get the history of the current branch.
+ ///
+ /// A custom output processor instance
+ /// of output
ITask> Log(BaseOutputListProcessor processor = null);
+
+ ///
+ /// Executes `git log -- ` to get the history of a specific file.
+ ///
+ ///
+ /// A custom output processor instance
+ /// of output
+ ITask> LogFile(string file, BaseOutputListProcessor processor = null);
+
+ ///
+ /// Executes `git --version` to get the git version.
+ ///
+ /// A custom output processor instance
+ /// output
ITask Version(IOutputProcessor processor = null);
+
+ ///
+ /// Executes `git lfs version` to get the git lfs version.
+ ///
+ /// A custom output processor instance
+ /// output
ITask LfsVersion(IOutputProcessor processor = null);
+
+ ///
+ /// Executes `git count-objects` to get the size of the git repo in kilobytes.
+ ///
+ /// A custom output processor instance
+ /// output
ITask CountObjects(IOutputProcessor processor = null);
+
+ ///
+ /// Executes two `git set config` commands to set the git name and email.
+ ///
+ /// The username to set
+ /// The email to set
+ /// output
ITask SetConfigNameAndEmail(string username, string email);
+
+ ///
+ /// Executes `git rev-parse --short HEAD` to get the current commit sha of the current branch.
+ ///
+ /// A custom output processor instance
+ /// String output of git command
ITask GetHead(IOutputProcessor processor = null);
}
- class GitClient : IGitClient
+ public class GitClient : IGitClient
{
private const string UserNameConfigKey = "user.name";
private const string UserEmailConfigKey = "user.email";
@@ -58,66 +320,104 @@ public GitClient(IEnvironment environment, IProcessManager processManager, Cance
this.cancellationToken = cancellationToken;
}
+ ///
public ITask Init(IOutputProcessor processor = null)
{
return new GitInitTask(cancellationToken, processor)
.Configure(processManager);
}
+ ///
public ITask LfsInstall(IOutputProcessor processor = null)
{
return new GitLfsInstallTask(cancellationToken, processor)
.Configure(processManager);
}
+ ///
public ITask Status(IOutputProcessor processor = null)
{
return new GitStatusTask(new GitObjectFactory(environment), cancellationToken, processor)
.Configure(processManager);
}
+ ///
public ITask AheadBehindStatus(string gitRef, string otherRef, IOutputProcessor processor = null)
{
return new GitAheadBehindStatusTask(gitRef, otherRef, cancellationToken, processor)
.Configure(processManager);
}
+ ///
public ITask> Log(BaseOutputListProcessor processor = null)
{
return new GitLogTask(new GitObjectFactory(environment), cancellationToken, processor)
- .Configure(processManager);
+ .Configure(processManager)
+ .Catch(exception => exception is ProcessException &&
+ exception.Message.StartsWith("fatal: your current branch") &&
+ exception.Message.EndsWith("does not have any commits yet"))
+ .Then((success, list) => success ? list : new List());
+ }
+
+ ///
+ public ITask> LogFile(string file, BaseOutputListProcessor processor = null)
+ {
+ if (file == NPath.Default)
+ {
+ return new FuncTask>(cancellationToken, () => new List(0));
+ }
+
+ return new GitLogTask(file, new GitObjectFactory(environment), cancellationToken, processor)
+ .Configure(processManager)
+ .Catch(exception => exception is ProcessException &&
+ exception.Message.StartsWith("fatal: your current branch") &&
+ exception.Message.EndsWith("does not have any commits yet"))
+ .Then((success, list) => success ? list : new List());
}
+ ///
public ITask Version(IOutputProcessor processor = null)
{
return new GitVersionTask(cancellationToken, processor)
.Configure(processManager);
}
+ ///
public ITask LfsVersion(IOutputProcessor processor = null)
{
return new GitLfsVersionTask(cancellationToken, processor)
.Configure(processManager);
}
+ ///
public ITask CountObjects(IOutputProcessor processor = null)
{
return new GitCountObjectsTask(cancellationToken, processor)
.Configure(processManager);
}
+ ///
public ITask GetConfig(string key, GitConfigSource configSource, IOutputProcessor processor = null)
{
return new GitConfigGetTask(key, configSource, cancellationToken, processor)
.Configure(processManager);
}
+ ///
public ITask SetConfig(string key, string value, GitConfigSource configSource, IOutputProcessor processor = null)
{
return new GitConfigSetTask(key, value, configSource, cancellationToken, processor)
.Configure(processManager);
}
+ ///
+ public ITask UnSetConfig(string key, GitConfigSource configSource, IOutputProcessor processor = null)
+ {
+ return new GitConfigUnSetTask(key, configSource, cancellationToken, processor)
+ .Configure(processManager);
+ }
+
+ ///
public ITask GetConfigUserAndEmail()
{
string username = null;
@@ -141,6 +441,7 @@ public ITask GetConfigUserAndEmail()
});
}
+ ///
public ITask SetConfigNameAndEmail(string username, string email)
{
return SetConfig(UserNameConfigKey, username, GitConfigSource.User)
@@ -148,18 +449,21 @@ public ITask SetConfigNameAndEmail(string username, string email)
.Then(b => new GitUser(username, email));
}
+ ///
public ITask> ListLocks(bool local, BaseOutputListProcessor processor = null)
{
return new GitListLocksTask(local, cancellationToken, processor)
.Configure(processManager, environment.GitLfsExecutablePath);
}
+ ///
public ITask Pull(string remote, string branch, IOutputProcessor processor = null)
{
return new GitPullTask(remote, branch, cancellationToken, processor)
.Configure(processManager);
}
+ ///
public ITask Push(string remote, string branch,
IOutputProcessor processor = null)
{
@@ -167,12 +471,14 @@ public ITask Push(string remote, string branch,
.Configure(processManager);
}
+ ///
public ITask Revert(string changeset, IOutputProcessor processor = null)
{
return new GitRevertTask(changeset, cancellationToken, processor)
.Configure(processManager);
}
+ ///
public ITask Fetch(string remote,
IOutputProcessor processor = null)
{
@@ -180,12 +486,14 @@ public ITask Fetch(string remote,
.Configure(processManager);
}
+ ///
public ITask SwitchBranch(string branch, IOutputProcessor processor = null)
{
return new GitSwitchBranchesTask(branch, cancellationToken, processor)
.Configure(processManager);
}
+ ///
public ITask DeleteBranch(string branch, bool deleteUnmerged = false,
IOutputProcessor processor = null)
{
@@ -193,6 +501,7 @@ public ITask DeleteBranch(string branch, bool deleteUnmerged = false,
.Configure(processManager);
}
+ ///
public ITask CreateBranch(string branch, string baseBranch,
IOutputProcessor processor = null)
{
@@ -200,6 +509,7 @@ public ITask CreateBranch(string branch, string baseBranch,
.Configure(processManager);
}
+ ///
public ITask RemoteAdd(string remote, string url,
IOutputProcessor processor = null)
{
@@ -207,6 +517,7 @@ public ITask RemoteAdd(string remote, string url,
.Configure(processManager);
}
+ ///
public ITask RemoteRemove(string remote,
IOutputProcessor processor = null)
{
@@ -214,6 +525,7 @@ public ITask RemoteRemove(string remote,
.Configure(processManager);
}
+ ///
public ITask RemoteChange(string remote, string url,
IOutputProcessor processor = null)
{
@@ -221,6 +533,7 @@ public ITask RemoteChange(string remote, string url,
.Configure(processManager);
}
+ ///
public ITask Commit(string message, string body,
IOutputProcessor processor = null)
{
@@ -228,12 +541,14 @@ public ITask Commit(string message, string body,
.Configure(processManager);
}
+ ///
public ITask AddAll(IOutputProcessor processor = null)
{
return new GitAddTask(cancellationToken, processor)
.Configure(processManager);
}
+ ///
public ITask Add(IList files,
IOutputProcessor processor = null)
{
@@ -255,6 +570,7 @@ public ITask Add(IList files,
return last;
}
+ ///
public ITask Discard( IList files,
IOutputProcessor processor = null)
{
@@ -276,19 +592,43 @@ public ITask Discard( IList files,
return last;
}
+ ///
public ITask DiscardAll(IOutputProcessor processor = null)
{
return new GitCheckoutTask(cancellationToken, processor)
.Configure(processManager);
}
+ ///
+ public ITask CheckoutVersion(string changeset, IList files, IOutputProcessor processor = null)
+ {
+ return new GitCheckoutTask(changeset, files, cancellationToken, processor)
+ .Configure(processManager);
+ }
+
+ ///
public ITask Remove(IList files,
IOutputProcessor processor = null)
{
- return new GitRemoveFromIndexTask(files, cancellationToken, processor)
- .Configure(processManager);
+ GitRemoveFromIndexTask last = null;
+ foreach (var batch in files.Spool(5000))
+ {
+ var current = new GitRemoveFromIndexTask(batch, cancellationToken, processor).Configure(processManager);
+ if (last == null)
+ {
+ last = current;
+ }
+ else
+ {
+ last.Then(current);
+ last = current;
+ }
+ }
+
+ return last;
}
+ ///
public ITask AddAndCommit(IList files, string message, string body,
IOutputProcessor processor = null)
{
@@ -297,6 +637,7 @@ public ITask AddAndCommit(IList files, string message, string bo
.Configure(processManager));
}
+ ///
public ITask Lock(NPath file,
IOutputProcessor processor = null)
{
@@ -304,6 +645,7 @@ public ITask Lock(NPath file,
.Configure(processManager, environment.GitLfsExecutablePath);
}
+ ///
public ITask Unlock(NPath file, bool force,
IOutputProcessor processor = null)
{
@@ -311,10 +653,15 @@ public ITask Unlock(NPath file, bool force,
.Configure(processManager, environment.GitLfsExecutablePath);
}
+ ///
public ITask GetHead(IOutputProcessor processor = null)
{
return new FirstNonNullLineProcessTask(cancellationToken, "rev-parse --short HEAD") { Name = "Getting current head..." }
- .Configure(processManager);
+ .Configure(processManager)
+ .Catch(exception => exception is ProcessException &&
+ exception.Message.StartsWith("fatal: your current branch") &&
+ exception.Message.EndsWith("does not have any commits yet"))
+ .Then((success, head) => success ? head : null);
}
protected static ILogging Logger { get; } = LogHelper.GetLogger();
diff --git a/src/GitHub.Api/Git/GitConfig.cs b/src/GitHub.Api/Git/GitConfig.cs
index 2faa1c0ba..882538af3 100644
--- a/src/GitHub.Api/Git/GitConfig.cs
+++ b/src/GitHub.Api/Git/GitConfig.cs
@@ -167,7 +167,7 @@ public interface IGitConfig
void SetInt(string section, string key, int value);
}
- class GitConfig : IGitConfig
+ public class GitConfig : IGitConfig
{
private readonly ConfigFileManager manager;
private SectionParser sectionParser;
@@ -296,7 +296,7 @@ private void SetAndWrite(string section, string key, string value)
manager.Save(sb.ToString());
}
- class Section : Dictionary>
+ public class Section : Dictionary>
{
public Section(string name, string description = null)
{
@@ -364,7 +364,7 @@ public override string ToString()
public string Description { get; private set; }
}
- class SectionParser
+ public class SectionParser
{
private static readonly Regex CommentPattern = new Regex(@"^[;#].*", RegexOptions.Compiled);
private static readonly Regex SectionPattern = new Regex(@"^\[(.*)\]$", RegexOptions.Compiled);
@@ -463,7 +463,7 @@ private void EnsureFileBeginsWithSection()
public Dictionary> GroupSections { get; private set; }
}
- class ConfigFileManager
+ public class ConfigFileManager
{
private static readonly string[] emptyContents = new string[0];
diff --git a/src/GitHub.Api/Git/GitCredentialManager.cs b/src/GitHub.Api/Git/GitCredentialManager.cs
index 25b946d1b..fe090d0c7 100644
--- a/src/GitHub.Api/Git/GitCredentialManager.cs
+++ b/src/GitHub.Api/Git/GitCredentialManager.cs
@@ -1,18 +1,20 @@
using GitHub.Logging;
using System;
using System.Collections.Generic;
+using System.Linq;
+using GitHub.Unity.Git.Tasks;
namespace GitHub.Unity
{
- class GitCredentialManager : ICredentialManager
+ public class GitCredentialManager : ICredentialManager
{
private static ILogging Logger { get; } = LogHelper.GetLogger();
- private ICredential credential;
private string credHelper = null;
private readonly IProcessManager processManager;
private readonly ITaskManager taskManager;
+ private readonly Dictionary credentials = new Dictionary();
public GitCredentialManager(IProcessManager processManager,
ITaskManager taskManager)
@@ -23,11 +25,9 @@ public GitCredentialManager(IProcessManager processManager,
public bool HasCredentials()
{
- return credential != null;
+ return credentials != null && credentials.Any();
}
- public ICredential CachedCredentials { get { return credential; } }
-
public void Delete(UriString host)
{
if (!LoadCredentialHelper())
@@ -39,12 +39,13 @@ public void Delete(UriString host)
String.Format("protocol={0}", host.Protocol),
String.Format("host={0}", host.Host)
}).RunSynchronously();
- credential = null;
+ credentials.Remove(host);
}
public ICredential Load(UriString host)
{
- if (credential == null)
+ ICredential credential;
+ if (!credentials.TryGetValue(host, out credential))
{
if (!LoadCredentialHelper())
return null;
@@ -60,7 +61,7 @@ public ICredential Load(UriString host)
if (String.IsNullOrEmpty(kvpCreds))
{
- Logger.Error("No credentials are stored");
+ // we didn't find credentials, stop here
return null;
}
@@ -87,23 +88,25 @@ public ICredential Load(UriString host)
}
credential = new Credential(host, user, password);
+ credentials.Add(host, credential);
}
+
return credential;
}
public void Save(ICredential cred)
{
- this.credential = cred;
+ this.credentials.Add(cred.Host, cred);
if (!LoadCredentialHelper())
return;
var data = new List
{
- String.Format("protocol={0}", credential.Host.Protocol),
- String.Format("host={0}", credential.Host.Host),
- String.Format("username={0}", credential.Username),
- String.Format("password={0}", credential.Token)
+ String.Format("protocol={0}", cred.Host.Protocol),
+ String.Format("host={0}", cred.Host.Host),
+ String.Format("username={0}", cred.Username),
+ String.Format("password={0}", cred.Token)
};
var task = RunCredentialHelper("store", data.ToArray());
diff --git a/src/GitHub.Api/Git/GitFileLog.cs b/src/GitHub.Api/Git/GitFileLog.cs
new file mode 100644
index 000000000..6795d6c4d
--- /dev/null
+++ b/src/GitHub.Api/Git/GitFileLog.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+
+namespace GitHub.Unity
+{
+ [Serializable]
+ public struct GitFileLog
+ {
+ public static GitFileLog Default = new GitFileLog(null, new List(0));
+
+ public string path;
+ public List logEntries;
+
+ public GitFileLog(string path, List logEntries)
+ {
+ this.path = path;
+ this.logEntries = logEntries;
+ }
+
+ public string Path
+ {
+ get { return path; }
+ set { path = value; }
+ }
+
+ public List LogEntries
+ {
+ get { return logEntries; }
+ set { logEntries = value; }
+ }
+ }
+}
diff --git a/src/GitHub.Api/Git/GitObjectFactory.cs b/src/GitHub.Api/Git/GitObjectFactory.cs
index 16aa5fdde..d0fe3b640 100644
--- a/src/GitHub.Api/Git/GitObjectFactory.cs
+++ b/src/GitHub.Api/Git/GitObjectFactory.cs
@@ -3,7 +3,7 @@
namespace GitHub.Unity
{
- class GitObjectFactory : IGitObjectFactory
+ public class GitObjectFactory : IGitObjectFactory
{
private readonly IEnvironment environment;
@@ -12,13 +12,13 @@ public GitObjectFactory(IEnvironment environment)
this.environment = environment;
}
- public GitStatusEntry CreateGitStatusEntry(string path, GitFileStatus status, string originalPath = null, bool staged = false)
+ public GitStatusEntry CreateGitStatusEntry(string path, GitFileStatus indexStatus, GitFileStatus workTreeStatus = GitFileStatus.None, string originalPath = null)
{
var absolutePath = new NPath(path).MakeAbsolute();
var relativePath = absolutePath.RelativeTo(environment.RepositoryPath);
var projectPath = absolutePath.RelativeTo(environment.UnityProjectPath);
- return new GitStatusEntry(relativePath, absolutePath, projectPath, status, originalPath?.ToNPath(), staged);
+ return new GitStatusEntry(relativePath, absolutePath, projectPath, indexStatus, workTreeStatus, originalPath?.ToNPath());
}
}
}
diff --git a/src/GitHub.Api/Git/GitStatusEntry.cs b/src/GitHub.Api/Git/GitStatusEntry.cs
index 1421c5554..8ba7c6d58 100644
--- a/src/GitHub.Api/Git/GitStatusEntry.cs
+++ b/src/GitHub.Api/Git/GitStatusEntry.cs
@@ -11,22 +11,22 @@ public struct GitStatusEntry
public string fullPath;
public string projectPath;
public string originalPath;
- public GitFileStatus status;
- public bool staged;
+ public GitFileStatus indexStatus;
+ public GitFileStatus workTreeStatus;
public GitStatusEntry(string path, string fullPath, string projectPath,
- GitFileStatus status,
- string originalPath = null, bool staged = false)
+ GitFileStatus indexStatus, GitFileStatus workTreeStatus,
+ string originalPath = null)
{
Guard.ArgumentNotNullOrWhiteSpace(path, "path");
Guard.ArgumentNotNullOrWhiteSpace(fullPath, "fullPath");
this.path = path;
- this.status = status;
+ this.indexStatus = indexStatus;
+ this.workTreeStatus = workTreeStatus;
this.fullPath = fullPath;
this.projectPath = projectPath;
this.originalPath = originalPath;
- this.staged = staged;
}
public override int GetHashCode()
@@ -36,8 +36,8 @@ public override int GetHashCode()
hash = hash * 23 + (fullPath?.GetHashCode() ?? 0);
hash = hash * 23 + (projectPath?.GetHashCode() ?? 0);
hash = hash * 23 + (originalPath?.GetHashCode() ?? 0);
- hash = hash * 23 + status.GetHashCode();
- hash = hash * 23 + staged.GetHashCode();
+ hash = hash * 23 + indexStatus.GetHashCode();
+ hash = hash * 23 + workTreeStatus.GetHashCode();
return hash;
}
@@ -55,8 +55,8 @@ public bool Equals(GitStatusEntry other)
String.Equals(fullPath, other.fullPath) &&
String.Equals(projectPath, other.projectPath) &&
String.Equals(originalPath, other.originalPath) &&
- status == other.status &&
- staged == other.staged
+ indexStatus == other.indexStatus &&
+ workTreeStatus == other.workTreeStatus
;
}
@@ -79,6 +79,49 @@ public bool Equals(GitStatusEntry other)
return !(lhs == rhs);
}
+ public static GitFileStatus ParseStatusMarker(char changeFlag)
+ {
+ GitFileStatus status = GitFileStatus.None;
+ switch (changeFlag)
+ {
+ case 'M':
+ status = GitFileStatus.Modified;
+ break;
+ case 'A':
+ status = GitFileStatus.Added;
+ break;
+ case 'D':
+ status = GitFileStatus.Deleted;
+ break;
+ case 'R':
+ status = GitFileStatus.Renamed;
+ break;
+ case 'C':
+ status = GitFileStatus.Copied;
+ break;
+ case 'U':
+ status = GitFileStatus.Unmerged;
+ break;
+ case 'T':
+ status = GitFileStatus.TypeChange;
+ break;
+ case 'X':
+ status = GitFileStatus.Unknown;
+ break;
+ case 'B':
+ status = GitFileStatus.Broken;
+ break;
+ case '?':
+ status = GitFileStatus.Untracked;
+ break;
+ case '!':
+ status = GitFileStatus.Ignored;
+ break;
+ default: break;
+ }
+ return status;
+ }
+
public string Path => path;
public string FullPath => fullPath;
@@ -87,13 +130,21 @@ public bool Equals(GitStatusEntry other)
public string OriginalPath => originalPath;
- public GitFileStatus Status => status;
+ public GitFileStatus Status => workTreeStatus != GitFileStatus.None ? workTreeStatus : indexStatus;
+ public GitFileStatus IndexStatus => indexStatus;
+ public GitFileStatus WorkTreeStatus => workTreeStatus;
+
+ public bool Staged => indexStatus != GitFileStatus.None && !Unmerged && !Untracked && !Ignored;
+
+ public bool Unmerged => (indexStatus == workTreeStatus && (indexStatus == GitFileStatus.Added || indexStatus == GitFileStatus.Deleted)) ||
+ indexStatus == GitFileStatus.Unmerged || workTreeStatus == GitFileStatus.Unmerged;
- public bool Staged => staged;
+ public bool Untracked => workTreeStatus == GitFileStatus.Untracked;
+ public bool Ignored => workTreeStatus == GitFileStatus.Ignored;
public override string ToString()
{
- return $"Path:'{Path}' Status:'{Status}' FullPath:'{FullPath}' ProjectPath:'{ProjectPath}' OriginalPath:'{OriginalPath}' Staged:'{Staged}'";
+ return $"Path:'{Path}' Status:'{Status}' FullPath:'{FullPath}' ProjectPath:'{ProjectPath}' OriginalPath:'{OriginalPath}' Staged:'{Staged}' Unmerged:'{Unmerged}' Status:'{IndexStatus}' Status:'{WorkTreeStatus}' ";
}
}
}
diff --git a/src/GitHub.Api/Git/IGitObjectFactory.cs b/src/GitHub.Api/Git/IGitObjectFactory.cs
index 7d4e42bc9..7458c9403 100644
--- a/src/GitHub.Api/Git/IGitObjectFactory.cs
+++ b/src/GitHub.Api/Git/IGitObjectFactory.cs
@@ -1,7 +1,7 @@
namespace GitHub.Unity
{
- interface IGitObjectFactory
+ public interface IGitObjectFactory
{
- GitStatusEntry CreateGitStatusEntry(string path, GitFileStatus status, string originalPath = null, bool staged = false);
+ GitStatusEntry CreateGitStatusEntry(string path, GitFileStatus indexStatus, GitFileStatus workTreeStatus, string originalPath = null);
}
}
diff --git a/src/GitHub.Api/Git/IRepository.cs b/src/GitHub.Api/Git/IRepository.cs
index 148015282..7bd579d37 100644
--- a/src/GitHub.Api/Git/IRepository.cs
+++ b/src/GitHub.Api/Git/IRepository.cs
@@ -21,6 +21,7 @@ public interface IRepository : IEquatable, IDisposable, IBackedByCa
ITask RequestLock(NPath file);
ITask ReleaseLock(NPath file, bool force);
ITask DiscardChanges(GitStatusEntry[] discardEntries);
+ ITask CheckoutVersion(string changeset, IList files);
///
/// Gets the name of the repository.
@@ -61,8 +62,10 @@ public interface IRepository : IEquatable, IDisposable, IBackedByCa
List CurrentLog { get; }
bool IsBusy { get; }
string CurrentHead { get; }
+ GitFileLog CurrentFileLog { get; }
event Action LogChanged;
+ event Action FileLogChanged;
event Action TrackingStatusChanged;
event Action StatusEntriesChanged;
event Action CurrentBranchChanged;
@@ -78,7 +81,8 @@ public interface IRepository : IEquatable, IDisposable, IBackedByCa
ITask DeleteBranch(string branch, bool force);
ITask CreateBranch(string branch, string baseBranch);
ITask SwitchBranch(string branch);
+ ITask UpdateFileLog(string path);
void Refresh(CacheType cacheType);
event Action OnProgress;
}
-}
\ No newline at end of file
+}
diff --git a/src/GitHub.Api/Git/Repository.cs b/src/GitHub.Api/Git/Repository.cs
index f9d6fe38e..84cd58853 100644
--- a/src/GitHub.Api/Git/Repository.cs
+++ b/src/GitHub.Api/Git/Repository.cs
@@ -13,7 +13,7 @@ public interface IBackedByCache
}
[DebuggerDisplay("{DebuggerDisplay,nq}")]
- sealed class Repository : IEquatable, IRepository
+ public class Repository : IEquatable, IRepository
{
private static ILogging Logger = LogHelper.GetLogger