summaryrefslogtreecommitdiffstats
path: root/git_superproject.py
diff options
context:
space:
mode:
authorRaman Tenneti <rtenneti@google.com>2021-01-14 19:17:50 -0800
committerRaman Tenneti <rtenneti@google.com>2021-01-21 19:41:52 +0000
commit6a872c9daeec58dda61786e6f65591330709f6ad (patch)
treedcd22dcc29c91de8a097cf7075400bb463ef40e0 /git_superproject.py
parentdf6c506a8af47c19e853c96ba15e4446f93304b2 (diff)
downloadgit-repo-6a872c9daeec58dda61786e6f65591330709f6ad.tar.gz
sync: Added --use-superproject option and support for superproject.v2.12
Added "--use-superporject" option to sync.py to fetch project SHAs from superproject. If there are any missing projects in superprojects, it prints the missing entries and exits. If there are no missing entries, it will use SHAs from superproject to fetch the projects from git. Tested the code with the following commands. $ ./run_tests tests/test_manifest_xml.py $ ./run_tests -v tests/test_git_superproject.py $ ./run_tests -v Tested the sync code by copying all the repo changes into my Android AOSP checkout and adding <superporject> tag to default.xml. With local modification to the code to print the status, .../WORKING_DIRECTORY$ repo sync --use-superproject repo: executing 'git clone' url: sso://android/platform/superproject repo: executing 'git ls-tree' Success: [] Bug: https://crbug.com/gerrit/13709 Tested-by: Raman Tenneti <rtenneti@google.com> Change-Id: Id18665992428dd684c04b0e0b3a52f46316873a0 Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/293822 Reviewed-by: Mike Frysinger <vapier@google.com>
Diffstat (limited to 'git_superproject.py')
-rw-r--r--git_superproject.py149
1 files changed, 149 insertions, 0 deletions
diff --git a/git_superproject.py b/git_superproject.py
new file mode 100644
index 00000000..3e87e929
--- /dev/null
+++ b/git_superproject.py
@@ -0,0 +1,149 @@
1# Copyright (C) 2021 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Provide functionality to get all projects and their SHAs from Superproject.
16
17For more information on superproject, check out:
18https://en.wikibooks.org/wiki/Git/Submodules_and_Superprojects
19
20Examples:
21 superproject = Superproject()
22 project_shas = superproject.GetAllProjectsSHAs()
23"""
24
25import os
26import sys
27
28from error import GitError
29from git_command import GitCommand
30import platform_utils
31
32
33class Superproject(object):
34 """Get SHAs from superproject.
35
36 It does a 'git clone' of superproject and 'git ls-tree' to get list of SHAs for all projects.
37 It contains project_shas which is a dictionary with project/sha entries.
38 """
39 def __init__(self, repodir, superproject_dir='exp-superproject'):
40 """Initializes superproject.
41
42 Args:
43 repodir: Path to the .repo/ dir for holding all internal checkout state.
44 superproject_dir: Relative path under |repodir| to checkout superproject.
45 """
46 self._project_shas = None
47 self._repodir = os.path.abspath(repodir)
48 self._superproject_dir = superproject_dir
49 self._superproject_path = os.path.join(self._repodir, superproject_dir)
50
51 @property
52 def project_shas(self):
53 """Returns a dictionary of projects and their SHAs."""
54 return self._project_shas
55
56 def _Clone(self, url, branch=None):
57 """Do a 'git clone' for the given url and branch.
58
59 Args:
60 url: superproject's url to be passed to git clone.
61 branch: the branchname to be passed as argument to git clone.
62
63 Returns:
64 True if 'git clone <url> <branch>' is successful, or False.
65 """
66 cmd = ['clone', url, '--depth', '1']
67 if branch:
68 cmd += ['--branch', branch]
69 p = GitCommand(None,
70 cmd,
71 cwd=self._superproject_path,
72 capture_stdout=True,
73 capture_stderr=True)
74 retval = p.Wait()
75 if retval:
76 # `git clone` is documented to produce an exit status of `128` if
77 # the requested url or branch are not present in the configuration.
78 print('repo: error: git clone call failed with return code: %r, stderr: %r' %
79 (retval, p.stderr), file=sys.stderr)
80 return False
81 return True
82
83 def _LsTree(self):
84 """Returns the data from 'git ls-tree -r HEAD'.
85
86 Works only in git repositories.
87
88 Returns:
89 data: data returned from 'git ls-tree -r HEAD' instead of None.
90 """
91 git_dir = os.path.join(self._superproject_path, 'superproject')
92 if not os.path.exists(git_dir):
93 raise GitError('git ls-tree. Missing drectory: %s' % git_dir)
94 data = None
95 cmd = ['ls-tree', '-z', '-r', 'HEAD']
96 p = GitCommand(None,
97 cmd,
98 cwd=git_dir,
99 capture_stdout=True,
100 capture_stderr=True)
101 retval = p.Wait()
102 if retval == 0:
103 data = p.stdout
104 else:
105 # `git clone` is documented to produce an exit status of `128` if
106 # the requested url or branch are not present in the configuration.
107 print('repo: error: git ls-tree call failed with return code: %r, stderr: %r' % (
108 retval, p.stderr), file=sys.stderr)
109 return data
110
111 def GetAllProjectsSHAs(self, url, branch=None):
112 """Get SHAs for all projects from superproject and save them in _project_shas.
113
114 Args:
115 url: superproject's url to be passed to git clone.
116 branch: the branchname to be passed as argument to git clone.
117
118 Returns:
119 A dictionary with the projects/SHAs instead of None.
120 """
121 if not url:
122 raise ValueError('url argument is not supplied.')
123 if os.path.exists(self._superproject_path):
124 platform_utils.rmtree(self._superproject_path)
125 os.mkdir(self._superproject_path)
126
127 # TODO(rtenneti): we shouldn't be cloning the repo from scratch every time.
128 if not self._Clone(url, branch):
129 raise GitError('git clone failed for url: %s' % url)
130
131 data = self._LsTree()
132 if not data:
133 raise GitError('git ls-tree failed for url: %s' % url)
134
135 # Parse lines like the following to select lines starting with '160000' and
136 # build a dictionary with project path (last element) and its SHA (3rd element).
137 #
138 # 160000 commit 2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea\tart\x00
139 # 120000 blob acc2cbdf438f9d2141f0ae424cec1d8fc4b5d97f\tbootstrap.bash\x00
140 shas = {}
141 for line in data.split('\x00'):
142 ls_data = line.split(None, 3)
143 if not ls_data:
144 break
145 if ls_data[0] == '160000':
146 shas[ls_data[3]] = ls_data[2]
147
148 self._project_shas = shas
149 return shas