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

Delta Between Two Patch Sets: 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/
Left Patch Set: Using this tool to release 1.5.0-beta Created 10 years, 7 months ago
Right 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:
Left: Side by side diff | Download
Right: Side by side diff | Download
LEFTRIGHT
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.Utils; 25 using Google.Apis.Utils;
26 using Google.Apis.Utils.Trace;
26 27
27 namespace Google.Apis.Release.Repositories 28 namespace Google.Apis.Release.Repositories
28 { 29 {
29 /// <summary>Mercurial repository interface class.</summary> 30 /// <summary>Mercurial repository interface class.</summary>
30 public sealed class Hg : IDisposable 31 public sealed class Hg : IDisposable
31 { 32 {
32 static readonly TraceSource TraceSource = new TraceSource("Google.Apis") ; 33 static readonly TraceSource TraceSource = new TraceSource("Google.Apis") ;
33 34
34 /// <summary>Gets or sets the name of the repository.summary> 35 /// <summary>Gets the name of the repository.summary>
35 public string Name { get; private set; } 36 public string Name { get; private set; }
36 37
37 /// <summary>Gets or sets the URI of the repository</summary> 38 /// <summary>Gets the URI of the repository</summary>
38 public Uri RepositoryUri { get; private set; } 39 public Uri RepositoryUri { get; private set; }
39 40
40 /// <summary>Gets or sets the local working directory.</summary> 41 /// <summary>Gets the local working directory.</summary>
41 public string WorkingDirectory { get; private set; } 42 public string WorkingDirectory { get; private set; }
42 43
43 /// <summary>Gets indication if this working copy has pending changes.</ summary> 44 /// <summary>Gets indication if repository has pending changes.</summary >
44 public bool HasUncommitedChanges 45 public bool HasUncommitedChanges
45 { 46 {
46 get 47 get
47 { 48 {
48 string[] changes = RunListeningHg("status"); 49 string[] changes = RunListeningHg("status");
49 return changes.Length >= 1; 50 return changes.Length >= 1;
50 } 51 }
51 } 52 }
52 53
53 /// <summary>Gets indication if there are incoming changes.</summary> 54 /// <summary>Gets indication if there are incoming changes.</summary>
(...skipping 13 matching lines...) Expand all
67 } 68 }
68 } 69 }
69 70
70 /// <summary>Gets indication if the working copy has un-pushed changes.< /summary> 71 /// <summary>Gets indication if the working copy has un-pushed changes.< /summary>
71 public bool HasUnpushedChanges 72 public bool HasUnpushedChanges
72 { 73 {
73 get 74 get
74 { 75 {
75 try 76 try
76 { 77 {
77 string[] changes = RunListeningHg("outgoing {0} {1}", "-l", "1"); 78 string[] changes = RunListeningHg(string.Format("outgoing {0 } {1}", "-l", "1"));
78 return changes.Length >= 4; 79 return changes.Length >= 4;
79 } 80 }
80 catch (ExternalException) 81 catch (ExternalException)
81 { 82 {
82 // 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
83 // the executable "hg outgoing" returns 1 if there where no changes. 84 // the executable "hg outgoing" returns 1 if there where no changes.
84 // So treat ExternalException as no changes. 85 // So treat ExternalException as no changes.
85 return false; 86 return false;
86 } 87 }
87 } 88 }
88 } 89 }
89 90
90 private Hg(Uri repositoryUri) 91 /// <summary>
91 : 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
92 { 93 /// the current status of the repository.
93 } 94 /// </summary>
94 95 /// <param name="repositoryUri">The URI of this repository</param>
95 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)
96 { 98 {
97 RepositoryUri = repositoryUri; 99 RepositoryUri = repositoryUri;
98 WorkingDirectory = Path.GetFullPath(localDir); 100 WorkingDirectory = Path.GetFullPath(localDir);
99 Name = repositoryUri.Segments.Last(); 101 Name = repositoryUri.Segments.Last();
100 102
101 if (!Directory.Exists(WorkingDirectory)) 103 if (!Directory.Exists(WorkingDirectory))
102 { 104 {
105 TraceSource.TraceEvent(TraceEventType.Information, "[{0}] Fetchi ng {1}", Name, repositoryUri);
103 Directory.CreateDirectory(WorkingDirectory); 106 Directory.CreateDirectory(WorkingDirectory);
104 this.CloneFrom(repositoryUri); 107 RunHg(string.Format("clone {0} {1}", repositoryUri.ToString(), W orkingDirectory));
105 } 108 }
106 else 109 else
107 { 110 {
108 TraceSource.TraceEvent(TraceEventType.Information, "[{0}] Using an existing repository", Name); 111 TraceSource.TraceEvent(TraceEventType.Information, "[{0}] Using an existing repository", Name);
109 RunHg("status"); 112 RunHg("status");
110 } 113 }
111 } 114 }
112 115
113 /// <summary>Commits the pending changes.</summary> 116 /// <summary>Commits the pending changes.</summary>
114 /// <param name="message">Description of the changes/the commit.</param> 117 /// <param name="message">Description of the changes/the commit.</param>
115 /// <returns>Whether a commit was made/possible.</returns> 118 /// <returns>Whether a commit was made/possible.</returns>
116 public bool Commit(string message) 119 public bool Commit(string message)
117 { 120 {
118 if (!HasUncommitedChanges) 121 if (!HasUncommitedChanges)
119 { 122 {
120 return false; 123 return false;
121 } 124 }
122 125
123 RunHg("status -m -a -r"); 126 RunHg("status -m -a -r");
124 127
125 TraceSource.TraceEvent(TraceEventType.Information, "[{0}] Committing changes", Name); 128 TraceSource.TraceEvent(TraceEventType.Information, "[{0}] Committing changes", Name);
126 RunHg("commit -m \"{0}\"", message); 129 RunHg(string.Format("commit -m \"{0}\"", message));
127 return true; 130 return true;
128 } 131 }
129 132
130 /// <summary>Adds a tag to the last revision.</summary> 133 /// <summary>Adds a tag to the last revision.</summary>
131 /// <param name="tagName">The tag to add.</param> 134 /// <param name="tagName">The tag to add.</param>
132 /// <param name="force"> 135 /// <param name="force">
133 /// 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.
134 /// --force parameter.
135 /// </param> 137 /// </param>
136 public void Tag(string tagName, bool force = false) 138 public void Tag(string tagName, bool force = false)
137 { 139 {
138 TraceSource.TraceEvent(TraceEventType.Information, "[{0}] Tagging wi th {1}", Name, tagName); 140 TraceSource.TraceEvent(TraceEventType.Information, "[{0}] Tagging wi th {1}", Name, tagName);
139 string options = (force ? "-f " : ""); 141 string options = (force ? "-f " : "");
140 RunHg("tag {0}\"{1}\"", options, tagName); 142 RunHg(string.Format("tag {0}\"{1}\"", options, tagName));
141 } 143 }
142 144
143 /// <summary>Adds all un-versioned files to the current commit.</summary > 145 /// <summary>Adds all unversioned files and remove all versioned files.< /summary>
144 public void AddUnversionedFiles() 146 public void AddRemoveFiles()
145 { 147 {
146 RunHg("add"); 148 RunHg("addremove");
147 }
148
149 /// <summary>Marks all versioned files which have been deleted as remove d.</summary>
150 public void RemoveDeletedFiles()
151 {
152 var missingFiles = RunListeningHg("status -d")
153 .Where(l => l.StartsWith("! "))
154 .Select(l => "\"" + l.Substring("! ".Length) + " \"");
155
156 string files = String.Join(" ", missingFiles);
157 if (!string.IsNullOrEmpty(files))
158 {
159 TraceSource.TraceEvent(TraceEventType.Information, "[{0}] Removi ng files from {1}", Name, files);
160 RunHg("remove {0}", files);
161 }
162 } 149 }
163 150
164 /// <summary>Pushes all committed changes to the server.</summary> 151 /// <summary>Pushes all committed changes to the server.</summary>
165 public void Push() 152 public void Push()
166 { 153 {
167 if (!HasUnpushedChanges) 154 if (!HasUnpushedChanges)
168 { 155 {
169 return; 156 return;
170 } 157 }
171 158
172 TraceSource.TraceEvent(TraceEventType.Information, "Pushing {0}", Na me); 159 TraceSource.TraceEvent(TraceEventType.Information, "Pushing {0}", Na me);
173 RunShellHg("push"); 160 RunHg("push");
174 } 161 }
175 162
176 /// <summary>Updates the repository by the branch name.</summary> 163 /// <summary>Updates the repository by the branch name.</summary>
177 public void Update(string branchName) 164 public void Update(string branchName)
178 { 165 {
179 TraceSource.TraceEvent(TraceEventType.Information, "[{0}] Updating b ranch to {1}", Name, branchName); 166 TraceSource.TraceEvent(TraceEventType.Information, "[{0}] Updating b ranch to {1}", Name, branchName);
180 RunShellHg("update " + branchName); 167 RunHg("update " + branchName);
181 }
182
183 /// <summary>Pulls and updates this clone.</summary>
184 public void PullUpdate()
185 {
186 RunShellHg("hg pull");
187 RunShellHg("hg update");
188 }
189
190 /// <summary>Outputs the diff to the default output stream.</summary>
191 public void ShowDiff()
192 {
193 RunShellHg("diff");
194 } 168 }
195 169
196 /// <summary>Creates the combined path of the specified directories.</su mmary> 170 /// <summary>Creates the combined path of the specified directories.</su mmary>
197 public string Combine(params string[] dirs) 171 public string Combine(params string[] dirs)
198 { 172 {
199 return dirs.Aggregate(WorkingDirectory, Path.Combine); 173 return dirs.Aggregate(WorkingDirectory, Path.Combine);
200 } 174 }
201 175
202 /// <summary>Creates a change list by listing all changes made since the last release.</summary> 176 /// <summary>Creates a change list by listing all changes made since the last release.</summary>
203 public IEnumerable<string> CreateChangelist() 177 public IEnumerable<string> CreateChangelist()
204 { 178 {
205 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);
206 string branch = RunListeningHg("branch").Single(); 180 string branch = RunListeningHg("branch").Single();
207 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))
208 .TakeWhile(line => !tagRegex.IsMatch(line)); 182 .TakeWhile(line => !tagRegex.IsMatch(line));
209 } 183 }
210 184
211 private void CloneFrom(Uri uri) 185 /// <summary>Runs a HG command. In addition it prints errors and message s to trace.</summary>
212 { 186 private void RunHg(string command)
213 TraceSource.TraceEvent(TraceEventType.Information, "[{0}] Fetching { 1}", Name, uri); 187 {
214 RunHg("clone {0} {1}", uri.ToString(), WorkingDirectory); 188 RunHg(command,
215 } 189 error =>
216
217 private void RunShellHg(string command, params string[] args)
218 {
219 RunHg(command, null, null, args);
220 }
221
222 private void RunHg(string command, params string[] args)
223 {
224 RunHg(command, error =>
225 { 190 {
226 if (!string.IsNullOrEmpty(error)) 191 if (!string.IsNullOrEmpty(error))
227 { 192 {
228 TraceSource.TraceEvent(TraceEventType.Error, "[{0}] {1}" , Name, error); 193 TraceSource.TraceEvent(TraceEventType.Error, "[{0}] {1}" , Name, error);
229 } 194 }
230 }, msg => 195 },
196 msg =>
231 { 197 {
232 if (!string.IsNullOrEmpty(msg)) 198 if (!string.IsNullOrEmpty(msg))
233 { 199 {
234 TraceSource.TraceEvent(TraceEventType.Information, "[{0} ] hg {1}", Name, msg); 200 TraceSource.TraceEvent(TraceEventType.Information, "[{0} ] hg {1}", Name, msg);
235 } 201 }
236 }, args); 202 });
237 } 203 }
238 204
239 private string[] RunListeningHg(string command, params string[] args) 205 /// <summary>
240 { 206 /// Run a HG command which the specific callback for errors or messages returned from the command.
241 var msgs = new List<string>(); 207 /// </summary>
242 RunHg(command, msgs.Add, msgs.Add, args); 208 private void RunHg(string command, Action<string> errorCallback = null, Action<string> messageCallback = null)
243 return msgs.ToArray();
244 }
245
246 private void RunHg(string command, Action<string> errors, Action<string> msgs, params string[] args)
247 { 209 {
248 var process = new Process(); 210 var process = new Process();
249 211
250 string commandLine = string.Format(command, args); 212 process.StartInfo = new ProcessStartInfo("hg", command);
251 process.StartInfo = new ProcessStartInfo("hg", commandLine);
252 process.StartInfo.WorkingDirectory = WorkingDirectory; 213 process.StartInfo.WorkingDirectory = WorkingDirectory;
253 214
254 if (errors != null || msgs != null) 215 if (errorCallback != null || messageCallback != null)
255 { 216 {
256 process.StartInfo.RedirectStandardError = true; 217 process.StartInfo.RedirectStandardError = true;
257 process.StartInfo.RedirectStandardOutput = true; 218 process.StartInfo.RedirectStandardOutput = true;
258 process.StartInfo.CreateNoWindow = true; 219 process.StartInfo.CreateNoWindow = true;
259 process.StartInfo.UseShellExecute = false; 220 process.StartInfo.UseShellExecute = false;
260 } 221 }
261 222
262 process.Start(); 223 process.Start();
263 224 if (errorCallback != null)
264 if (errors != null)
265 { 225 {
266 process.ErrorDataReceived += (sender, msg) => 226 process.ErrorDataReceived += (sender, msg) =>
267 { 227 {
268 if (msg.Data != null) 228 if (msg.Data != null)
269 { 229 {
270 errors(msg.Data); 230 errorCallback(msg.Data);
271 } 231 }
272 }; 232 };
273 process.BeginErrorReadLine(); 233 process.BeginErrorReadLine();
274 } 234 }
275 if (msgs != null) 235 if (messageCallback != null)
276 { 236 {
277 process.OutputDataReceived += (sender, msg) => 237 process.OutputDataReceived += (sender, msg) =>
278 { 238 {
279 if (msg.Data != null) 239 if (msg.Data != null)
280 { 240 {
281 msgs(msg.Data); 241 messageCallback(msg.Data);
282 } 242 }
283 }; 243 };
284 process.BeginOutputReadLine(); 244 process.BeginOutputReadLine();
285 } 245 }
286 246
287 process.WaitForExit(); 247 process.WaitForExit();
248
288 if (process.ExitCode != 0) 249 if (process.ExitCode != 0)
289 { 250 {
290 string cmdLine = process.StartInfo.FileName + " " + process.Star tInfo.Arguments; 251 string cmdLine = process.StartInfo.FileName + " " + process.Star tInfo.Arguments;
291 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);
292 } 253 }
293 } 254 }
294 255
295 /// <summary>Clones a new Mercurial repository.</summary> 256 /// <summary>Runs a HG command and returns all its errors and messages o utput.</summary>
296 public static Hg Clone(string repository) 257 private string[] RunListeningHg(string command)
297 { 258 {
298 return new Hg(new Uri(repository)); 259 var msgs = new List<string>();
299 } 260 RunHg(command, msgs.Add, msgs.Add);
300 261 return msgs.ToArray();
301 /// <summary>Reuses the existing local repository, or creates a new clon e of it does not exist.</summary>
302 /// <param name="localDir">The local Mercurial repository.</param>
303 /// <param name="repository">The remote URL.</param>
304 public static Hg Get(string localDir, string repository)
305 {
306 return new Hg(new Uri(repository), localDir);
307 } 262 }
308 263
309 public void Dispose() 264 public void Dispose()
310 { 265 {
311 TraceSource.TraceEvent(TraceEventType.Information, "[{0}] Cleaning u p", Name); 266 TraceSource.TraceEvent(TraceEventType.Information, "[{0}] Cleaning u p", Name);
312 Directory.Delete(WorkingDirectory, true); 267 Directory.Delete(WorkingDirectory, true);
313 } 268 }
314 } 269 }
315 } 270 }
LEFTRIGHT

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