-
Notifications
You must be signed in to change notification settings - Fork 20
/
ClassRepos.cs
271 lines (247 loc) · 10.7 KB
/
ClassRepos.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
using System;
using System.Globalization;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Windows.Forms;
namespace GitForce
{
/// <summary>
/// Class containing a set of repos that the program knows about.
/// It also contains a pointer to the current, active repo.
/// </summary>
public class ClassRepos
{
/// <summary>
/// Contains a list of repos in this workspace
/// </summary>
public List<ClassRepo> Repos = new List<ClassRepo>();
/// <summary>
/// Returns an existing repo with the root on the given path; otherwise returns null
/// </summary>
public ClassRepo Find(string path)
{
path = Path.GetFullPath(path);
return Repos.Find(r => r.Path.Equals(path, StringComparison.CurrentCultureIgnoreCase));
}
/// <summary>
/// Pointer to the default repo to switch to upon start
/// </summary>
public ClassRepo Default { get; set; }
/// <summary>
/// Pointer to the currently active repo
/// </summary>
public ClassRepo Current { get; private set; }
/// <summary>
/// ToString override returns the number of repos.
/// </summary>
public override string ToString()
{
return Repos.Count.ToString(CultureInfo.InvariantCulture);
}
/// <summary>
/// Load or merge a set of repositories from a file.
/// Returns true is loading succeeded and this class is assigned a new set of repos.
/// Returns false if loading failed, or was cancelled. The repos in this class did not change.
/// If the requested operation is a merge, isImport=true, the new set of repos will be merged with the existing one.
/// </summary>
public bool Load(string fileName, bool isImport)
{
// Wrap the opening of a repository database with an outer handler
try
{
using (FileStream file = new FileStream(fileName, FileMode.Open))
{
try
{
// Load list of repos and the default repo string into temporary objects
BinaryFormatter rd = new BinaryFormatter();
List<ClassRepo> newRepos = (List<ClassRepo>)rd.Deserialize(file);
string defaultRepo = (string)rd.Deserialize(file);
// WAR: Mono 2.6.7 does not support serialization of a HashSet. At the same time...
// Quickly check that each repo is valid (find if at least one is not)
bool allOK = true;
foreach (ClassRepo repo in newRepos)
{
allOK &= ClassUtils.DirStat(repo.Path) == ClassUtils.DirStatType.Git;
repo.ExpansionReset(null);
}
// If at least one repo was not valid, give the user a chance to recreate it
if (allOK == false)
{
FormRecreateRepos recreateRepos = new FormRecreateRepos();
recreateRepos.Repos = newRepos;
// The "Accept" button in the recreate repos dialog will not be enabled
// until every repo has been configured properly. User can always cancel
// that process in which case we will not load the selected repo.
if (recreateRepos.ShowDialog() == DialogResult.Cancel)
return false;
newRepos = recreateRepos.Repos;
}
// If the operation is a simple load, assign our object's list of repos
// Otherwise, we merge the new set with the existing one
if (isImport)
Repos.AddRange(newRepos);
// After we merge the new set of repos, current/default repo remains the same
else
{
Repos = newRepos;
// Upon load, set the current based on the default repo
Default = Repos.Find(r => r.Path == defaultRepo);
SetCurrent(Default);
}
return true;
}
catch (Exception ex)
{
throw new ClassException(ex.Message);
}
}
}
catch (Exception ex)
{
App.PrintLogMessage(ex.Message, MessageType.Error);
}
return false;
}
/// <summary>
/// Saves the current set of repositories to a file.
/// Returns true if save succeeded.
/// </summary>
public bool Save(string fileName)
{
try
{
using (FileStream file = new FileStream(fileName, FileMode.Create))
{
try
{
BinaryFormatter wr = new BinaryFormatter();
wr.Serialize(file, Repos);
wr.Serialize(file, Default == null ? "" : Default.Path);
return true;
}
catch (Exception ex)
{
throw new ClassException(ex.Message);
}
}
}
catch (Exception ex)
{
App.PrintLogMessage(ex.Message, MessageType.Error);
}
return false;
}
/// <summary>
/// Loads username/email for all repos in this workspace.
/// Removes repos that are not valid.
/// </summary>
public void InitAll()
{
// Get the user name and email for each repo adding invalid ones to the list
List<ClassRepo> invalidRepos = Repos.Where(r => r.Init() == false).ToList();
// Remove all invalid repos from the workspace
foreach (var r in invalidRepos)
{
App.PrintStatusMessage("Removing invalid repo " + r, MessageType.General);
Delete(r);
}
}
/// <summary>
/// Global status refresh function refreshes status of the current repo.
/// </summary>
public static void Refresh()
{
ClassRepo repo = App.Repos.Current;
if (repo != null)
repo.Status = new ClassStatus(repo);
}
/// <summary>
/// Add a new repository with the root at the given path. Create a directory if it does not exist.
/// This function throws exceptions!
/// </summary>
/// <param name="root">The root path of a new repository</param>
/// <returns>Newly created repository class or null if a repo already exists at that root directory</returns>
public ClassRepo Add(string root)
{
// Detect a repository with the same root path (case insensitive directory name compare)
if (Find(root) != null)
throw new ClassException("Repository with the same name already exists!");
Directory.CreateDirectory(root);
ClassRepo repo = new ClassRepo(root);
if (!repo.Init())
throw new ClassException("Unable to initialize git repository!");
Repos.Add(repo);
App.PrintStatusMessage("Adding repo " + repo, MessageType.General);
// If this is a very first repo, set it as default and current
if (Repos.Count == 1)
Current = Default = repo;
return repo;
}
/// <summary>
/// Delete a repository given by its class variable
/// </summary>
public void Delete(ClassRepo repo)
{
Repos.Remove(repo);
// If the current has been deleted, find a new current
if (repo == Current)
SetCurrent(Repos.Count > 0 ? Repos[0] : null);
// Reset the default if that one has been deleted
if (repo == Default)
Default = Current;
}
/// <summary>
/// Set the new current repo.
/// Given repo can be null in which case the first repo will be selected as current.
/// </summary>
public void SetCurrent(ClassRepo repo)
{
// Assign the new 'current' repo but if null, try to find the first valid repo
Current = (repo == null && Repos.Count > 0)? Repos[0] : repo;
if (Current != null)
Current.Remotes.Refresh(Current); // Refresh the list of remote repos
}
/// <summary>
/// Reorder the list of repos according to the ordering given in the argument
/// That list of strings should list each and all ClassRepo' names in the desired new order
/// </summary>
public void SetOrder(List<string> order)
{
// Since ClassRepo implements sorting, use it but provide our own delegate to compare
// That delegate simply returns desired compare based on our input list of names
Repos.Sort(
delegate(ClassRepo a, ClassRepo b) {
int ia = order.IndexOf(a.Path.ToString());
int ib = order.IndexOf(b.Path.ToString());
return ia.CompareTo(ib);
}
);
}
/// <summary>
/// Return a unique list of all remote repos' URLs that are used with the Fetch and Push commands
/// This is a convenience method that is used by the RemoteEdit form to populate new Remote pull
/// down listbox so the user can easily pick from the existing URLs.
/// </summary>
public List<string> GetRemoteUrls()
{
var remotes = new List<string>();
foreach (ClassRepo repo in Repos)
{
var r = repo.Remotes.GetListNames();
foreach (var name in r)
{
string url = repo.Remotes.Get(name).UrlFetch;
if (!remotes.Contains(url))
remotes.Add(url);
url = repo.Remotes.Get(name).UrlPush;
if (!remotes.Contains(url))
remotes.Add(url);
}
}
return remotes;
}
}
}