summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md14
-rw-r--r--SUBMITTING_PATCHES.md (renamed from SUBMITTING_PATCHES)28
-rw-r--r--project.py104
-rw-r--r--subcmds/upload.py4
4 files changed, 113 insertions, 37 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..e35f8e99
--- /dev/null
+++ b/README.md
@@ -0,0 +1,14 @@
1# repo
2
3Repo is a tool built on top of Git. Repo helps manage many Git repositories,
4does the uploads to revision control systems, and automates parts of the
5development workflow. Repo is not meant to replace Git, only to make it
6easier to work with Git. The repo command is an executable Python script
7that you can put anywhere in your path.
8
9* Homepage: https://code.google.com/p/git-repo/
10* Bug reports: https://code.google.com/p/git-repo/issues/
11* Source: https://code.google.com/p/git-repo/
12* Overview: https://source.android.com/source/developing.html
13* Docs: https://source.android.com/source/using-repo.html
14* [Submitting patches](./SUBMITTING_PATCHES.md)
diff --git a/SUBMITTING_PATCHES b/SUBMITTING_PATCHES.md
index 8656ee7d..085ae06a 100644
--- a/SUBMITTING_PATCHES
+++ b/SUBMITTING_PATCHES.md
@@ -1,4 +1,4 @@
1Short Version: 1# Short Version
2 2
3 - Make small logical changes. 3 - Make small logical changes.
4 - Provide a meaningful commit message. 4 - Provide a meaningful commit message.
@@ -8,10 +8,10 @@ Short Version:
8 - Make corrections if requested. 8 - Make corrections if requested.
9 - Verify your changes on gerrit so they can be submitted. 9 - Verify your changes on gerrit so they can be submitted.
10 10
11 git push https://gerrit-review.googlesource.com/git-repo HEAD:refs/for/master 11 `git push https://gerrit-review.googlesource.com/git-repo HEAD:refs/for/master`
12 12
13 13
14Long Version: 14# Long Version
15 15
16I wanted a file describing how to submit patches for repo, 16I wanted a file describing how to submit patches for repo,
17so I started with the one found in the core Git distribution 17so I started with the one found in the core Git distribution
@@ -19,10 +19,10 @@ so I started with the one found in the core Git distribution
19patch submission guidelines for the Linux kernel. 19patch submission guidelines for the Linux kernel.
20 20
21However there are some differences, so please review and familiarize 21However there are some differences, so please review and familiarize
22yourself with the following relevant bits: 22yourself with the following relevant bits.
23 23
24 24
25(1) Make separate commits for logically separate changes. 25## Make separate commits for logically separate changes.
26 26
27Unless your patch is really trivial, you should not be sending 27Unless your patch is really trivial, you should not be sending
28out a patch that was generated between your working tree and your 28out a patch that was generated between your working tree and your
@@ -36,14 +36,14 @@ If your description starts to get too long, that's a sign that you
36probably need to split up your commit to finer grained pieces. 36probably need to split up your commit to finer grained pieces.
37 37
38 38
39(2) Check for coding errors with pylint 39## Check for coding errors with pylint
40 40
41Run pylint on changed modules using the provided configuration: 41Run pylint on changed modules using the provided configuration:
42 42
43 pylint --rcfile=.pylintrc file.py 43 pylint --rcfile=.pylintrc file.py
44 44
45 45
46(3) Check the license 46## Check the license
47 47
48repo is licensed under the Apache License, 2.0. 48repo is licensed under the Apache License, 2.0.
49 49
@@ -59,7 +59,7 @@ your patch. It is virtually impossible to remove a patch once it
59has been applied and pushed out. 59has been applied and pushed out.
60 60
61 61
62(4) Sending your patches. 62## Sending your patches.
63 63
64Do not email your patches to anyone. 64Do not email your patches to anyone.
65 65
@@ -91,23 +91,23 @@ to get the ChangeId added.
91Push your patches over HTTPS to the review server, possibly through 91Push your patches over HTTPS to the review server, possibly through
92a remembered remote to make this easier in the future: 92a remembered remote to make this easier in the future:
93 93
94 git config remote.review.url https://gerrit-review.googlesource.com/git-repo 94 git config remote.review.url https://gerrit-review.googlesource.com/git-repo
95 git config remote.review.push HEAD:refs/for/master 95 git config remote.review.push HEAD:refs/for/master
96 96
97 git push review 97 git push review
98 98
99You will be automatically emailed a copy of your commits, and any 99You will be automatically emailed a copy of your commits, and any
100comments made by the project maintainers. 100comments made by the project maintainers.
101 101
102 102
103(5) Make changes if requested 103## Make changes if requested
104 104
105The project maintainer who reviews your changes might request changes to your 105The project maintainer who reviews your changes might request changes to your
106commit. If you make the requested changes you will need to amend your commit 106commit. If you make the requested changes you will need to amend your commit
107and push it to the review server again. 107and push it to the review server again.
108 108
109 109
110(6) Verify your changes on gerrit 110## Verify your changes on gerrit
111 111
112After you receive a Code-Review+2 from the maintainer, select the Verified 112After you receive a Code-Review+2 from the maintainer, select the Verified
113button on the gerrit page for the change. This verifies that you have tested 113button on the gerrit page for the change. This verifies that you have tested
diff --git a/project.py b/project.py
index 918ee09c..019f29d4 100644
--- a/project.py
+++ b/project.py
@@ -40,7 +40,13 @@ from trace import IsTrace, Trace
40from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M 40from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M
41 41
42from pyversion import is_python3 42from pyversion import is_python3
43if not is_python3(): 43if is_python3():
44 import urllib.parse
45else:
46 import imp
47 import urlparse
48 urllib = imp.new_module('urllib')
49 urllib.parse = urlparse
44 # pylint:disable=W0622 50 # pylint:disable=W0622
45 input = raw_input 51 input = raw_input
46 # pylint:enable=W0622 52 # pylint:enable=W0622
@@ -343,6 +349,7 @@ class RepoHook(object):
343 hook_type, 349 hook_type,
344 hooks_project, 350 hooks_project,
345 topdir, 351 topdir,
352 manifest_url,
346 abort_if_user_denies=False): 353 abort_if_user_denies=False):
347 """RepoHook constructor. 354 """RepoHook constructor.
348 355
@@ -356,11 +363,13 @@ class RepoHook(object):
356 topdir: Repo's top directory (the one containing the .repo directory). 363 topdir: Repo's top directory (the one containing the .repo directory).
357 Scripts will run with CWD as this directory. If you have a manifest, 364 Scripts will run with CWD as this directory. If you have a manifest,
358 this is manifest.topdir 365 this is manifest.topdir
366 manifest_url: The URL to the manifest git repo.
359 abort_if_user_denies: If True, we'll throw a HookError() if the user 367 abort_if_user_denies: If True, we'll throw a HookError() if the user
360 doesn't allow us to run the hook. 368 doesn't allow us to run the hook.
361 """ 369 """
362 self._hook_type = hook_type 370 self._hook_type = hook_type
363 self._hooks_project = hooks_project 371 self._hooks_project = hooks_project
372 self._manifest_url = manifest_url
364 self._topdir = topdir 373 self._topdir = topdir
365 self._abort_if_user_denies = abort_if_user_denies 374 self._abort_if_user_denies = abort_if_user_denies
366 375
@@ -409,9 +418,9 @@ class RepoHook(object):
409 def _CheckForHookApproval(self): 418 def _CheckForHookApproval(self):
410 """Check to see whether this hook has been approved. 419 """Check to see whether this hook has been approved.
411 420
412 We'll look at the hash of all of the hooks. If this matches the hash that 421 We'll accept approval of manifest URLs if they're using secure transports.
413 the user last approved, we're done. If it doesn't, we'll ask the user 422 This way the user can say they trust the manifest hoster. For insecure
414 about approval. 423 hosts, we fall back to checking the hash of the hooks repo.
415 424
416 Note that we ask permission for each individual hook even though we use 425 Note that we ask permission for each individual hook even though we use
417 the hash of all hooks when detecting changes. We'd like the user to be 426 the hash of all hooks when detecting changes. We'd like the user to be
@@ -425,44 +434,58 @@ class RepoHook(object):
425 HookError: Raised if the user doesn't approve and abort_if_user_denies 434 HookError: Raised if the user doesn't approve and abort_if_user_denies
426 was passed to the consturctor. 435 was passed to the consturctor.
427 """ 436 """
428 hooks_config = self._hooks_project.config 437 if self._ManifestUrlHasSecureScheme():
429 git_approval_key = 'repo.hooks.%s.approvedhash' % self._hook_type 438 return self._CheckForHookApprovalManifest()
439 else:
440 return self._CheckForHookApprovalHash()
441
442 def _CheckForHookApprovalHelper(self, subkey, new_val, main_prompt,
443 changed_prompt):
444 """Check for approval for a particular attribute and hook.
445
446 Args:
447 subkey: The git config key under [repo.hooks.<hook_type>] to store the
448 last approved string.
449 new_val: The new value to compare against the last approved one.
450 main_prompt: Message to display to the user to ask for approval.
451 changed_prompt: Message explaining why we're re-asking for approval.
452
453 Returns:
454 True if this hook is approved to run; False otherwise.
430 455
431 # Get the last hash that the user approved for this hook; may be None. 456 Raises:
432 old_hash = hooks_config.GetString(git_approval_key) 457 HookError: Raised if the user doesn't approve and abort_if_user_denies
458 was passed to the consturctor.
459 """
460 hooks_config = self._hooks_project.config
461 git_approval_key = 'repo.hooks.%s.%s' % (self._hook_type, subkey)
433 462
434 # Get the current hash so we can tell if scripts changed since approval. 463 # Get the last value that the user approved for this hook; may be None.
435 new_hash = self._GetHash() 464 old_val = hooks_config.GetString(git_approval_key)
436 465
437 if old_hash is not None: 466 if old_val is not None:
438 # User previously approved hook and asked not to be prompted again. 467 # User previously approved hook and asked not to be prompted again.
439 if new_hash == old_hash: 468 if new_val == old_val:
440 # Approval matched. We're done. 469 # Approval matched. We're done.
441 return True 470 return True
442 else: 471 else:
443 # Give the user a reason why we're prompting, since they last told 472 # Give the user a reason why we're prompting, since they last told
444 # us to "never ask again". 473 # us to "never ask again".
445 prompt = 'WARNING: Scripts have changed since %s was allowed.\n\n' % ( 474 prompt = 'WARNING: %s\n\n' % (changed_prompt,)
446 self._hook_type)
447 else: 475 else:
448 prompt = '' 476 prompt = ''
449 477
450 # Prompt the user if we're not on a tty; on a tty we'll assume "no". 478 # Prompt the user if we're not on a tty; on a tty we'll assume "no".
451 if sys.stdout.isatty(): 479 if sys.stdout.isatty():
452 prompt += ('Repo %s run the script:\n' 480 prompt += main_prompt + ' (yes/always/NO)? '
453 ' %s\n'
454 '\n'
455 'Do you want to allow this script to run '
456 '(yes/yes-never-ask-again/NO)? ') % (self._GetMustVerb(),
457 self._script_fullpath)
458 response = input(prompt).lower() 481 response = input(prompt).lower()
459 print() 482 print()
460 483
461 # User is doing a one-time approval. 484 # User is doing a one-time approval.
462 if response in ('y', 'yes'): 485 if response in ('y', 'yes'):
463 return True 486 return True
464 elif response == 'yes-never-ask-again': 487 elif response == 'always':
465 hooks_config.SetString(git_approval_key, new_hash) 488 hooks_config.SetString(git_approval_key, new_val)
466 return True 489 return True
467 490
468 # For anything else, we'll assume no approval. 491 # For anything else, we'll assume no approval.
@@ -472,6 +495,42 @@ class RepoHook(object):
472 495
473 return False 496 return False
474 497
498 def _ManifestUrlHasSecureScheme(self):
499 """Check if the URI for the manifest is a secure transport."""
500 secure_schemes = ('file', 'https', 'ssh', 'persistent-https', 'sso', 'rpc')
501 parse_results = urllib.parse.urlparse(self._manifest_url)
502 return parse_results.scheme in secure_schemes
503
504 def _CheckForHookApprovalManifest(self):
505 """Check whether the user has approved this manifest host.
506
507 Returns:
508 True if this hook is approved to run; False otherwise.
509 """
510 return self._CheckForHookApprovalHelper(
511 'approvedmanifest',
512 self._manifest_url,
513 'Run hook scripts from %s' % (self._manifest_url,),
514 'Manifest URL has changed since %s was allowed.' % (self._hook_type,))
515
516 def _CheckForHookApprovalHash(self):
517 """Check whether the user has approved the hooks repo.
518
519 Returns:
520 True if this hook is approved to run; False otherwise.
521 """
522 prompt = ('Repo %s run the script:\n'
523 ' %s\n'
524 '\n'
525 'Do you want to allow this script to run '
526 '(yes/yes-never-ask-again/NO)? ') % (self._GetMustVerb(),
527 self._script_fullpath)
528 return self._CheckForHookApprovalHelper(
529 'approvedhash',
530 self._GetHash(),
531 prompt,
532 'Scripts have changed since %s was allowed.' % (self._hook_type,))
533
475 def _ExecuteHook(self, **kwargs): 534 def _ExecuteHook(self, **kwargs):
476 """Actually execute the given hook. 535 """Actually execute the given hook.
477 536
@@ -2235,6 +2294,7 @@ class Project(object):
2235 for key in ['user.name', 'user.email']: 2294 for key in ['user.name', 'user.email']:
2236 if m.Has(key, include_defaults=False): 2295 if m.Has(key, include_defaults=False):
2237 self.config.SetString(key, m.GetString(key)) 2296 self.config.SetString(key, m.GetString(key))
2297 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
2238 if self.manifest.IsMirror: 2298 if self.manifest.IsMirror:
2239 self.config.SetString('core.bare', 'true') 2299 self.config.SetString('core.bare', 'true')
2240 else: 2300 else:
diff --git a/subcmds/upload.py b/subcmds/upload.py
index 674fc17d..4b05f1e8 100644
--- a/subcmds/upload.py
+++ b/subcmds/upload.py
@@ -456,7 +456,9 @@ Gerrit Code Review: http://code.google.com/p/gerrit/
456 456
457 if pending and (not opt.bypass_hooks): 457 if pending and (not opt.bypass_hooks):
458 hook = RepoHook('pre-upload', self.manifest.repo_hooks_project, 458 hook = RepoHook('pre-upload', self.manifest.repo_hooks_project,
459 self.manifest.topdir, abort_if_user_denies=True) 459 self.manifest.topdir,
460 self.manifest.manifestProject.GetRemote('origin').url,
461 abort_if_user_denies=True)
460 pending_proj_names = [project.name for (project, avail) in pending] 462 pending_proj_names = [project.name for (project, avail) in pending]
461 pending_worktrees = [project.worktree for (project, avail) in pending] 463 pending_worktrees = [project.worktree for (project, avail) in pending]
462 try: 464 try: