blob: e53d0338bb4e65fc48f88c94a4830eda6fe39182 [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001# Copyright (C) 2008 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
15import os
16import sys
17
18from color import Coloring
Mike Frysinger64477332023-08-21 21:20:32 -040019from command import InteractiveCommand
20from command import MirrorSafeCommand
Jason Changf9aacd42023-08-03 14:38:00 -070021from error import RepoUnhandledExceptionError
Mike Frysinger64477332023-08-21 21:20:32 -040022from error import UpdateManifestError
23from git_command import git_require
24from git_command import MIN_GIT_VERSION_HARD
25from git_command import MIN_GIT_VERSION_SOFT
Aravind Vasudevanc993c502023-09-14 08:46:44 +000026from repo_logging import RepoLogger
Mike Frysinger64477332023-08-21 21:20:32 -040027from wrapper import Wrapper
28
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070029
Aravind Vasudevanc993c502023-09-14 08:46:44 +000030logger = RepoLogger(__file__)
31
Jason Chang17833322023-05-23 13:06:55 -070032_REPO_ALLOW_SHALLOW = os.environ.get("REPO_ALLOW_SHALLOW")
33
David Pursehouse819827a2020-02-12 15:20:19 +090034
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080035class Init(InteractiveCommand, MirrorSafeCommand):
Gavin Makea2e3302023-03-11 06:46:20 +000036 COMMON = True
37 MULTI_MANIFEST_SUPPORT = True
38 helpSummary = "Initialize a repo client checkout in the current directory"
39 helpUsage = """
Mike Frysinger401c6f02021-02-18 15:20:15 -050040%prog [options] [manifest url]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070041"""
Gavin Makea2e3302023-03-11 06:46:20 +000042 helpDescription = """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070043The '%prog' command is run once to install and initialize repo.
44The latest repo source code and manifest collection is downloaded
45from the server and is installed in the .repo/ directory in the
46current working directory.
47
Mike Frysinger401c6f02021-02-18 15:20:15 -050048When creating a new checkout, the manifest URL is the only required setting.
49It may be specified using the --manifest-url option, or as the first optional
50argument.
51
Shawn O. Pearce77bb4af2009-04-18 11:33:32 -070052The optional -b argument can be used to select the manifest branch
Mike Frysinger50a81de2020-09-06 15:51:21 -040053to checkout and use. If no branch is specified, the remote's default
Mike Frysinger23882b32021-02-23 15:43:07 -050054branch is used. This is equivalent to using -b HEAD.
Shawn O. Pearce77bb4af2009-04-18 11:33:32 -070055
56The optional -m argument can be used to specify an alternate manifest
57to be used. If no manifest is specified, the manifest default.xml
58will be used.
59
Jack Neusc474c9c2021-07-26 23:08:54 +000060If the --standalone-manifest argument is set, the manifest will be downloaded
61directly from the specified --manifest-url as a static file (rather than
62setting up a manifest git checkout). With --standalone-manifest, the manifest
63will be fully static and will not be re-downloaded during subsesquent
64`repo init` and `repo sync` calls.
65
Shawn O. Pearce88443382010-10-08 10:02:09 +020066The --reference option can be used to point to a directory that
67has the content of a --mirror sync. This will make the working
68directory use as much data as possible from the local reference
69directory when fetching from the server. This will make the sync
70go a lot faster by reducing data traffic on the network.
71
Nikolai Merinov09f0abb2018-10-19 15:07:05 +050072The --dissociate option can be used to borrow the objects from
73the directory specified with the --reference option only to reduce
74network transfer, and stop borrowing from them after a first clone
75is made by making necessary local copies of borrowed objects.
76
Hu xiuyun9711a982015-12-11 11:16:41 +080077The --no-clone-bundle option disables any attempt to use
78$URL/clone.bundle to bootstrap a new Git repository from a
79resumeable bundle file on a content delivery network. This
80may be necessary if there are problems with the local Python
81HTTP client or proxy configuration, but the Git binary works.
Shawn O. Pearce88443382010-10-08 10:02:09 +020082
Mike Frysingerb8f7bb02018-10-10 01:05:11 -040083# Switching Manifest Branches
Shawn O. Pearce77bb4af2009-04-18 11:33:32 -070084
85To switch to another manifest branch, `repo init -b otherbranch`
86may be used in an existing client. However, as this only updates the
87manifest, a subsequent `repo sync` (or `repo sync -d`) is necessary
88to update the working directory files.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070089"""
90
Gavin Makea2e3302023-03-11 06:46:20 +000091 def _CommonOptions(self, p):
92 """Disable due to re-use of Wrapper()."""
Mike Frysinger9180a072021-04-13 14:57:40 -040093
Jason Chang8914b1f2023-05-26 12:44:50 -070094 def _Options(self, p):
95 Wrapper().InitParser(p)
Gavin Makea2e3302023-03-11 06:46:20 +000096 m = p.add_option_group("Multi-manifest")
97 m.add_option(
98 "--outer-manifest",
99 action="store_true",
100 default=True,
101 help="operate starting at the outermost manifest",
102 )
103 m.add_option(
104 "--no-outer-manifest",
105 dest="outer_manifest",
106 action="store_false",
107 help="do not operate on outer manifests",
108 )
109 m.add_option(
110 "--this-manifest-only",
111 action="store_true",
112 default=None,
113 help="only operate on this (sub)manifest",
114 )
115 m.add_option(
116 "--no-this-manifest-only",
117 "--all-manifests",
118 dest="this_manifest_only",
119 action="store_false",
120 help="operate on this manifest and its submanifests",
121 )
Victor Boivie841be342011-04-05 11:31:10 +0200122
Gavin Makea2e3302023-03-11 06:46:20 +0000123 def _RegisteredEnvironmentOptions(self):
124 return {
125 "REPO_MANIFEST_URL": "manifest_url",
126 "REPO_MIRROR_LOCATION": "reference",
127 }
David Pursehouse3f5ea0b2012-11-17 03:13:09 +0900128
Gavin Makea2e3302023-03-11 06:46:20 +0000129 def _SyncManifest(self, opt):
130 """Call manifestProject.Sync with arguments from opt.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700131
Gavin Makea2e3302023-03-11 06:46:20 +0000132 Args:
133 opt: options from optparse.
134 """
135 # Normally this value is set when instantiating the project, but the
136 # manifest project is special and is created when instantiating the
137 # manifest which happens before we parse options.
138 self.manifest.manifestProject.clone_depth = opt.manifest_depth
Jason Chang17833322023-05-23 13:06:55 -0700139 clone_filter_for_depth = (
140 "blob:none" if (_REPO_ALLOW_SHALLOW == "0") else None
141 )
Gavin Makea2e3302023-03-11 06:46:20 +0000142 if not self.manifest.manifestProject.Sync(
143 manifest_url=opt.manifest_url,
144 manifest_branch=opt.manifest_branch,
145 standalone_manifest=opt.standalone_manifest,
146 groups=opt.groups,
147 platform=opt.platform,
148 mirror=opt.mirror,
149 dissociate=opt.dissociate,
150 reference=opt.reference,
151 worktree=opt.worktree,
152 submodules=opt.submodules,
153 archive=opt.archive,
154 partial_clone=opt.partial_clone,
155 clone_filter=opt.clone_filter,
156 partial_clone_exclude=opt.partial_clone_exclude,
Jason Chang17833322023-05-23 13:06:55 -0700157 clone_filter_for_depth=clone_filter_for_depth,
Gavin Makea2e3302023-03-11 06:46:20 +0000158 clone_bundle=opt.clone_bundle,
159 git_lfs=opt.git_lfs,
160 use_superproject=opt.use_superproject,
161 verbose=opt.verbose,
162 current_branch_only=opt.current_branch_only,
163 tags=opt.tags,
164 depth=opt.depth,
165 git_event_log=self.git_event_log,
166 manifest_name=opt.manifest_name,
167 ):
Jason Changf9aacd42023-08-03 14:38:00 -0700168 manifest_name = opt.manifest_name
169 raise UpdateManifestError(
170 f"Unable to sync manifest {manifest_name}"
171 )
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700172
Gavin Makea2e3302023-03-11 06:46:20 +0000173 def _Prompt(self, prompt, value):
174 print("%-10s [%s]: " % (prompt, value), end="", flush=True)
175 a = sys.stdin.readline().strip()
176 if a == "":
177 return value
178 return a
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700179
Gavin Makea2e3302023-03-11 06:46:20 +0000180 def _ShouldConfigureUser(self, opt, existing_checkout):
181 gc = self.client.globalConfig
182 mp = self.manifest.manifestProject
Victor Boivie841be342011-04-05 11:31:10 +0200183
Gavin Makea2e3302023-03-11 06:46:20 +0000184 # If we don't have local settings, get from global.
185 if not mp.config.Has("user.name") or not mp.config.Has("user.email"):
186 if not gc.Has("user.name") or not gc.Has("user.email"):
187 return True
Victor Boivie841be342011-04-05 11:31:10 +0200188
Gavin Makea2e3302023-03-11 06:46:20 +0000189 mp.config.SetString("user.name", gc.GetString("user.name"))
190 mp.config.SetString("user.email", gc.GetString("user.email"))
Victor Boivie841be342011-04-05 11:31:10 +0200191
Gavin Makea2e3302023-03-11 06:46:20 +0000192 if not opt.quiet and not existing_checkout or opt.verbose:
193 print()
194 print(
195 "Your identity is: %s <%s>"
196 % (
197 mp.config.GetString("user.name"),
198 mp.config.GetString("user.email"),
199 )
200 )
201 print(
202 "If you want to change this, please re-run 'repo init' with "
203 "--config-name"
204 )
205 return False
Victor Boivie841be342011-04-05 11:31:10 +0200206
Gavin Makea2e3302023-03-11 06:46:20 +0000207 def _ConfigureUser(self, opt):
208 mp = self.manifest.manifestProject
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700209
Gavin Makea2e3302023-03-11 06:46:20 +0000210 while True:
211 if not opt.quiet:
212 print()
213 name = self._Prompt("Your Name", mp.UserName)
214 email = self._Prompt("Your Email", mp.UserEmail)
215
216 if not opt.quiet:
217 print()
Jason R. Coombsb32ccbb2023-09-29 11:04:49 -0400218 print(f"Your identity is: {name} <{email}>")
Gavin Makea2e3302023-03-11 06:46:20 +0000219 print("is this correct [y/N]? ", end="", flush=True)
220 a = sys.stdin.readline().strip().lower()
221 if a in ("yes", "y", "t", "true"):
222 break
223
224 if name != mp.UserName:
225 mp.config.SetString("user.name", name)
226 if email != mp.UserEmail:
227 mp.config.SetString("user.email", email)
228
229 def _HasColorSet(self, gc):
230 for n in ["ui", "diff", "status"]:
231 if gc.Has("color.%s" % n):
232 return True
233 return False
234
235 def _ConfigureColor(self):
236 gc = self.client.globalConfig
237 if self._HasColorSet(gc):
238 return
239
240 class _Test(Coloring):
241 def __init__(self):
242 Coloring.__init__(self, gc, "test color display")
243 self._on = True
244
245 out = _Test()
246
Mike Frysinger0b888912020-02-21 22:48:40 -0500247 print()
Gavin Makea2e3302023-03-11 06:46:20 +0000248 print("Testing colorized output (for 'repo diff', 'repo status'):")
Shawn O. Pearce37dbf2b2009-07-02 10:53:04 -0700249
Gavin Makea2e3302023-03-11 06:46:20 +0000250 for c in ["black", "red", "green", "yellow", "blue", "magenta", "cyan"]:
251 out.write(" ")
252 out.printer(fg=c)(" %-6s ", c)
253 out.write(" ")
254 out.printer(fg="white", bg="black")(" %s " % "white")
255 out.nl()
256
257 for c in ["bold", "dim", "ul", "reverse"]:
258 out.write(" ")
259 out.printer(fg="black", attr=c)(" %-6s ", c)
260 out.nl()
261
262 print(
263 "Enable color display in this user account (y/N)? ",
264 end="",
265 flush=True,
266 )
267 a = sys.stdin.readline().strip().lower()
268 if a in ("y", "yes", "t", "true", "on"):
269 gc.SetString("color.ui", "auto")
270
271 def _DisplayResult(self):
272 if self.manifest.IsMirror:
273 init_type = "mirror "
274 else:
275 init_type = ""
276
Mike Frysinger0b888912020-02-21 22:48:40 -0500277 print()
Gavin Makea2e3302023-03-11 06:46:20 +0000278 print(
279 "repo %shas been initialized in %s"
280 % (init_type, self.manifest.topdir)
281 )
Shawn O. Pearce37dbf2b2009-07-02 10:53:04 -0700282
Gavin Makea2e3302023-03-11 06:46:20 +0000283 current_dir = os.getcwd()
284 if current_dir != self.manifest.topdir:
285 print(
286 "If this is not the directory in which you want to initialize "
287 "repo, please run:"
288 )
289 print(" rm -r %s" % os.path.join(self.manifest.topdir, ".repo"))
290 print("and try again.")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700291
Gavin Makea2e3302023-03-11 06:46:20 +0000292 def ValidateOptions(self, opt, args):
293 if opt.reference:
294 opt.reference = os.path.expanduser(opt.reference)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700295
Gavin Makea2e3302023-03-11 06:46:20 +0000296 # Check this here, else manifest will be tagged "not new" and init won't
297 # be possible anymore without removing the .repo/manifests directory.
298 if opt.mirror:
299 if opt.archive:
300 self.OptionParser.error(
301 "--mirror and --archive cannot be used " "together."
302 )
303 if opt.use_superproject is not None:
304 self.OptionParser.error(
305 "--mirror and --use-superproject cannot be "
306 "used together."
307 )
308 if opt.archive and opt.use_superproject is not None:
309 self.OptionParser.error(
310 "--archive and --use-superproject cannot be used " "together."
311 )
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700312
Gavin Makea2e3302023-03-11 06:46:20 +0000313 if opt.standalone_manifest and (
314 opt.manifest_branch or opt.manifest_name != "default.xml"
315 ):
316 self.OptionParser.error(
317 "--manifest-branch and --manifest-name cannot"
318 " be used with --standalone-manifest."
319 )
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700320
Gavin Makea2e3302023-03-11 06:46:20 +0000321 if args:
322 if opt.manifest_url:
323 self.OptionParser.error(
324 "--manifest-url option and URL argument both specified: "
325 "only use one to select the manifest URL."
326 )
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700327
Gavin Makea2e3302023-03-11 06:46:20 +0000328 opt.manifest_url = args.pop(0)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700329
Gavin Makea2e3302023-03-11 06:46:20 +0000330 if args:
331 self.OptionParser.error("too many arguments to init")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700332
Gavin Makea2e3302023-03-11 06:46:20 +0000333 def Execute(self, opt, args):
334 git_require(MIN_GIT_VERSION_HARD, fail=True)
335 if not git_require(MIN_GIT_VERSION_SOFT):
Aravind Vasudevanc993c502023-09-14 08:46:44 +0000336 logger.warning(
337 "repo: warning: git-%s+ will soon be required; "
338 "please upgrade your version of git to maintain "
339 "support.",
340 ".".join(str(x) for x in MIN_GIT_VERSION_SOFT),
Gavin Makea2e3302023-03-11 06:46:20 +0000341 )
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700342
Gavin Makea2e3302023-03-11 06:46:20 +0000343 rp = self.manifest.repoProject
Yang Zhenhui75cc3532012-10-23 15:41:54 +0800344
Gavin Makea2e3302023-03-11 06:46:20 +0000345 # Handle new --repo-url requests.
346 if opt.repo_url:
347 remote = rp.GetRemote("origin")
348 remote.url = opt.repo_url
349 remote.Save()
Yang Zhenhui75cc3532012-10-23 15:41:54 +0800350
Gavin Makea2e3302023-03-11 06:46:20 +0000351 # Handle new --repo-rev requests.
352 if opt.repo_rev:
353 wrapper = Wrapper()
354 try:
355 remote_ref, rev = wrapper.check_repo_rev(
Jason R. Coombs560a7972023-11-01 10:35:30 -0400356 rp.worktree,
Gavin Makea2e3302023-03-11 06:46:20 +0000357 opt.repo_rev,
358 repo_verify=opt.repo_verify,
359 quiet=opt.quiet,
360 )
Jason Changf9aacd42023-08-03 14:38:00 -0700361 except wrapper.CloneFailure as e:
Josip Sokcevic131fc962023-05-12 17:00:46 -0700362 err_msg = "fatal: double check your --repo-rev setting."
Aravind Vasudevanc993c502023-09-14 08:46:44 +0000363 logger.error(err_msg)
Josip Sokcevic131fc962023-05-12 17:00:46 -0700364 self.git_event_log.ErrorEvent(err_msg)
Jason Changf9aacd42023-08-03 14:38:00 -0700365 raise RepoUnhandledExceptionError(e)
366
Gavin Makea2e3302023-03-11 06:46:20 +0000367 branch = rp.GetBranch("default")
368 branch.merge = remote_ref
369 rp.work_git.reset("--hard", rev)
370 branch.Save()
Yang Zhenhui75cc3532012-10-23 15:41:54 +0800371
Gavin Makea2e3302023-03-11 06:46:20 +0000372 if opt.worktree:
373 # Older versions of git supported worktree, but had dangerous gc
374 # bugs.
375 git_require((2, 15, 0), fail=True, msg="git gc worktree corruption")
Victor Boivie297e7c62012-10-05 14:50:05 +0200376
Gavin Makea2e3302023-03-11 06:46:20 +0000377 # Provide a short notice that we're reinitializing an existing checkout.
378 # Sometimes developers might not realize that they're in one, or that
379 # repo doesn't do nested checkouts.
380 existing_checkout = self.manifest.manifestProject.Exists
381 if not opt.quiet and existing_checkout:
382 print(
383 "repo: reusing existing repo client checkout in",
384 self.manifest.topdir,
385 )
Mike Frysingerae6cb082019-08-27 01:10:59 -0400386
Gavin Makea2e3302023-03-11 06:46:20 +0000387 self._SyncManifest(opt)
Jack Neusc474c9c2021-07-26 23:08:54 +0000388
Gavin Makea2e3302023-03-11 06:46:20 +0000389 if os.isatty(0) and os.isatty(1) and not self.manifest.IsMirror:
390 if opt.config_name or self._ShouldConfigureUser(
391 opt, existing_checkout
392 ):
393 self._ConfigureUser(opt)
394 self._ConfigureColor()
Mike Frysinger401c6f02021-02-18 15:20:15 -0500395
Gavin Makea2e3302023-03-11 06:46:20 +0000396 if not opt.quiet:
397 self._DisplayResult()