Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(1676)

Side by Side Diff: Tools/Google.Apis.Release/Repositories/Hg.cs

Issue 12767046: Issue 377: New build for releasing a new version (Closed) Base URL: https://google-api-dotnet-client.googlecode.com/hg/
Patch Set: david comments Created 10 years, 6 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 Copyright 2011 Google Inc 2 Copyright 2011 Google Inc
3 3
4 Licensed under the Apache License, Version 2.0 (the "License"); 4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License. 5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at 6 You may obtain a copy of the License at
7 7
8 http://www.apache.org/licenses/LICENSE-2.0 8 http://www.apache.org/licenses/LICENSE-2.0
9 9
10 Unless required by applicable law or agreed to in writing, software 10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS, 11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and 13 See the License for the specific language governing permissions and
14 limitations under the License. 14 limitations under the License.
15 */ 15 */
16 16
17 using System; 17 using System;
18 using System.Collections.Generic; 18 using System.Collections.Generic;
19 using System.Diagnostics; 19 using System.Diagnostics;
20 using System.IO; 20 using System.IO;
21 using System.Linq; 21 using System.Linq;
22 using System.Runtime.InteropServices; 22 using System.Runtime.InteropServices;
23 using System.Text.RegularExpressions; 23 using System.Text.RegularExpressions;
24 24
25 using Google.Apis.Samples.Helper; 25 using Google.Apis.Utils;
26 using Google.Apis.Utils.Trace;
26 27
27 namespace Google.Build.Utils.Repositories 28 namespace Google.Apis.Release.Repositories
28 { 29 {
29 /// <summary> 30 /// <summary>Mercurial repository interface class.</summary>
30 /// Mercurial interfacing class
31 /// </summary>
32 public sealed class Hg : IDisposable 31 public sealed class Hg : IDisposable
33 { 32 {
34 /// <summary> 33 static readonly TraceSource TraceSource = new TraceSource("Google.Apis") ;
35 /// Name of the repository. 34
36 /// </summary> 35 /// <summary>Gets the name of the repository.summary>
37 public string Name { get; private set; } 36 public string Name { get; private set; }
38 37
39 /// <summary> 38 /// <summary>Gets the URI of the repository</summary>
40 /// The URI of the repositoty
41 /// </summary>
42 public Uri RepositoryUri { get; private set; } 39 public Uri RepositoryUri { get; private set; }
43 40
44 /// <summary> 41 /// <summary>Gets the local working directory.</summary>
45 /// The local working directory.
46 /// </summary>
47 public string WorkingDirectory { get; private set; } 42 public string WorkingDirectory { get; private set; }
48 43
49 /// <summary> 44 /// <summary>Gets indication if repository has pending changes.</summary >
50 /// True if this working copy has pending changes.
51 /// </summary>
52 public bool HasUncommitedChanges 45 public bool HasUncommitedChanges
53 { 46 {
54 get 47 get
55 { 48 {
56 string[] changes = RunListeningHg("status"); 49 string[] changes = RunListeningHg("status");
57 return changes.Length >= 1; 50 return changes.Length >= 1;
58 } 51 }
59 } 52 }
60 53
61 /// <summary> 54 /// <summary>Gets indication if there are incoming changes.</summary>
62 /// True if there are unpulled, incoming changes.
63 /// </summary>
64 public bool HasIncomingChanges 55 public bool HasIncomingChanges
65 { 56 {
66 get 57 get
67 { 58 {
68 try 59 try
69 { 60 {
70 RunListeningHg("incoming"); 61 RunListeningHg("incoming");
71 return true; // Exit code was 0 62 return true; // Exit code was 0
72 } 63 }
73 catch (ExternalException) 64 catch (ExternalException)
74 { 65 {
75 return false; 66 return false;
76 } 67 }
77 } 68 }
78 } 69 }
79 70
80 /// <summary> 71 /// <summary>Gets indication if the working copy has un-pushed changes.< /summary>
81 /// True if this working copy has unpushed changes.
82 /// </summary>
83 public bool HasUnpushedChanges 72 public bool HasUnpushedChanges
84 { 73 {
85 get 74 get
86 { 75 {
87 try 76 try
88 { 77 {
89 string[] changes = RunListeningHg("outgoing {0} {1}", "-l", "1"); 78 string[] changes = RunListeningHg(string.Format("outgoing {0 } {1}", "-l", "1"));
90 return changes.Length >= 4; 79 return changes.Length >= 4;
91 } 80 }
92 catch (ExternalException) 81 catch (ExternalException)
93 { 82 {
94 // RunListeningHg throws an ExternalException when the launc hed process returns non-zero on exiting 83 // RunListeningHg throws an ExternalException when the launc hed process returns non-zero on exiting
95 // the executable "hg outgoing" returns 1 if there where no changes. 84 // the executable "hg outgoing" returns 1 if there where no changes.
96 // So treat ExternalException as no changes. 85 // So treat ExternalException as no changes.
97 return false; 86 return false;
98 } 87 }
99 } 88 }
100 } 89 }
101 90
102 private Hg(Uri repositoryUri) 91 /// <summary>
103 : this(repositoryUri, Path.Combine(Path.GetTempPath(), Path.GetRando mFileName())) 92 /// Constructs a new repository. It creates a new repository if the fold er doesn't exists, otherwise it gets
104 { 93 /// the current status of the repository.
105 94 /// </summary>
106 } 95 /// <param name="repositoryUri">The URI of this repository</param>
107 private Hg(Uri repositoryUri, string localDir) 96 /// <param name="localDir">The local directory which contains the reposi tory files</param>
97 public Hg(Uri repositoryUri, string localDir)
108 { 98 {
109 RepositoryUri = repositoryUri; 99 RepositoryUri = repositoryUri;
110 WorkingDirectory = Path.GetFullPath(localDir); 100 WorkingDirectory = Path.GetFullPath(localDir);
111 Name = repositoryUri.Segments.Last(); 101 Name = repositoryUri.Segments.Last();
112 102
113 if (!Directory.Exists(WorkingDirectory)) 103 if (!Directory.Exists(WorkingDirectory))
114 { 104 {
105 TraceSource.TraceEvent(TraceEventType.Information, "[{0}] Fetchi ng {1}", Name, repositoryUri);
115 Directory.CreateDirectory(WorkingDirectory); 106 Directory.CreateDirectory(WorkingDirectory);
116 this.CloneFrom(repositoryUri); 107 RunHg(string.Format("clone {0} {1}", repositoryUri.ToString(), W orkingDirectory));
117 } 108 }
118 else 109 else
119 { 110 {
120 CommandLine.WriteAction("Using existing repository for " + Name + " at " + WorkingDirectory + " .."); 111 TraceSource.TraceEvent(TraceEventType.Information, "[{0}] Using an existing repository", Name);
121 RunHg("status"); 112 RunHg("status");
122 } 113 }
123 } 114 }
124 115
125 /// <summary> 116 /// <summary>Commits the pending changes.</summary>
126 /// Commits the pending changes.
127 /// </summary>
128 /// <param name="message">Description of the changes/the commit.</param> 117 /// <param name="message">Description of the changes/the commit.</param>
129 /// <returns>Whether a commit was made/possible.</returns> 118 /// <returns>Whether a commit was made/possible.</returns>
130 public bool Commit(string message) 119 public bool Commit(string message)
131 { 120 {
132 if (!HasUncommitedChanges) 121 if (!HasUncommitedChanges)
133 { 122 {
134 return false; 123 return false;
135 } 124 }
136 125
137 RunHg("status -m -a -r"); 126 RunHg("status -m -a -r");
138 127
139 CommandLine.WriteAction("Commiting changes of " + Name + " .."); 128 TraceSource.TraceEvent(TraceEventType.Information, "[{0}] Committing changes", Name);
140 RunHg("commit -m \"{0}\"", message); 129 RunHg(string.Format("commit -m \"{0}\"", message));
141 return true; 130 return true;
142 } 131 }
143 132
144 /// <summary> 133 /// <summary>Adds a tag to the last revision.</summary>
145 /// Adds a tag to the last revision.
146 /// </summary>
147 /// <param name="tagName">The tag to add.</param> 134 /// <param name="tagName">The tag to add.</param>
148 /// <param name="force"> 135 /// <param name="force">
149 /// If set to true will overwrite existing labels of this name see "hg h elp tag" and its 136 /// If set to true will overwrite existing labels of this name see "hg h elp tag" and its --force parameter.
150 /// --force parameter.
151 /// </param> 137 /// </param>
152 public void Tag(string tagName, bool force = false) 138 public void Tag(string tagName, bool force = false)
153 { 139 {
154 CommandLine.WriteAction("Tagging " + Name + " with " + tagName + ".. "); 140 TraceSource.TraceEvent(TraceEventType.Information, "[{0}] Tagging wi th {1}", Name, tagName);
155 string options = (force ? "-f " : ""); 141 string options = (force ? "-f " : "");
156 RunHg("tag {0}\"{1}\"", options, tagName); 142 RunHg(string.Format("tag {0}\"{1}\"", options, tagName));
157 } 143 }
158 144
159 /// <summary> 145 /// <summary>Adds all unversioned files and remove all versioned files.< /summary>
160 /// Adds all unversioned files to the current commit. 146 public void AddRemoveFiles()
161 /// </summary>
162 public void AddUnversionedFiles()
163 { 147 {
164 RunHg("add"); 148 RunHg("addremove");
165 } 149 }
166 150
167 /// <summary> 151 /// <summary>Pushes all committed changes to the server.</summary>
168 /// Marks all versioned files which have been deleted as removed.
169 /// </summary>
170 public void RemoveDeletedFiles()
171 {
172 var missingFiles = RunListeningHg("status -d")
173 .Where(l => l.StartsWith("! "))
174 .Select(l => "\"" + l.Substring("! ".Length) + " \"");
175
176 string files = String.Join(" ", missingFiles);
177 if (!string.IsNullOrEmpty(files))
178 {
179 CommandLine.WriteAction("Removing files: " + files);
180 RunHg("remove {0}", files);
181 }
182 }
183
184 /// <summary>
185 /// Pushes all commited changes to the server.
186 /// </summary>
187 public void Push() 152 public void Push()
188 { 153 {
189 if (!HasUnpushedChanges) 154 if (!HasUnpushedChanges)
190 { 155 {
191 return; 156 return;
192 } 157 }
193 158
194 CommandLine.WriteAction("Pushing " + Name + " .."); 159 TraceSource.TraceEvent(TraceEventType.Information, "Pushing {0}", Na me);
195 RunShellHg("push"); 160 RunHg("push");
196 } 161 }
197 162
198 /// <summary> Updates the repository by the branch name. </summary> 163 /// <summary>Updates the repository by the branch name.</summary>
199 /// <param name="branchName"></param>
200 public void Update(string branchName) 164 public void Update(string branchName)
201 { 165 {
202 CommandLine.WriteAction("Updating branch to " + branchName); 166 TraceSource.TraceEvent(TraceEventType.Information, "[{0}] Updating b ranch to {1}", Name, branchName);
203 RunShellHg("update " + branchName); 167 RunHg("update " + branchName);
204 } 168 }
205 169
206 /// <summary> 170 /// <summary>Creates the combined path of the specified directories.</su mmary>
207 /// Pulls and updates this clone.
208 /// </summary>
209 public void PullUpdate()
210 {
211 RunShellHg("hg pull");
212 RunShellHg("hg update");
213 }
214
215 /// <summary>
216 /// Outputs the diff to the default output stream.
217 /// </summary>
218 public void ShowDiff()
219 {
220 RunShellHg("diff");
221 }
222
223 /// <summary>
224 /// Creates the combined path of the specified directories.
225 /// </summary>
226 public string Combine(params string[] dirs) 171 public string Combine(params string[] dirs)
227 { 172 {
228 return dirs.Aggregate(WorkingDirectory, Path.Combine); 173 return dirs.Aggregate(WorkingDirectory, Path.Combine);
229 } 174 }
230 175
231 /// <summary> 176 /// <summary>Creates a change list by listing all changes made since the last release.</summary>
232 /// Creates a change list by listing all changes made since the last rel ease.
233 /// </summary>
234 public IEnumerable<string> CreateChangelist() 177 public IEnumerable<string> CreateChangelist()
235 { 178 {
236 Regex tagRegex = new Regex("Added tag [0-9A-Za-z.-]+ for changeset [ ^b]+", RegexOptions.Compiled); 179 Regex tagRegex = new Regex("Added tag [0-9A-Za-z.-]+ for changeset [ ^b]+", RegexOptions.Compiled);
237 string branch = RunListeningHg("branch").Single(); 180 string branch = RunListeningHg("branch").Single();
238 return RunListeningHg("log --template \"{{rev}}: {{desc}}\\r\\n\" -b {0}", branch) 181 return RunListeningHg(string.Format("log --template \"{{rev}}: {{des c}}\\r\\n\" -b {0}", branch))
239 .TakeWhile(line => !tagRegex.IsMatch(line)); 182 .TakeWhile(line => !tagRegex.IsMatch(line));
240 } 183 }
241 184
242 private void CloneFrom(Uri uri) 185 /// <summary>Runs a HG command. In addition it prints errors and message s to trace.</summary>
186 private void RunHg(string command)
243 { 187 {
244 CommandLine.WriteAction("Fetching " + uri + " .."); 188 RunHg(command,
245 RunHg("clone {0} {1}", uri.ToString(), WorkingDirectory); 189 error =>
190 {
191 if (!string.IsNullOrEmpty(error))
192 {
193 TraceSource.TraceEvent(TraceEventType.Error, "[{0}] {1}" , Name, error);
194 }
195 },
196 msg =>
197 {
198 if (!string.IsNullOrEmpty(msg))
199 {
200 TraceSource.TraceEvent(TraceEventType.Information, "[{0} ] hg {1}", Name, msg);
201 }
202 });
246 } 203 }
247 204
248 private void RunShellHg(string command, params string[] args) 205 /// <summary>
249 { 206 /// Run a HG command which the specific callback for errors or messages returned from the command.
250 RunHg(command, null, null, args); 207 /// </summary>
251 } 208 private void RunHg(string command, Action<string> errorCallback = null, Action<string> messageCallback = null)
252
253 private void RunHg(string command, params string[] args)
254 {
255 RunHg(
256 command, error =>
257 {
258 if (!string.IsNullOrEmpty(error))
259 {
260 CommandLine.WriteError(error);
261 }
262 }, msg =>
263 {
264 if (!string.IsNullOrEmpty(msg))
265 {
266 CommandLine.WriteResult("hg", msg);
267 }
268 }, args);
269 }
270
271 private string[] RunListeningHg(string command, params string[] args)
272 {
273 var msgs = new List<string>();
274 RunHg(command, msgs.Add, msgs.Add, args);
275 return msgs.ToArray();
276 }
277
278 private void RunHg(string command, Action<string> errors, Action<string> msgs, params string[] args)
279 { 209 {
280 var process = new Process(); 210 var process = new Process();
281 211
282 string commandLine = string.Format(command, args); 212 process.StartInfo = new ProcessStartInfo("hg", command);
283 process.StartInfo = new ProcessStartInfo("hg", commandLine);
284 process.StartInfo.WorkingDirectory = WorkingDirectory; 213 process.StartInfo.WorkingDirectory = WorkingDirectory;
285 214
286 if (errors != null || msgs != null) 215 if (errorCallback != null || messageCallback != null)
287 { 216 {
288 process.StartInfo.RedirectStandardError = true; 217 process.StartInfo.RedirectStandardError = true;
289 process.StartInfo.RedirectStandardOutput = true; 218 process.StartInfo.RedirectStandardOutput = true;
290 process.StartInfo.CreateNoWindow = true; 219 process.StartInfo.CreateNoWindow = true;
291 process.StartInfo.UseShellExecute = false; 220 process.StartInfo.UseShellExecute = false;
292 } 221 }
293 222
294 process.Start(); 223 process.Start();
295 224 if (errorCallback != null)
296 if (errors != null)
297 { 225 {
298 process.ErrorDataReceived += (sender, msg) => 226 process.ErrorDataReceived += (sender, msg) =>
299 { 227 {
300 if (msg.Data != null) 228 if (msg.Data != null)
301 { 229 {
302 errors(msg.Data); 230 errorCallback(msg.Data);
303 } 231 }
304 }; 232 };
305 process.BeginErrorReadLine(); 233 process.BeginErrorReadLine();
306 } 234 }
307 if (msgs != null) 235 if (messageCallback != null)
308 { 236 {
309 process.OutputDataReceived += (sender, msg) => 237 process.OutputDataReceived += (sender, msg) =>
310 { 238 {
311 if (msg.Data != null) 239 if (msg.Data != null)
312 { 240 {
313 msgs(msg.Data); 241 messageCallback(msg.Data);
314 } 242 }
315 }; 243 };
316 process.BeginOutputReadLine(); 244 process.BeginOutputReadLine();
317 } 245 }
318 246
319 process.WaitForExit(); 247 process.WaitForExit();
248
320 if (process.ExitCode != 0) 249 if (process.ExitCode != 0)
321 { 250 {
322 string cmdLine = process.StartInfo.FileName + " " + process.Star tInfo.Arguments; 251 string cmdLine = process.StartInfo.FileName + " " + process.Star tInfo.Arguments;
323 throw new ExternalException("The process '" + cmdLine + "' exite d with errors.", process.ExitCode); 252 throw new ExternalException("The process '" + cmdLine + "' exite d with errors.", process.ExitCode);
324 } 253 }
325 } 254 }
326 255
327 /// <summary> 256 /// <summary>Runs a HG command and returns all its errors and messages o utput.</summary>
328 /// Clones a new Mercurial repository. 257 private string[] RunListeningHg(string command)
329 /// </summary>
330 public static Hg Clone(string repository)
331 { 258 {
332 return new Hg(new Uri(repository)); 259 var msgs = new List<string>();
333 } 260 RunHg(command, msgs.Add, msgs.Add);
334 261 return msgs.ToArray();
335 /// <summary>
336 /// Reuses the existing local repository, or creates a new clone of it d oes not exist.
337 /// </summary>
338 /// <param name="localDir">The local Mercurial repository.</param>
339 /// <param name="repository">The remote URL.</param>
340 public static Hg Get(string localDir, string repository)
341 {
342 return new Hg(new Uri(repository), localDir);
343 } 262 }
344 263
345 public void Dispose() 264 public void Dispose()
346 { 265 {
347 CommandLine.WriteAction("Cleaning up " + Name + " .."); 266 TraceSource.TraceEvent(TraceEventType.Information, "[{0}] Cleaning u p", Name);
348 Directory.Delete(WorkingDirectory, true); 267 Directory.Delete(WorkingDirectory, true);
349 } 268 }
350 } 269 }
351 } 270 }
OLDNEW

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b