Advertisement
Guest User

Untitled

a guest
Sep 21st, 2019
124
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 3.33 KB | None | 0 0
  1. #!/usr/bin/env python3
  2.  
  3. """
  4. Tool to do git commands in parallel across five repos
  5. that ignore submodule tracking and instead rely on
  6. having branches with the same name.
  7.  
  8. Save this somewhere on your PATH as metagit and
  9. chmod +x it. Make sure you have Python 3.6+. And:
  10.  
  11. $ pip3 install click python-dateutil gitpython
  12.  
  13. Then you can do this:
  14.  
  15. $ metagit asof 20190906T12 --checkout
  16.  
  17. ... which will find and checkout the last commit on
  18. your current branch before (local) 2019-09-06T12:00:00
  19. in each repo. Unless you're really unlucky (e.g.,
  20. someone committed to XLE at 11:59 and then made the
  21. corresponding commit to cocos3d at 12:03), this should
  22. give you a buildable state.
  23.  
  24. Leave off the --checkout to just list the commits
  25. without automatically checking them out; leave off the
  26. timestamp to go to HEAD everywhere.
  27.  
  28. This uses the normal git checkout logic, so if one of
  29. your repos isn't in a clean state, it won't get blown
  30. away, you'll get the usual error telling you to stash
  31. or commit your changes first, but it will be uglier.
  32. And you'll have 0-4 repos that checked out beforehand
  33. successfully and 0-4 that didn't get done, so you'll
  34. probably want to go back to HEAD on all of them.
  35.  
  36.  
  37. If your current branch isn't drg-metal on any repo,
  38. you probably won't be happy.
  39.  
  40. It would be useful to have commands to pull all branches,
  41. to stash/pull/apply, to stash/asof/apply, and to log
  42. (with the entries from all five repos interleaved
  43. as appropriate), but I haven't written them.
  44. """
  45.  
  46. import datetime
  47. import os
  48.  
  49. import click
  50. import dateutil
  51. import dateutil.parser
  52. import git
  53.  
  54. projects = (
  55. 'dragons3d',
  56. 'dragons3d/Externals/cocos3d',
  57. 'dragons3d/Externals/cocos3d/XLE',
  58. 'dragons3d/Externals/pgengine',
  59. 'dragons3d/Externals/pgengine/pgcommon/Externals/cocos2d2x',
  60. )
  61.  
  62. def findproj(cwd=None):
  63. if cwd is None:
  64. cwd = os.getcwd()
  65. if cwd == '/':
  66. return '/Users/andrewbarnert/src/', 'dragons3d'
  67. for project in projects:
  68. if cwd.endswith(project):
  69. top = cwd[:-len(project)]
  70. return top, project
  71. return findproj(os.path.dirname(cwd))
  72.  
  73. def repos(cwd=None):
  74. top, project = findproj(cwd)
  75. repo = git.Repo(os.path.join(top, project))
  76. others = [git.Repo(os.path.join(top, proj))
  77. for proj in projects if proj != project]
  78. return [repo] + others
  79.  
  80. def commit_as_of(repo, timestamp):
  81. for commit in repo.iter_commits():
  82. if commit.committed_datetime < timestamp:
  83. return commit
  84.  
  85. @click.group()
  86. def cli():
  87. pass
  88.  
  89. @cli.command()
  90. @click.option("--checkout", is_flag=True)
  91. @click.argument('timestamp', default=None, required=False)
  92. def asof(checkout, timestamp):
  93. if timestamp:
  94. timestamp = dateutil.parser.parse(timestamp).astimezone()
  95. else:
  96. timestamp = datetime.datetime.now(datetime.timezone.utc)
  97. for repo in repos():
  98. dirname = os.path.basename(repo.working_dir)
  99. commit = commit_as_of(repo, timestamp)
  100. print(f'{dirname}: {commit} at {commit.committed_datetime}')
  101. if checkout:
  102. # TODO: Should we handle errors here and continue to the
  103. # other subtrees? (The reason to use git checkout rather
  104. # than just setting repo.head.reference is so that we _will_
  105. # get errors, instead of blowing away local copies.)
  106. repo.git.checkout(commit)
  107.  
  108. if __name__ == '__main__':
  109. cli()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement