├── tools
└── NuGet.exe
├── CONTRIBUTING.md
├── ReactiveGit.Demo
├── App.config
├── App.xaml
├── App.xaml.cs
├── ViewModels
│ ├── BranchViewModel.cs
│ ├── ShellViewModel.cs
│ └── CloneRepositoryViewModel.cs
├── packages.config
├── ObservableFolderPicker.cs
├── Views
│ ├── ShellView.xaml
│ ├── CloneRepositoryView.xaml
│ ├── CloneRepositoryView.xaml.cs
│ └── ShellView.xaml.cs
├── Properties
│ └── AssemblyInfo.cs
└── ReactiveGit.Demo.csproj
├── SolutionInfo.cs
├── ReactiveGit.sln.DotSettings
├── ReactiveGit
├── packages.config
├── Properties
│ └── AssemblyInfo.cs
├── ProgressFactory.cs
├── ObservableRepository.cs
├── ObservableRepository.Push.cs
├── ObservableRepository.Pull.cs
├── IObservableRepository.cs
├── ObservableRepository.Checkout.cs
├── ObservableRepository.Clone.cs
└── ReactiveGit.csproj
├── ReleaseNotes.md
├── .gitattributes
├── ReactiveGit.Tests
├── packages.config
├── Properties
│ └── AssemblyInfo.cs
├── ObservableRepositoryTests.cs
├── TestDirectory.cs
└── ReactiveGit.Tests.csproj
├── nuspec
├── ReactiveGit.nuspec
└── ReactiveGit.Source.nuspec
├── LICENSE.md
├── README.md
├── ReactiveGit.sln
└── .gitignore
/tools/NuGet.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shiftkey/ReactiveGit/HEAD/tools/NuGet.exe
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # ReactiveGit - Contributing Guide
2 |
3 | ## Building the project
4 |
5 | You can build, test and create the NuGet packages by simply running the `build.ps1` script at the root of the repository
6 |
--------------------------------------------------------------------------------
/ReactiveGit.Demo/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ReactiveGit.Demo/App.xaml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
--------------------------------------------------------------------------------
/SolutionInfo.cs:
--------------------------------------------------------------------------------
1 | //
2 | using System.Reflection;
3 | using System.Runtime.InteropServices;
4 |
5 | [assembly: AssemblyProductAttribute("ReactiveGit")]
6 | [assembly: AssemblyVersionAttribute("0.0.4")]
7 | [assembly: AssemblyFileVersionAttribute("0.0.4")]
8 | [assembly: ComVisibleAttribute(false)]
9 | namespace System {
10 | internal static class AssemblyVersionInformation {
11 | internal const string Version = "0.0.4";
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/ReactiveGit.sln.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
--------------------------------------------------------------------------------
/ReactiveGit/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ReactiveGit.Demo/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using ReactiveGit.Demo.ViewModels;
3 | using ReactiveGit.Demo.Views;
4 |
5 | namespace ReactiveGit.Demo
6 | {
7 | public partial class App
8 | {
9 | protected override void OnStartup(StartupEventArgs e)
10 | {
11 | base.OnStartup(e);
12 |
13 | var view = new ShellView();
14 | var viewModel = new ShellViewModel();
15 |
16 | view.ViewModel = viewModel;
17 |
18 | view.Show();
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ReleaseNotes.md:
--------------------------------------------------------------------------------
1 | ### New in 0.0.4 (Released 2014/10/14)
2 | * clone messages improved to match up with Git behaviour
3 | * clone no longer displays checkout file paths
4 | * `IObservableRepository.Inner` is available for when you need to access the
5 | underlying LibGit2Sharp repository
6 |
7 | ### New in 0.0.3 (Released 2014/10/12)
8 | * a proper build process, just like a real OSS project
9 | * now targets LibGit2Sharp v0.19
10 |
11 | **Breaking changes:**
12 | - The `Credentials` `ctor` have been deprecated upstream in favour of
13 | `CredentialsHandler` delegates.
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 | *.sln merge=union
7 | *.csproj merge=union
8 | *.vbproj merge=union
9 | *.fsproj merge=union
10 | *.dbproj merge=union
11 |
12 | # Standard to msysgit
13 | *.doc diff=astextplain
14 | *.DOC diff=astextplain
15 | *.docx diff=astextplain
16 | *.DOCX diff=astextplain
17 | *.dot diff=astextplain
18 | *.DOT diff=astextplain
19 | *.pdf diff=astextplain
20 | *.PDF diff=astextplain
21 | *.rtf diff=astextplain
22 | *.RTF diff=astextplain
23 |
--------------------------------------------------------------------------------
/ReactiveGit.Demo/ViewModels/BranchViewModel.cs:
--------------------------------------------------------------------------------
1 | using ReactiveUI;
2 |
3 | namespace ReactiveGit.Demo.ViewModels
4 | {
5 | public class BranchViewModel : ReactiveObject
6 | {
7 | string name;
8 | public string Name
9 | {
10 | get { return name; }
11 | set { this.RaiseAndSetIfChanged(ref name, value); }
12 | }
13 |
14 | string canonicalName;
15 | public string CanonicalName
16 | {
17 | get { return canonicalName; }
18 | set { this.RaiseAndSetIfChanged(ref canonicalName, value); }
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/ReactiveGit/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("ReactiveGit")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyCopyright("Copyright © 2014")]
12 | [assembly: AssemblyTrademark("")]
13 | [assembly: AssemblyCulture("")]
14 |
15 | // The following GUID is for the ID of the typelib if this project is exposed to COM
16 | [assembly: Guid("bf0b74cb-1f13-4276-9a43-dafed977b102")]
--------------------------------------------------------------------------------
/ReactiveGit.Demo/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/ReactiveGit.Demo/ObservableFolderPicker.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reactive.Linq;
3 | using System.Windows.Forms;
4 |
5 | namespace ReactiveGit.Demo
6 | {
7 | public class ObservableFolderPicker
8 | {
9 | public static IObservable SelectFolder()
10 | {
11 | return Observable.Defer(() =>
12 | {
13 | var fileDialog = new FolderBrowserDialog
14 | {
15 | RootFolder = Environment.SpecialFolder.MyDocuments,
16 | ShowNewFolderButton = true
17 | };
18 |
19 | var result = fileDialog.ShowDialog();
20 |
21 | if (result == DialogResult.OK)
22 | {
23 | return Observable.Return(fileDialog.SelectedPath);
24 | }
25 |
26 | return Observable.Throw(new InvalidOperationException());
27 | });
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/ReactiveGit.Tests/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/nuspec/ReactiveGit.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ReactiveGit
5 | 0.0.1
6 | Brendan Forster
7 | Brendan Forster
8 | https://github.com/shiftkey/ReactiveGit/blob/master/LICENSE.md
9 | https://github.com/shiftkey/ReactiveGit
10 |
11 | false
12 | A wrapper for libgit2sharp to make git operations asynchronous
13 | First preview
14 | Copyright 2014
15 | Git libgit2
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/nuspec/ReactiveGit.Source.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ReactiveGit.Source
5 | 0.0.1
6 | Brendan Forster
7 | Brendan Forster
8 | https://github.com/shiftkey/ReactiveGit/blob/master/LICENSE.md
9 | https://github.com/shiftkey/ReactiveGit
10 |
11 | false
12 | The source code for wrapping libgit2sharp in asynchrony - unless you're building LibGit2Sharp from source, this isn't the package you want
13 | First preview
14 | Copyright 2014
15 | Git libgit2
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ReactiveGit.Demo/Views/ShellView.xaml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Brendan Forster
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/ReactiveGit/ProgressFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using LibGit2Sharp.Handlers;
3 |
4 | namespace ReactiveGit
5 | {
6 | internal static class ProgressFactory
7 | {
8 | public static CheckoutProgressHandler CreateHandler(
9 | IObserver> observer,
10 | int start = 0,
11 | int count = 100)
12 | {
13 | return (path, completedSteps, totalSteps) =>
14 | {
15 | if (totalSteps == 0)
16 | {
17 | observer.OnNext(Tuple.Create(path, 0));
18 | }
19 | else
20 | {
21 | var progress = start + (count * completedSteps) / totalSteps;
22 | observer.OnNext(Tuple.Create(path, progress));
23 | }
24 | };
25 | }
26 |
27 | public static CheckoutProgressHandler CreateHandlerWithoutMessages(
28 | IObserver> observer,
29 | int start = 0,
30 | int count = 100)
31 | {
32 | return (path, completedSteps, totalSteps) =>
33 | {
34 | if (totalSteps == 0)
35 | {
36 | observer.OnNext(Tuple.Create(path, 0));
37 | }
38 | else
39 | {
40 | var progress = start + (count * completedSteps) / totalSteps;
41 | observer.OnNext(Tuple.Create("", progress));
42 | }
43 | };
44 | }
45 |
46 | }
47 | }
--------------------------------------------------------------------------------
/ReactiveGit.Tests/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("ReactiveGit.Tests")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("ReactiveGit.Tests")]
13 | [assembly: AssemblyCopyright("Copyright © 2014")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("5ba728b9-3a59-4cdc-8f59-0e706e78c969")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/ReactiveGit.Demo/ViewModels/ShellViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reactive.Linq;
3 | using ReactiveUI;
4 |
5 | namespace ReactiveGit.Demo.ViewModels
6 | {
7 | public class ShellViewModel : ReactiveObject
8 | {
9 | readonly ObservableAsPropertyHelper cloneViewModel;
10 |
11 | public ShellViewModel()
12 | {
13 | // some default values because lazy
14 | CloneUrl = "https://github.com/octokit/octokit.net.git";
15 |
16 | // setup the option to clone a repository down
17 | CloneRepository = ReactiveCommand.CreateAsyncObservable(
18 | this.WhenAny(x => x.CloneUrl, x => IsValidUri(x.Value)),
19 | _ => ObservableFolderPicker.SelectFolder());
20 |
21 | cloneViewModel = CloneRepository
22 | .Select(path => new CloneRepositoryViewModel(CloneUrl, path))
23 | .ToProperty(this, x => x.CloneViewModel);
24 | }
25 |
26 | static bool IsValidUri(string x)
27 | {
28 | Uri result;
29 | return Uri.TryCreate(x, UriKind.Absolute, out result);
30 | }
31 |
32 | public CloneRepositoryViewModel CloneViewModel
33 | {
34 | get { return cloneViewModel.Value; }
35 | }
36 |
37 | public ReactiveCommand CloneRepository { get; private set; }
38 |
39 | string cloneUrl;
40 | public string CloneUrl
41 | {
42 | get { return cloneUrl; }
43 | set { this.RaiseAndSetIfChanged(ref cloneUrl, value); }
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/ReactiveGit.Demo/Views/CloneRepositoryView.xaml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
29 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/ReactiveGit.Demo/Views/CloneRepositoryView.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using ReactiveGit.Demo.ViewModels;
3 | using ReactiveUI;
4 |
5 | namespace ReactiveGit.Demo.Views
6 | {
7 | public partial class CloneRepositoryView : IViewFor
8 | {
9 | public CloneRepositoryView()
10 | {
11 | InitializeComponent();
12 |
13 | // display the progress messages
14 | this.OneWayBind(ViewModel, vm => vm.ProgressText, v => v.progressMessage.Text);
15 | this.OneWayBind(ViewModel, vm => vm.ProgressValue, v => v.progressBar.Value);
16 |
17 | // only show the checkout panel after the clone is done
18 | this.OneWayBind(ViewModel, vm => vm.IsEmpty, v => v.ready.Visibility,
19 | conversionHint: BooleanToVisibilityHint.Inverse);
20 |
21 | // bind the other UI elements
22 | this.BindCommand(ViewModel, vm => vm.Checkout, v => v.checkout);
23 | this.OneWayBind(ViewModel, vm => vm.Branches, v => v.branches.ItemsSource);
24 | this.Bind(ViewModel, vm => vm.SelectedBranch, v => v.branches.SelectedItem);
25 | }
26 |
27 | public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(
28 | "ViewModel", typeof (CloneRepositoryViewModel), typeof (CloneRepositoryView), new PropertyMetadata(default(CloneRepositoryViewModel)));
29 |
30 | public CloneRepositoryViewModel ViewModel
31 | {
32 | get { return (CloneRepositoryViewModel) GetValue(ViewModelProperty); }
33 | set { SetValue(ViewModelProperty, value); }
34 | }
35 | object IViewFor.ViewModel
36 | {
37 | get { return ViewModel; }
38 | set { ViewModel = value as CloneRepositoryViewModel; }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/ReactiveGit/ObservableRepository.cs:
--------------------------------------------------------------------------------
1 | using LibGit2Sharp;
2 | using LibGit2Sharp.Handlers;
3 |
4 | namespace ReactiveGit
5 | {
6 | ///
7 | /// A LibGit2Sharp-based repository which encapsulates asynchronous operations
8 | ///
9 | public partial class ObservableRepository
10 | : IObservableRepository
11 | {
12 | readonly Repository _repository;
13 | readonly CredentialsHandler _credentialsHandler;
14 |
15 | ///
16 | /// Create an ObservableRepository from a folder on disk
17 | ///
18 | /// The repository root folder
19 | ///
20 | /// Do not use this constructor if you require authenticated access to the
21 | /// remote repository
22 | ///
23 | public ObservableRepository(string directory)
24 | : this(directory, null) { }
25 |
26 | ///
27 | /// Create an ObservableRepository from a folder on disk,
28 | /// with a credential handler for authentication
29 | ///
30 | /// The repository root folder
31 | /// Credential provider
32 | public ObservableRepository(string directory, CredentialsHandler credentialsHandler)
33 | {
34 | _repository = new Repository(directory);
35 | _credentialsHandler = credentialsHandler;
36 | }
37 |
38 | ///
39 | public IRepository Inner { get { return _repository; } }
40 |
41 | ///
42 | /// Dispose the resources held by the repository
43 | ///
44 | public void Dispose()
45 | {
46 | _repository.Dispose();
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/ReactiveGit/ObservableRepository.Push.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reactive;
3 | using System.Reactive.Concurrency;
4 | using System.Reactive.Disposables;
5 | using System.Reactive.Linq;
6 | using LibGit2Sharp;
7 |
8 | namespace ReactiveGit
9 | {
10 | public partial class ObservableRepository
11 | {
12 | ///
13 | public IObservable Push(IObserver> observer)
14 | {
15 | var branch = _repository.Head;
16 |
17 | var isCancelled = false;
18 | var options = new PushOptions
19 | {
20 | CredentialsProvider = _credentialsHandler,
21 | OnPushTransferProgress = (current, total, bytes) =>
22 | {
23 | var progress = 0;
24 | if (total != 0)
25 | {
26 | progress = 50 + (50 * current) / total;
27 | }
28 |
29 | observer.OnNext(Tuple.Create("", progress));
30 |
31 | return !isCancelled;
32 | }
33 | };
34 |
35 | return Observable.Create(subj =>
36 | {
37 | var sub = Observable.Start(() =>
38 | {
39 | _repository.Network.Push(branch, options);
40 |
41 | observer.OnNext(Tuple.Create("push completed", 100));
42 | observer.OnCompleted();
43 | }, Scheduler.Default).Subscribe(subj);
44 |
45 | return new CompositeDisposable(
46 | sub,
47 | Disposable.Create(() =>
48 | {
49 | isCancelled = true;
50 | observer.OnCompleted();
51 | }));
52 | });
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/ReactiveGit.Demo/Views/ShellView.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reactive.Linq;
3 | using System.Windows;
4 | using ReactiveGit.Demo.ViewModels;
5 | using ReactiveUI;
6 |
7 | namespace ReactiveGit.Demo.Views
8 | {
9 | public partial class ShellView : IViewFor
10 | {
11 | public ShellView()
12 | {
13 | InitializeComponent();
14 |
15 | // wireup the data necessary for the clone
16 | this.Bind(ViewModel, vm => vm.CloneUrl, v => v.cloneUrl.Text);
17 | this.BindCommand(ViewModel, vm => vm.CloneRepository, v => v.cloneRepository);
18 |
19 | // hide the clone panel after we have kicked off the clone
20 | this.WhenAnyValue(x => x.ViewModel.CloneViewModel)
21 | .Select(vm => vm == null)
22 | .BindTo(this, v => v.clonePanel.Visibility);
23 |
24 | // once setup, display the view and trigger the clone operation
25 | this.WhenAnyValue(x => x.ViewModel.CloneViewModel)
26 | .Where(vm => vm != null)
27 | .SelectMany(vm =>
28 | {
29 | var view = new CloneRepositoryView { ViewModel = vm };
30 | content.Content = view;
31 | return vm.Clone.ExecuteAsync();
32 | })
33 | .Subscribe();
34 | }
35 |
36 | public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(
37 | "ViewModel", typeof(ShellViewModel), typeof(ShellView), new PropertyMetadata(default(ShellViewModel)));
38 |
39 | public ShellViewModel ViewModel
40 | {
41 | get { return (ShellViewModel)GetValue(ViewModelProperty); }
42 | set { SetValue(ViewModelProperty, value); }
43 | }
44 |
45 | object IViewFor.ViewModel
46 | {
47 | get { return ViewModel; }
48 | set { ViewModel = value as ShellViewModel; }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ReactiveGit
2 | ===========
3 |
4 | This is an experiment in combining Rx and `LibGit2Sharp` to create an API to make git operations friendly for asynchronous situations.
5 |
6 | `libgit2` operations are, by their nature, synchronous. This is reflected in the wrapper APIs such as `LibGit2Sharp`. This is great for simple interactions with repositories, but working with complex tasks against large repositories requires embracing asynchrony and pushing operations onto background threads.
7 |
8 | Rather than create a duplicate set of asynchronous APIs for `LibGit2Sharp`, this project is intending to represent specific Git actions as a thing which can be observed. Many operations support cancellation, and this can be handled in `ReactiveGit` by simply disposing of the subscription to an observable action.
9 |
10 | The other focus of this framework is around progress handlers, which `LibGit2Sharp` added support for recently. This enables a user to specify a callbacks to events in `libgit2`. By passing an observer to the operation in `ReactiveGit`, you can receive progress information in real-time to display in your application.
11 |
12 | ### Installation
13 |
14 | Install the package from NuGet!
15 |
16 | ```
17 | Install-Package ReactiveGit
18 | ```
19 |
20 | ### Examples
21 |
22 | ```
23 | // many operations require authentication
24 | CredentialsHandler credentials = (url, usernameFromUrl, types) =>
25 | new UsernamePasswordCredentials
26 | {
27 | Username = "shiftkey-tester",
28 | Password = "haha-password"
29 | };
30 |
31 |
32 | // you can also pass an existing LibGit2Sharp repository here
33 | var repository = new ObservableRepository(
34 | @"C:\Users\brendanforster\Documents\GìtHūb\testing-pushspecs",
35 | credentials);
36 |
37 | // specify a progress observer to report progress
38 | var pullObserver = new ReplaySubject>();
39 | pullObserver.Subscribe(
40 | next => Console.WriteLine("Progress: " + next.Item2));
41 |
42 | // execute the operation
43 | var pullResult = await repository.Pull(pullObserver);
44 | ```
45 |
46 | ### Contributing
47 |
48 | If you'd like to get involved, check out the [CONTRIBUTING.md](https://github.com/shiftkey/ReactiveGit/blob/master/CONTRIBUTING.md) docs for more information.
49 |
--------------------------------------------------------------------------------
/ReactiveGit/ObservableRepository.Pull.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reactive.Concurrency;
3 | using System.Reactive.Disposables;
4 | using System.Reactive.Linq;
5 | using LibGit2Sharp;
6 |
7 | namespace ReactiveGit
8 | {
9 | public partial class ObservableRepository
10 | {
11 | ///
12 | public IObservable Pull(
13 | IObserver> observer)
14 | {
15 | var signature = _repository.Config.BuildSignature(DateTimeOffset.Now);
16 | var isCancelled = false;
17 |
18 | var options = new PullOptions
19 | {
20 | FetchOptions = new FetchOptions
21 | {
22 | TagFetchMode = TagFetchMode.None,
23 | CredentialsProvider = _credentialsHandler,
24 | OnTransferProgress = progress =>
25 | {
26 | // TODO: how should we signal for the "indexing objects" events
27 | var p = (50 * progress.ReceivedObjects) / progress.TotalObjects;
28 | observer.OnNext(Tuple.Create("", p));
29 | return !isCancelled;
30 | }
31 | },
32 | MergeOptions = new MergeOptions
33 | {
34 | OnCheckoutProgress = ProgressFactory.CreateHandler(observer, start:50, count:50)
35 | }
36 | };
37 |
38 | return Observable.Create(subj =>
39 | {
40 | var sub = Observable.Start(() =>
41 | {
42 | var result = _repository.Network.Pull(signature, options);
43 |
44 | observer.OnNext(Tuple.Create("pull completed", 100));
45 | observer.OnCompleted();
46 |
47 | return result;
48 | }, Scheduler.Default).Subscribe(subj);
49 |
50 | return new CompositeDisposable(
51 | sub,
52 | Disposable.Create(() =>
53 | {
54 | isCancelled = true;
55 | observer.OnCompleted();
56 | }));
57 | });
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/ReactiveGit.Demo/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 | using System.Windows;
6 |
7 | // General Information about an assembly is controlled through the following
8 | // set of attributes. Change these attribute values to modify the information
9 | // associated with an assembly.
10 | [assembly: AssemblyTitle("ReactiveGit.Demo")]
11 | [assembly: AssemblyDescription("")]
12 | [assembly: AssemblyConfiguration("")]
13 | [assembly: AssemblyCompany("")]
14 | [assembly: AssemblyProduct("ReactiveGit.Demo")]
15 | [assembly: AssemblyCopyright("Copyright © 2014")]
16 | [assembly: AssemblyTrademark("")]
17 | [assembly: AssemblyCulture("")]
18 |
19 | // Setting ComVisible to false makes the types in this assembly not visible
20 | // to COM components. If you need to access a type in this assembly from
21 | // COM, set the ComVisible attribute to true on that type.
22 | [assembly: ComVisible(false)]
23 |
24 | //In order to begin building localizable applications, set
25 | //CultureYouAreCodingWith in your .csproj file
26 | //inside a . For example, if you are using US english
27 | //in your source files, set the to en-US. Then uncomment
28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in
29 | //the line below to match the UICulture setting in the project file.
30 |
31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
32 |
33 |
34 | [assembly: ThemeInfo(
35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
36 | //(used if a resource is not found in the page,
37 | // or application resource dictionaries)
38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
39 | //(used if a resource is not found in the page,
40 | // app, or any theme specific resource dictionaries)
41 | )]
42 |
43 |
44 | // Version information for an assembly consists of the following four values:
45 | //
46 | // Major Version
47 | // Minor Version
48 | // Build Number
49 | // Revision
50 | //
51 | // You can specify all the values or you can default the Build and Revision Numbers
52 | // by using the '*' as shown below:
53 | // [assembly: AssemblyVersion("1.0.*")]
54 | [assembly: AssemblyVersion("1.0.0.0")]
55 | [assembly: AssemblyFileVersion("1.0.0.0")]
56 |
--------------------------------------------------------------------------------
/ReactiveGit.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2013
4 | VisualStudioVersion = 12.0.30723.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactiveGit", "ReactiveGit\ReactiveGit.csproj", "{3BB24A21-6065-4FCD-8BB8-83E875AF2C99}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactiveGit.Tests", "ReactiveGit.Tests\ReactiveGit.Tests.csproj", "{26BC0BE1-5C57-44DE-B0CC-EA9AAD9D2587}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "meta", "meta", "{84AB502D-9BE4-4ACF-A8E7-6DB56106E520}"
11 | ProjectSection(SolutionItems) = preProject
12 | build.fsx = build.fsx
13 | build.ps1 = build.ps1
14 | nuspec\ReactiveGit.nuspec = nuspec\ReactiveGit.nuspec
15 | nuspec\ReactiveGit.Source.nuspec = nuspec\ReactiveGit.Source.nuspec
16 | EndProjectSection
17 | EndProject
18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactiveGit.Demo", "ReactiveGit.Demo\ReactiveGit.Demo.csproj", "{BD02CBC4-2981-430A-AD5A-C3CCF05F0BBC}"
19 | EndProject
20 | Global
21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
22 | Debug|Any CPU = Debug|Any CPU
23 | Release|Any CPU = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
26 | {3BB24A21-6065-4FCD-8BB8-83E875AF2C99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {3BB24A21-6065-4FCD-8BB8-83E875AF2C99}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {3BB24A21-6065-4FCD-8BB8-83E875AF2C99}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {3BB24A21-6065-4FCD-8BB8-83E875AF2C99}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {26BC0BE1-5C57-44DE-B0CC-EA9AAD9D2587}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {26BC0BE1-5C57-44DE-B0CC-EA9AAD9D2587}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {26BC0BE1-5C57-44DE-B0CC-EA9AAD9D2587}.Release|Any CPU.ActiveCfg = Release|Any CPU
33 | {26BC0BE1-5C57-44DE-B0CC-EA9AAD9D2587}.Release|Any CPU.Build.0 = Release|Any CPU
34 | {BD02CBC4-2981-430A-AD5A-C3CCF05F0BBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35 | {BD02CBC4-2981-430A-AD5A-C3CCF05F0BBC}.Debug|Any CPU.Build.0 = Debug|Any CPU
36 | {BD02CBC4-2981-430A-AD5A-C3CCF05F0BBC}.Release|Any CPU.ActiveCfg = Release|Any CPU
37 | {BD02CBC4-2981-430A-AD5A-C3CCF05F0BBC}.Release|Any CPU.Build.0 = Release|Any CPU
38 | EndGlobalSection
39 | GlobalSection(SolutionProperties) = preSolution
40 | HideSolutionNode = FALSE
41 | EndGlobalSection
42 | EndGlobal
43 |
--------------------------------------------------------------------------------
/ReactiveGit.Tests/ObservableRepositoryTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Linq;
4 | using System.Reactive;
5 | using System.Reactive.Linq;
6 | using System.Reactive.Subjects;
7 | using System.Threading.Tasks;
8 | using LibGit2Sharp;
9 | using LibGit2Sharp.Handlers;
10 | using Xunit;
11 |
12 | namespace ReactiveGit.Tests
13 | {
14 | public class ObservableRepositoryTests
15 | {
16 | [Fact]
17 | public async Task CanCloneARepository()
18 | {
19 | using (var directory = TestDirectory.Create())
20 | {
21 | var cloneObserver = new ReplaySubject>();
22 | using (await ObservableRepository.Clone(
23 | "https://github.com/shiftkey/rxui-design-guidelines.git",
24 | directory.Path,
25 | cloneObserver))
26 | {
27 | Assert.NotEmpty(Directory.GetFiles(directory.Path));
28 | var progressList = await cloneObserver.Select(x => x.Item2).ToList();
29 | Assert.Equal(100, progressList.Last());
30 | }
31 | }
32 | }
33 |
34 | [Fact]
35 | public async Task GetProgressFromASyncOperation()
36 | {
37 | CredentialsHandler credentials = (url, usernameFromUrl, types) =>
38 | new UsernamePasswordCredentials
39 | {
40 | Username = "shiftkey-tester",
41 | Password = "haha-password"
42 | };
43 |
44 | var repository = new ObservableRepository(
45 | @"C:\Users\brendanforster\Documents\GìtHūb\testing-pushspecs",
46 | credentials);
47 |
48 | Func translate = x => x / 3;
49 |
50 | var pullObserver = new ReplaySubject>();
51 | var pushObserver = new ReplaySubject>();
52 |
53 | var pullResult = await repository.Pull(pullObserver);
54 |
55 | Assert.NotEqual(MergeStatus.Conflicts, pullResult.Status);
56 |
57 | await repository.Push(pushObserver);
58 |
59 | var list = await pullObserver.Select(x => translate(x.Item2) * 2)
60 | .Concat(pushObserver.Select(x => 67 + translate(x.Item2)))
61 | .ToList();
62 |
63 | Assert.NotEmpty(list);
64 | Assert.Equal(100, list.Last());
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/ReactiveGit/IObservableRepository.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reactive;
3 | using LibGit2Sharp;
4 |
5 | namespace ReactiveGit
6 | {
7 | ///
8 | /// Abstraction for performing asynchronous operations against a LibGit2Sharp repository
9 | ///
10 | public interface IObservableRepository : IDisposable
11 | {
12 | ///
13 | /// Push the changes from this repository to the remote repository
14 | ///
15 | /// An observer to report progress
16 | /// A signal indicating completion
17 | IObservable Push(IObserver> observer);
18 |
19 | ///
20 | /// Pull changes from the remote repository into this repository
21 | ///
22 | /// An observer to report progress
23 | /// A signal indicating the result of the merge
24 | IObservable Pull(IObserver> observer);
25 |
26 | ///
27 | /// Checkout a specific commit for this repository
28 | ///
29 | /// The desired commit
30 | /// An observer to report progress
31 | /// A signal indicating completion
32 | IObservable Checkout(Commit commit, IObserver> observer);
33 |
34 | ///
35 | /// Checkout a specific branch for this repository
36 | ///
37 | /// The desired branch
38 | /// An observer to report progress
39 | /// A signal indicating completion
40 | IObservable Checkout(Branch branch, IObserver> observer);
41 |
42 | ///
43 | /// Checkout a specific commitish for this repository
44 | ///
45 | /// The desired commit
46 | /// An observer to report progress
47 | /// A signal indicating completion
48 | IObservable Checkout(string commitOrBranchSpec, IObserver> observer);
49 |
50 | ///
51 | /// Access to the underlying LibGit2Sharp repository
52 | ///
53 | IRepository Inner { get; }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/ReactiveGit/ObservableRepository.Checkout.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reactive;
3 | using System.Reactive.Concurrency;
4 | using System.Reactive.Linq;
5 | using LibGit2Sharp;
6 |
7 | namespace ReactiveGit
8 | {
9 | public partial class ObservableRepository
10 | {
11 | ///
12 | public IObservable Checkout(
13 | Branch branch,
14 | IObserver> observer)
15 | {
16 | var signature = _repository.Config.BuildSignature(DateTimeOffset.Now);
17 |
18 | var options = new CheckoutOptions
19 | {
20 | OnCheckoutProgress = ProgressFactory.CreateHandler(observer)
21 | };
22 |
23 | return Observable.Start(() =>
24 | {
25 | _repository.Checkout(branch, options, signature);
26 | SignalCompleted(observer);
27 | }, Scheduler.Default);
28 | }
29 |
30 | ///
31 | public IObservable Checkout(
32 | Commit commit,
33 | IObserver> observer)
34 | {
35 | var signature = _repository.Config.BuildSignature(DateTimeOffset.Now);
36 |
37 | var options = new CheckoutOptions
38 | {
39 | OnCheckoutProgress = ProgressFactory.CreateHandler(observer)
40 | };
41 |
42 | return Observable.Start(() =>
43 | {
44 | _repository.Checkout(commit, options, signature);
45 | SignalCompleted(observer);
46 | }, Scheduler.Default);
47 | }
48 |
49 | ///
50 | public IObservable Checkout(
51 | string commitOrBranchSpec,
52 | IObserver> observer)
53 | {
54 | var signature = _repository.Config.BuildSignature(DateTimeOffset.Now);
55 |
56 | var options = new CheckoutOptions
57 | {
58 | OnCheckoutProgress = ProgressFactory.CreateHandler(observer)
59 | };
60 |
61 | return Observable.Start(() =>
62 | {
63 | _repository.Checkout(commitOrBranchSpec, options, signature);
64 | SignalCompleted(observer);
65 | }, Scheduler.Default);
66 | }
67 |
68 | static void SignalCompleted(IObserver> observer)
69 | {
70 | observer.OnNext(Tuple.Create("checkout completed", 100));
71 | observer.OnCompleted();
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/ReactiveGit/ObservableRepository.Clone.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Reactive.Concurrency;
4 | using System.Reactive.Disposables;
5 | using System.Reactive.Linq;
6 | using LibGit2Sharp;
7 | using LibGit2Sharp.Handlers;
8 |
9 | namespace ReactiveGit
10 | {
11 | public partial class ObservableRepository
12 | {
13 | ///
14 | public static IObservable Clone(
15 | string sourceUrl,
16 | string workingDirectory,
17 | IObserver> observer,
18 | CredentialsHandler credentials = null)
19 | {
20 | var isCancelled = false;
21 | var options = new CloneOptions
22 | {
23 | Checkout = true,
24 | CredentialsProvider = credentials,
25 | OnTransferProgress = progress =>
26 | {
27 | // TODO: how should we signal for the "indexing objects" events
28 | var p = (100 * progress.ReceivedObjects) / progress.TotalObjects;
29 | var receivingMessage = String.Format("Receiving objects: {0}% ({1}/{2})", p, progress.ReceivedObjects, progress.TotalObjects);
30 | observer.OnNext(Tuple.Create(receivingMessage, p));
31 | return !isCancelled;
32 | },
33 | IsBare = false,
34 | OnCheckoutProgress = ProgressFactory.CreateHandlerWithoutMessages(observer)
35 | };
36 |
37 | var directoryInfo = new DirectoryInfo(workingDirectory);
38 | var initialMessage = String.Format("Cloning into '{0}'...", directoryInfo.Name);
39 | observer.OnNext(Tuple.Create(initialMessage, 0));
40 |
41 | return Observable.Create(subj =>
42 | {
43 | var sub = Observable.Start(() =>
44 | {
45 | var directory = Repository.Clone(sourceUrl, workingDirectory, options);
46 |
47 | observer.OnNext(Tuple.Create("clone completed", 100));
48 | observer.OnCompleted();
49 |
50 | return new ObservableRepository(directory, credentials);
51 | }, Scheduler.Default).Subscribe(subj);
52 |
53 | return new CompositeDisposable(
54 | sub,
55 | Disposable.Create(() =>
56 | {
57 | isCancelled = true;
58 | observer.OnCompleted();
59 | }));
60 | });
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/ReactiveGit.Tests/TestDirectory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using System.Reactive;
5 | using System.Reactive.Linq;
6 |
7 | namespace ReactiveGit.Tests
8 | {
9 | public class TestDirectory : IDisposable
10 | {
11 | public TestDirectory()
12 | {
13 | Path = System.IO.Path.Combine(System.IO.Path.GetTempPath(), System.IO.Path.GetRandomFileName());
14 | }
15 |
16 | public string Path { get; private set; }
17 |
18 | public static TestDirectory Create()
19 | {
20 | return new TestDirectory();
21 | }
22 |
23 | public async void Dispose()
24 | {
25 | await Observable.Start(
26 | () => DeleteDirectory(Path))
27 | .Retry(3);
28 | }
29 |
30 | // From https://github.com/libgit2/libgit2sharp/blob/vNext/LibGit2Sharp.Tests/TestHelpers/DirectoryHelper.cs
31 | // License: MIT (https://github.com/libgit2/libgit2sharp/blob/vNext/LICENSE.md)
32 |
33 | private static void DeleteDirectory(string directoryPath)
34 | {
35 | // From http://stackoverflow.com/questions/329355/cannot-delete-directory-with-directory-deletepath-true/329502#329502
36 |
37 | if (!Directory.Exists(directoryPath))
38 | {
39 | Trace.WriteLine(
40 | string.Format("Directory '{0}' is missing and can't be removed.",
41 | directoryPath));
42 |
43 | return;
44 | }
45 |
46 | string[] files = Directory.GetFiles(directoryPath);
47 | string[] dirs = Directory.GetDirectories(directoryPath);
48 |
49 | foreach (string file in files)
50 | {
51 | File.SetAttributes(file, FileAttributes.Normal);
52 | File.Delete(file);
53 | }
54 |
55 | foreach (string dir in dirs)
56 | {
57 | DeleteDirectory(dir);
58 | }
59 |
60 | File.SetAttributes(directoryPath, FileAttributes.Normal);
61 | try
62 | {
63 | Directory.Delete(directoryPath, false);
64 | }
65 | catch (IOException)
66 | {
67 | Trace.WriteLine(string.Format("{0}The directory '{1}' could not be deleted!" +
68 | "{0}Most of the time, this is due to an external process accessing the files in the temporary repositories created during the test runs, and keeping a handle on the directory, thus preventing the deletion of those files." +
69 | "{0}Known and common causes include:" +
70 | "{0}- Windows Search Indexer" +
71 | "{0}- Antivirus{0}",
72 | Environment.NewLine, System.IO.Path.GetFullPath(directoryPath)));
73 | }
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #################
2 | ## Eclipse
3 | #################
4 |
5 | *.pydevproject
6 | .project
7 | .metadata
8 | bin/
9 | tmp/
10 | *.tmp
11 | *.bak
12 | *.swp
13 | *~.nib
14 | local.properties
15 | .classpath
16 | .settings/
17 | .loadpath
18 |
19 | # External tool builders
20 | .externalToolBuilders/
21 |
22 | # Locally stored "Eclipse launch configurations"
23 | *.launch
24 |
25 | # CDT-specific
26 | .cproject
27 |
28 | # PDT-specific
29 | .buildpath
30 |
31 |
32 | #################
33 | ## Visual Studio
34 | #################
35 |
36 | ## Ignore Visual Studio temporary files, build results, and
37 | ## files generated by popular Visual Studio add-ons.
38 |
39 | # User-specific files
40 | *.suo
41 | *.user
42 | *.sln.docstates
43 |
44 | # Build results
45 |
46 | [Dd]ebug/
47 | [Rr]elease/
48 | x64/
49 | build/
50 | [Bb]in/
51 | [Oo]bj/
52 |
53 | # MSTest test Results
54 | [Tt]est[Rr]esult*/
55 | [Bb]uild[Ll]og.*
56 |
57 | *_i.c
58 | *_p.c
59 | *.ilk
60 | *.meta
61 | *.obj
62 | *.pch
63 | *.pdb
64 | *.pgc
65 | *.pgd
66 | *.rsp
67 | *.sbr
68 | *.tlb
69 | *.tli
70 | *.tlh
71 | *.tmp
72 | *.tmp_proj
73 | *.log
74 | *.vspscc
75 | *.vssscc
76 | .builds
77 | *.pidb
78 | *.log
79 | *.scc
80 |
81 | # Visual C++ cache files
82 | ipch/
83 | *.aps
84 | *.ncb
85 | *.opensdf
86 | *.sdf
87 | *.cachefile
88 |
89 | # Visual Studio profiler
90 | *.psess
91 | *.vsp
92 | *.vspx
93 |
94 | # Guidance Automation Toolkit
95 | *.gpState
96 |
97 | # ReSharper is a .NET coding add-in
98 | _ReSharper*/
99 | *.[Rr]e[Ss]harper
100 |
101 | # TeamCity is a build add-in
102 | _TeamCity*
103 |
104 | # DotCover is a Code Coverage Tool
105 | *.dotCover
106 |
107 | # NCrunch
108 | *.ncrunch*
109 | .*crunch*.local.xml
110 |
111 | # Installshield output folder
112 | [Ee]xpress/
113 |
114 | # DocProject is a documentation generator add-in
115 | DocProject/buildhelp/
116 | DocProject/Help/*.HxT
117 | DocProject/Help/*.HxC
118 | DocProject/Help/*.hhc
119 | DocProject/Help/*.hhk
120 | DocProject/Help/*.hhp
121 | DocProject/Help/Html2
122 | DocProject/Help/html
123 |
124 | # Click-Once directory
125 | publish/
126 |
127 | # Publish Web Output
128 | *.Publish.xml
129 | *.pubxml
130 |
131 | # NuGet Packages Directory
132 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
133 | packages/
134 |
135 | # Windows Azure Build Output
136 | csx
137 | *.build.csdef
138 |
139 | # Windows Store app package directory
140 | AppPackages/
141 |
142 | # Others
143 | sql/
144 | *.Cache
145 | ClientBin/
146 | [Ss]tyle[Cc]op.*
147 | ~$*
148 | *~
149 | *.dbmdl
150 | *.[Pp]ublish.xml
151 | *.pfx
152 | *.publishsettings
153 |
154 | # RIA/Silverlight projects
155 | Generated_Code/
156 |
157 | # Backup & report files from converting an old project file to a newer
158 | # Visual Studio version. Backup files are not needed, because we have git ;-)
159 | _UpgradeReport_Files/
160 | Backup*/
161 | UpgradeLog*.XML
162 | UpgradeLog*.htm
163 |
164 | # SQL Server files
165 | App_Data/*.mdf
166 | App_Data/*.ldf
167 |
168 | #############
169 | ## Windows detritus
170 | #############
171 |
172 | # Windows image file caches
173 | Thumbs.db
174 | ehthumbs.db
175 |
176 | # Folder config file
177 | Desktop.ini
178 |
179 | # Recycle Bin used on file shares
180 | $RECYCLE.BIN/
181 |
182 | # Mac crap
183 | .DS_Store
184 |
185 |
186 | #############
187 | ## Python
188 | #############
189 |
190 | *.py[cod]
191 |
192 | # Packages
193 | *.egg
194 | *.egg-info
195 | dist/
196 | build/
197 | eggs/
198 | parts/
199 | var/
200 | sdist/
201 | develop-eggs/
202 | .installed.cfg
203 |
204 | # Installer logs
205 | pip-log.txt
206 |
207 | # Unit test / coverage reports
208 | .coverage
209 | .tox
210 |
211 | #Translations
212 | *.mo
213 |
214 | #Mr Developer
215 | .mr.developer.cfg
216 |
217 | *.nupkg
218 | tools/FAKE.Core
--------------------------------------------------------------------------------
/ReactiveGit.Demo/ViewModels/CloneRepositoryViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Reactive;
4 | using System.Reactive.Linq;
5 | using System.Reactive.Subjects;
6 | using LibGit2Sharp;
7 | using ReactiveUI;
8 |
9 | namespace ReactiveGit.Demo.ViewModels
10 | {
11 | public class CloneRepositoryViewModel : ReactiveObject
12 | {
13 | readonly ObservableAsPropertyHelper progressText;
14 | readonly ObservableAsPropertyHelper progressValue;
15 | readonly ObservableAsPropertyHelper repositoryObs;
16 | readonly ObservableAsPropertyHelper isCloningObs;
17 |
18 | public CloneRepositoryViewModel(string cloneUrl, string localDirectory)
19 | {
20 | IsEmpty = true;
21 | Branches = new ReactiveList();
22 |
23 | // omg this hack sucks
24 | Progress = new ReplaySubject>();
25 |
26 | var anyProgress = this.WhenAnyObservable(x => x.Progress);
27 | progressText = anyProgress.Select(x => x.Item1)
28 | .ToProperty(this, x => x.ProgressText, scheduler: RxApp.MainThreadScheduler);
29 | progressValue = anyProgress.Select(x => x.Item2)
30 | .ToProperty(this, x => x.ProgressValue, scheduler: RxApp.MainThreadScheduler);
31 |
32 | Clone = ReactiveCommand.CreateAsyncObservable(_ =>
33 | {
34 | Progress = new ReplaySubject>();
35 | return ObservableRepository.Clone(cloneUrl, localDirectory, Progress);
36 | });
37 |
38 | isCloningObs = Clone.IsExecuting.ToProperty(this, x => x.IsCloning);
39 |
40 | Clone.Subscribe(_ => { IsEmpty = false; });
41 |
42 | repositoryObs = Clone.ToProperty(this, x => x.Repository);
43 |
44 | this.WhenAnyValue(x => x.Repository)
45 | .Where(x => x != null)
46 | .Subscribe(RefreshBranches);
47 |
48 | Checkout = ReactiveCommand.CreateAsyncObservable(
49 | this.WhenAny(x => x.SelectedBranch, x => x != null),
50 | _ =>
51 | {
52 | Progress = new ReplaySubject>();
53 | var branch = Repository.Inner.Branches[SelectedBranch.Name];
54 | return Repository.Checkout(branch, Progress);
55 | });
56 | Checkout.Subscribe(_
57 | => RefreshBranches(Repository));
58 | }
59 |
60 | ISubject> progress;
61 | public ISubject> Progress
62 | {
63 | get { return progress; }
64 | private set { this.RaiseAndSetIfChanged(ref progress, value); }
65 | }
66 |
67 | void RefreshBranches(IObservableRepository repo)
68 | {
69 | Branches.Clear();
70 | foreach (var branch in repo.Inner.Branches
71 | .Select(x => new BranchViewModel { Name = x.Name, CanonicalName = x.CanonicalName }))
72 | {
73 | Branches.Add(branch);
74 | }
75 |
76 | SelectedBranch = Branches.FirstOrDefault(
77 | b => b.CanonicalName == repo.Inner.Head.CanonicalName);
78 | }
79 |
80 | bool isEmpty;
81 | public bool IsEmpty
82 | {
83 | get { return isEmpty; }
84 | private set { this.RaiseAndSetIfChanged(ref isEmpty, value); }
85 | }
86 |
87 | public bool IsCloning { get { return isCloningObs.Value; } }
88 |
89 | public ReactiveCommand Checkout { get; private set; }
90 |
91 | public IObservableRepository Repository { get { return repositoryObs.Value; } }
92 |
93 | public ReactiveCommand Clone { get; private set; }
94 |
95 | public int ProgressValue { get { return progressValue.Value; } }
96 |
97 | public string ProgressText { get { return progressText.Value; } }
98 |
99 | public ReactiveList Branches { get; private set; }
100 |
101 | BranchViewModel selectedBranch;
102 | public BranchViewModel SelectedBranch
103 | {
104 | get { return selectedBranch; }
105 | set { this.RaiseAndSetIfChanged(ref selectedBranch, value); }
106 | }
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/ReactiveGit/ReactiveGit.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Debug
7 | AnyCPU
8 | {3BB24A21-6065-4FCD-8BB8-83E875AF2C99}
9 | Library
10 | Properties
11 | ReactiveGit
12 | ReactiveGit
13 | v4.5
14 | 512
15 | 6db0d2bd
16 |
17 |
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 | bin\Release\ReactiveGit.XML
34 |
35 |
36 |
37 | ..\packages\LibGit2Sharp.0.21.0.176\lib\net40\LibGit2Sharp.dll
38 |
39 |
40 |
41 |
42 | ..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll
43 |
44 |
45 | ..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll
46 |
47 |
48 | ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll
49 |
50 |
51 | ..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | Properties\SolutionInfo.cs
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
83 |
84 |
85 |
86 |
93 |
--------------------------------------------------------------------------------
/ReactiveGit.Tests/ReactiveGit.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Debug
9 | AnyCPU
10 | {26BC0BE1-5C57-44DE-B0CC-EA9AAD9D2587}
11 | Library
12 | Properties
13 | ReactiveGit.Tests
14 | ReactiveGit.Tests
15 | v4.5
16 | 512
17 | afe307da
18 |
19 |
20 | true
21 | full
22 | false
23 | bin\Debug\
24 | DEBUG;TRACE
25 | prompt
26 | 4
27 |
28 |
29 | pdbonly
30 | true
31 | bin\Release\
32 | TRACE
33 | prompt
34 | 4
35 |
36 |
37 |
38 | ..\packages\LibGit2Sharp.0.21.0.176\lib\net40\LibGit2Sharp.dll
39 | True
40 |
41 |
42 |
43 |
44 | ..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll
45 |
46 |
47 | ..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll
48 |
49 |
50 | ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll
51 |
52 |
53 | ..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | ..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll
62 |
63 |
64 | ..\packages\xunit.assert.2.0.0\lib\portable-net45+win+wpa81+wp80+monotouch+monoandroid+Xamarin.iOS\xunit.assert.dll
65 |
66 |
67 | ..\packages\xunit.extensibility.core.2.0.0\lib\portable-net45+win+wpa81+wp80+monotouch+monoandroid+Xamarin.iOS\xunit.core.dll
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | {3BB24A21-6065-4FCD-8BB8-83E875AF2C99}
81 | ReactiveGit
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
107 |
--------------------------------------------------------------------------------
/ReactiveGit.Demo/ReactiveGit.Demo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Debug
7 | AnyCPU
8 | {BD02CBC4-2981-430A-AD5A-C3CCF05F0BBC}
9 | WinExe
10 | Properties
11 | ReactiveGit.Demo
12 | ReactiveGit.Demo
13 | v4.5
14 | 512
15 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
16 | 4
17 | 98e2694d
18 |
19 |
20 | AnyCPU
21 | true
22 | full
23 | false
24 | bin\Debug\
25 | DEBUG;TRACE
26 | prompt
27 | 4
28 |
29 |
30 | AnyCPU
31 | pdbonly
32 | true
33 | bin\Release\
34 | TRACE
35 | prompt
36 | 4
37 |
38 |
39 |
40 | ..\packages\LibGit2Sharp.0.21.0.176\lib\net40\LibGit2Sharp.dll
41 |
42 |
43 | ..\packages\reactiveui-core.6.4.0.1\lib\Net45\ReactiveUI.dll
44 |
45 |
46 | ..\packages\Splat.1.6.2\lib\Net45\Splat.dll
47 |
48 |
49 |
50 |
51 | ..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll
52 |
53 |
54 | ..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll
55 |
56 |
57 | ..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll
58 |
59 |
60 | ..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll
61 |
62 |
63 | ..\packages\Rx-XAML.2.2.5\lib\net45\System.Reactive.Windows.Threading.dll
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | 4.0
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | MSBuild:Compile
81 | Designer
82 |
83 |
84 |
85 |
86 |
87 |
88 | CloneRepositoryView.xaml
89 |
90 |
91 | ShellView.xaml
92 |
93 |
94 | App.xaml
95 | Code
96 |
97 |
98 | Designer
99 | MSBuild:Compile
100 |
101 |
102 | Designer
103 | MSBuild:Compile
104 |
105 |
106 |
107 |
108 | Code
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 | {3bb24a21-6065-4fcd-8bb8-83e875af2c99}
119 | ReactiveGit
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
130 |
131 |
132 |
133 |
140 |
--------------------------------------------------------------------------------