gitstats: Use Repo(".").git instead of Git(".")
[git-stats.git] / src / scripts / setupRepo.py
blob3f82e31a87d4a287d8a7f487c2fa76629daeaf66
1 #!/usr/bin/env python
3 import os
4 import tempfile
6 from git import Repo
8 testPath = os.path.join(tempfile.gettempdir(), "testrepo")
9 metricsPath = os.path.join(tempfile.gettempdir(), "metricsrepo")
11 class InitializationException(Exception):
12 """This exception is raised when something goes wrong during initialization.
13 """
15 def setupRepo(path):
16 """Creates a fresh repo under the specified path
18 If the specified path exists an exception is raised.
20 Args:
21 path: The path where the new repo should be created at.
22 """
24 print("Creating a new repository in " + path)
26 # Check if the path exists already, if so bail out
27 if os.path.exists(path):
28 raise InitializationException(
29 "The specified path, " + path + ", exists already, "
30 "please remove it or change the target path.")
32 # Create a new repo
33 repo = Repo.create(path)
34 os.chdir(path)
36 def setDefaultTime():
37 """Configures GIT_COMMITTER_DATE and GIT_AUTHOR_DATE to a default value
39 The actual date set is 2005-05-26 23:30, which was taken
40 from t/t1400-update-ref.sh and then converted to UNIX
41 time manually.
42 """
44 os.environ["GIT_COMMITTER_DATE"] = "1211844600"
45 os.environ["GIT_AUTHOR_DATE"] = "1211844600"
46 os.environ["TZ"] = "UTC"
48 def setDefaultInfo():
49 """Configures GIT_COMMITTER_NAME etc. to a default value.
51 These values are taken from t/test-lib.sh from git.git.
52 """
54 os.environ["GIT_COMMITTER_NAME"] = "C O Mitter"
55 os.environ["GIT_COMMITTER_EMAIL"] = "committer@example.com"
56 os.environ["GIT_AUTHOR_NAME"] = "A U Thor"
57 os.environ["GIT_AUTHOR_EMAIL"] = "author@example.com"
59 def setSecondaryInfo():
60 """Configures GIT_COMMITTER_NAME etc. to a secondary value.
62 The values are the default values but in reverse.
63 """
65 os.environ["GIT_COMMITTER_NAME"] = "Retti Mmoc"
66 os.environ["GIT_COMMITTER_EMAIL"] = "retti.mmoc@example.com"
67 os.environ["GIT_AUTHOR_NAME"] = "Roh Tua"
68 os.environ["GIT_AUTHOR_EMAIL"] = "roh.tua@example.com"
70 def bumpTime(amount = 60*60):
71 """Bumps the date in GIT_AUTHOR_DATE and GIT_COMMITTER_DATE
73 Args:
74 amount: The amount by which they should be bumped.
75 """
77 time = os.environ["GIT_AUTHOR_DATE"]
78 time = int(time)
79 time += amount
80 time = str(time)
82 os.environ["GIT_AUTHOR_DATE"] = time
83 os.environ["GIT_COMMITTER_DATE"] = time
86 def addFile(filename, createFile=True, commit=True):
87 """Adds the specified file
89 Args:
90 filename: The name of the file to add.
91 createFile: Whether the file should be created.
92 """
94 if createFile:
95 file = open(filename, "w")
96 file.close()
98 git = Repo(".").git
100 git.add(filename)
102 if commit:
103 git.commit("-m", "Added file " + filename)
104 bumpTime()
106 def deleteFile(filename, leaveOnDisk=False):
107 """Deletes the specified file
109 Args:
110 filename: The name of the file to delete.
111 leaveOnDisk: Whether the file should not be physically deleted.
114 args = []
116 # If the file should not be deleted, tell 'git rm' so
117 if leaveOnDisk:
118 args.append("--cached")
120 args.append(filename)
122 git = Repo(".").git
124 git.rm(*args)
126 git.commit("-m", "Deleted file " + filename)
127 bumpTime()
129 def createLinearHistory(filename, createFile=False, initialCommit=False, start=1, count=10, finalCommit=False):
130 """Creates a linear history in the directory of the current repository.
132 The history is fairly simple and is all acted upon the specified file.
133 Any exceptions thrown during IO operations are _not_ cought.
135 Params:
136 filename: The name of the file in which to generate the history.
137 createFile: Whether the specified file has to be created first.
138 initialCommit: Whether to create an initial commit.
139 start: Which commit to start the history at.
140 count: The size of the history.
141 finalCommit: Whether to create a final commit.
144 git = Repo(".").git
146 stop = start + count
148 if createFile:
149 addFile(filename, commit=False)
151 # Create or open the content file
152 file = open(filename, "a")
154 if initialCommit:
155 # Start out with an initial change
156 file.write("Initial change\n")
157 file.flush()
159 # Add the file and create the initial commit
160 git.commit("-a", "-m", "Initial commit")
161 bumpTime()
163 # Create a linear history
164 for i in range(start, stop):
165 file.write("Change " + str(i) + "\n")
166 file.flush()
168 git.commit("-a", "-m", "Commit " + str(i))
169 bumpTime()
171 if finalCommit:
172 # Finish it up with a final change
173 file.write("Last change\n")
174 file.flush()
176 # And a final commit
177 git.commit("-a", "-m", "Last commit")
178 bumpTime()
180 def createBranch(name, checkout=True):
181 """Creates a branch with the specified name and optionally checks it out
183 Params:
184 name: The name of the new branch.
185 checkout: Whether to perform a checkout of the branch.
188 git = Repo(".").git
190 git.branch(name)
191 bumpTime()
193 if checkout:
194 checkoutBranch(name)
196 def deleteBranch(name):
197 """Deletes a branch
199 The branch that is to be deleted should be a subset of the current branch.
201 Params:
202 name: The name of the branch to delete.
205 git = Repo(".").git
207 git.branch("-d", name)
208 bumpTime()
210 def createDir(name, changeDir=False):
211 """Creates a directory with the specified name
213 Args:
214 name: The name of the directory
217 os.mkdir(name)
219 def checkoutBranch(name):
220 """Switches to the specified branch
222 Params:
223 name: The name of the branch to be switched to.
226 git = Repo(".").git
228 git.checkout(name)
230 def checkoutRelease(release):
231 """Switches to the specified release
233 Params:
234 release: The release to check out
237 checkoutBranch('v' + str(release))
239 def mergeBranch(name):
240 """Merges the current branch with the specified branch
242 Params:
243 name: The name of the branch to merge with.
246 git = Repo(".").git
248 git.merge(name)
249 bumpTime()
251 def revertLastCommit(commitChanges=True):
252 """Reverts the last commit made
254 Params:
255 commitChanges: Whether to commit the revert.
258 git = Repo(".").git
260 options = ["--no-edit", "HEAD"]
262 if not commitChanges:
263 options.append("-n")
265 git.revert(*options)
267 if commitChanges:
268 bumpTime()
270 def tagRelease(releaseNumber, head="HEAD"):
271 """Tags a release.
273 The created tag is 'v' + releaseNumber.
275 Params:
276 releaseNumber: The number of the release.
279 git = Repo(".").git
281 git.tag('v' + str(releaseNumber), head)
283 def createRemoteBranch(name, start="HEAD"):
284 """Creates a new remote branch with the specified name
286 Args:
287 name: The plain name of the remote (no '/refs/remotes/' prefix)
288 start: The revision to branch off from
291 git = Repo(".").git
293 git.update_ref('refs/remotes/' + name, start)
294 bumpTime()
296 def checkHead(HEAD):
297 """Verifies that the HEAD is as expected.
299 Params:
300 HEAD: The expected value of HEAD.
303 git = Repo(".").git
305 result = git.rev_parse("HEAD")
307 # Eat the trailing newline
308 currentHEAD = result[:-1]
310 scriptSuccessful = (HEAD == currentHEAD)
312 if scriptSuccessful:
313 print("Done, repo created successfully")
314 return True
315 else:
316 print("Something went wrong, current HEAD doesn't match.")
317 print("Expected: '" + HEAD + "'.")
318 print("Actual: '" + currentHEAD + "'.")
319 return False
321 def createTestRepository():
322 """Creates a test repository under setupRepo.testpath
325 # Directories
326 d = "docs"
328 # Files
329 c = "content.txt"
330 n = "notes.txt"
331 t = "test.c"
332 r = os.path.join(d, "README.txt")
334 # Branches
335 m = "master"
336 mt = "maint"
337 p = "pu"
338 e = "erase"
339 j = "junio"
341 # Start afresh
342 setupRepo(testPath)
344 # Set the default author/committer
345 setDefaultInfo()
346 setDefaultTime()
348 # Create some linear history
349 createLinearHistory(c, createFile=True, initialCommit=True, finalCommit=True)
351 # Create a maintenance branch
352 createBranch(mt)
354 # Create some history there too
355 createLinearHistory(n, createFile=True, count=3)
357 # Go back to master and merge with maint
358 checkoutBranch(m)
359 mergeBranch(mt)
361 # Create a playground branch and create some history
362 # This branch will be left 'dead', e.g., unused after this
363 createBranch(p)
364 createLinearHistory(t, createFile=True, count=7, finalCommit=True)
366 # Start out with release 0
367 tagRelease(0, head="HEAD~3")
369 # Revert that commit
370 revertLastCommit()
372 # Go back to master and continue some history
373 checkoutBranch(m)
374 createLinearHistory(c, start=10, count=5)
376 # Yay, our first release!
377 tagRelease(1)
379 # Merge current master into maint
380 checkoutBranch(mt)
381 mergeBranch(m)
383 # Ouch! Brown paper bag fix there, correct it and merge into master
384 revertLastCommit(commitChanges=False)
385 createLinearHistory(c, start=42, count=1)
386 checkoutBranch(m)
387 mergeBranch(mt)
389 # Continue some work on master
390 createLinearHistory(c, start=16, count=6)
392 # Have someone else do some work on master
393 setSecondaryInfo()
394 createLinearHistory(c, start=22, count=2)
396 # Go back to the initial data
397 setDefaultInfo()
399 # Create a directory for the documentation
400 createDir(d)
402 # Create a readme and add some content to it
403 createLinearHistory(r, createFile=True, count=5, finalCommit=True)
404 tagRelease(2)
406 # Ah, but that last one was bad, we don't want it
407 revertLastCommit()
409 # Instead, continue the linear history
410 createLinearHistory(r, start=6, count=1)
411 tagRelease(3)
413 # Come to think of it, that -was- a really good change after all
414 revertLastCommit()
416 # Reinstate that final commit
417 createLinearHistory(r, count=0, finalCommit=True)
419 tagRelease(4, head="HEAD~1")
421 # Create a branch to delete a file on
422 createBranch(e)
424 # Remove a file from git
425 deleteFile(r, leaveOnDisk=True)
427 # Only to add it back later so that we don't have any dangling files
428 addFile(r, createFile=False)
430 # Create a release at this point for easy switching later
431 tagRelease(5)
433 # Switch back to master for the test suite
434 checkoutBranch(m)
436 # Create a remote branch
437 createRemoteBranch(j, start="HEAD^")
439 return 0
441 def createMetricsRepository():
442 """Creates a metrics repository in setupRepo.metricspath
445 # files
446 c = "content.txt"
447 n = "notes.txt"
448 r = "README"
450 # branches
451 m = "master"
452 mt = "maint"
453 h = "help"
455 # Create a repository to work in
456 setupRepo(metricsPath)
458 # Set the info
459 setDefaultInfo()
460 setDefaultTime()
462 # And create some history
463 createLinearHistory(c, createFile=True, count=2)
465 # Mark this commit so that we can jump back to it later
466 tagRelease(1)
468 # And create some more history
469 createLinearHistory(c, start=3, count=3)
471 # Go back to that marked commit
472 checkoutRelease(1)
474 # Now create a new branch to work on
475 createBranch(mt)
477 # Fix us up some more history
478 createLinearHistory(n, createFile=True, count=2)
480 # Mark this commit too so we can jump back
481 tagRelease(2)
483 # And make some more history
484 createLinearHistory(n, start=2, count=1)
486 # Now merge this back into master
487 checkoutBranch(m)
488 mergeBranch(mt)
490 # Go back to maint to create some more there
491 checkoutBranch(mt)
492 createLinearHistory(n, start=3, count=1)
494 # Now let's go back and fix that one commit
495 checkoutRelease(2)
496 createBranch(h)
497 createLinearHistory(r, createFile=True, count=2)
499 # Go back to maint, and merge it in
500 checkoutBranch(mt)
501 mergeBranch(h)
503 # Now that we merged that one in, kick it out
504 deleteBranch(h)
506 # Now we can create the last bit of history here
507 createLinearHistory(n, start=4, count=3)
509 # Now we can create our last bit of history on master
510 checkoutBranch(m)
511 createLinearHistory(c, start=6, count=3)
513 return 0
515 def main(args):
516 """Creates a test and a metrics repository
518 Args:
519 args: An array of arguments, when 2 in size the second
520 element should either be 'test' to just create the test
521 repo, or 'metrics' to just create the metrics
522 repository. If both should be created then the size of
523 args should be 1. The first element in the array is
524 always ignored.
526 Returns: 0 upon success, or nonzero upon failure.
529 size = len(args)
531 if size > 1 and (args[1] != "test" and args[1] != "metrics") or size > 2:
532 print "Please specify either 'test', 'metrics', or nothing to run both"
533 return 1
535 if len(args) == 1 or args[1] == "test":
536 ret = createTestRepository()
537 if ret != 0:
538 return ret
540 if len(args) == 1 or args[1] == "metrics":
541 ret = createMetricsRepository()
542 if ret != 0:
543 return ret
545 return 0
547 if __name__ == '__main__':
548 import sys
549 ret = main(sys.argv)
550 sys.exit(ret)