Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package git-repo for openSUSE:Factory checked in at 2025-11-13 17:26:37 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/git-repo (Old) and /work/SRC/openSUSE:Factory/.git-repo.new.2061 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "git-repo" Thu Nov 13 17:26:37 2025 rev:12 rq:1317427 version:2.59 Changes: -------- --- /work/SRC/openSUSE:Factory/git-repo/git-repo.changes 2025-10-07 18:30:20.458459700 +0200 +++ /work/SRC/openSUSE:Factory/.git-repo.new.2061/git-repo.changes 2025-11-13 17:28:49.290807492 +0100 @@ -1,0 +2,15 @@ +Wed Nov 05 18:38:43 UTC 2025 - BenoƮt Monin <[email protected]> + +- Update to version 2.59: + * sync: fix saving of fetch times and local state + * run_tests: log each command run + * sync: Use 'git rebase' during 'repo sync --rebase' + * Fix submodule initialization in interleaved sync mode + * Follow up "Fix shallow clones when upstream attribute is present" + * forall: fix crash with no command + * run_tests: add file header checker for licensing blocks + * man: regen after sync updates + * standardize file header wrt licensing + * CONTRIBUTING: rename doc per Google OSS policies + +------------------------------------------------------------------- Old: ---- git-repo-2.58.tar.xz New: ---- git-repo-2.59.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ git-repo.spec ++++++ --- /var/tmp/diff_new_pack.zjke7k/_old 2025-11-13 17:28:50.042839412 +0100 +++ /var/tmp/diff_new_pack.zjke7k/_new 2025-11-13 17:28:50.046839581 +0100 @@ -17,7 +17,7 @@ Name: git-repo -Version: 2.58 +Version: 2.59 Release: 0 Summary: The Multiple Git Repository Tool License: Apache-2.0 ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.zjke7k/_old 2025-11-13 17:28:50.150843995 +0100 +++ /var/tmp/diff_new_pack.zjke7k/_new 2025-11-13 17:28:50.166844675 +0100 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://gerrit.googlesource.com/git-repo</param> - <param name="changesrevision">38d2fe11b9df521727fcca23c9dac086ce8378d3</param></service></servicedata> + <param name="changesrevision">1afe96a7e997ce7748f066b206a85ac648f7a87c</param></service></servicedata> (No newline at EOF) ++++++ git-repo-2.58.tar.xz -> git-repo-2.59.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/.github/workflows/close-pull-request.yml new/git-repo-2.59/.github/workflows/close-pull-request.yml --- old/git-repo-2.58/.github/workflows/close-pull-request.yml 2025-08-15 01:35:26.000000000 +0200 +++ new/git-repo-2.59/.github/workflows/close-pull-request.yml 2025-10-20 20:28:21.000000000 +0200 @@ -18,5 +18,5 @@ Thanks for your contribution! Unfortunately, we don't use GitHub pull requests to manage code contributions to this repository. - Instead, please see [README.md](../blob/HEAD/SUBMITTING_PATCHES.md) + Instead, please see [README.md](../blob/HEAD/CONTRIBUTING.md) which provides full instructions on how to get involved. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/.isort.cfg new/git-repo-2.59/.isort.cfg --- old/git-repo-2.58/.isort.cfg 2025-08-15 01:35:26.000000000 +0200 +++ new/git-repo-2.59/.isort.cfg 2025-10-20 20:28:21.000000000 +0200 @@ -1,4 +1,4 @@ -# Copyright 2023 The Android Open Source Project +# Copyright (C) 2023 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/CONTRIBUTING.md new/git-repo-2.59/CONTRIBUTING.md --- old/git-repo-2.58/CONTRIBUTING.md 1970-01-01 01:00:00.000000000 +0100 +++ new/git-repo-2.59/CONTRIBUTING.md 2025-10-20 20:28:21.000000000 +0200 @@ -0,0 +1,190 @@ +# Submitting Changes + +Here's a short overview of the process. + +* Make small logical changes. +* [Provide a meaningful commit message][commit-message-style]. +* Make sure all code is under the Apache License, 2.0. +* Publish your changes for review. + * `git push origin HEAD:refs/for/main` +* Make corrections if requested. +* [Verify your changes on Gerrit.](#verify) +* [Send to the commit queue for testing & merging.](#cq) + +[TOC] + +## Long Version + +I wanted a file describing how to submit patches for repo, +so I started with the one found in the core Git distribution +(Documentation/SubmittingPatches), which itself was based on the +patch submission guidelines for the Linux kernel. + +However there are some differences, so please review and familiarize +yourself with the following relevant bits. + + +## Make separate commits for logically separate changes. + +Unless your patch is really trivial, you should not be sending out a patch that +was generated between your working tree and your commit head. +Instead, always make a commit with a complete +[commit message][commit-message-style] and generate a series of patches from +your repository. +It is a good discipline. + +Describe the technical detail of the change(s). + +If your description starts to get too long, that's a sign that you +probably need to split up your commit to finer grained pieces. + + +## Linting and formatting code + +Lint any changes by running: +```sh +$ tox -e lint -- file.py +``` + +And format with: +```sh +$ tox -e format -- file.py +``` + +Or format everything: +```sh +$ tox -e format +``` + +Repo uses [black](https://black.readthedocs.io/) with line length of 80 as its +formatter and flake8 as its linter. Repo also follows +[Google's Python Style Guide]. + +There should be no new errors or warnings introduced. + +Warnings that cannot be avoided without going against the Google Style Guide +may be suppressed inline individally using a `# noqa` comment as described +in the [flake8 documentation]. + +If there are many occurrences of the same warning, these may be suppressed for +the entire project in the included `.flake8` file. + +[Google's Python Style Guide]: https://google.github.io/styleguide/pyguide.html +[PEP 8]: https://www.python.org/dev/peps/pep-0008/ +[flake8 documentation]: https://flake8.pycqa.org/en/3.1.1/user/ignoring-errors.html#in-line-ignoring-errors + +## Running tests + +We use [pytest](https://pytest.org/) and [tox](https://tox.readthedocs.io/) for +running tests. You should make sure to install those first. + +To run the full suite against all supported Python versions, simply execute: +```sh +$ tox -p auto +``` + +We have [`./run_tests`](./run_tests) which is a simple wrapper around `pytest`: +```sh +# Run the full suite against the default Python version. +$ ./run_tests +# List each test as it runs. +$ ./run_tests -v + +# Run a specific unittest module (and all tests in it). +$ ./run_tests tests/test_git_command.py + +# Run a specific testsuite in a specific unittest module. +$ ./run_tests tests/test_editor.py::EditString + +# Run a single test. +$ ./run_tests tests/test_editor.py::EditString::test_cat_editor + +# List all available tests. +$ ./run_tests --collect-only + +# Run a single test using substring match. +$ ./run_tests -k test_cat_editor +``` + +The coverage isn't great currently, but it should still be run for all commits. +Adding more unittests for changes you make would be greatly appreciated :). +Check out the [tests/](./tests/) subdirectory for more details. + + +## Check the license + +repo is licensed under the Apache License, 2.0. + +Because of this licensing model *every* file within the project +*must* list the license that covers it in the header of the file. +Any new contributions to an existing file *must* be submitted under +the current license of that file. Any new files *must* clearly +indicate which license they are provided under in the file header. + +Please verify that you are legally allowed and willing to submit your +changes under the license covering each file *prior* to submitting +your patch. It is virtually impossible to remove a patch once it +has been applied and pushed out. + + +## Sending your patches. + +Do not email your patches to anyone. + +Instead, login to the Gerrit Code Review tool at: + + https://gerrit-review.googlesource.com/ + +Ensure you have completed one of the necessary contributor +agreements, providing documentation to the project maintainers that +they have right to redistribute your work under the Apache License: + + https://gerrit-review.googlesource.com/#/settings/agreements + +Ensure you have obtained an HTTP password to authenticate: + + https://gerrit-review.googlesource.com/new-password + +Ensure that you have the local commit hook installed to automatically +add a ChangeId to your commits: + + curl -Lo `git rev-parse --git-dir`/hooks/commit-msg https://gerrit-review.googlesource.com/tools/hooks/commit-msg + chmod +x `git rev-parse --git-dir`/hooks/commit-msg + +If you have already committed your changes you will need to amend the commit +to get the ChangeId added. + + git commit --amend + +Push your patches over HTTPS to the review server, possibly through +a remembered remote to make this easier in the future: + + git config remote.review.url https://gerrit-review.googlesource.com/git-repo + git config remote.review.push HEAD:refs/for/main + + git push review + +You will be automatically emailed a copy of your commits, and any +comments made by the project maintainers. + + +## Make changes if requested + +The project maintainer who reviews your changes might request changes to your +commit. If you make the requested changes you will need to amend your commit +and push it to the review server again. + + +## Verify your changes on Gerrit {#verify} + +After you receive a Code-Review+2 from the maintainer, select the Verified +button on the Gerrit page for the change. This verifies that you have tested +your changes and notifies the maintainer that they are ready to be submitted. + +## Merge your changes via the commit queue {#cq} + +Once a change is ready to be merged, select the Commit-Queue+2 setting on the +Gerrit page for it. This tells the CI system to test the change, and if it +passes all the checks, automatically merges it. + +[commit-message-style]: https://chris.beams.io/posts/git-commit/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/README.md new/git-repo-2.59/README.md --- old/git-repo-2.58/README.md 2025-08-15 01:35:26.000000000 +0200 +++ new/git-repo-2.59/README.md 2025-10-20 20:28:21.000000000 +0200 @@ -14,7 +14,7 @@ * Docs: <https://source.android.com/source/using-repo.html> * [repo Manifest Format](./docs/manifest-format.md) * [repo Hooks](./docs/repo-hooks.md) -* [Submitting patches](./SUBMITTING_PATCHES.md) +* [Contributing](./CONTRIBUTING.md) * Running Repo in [Microsoft Windows](./docs/windows.md) * GitHub mirror: <https://github.com/GerritCodeReview/git-repo> * Postsubmit tests: <https://github.com/GerritCodeReview/git-repo/actions> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/SUBMITTING_PATCHES.md new/git-repo-2.59/SUBMITTING_PATCHES.md --- old/git-repo-2.58/SUBMITTING_PATCHES.md 2025-08-15 01:35:26.000000000 +0200 +++ new/git-repo-2.59/SUBMITTING_PATCHES.md 1970-01-01 01:00:00.000000000 +0100 @@ -1,190 +0,0 @@ -# Submitting Changes - -Here's a short overview of the process. - -* Make small logical changes. -* [Provide a meaningful commit message][commit-message-style]. -* Make sure all code is under the Apache License, 2.0. -* Publish your changes for review. - * `git push origin HEAD:refs/for/main` -* Make corrections if requested. -* [Verify your changes on Gerrit.](#verify) -* [Send to the commit queue for testing & merging.](#cq) - -[TOC] - -## Long Version - -I wanted a file describing how to submit patches for repo, -so I started with the one found in the core Git distribution -(Documentation/SubmittingPatches), which itself was based on the -patch submission guidelines for the Linux kernel. - -However there are some differences, so please review and familiarize -yourself with the following relevant bits. - - -## Make separate commits for logically separate changes. - -Unless your patch is really trivial, you should not be sending out a patch that -was generated between your working tree and your commit head. -Instead, always make a commit with a complete -[commit message][commit-message-style] and generate a series of patches from -your repository. -It is a good discipline. - -Describe the technical detail of the change(s). - -If your description starts to get too long, that's a sign that you -probably need to split up your commit to finer grained pieces. - - -## Linting and formatting code - -Lint any changes by running: -```sh -$ tox -e lint -- file.py -``` - -And format with: -```sh -$ tox -e format -- file.py -``` - -Or format everything: -```sh -$ tox -e format -``` - -Repo uses [black](https://black.readthedocs.io/) with line length of 80 as its -formatter and flake8 as its linter. Repo also follows -[Google's Python Style Guide]. - -There should be no new errors or warnings introduced. - -Warnings that cannot be avoided without going against the Google Style Guide -may be suppressed inline individally using a `# noqa` comment as described -in the [flake8 documentation]. - -If there are many occurrences of the same warning, these may be suppressed for -the entire project in the included `.flake8` file. - -[Google's Python Style Guide]: https://google.github.io/styleguide/pyguide.html -[PEP 8]: https://www.python.org/dev/peps/pep-0008/ -[flake8 documentation]: https://flake8.pycqa.org/en/3.1.1/user/ignoring-errors.html#in-line-ignoring-errors - -## Running tests - -We use [pytest](https://pytest.org/) and [tox](https://tox.readthedocs.io/) for -running tests. You should make sure to install those first. - -To run the full suite against all supported Python versions, simply execute: -```sh -$ tox -p auto -``` - -We have [`./run_tests`](./run_tests) which is a simple wrapper around `pytest`: -```sh -# Run the full suite against the default Python version. -$ ./run_tests -# List each test as it runs. -$ ./run_tests -v - -# Run a specific unittest module (and all tests in it). -$ ./run_tests tests/test_git_command.py - -# Run a specific testsuite in a specific unittest module. -$ ./run_tests tests/test_editor.py::EditString - -# Run a single test. -$ ./run_tests tests/test_editor.py::EditString::test_cat_editor - -# List all available tests. -$ ./run_tests --collect-only - -# Run a single test using substring match. -$ ./run_tests -k test_cat_editor -``` - -The coverage isn't great currently, but it should still be run for all commits. -Adding more unittests for changes you make would be greatly appreciated :). -Check out the [tests/](./tests/) subdirectory for more details. - - -## Check the license - -repo is licensed under the Apache License, 2.0. - -Because of this licensing model *every* file within the project -*must* list the license that covers it in the header of the file. -Any new contributions to an existing file *must* be submitted under -the current license of that file. Any new files *must* clearly -indicate which license they are provided under in the file header. - -Please verify that you are legally allowed and willing to submit your -changes under the license covering each file *prior* to submitting -your patch. It is virtually impossible to remove a patch once it -has been applied and pushed out. - - -## Sending your patches. - -Do not email your patches to anyone. - -Instead, login to the Gerrit Code Review tool at: - - https://gerrit-review.googlesource.com/ - -Ensure you have completed one of the necessary contributor -agreements, providing documentation to the project maintainers that -they have right to redistribute your work under the Apache License: - - https://gerrit-review.googlesource.com/#/settings/agreements - -Ensure you have obtained an HTTP password to authenticate: - - https://gerrit-review.googlesource.com/new-password - -Ensure that you have the local commit hook installed to automatically -add a ChangeId to your commits: - - curl -Lo `git rev-parse --git-dir`/hooks/commit-msg https://gerrit-review.googlesource.com/tools/hooks/commit-msg - chmod +x `git rev-parse --git-dir`/hooks/commit-msg - -If you have already committed your changes you will need to amend the commit -to get the ChangeId added. - - git commit --amend - -Push your patches over HTTPS to the review server, possibly through -a remembered remote to make this easier in the future: - - git config remote.review.url https://gerrit-review.googlesource.com/git-repo - git config remote.review.push HEAD:refs/for/main - - git push review - -You will be automatically emailed a copy of your commits, and any -comments made by the project maintainers. - - -## Make changes if requested - -The project maintainer who reviews your changes might request changes to your -commit. If you make the requested changes you will need to amend your commit -and push it to the review server again. - - -## Verify your changes on Gerrit {#verify} - -After you receive a Code-Review+2 from the maintainer, select the Verified -button on the Gerrit page for the change. This verifies that you have tested -your changes and notifies the maintainer that they are ready to be submitted. - -## Merge your changes via the commit queue {#cq} - -Once a change is ready to be merged, select the Commit-Queue+2 setting on the -Gerrit page for it. This tells the CI system to test the change, and if it -passes all the checks, automatically merges it. - -[commit-message-style]: https://chris.beams.io/posts/git-commit/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/completion.bash new/git-repo-2.59/completion.bash --- old/git-repo-2.58/completion.bash 2025-08-15 01:35:26.000000000 +0200 +++ new/git-repo-2.59/completion.bash 2025-10-20 20:28:21.000000000 +0200 @@ -1,4 +1,4 @@ -# Copyright 2021 The Android Open Source Project +# Copyright (C) 2021 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/git_ssh new/git-repo-2.59/git_ssh --- old/git-repo-2.58/git_ssh 2025-08-15 01:35:26.000000000 +0200 +++ new/git-repo-2.59/git_ssh 2025-10-20 20:28:21.000000000 +0200 @@ -1,5 +1,4 @@ #!/bin/sh -# # Copyright (C) 2009 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/git_trace2_event_log.py new/git-repo-2.59/git_trace2_event_log.py --- old/git-repo-2.58/git_trace2_event_log.py 2025-08-15 01:35:26.000000000 +0200 +++ new/git-repo-2.59/git_trace2_event_log.py 2025-10-20 20:28:21.000000000 +0200 @@ -1,3 +1,19 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Event logging in the git trace2 EVENT format.""" + from git_command import GetEventTargetPath from git_command import RepoSourceVersion from git_trace2_event_log_base import BaseEventLog diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/main.py new/git-repo-2.59/main.py --- old/git-repo-2.58/main.py 2025-08-15 01:35:26.000000000 +0200 +++ new/git-repo-2.59/main.py 2025-10-20 20:28:21.000000000 +0200 @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# # Copyright (C) 2008 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/man/repo-smartsync.1 new/git-repo-2.59/man/repo-smartsync.1 --- old/git-repo-2.58/man/repo-smartsync.1 2025-08-15 01:35:26.000000000 +0200 +++ new/git-repo-2.59/man/repo-smartsync.1 2025-10-20 20:28:21.000000000 +0200 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man. -.TH REPO "1" "June 2025" "repo smartsync" "Repo Manual" +.TH REPO "1" "August 2025" "repo smartsync" "Repo Manual" .SH NAME repo \- repo smartsync - manual page for repo smartsync .SH SYNOPSIS @@ -20,12 +20,11 @@ .TP \fB\-\-jobs\-network\fR=\fI\,JOBS\/\fR number of network jobs to run in parallel (defaults to -\fB\-\-jobs\fR or 1). Ignored when \fB\-\-interleaved\fR is set +\fB\-\-jobs\fR or 1). Ignored unless \fB\-\-no\-interleaved\fR is set .TP \fB\-\-jobs\-checkout\fR=\fI\,JOBS\/\fR number of local checkout jobs to run in parallel -(defaults to \fB\-\-jobs\fR or 8). Ignored when \fB\-\-interleaved\fR -is set +(defaults to \fB\-\-jobs\fR or 8). Ignored unless \fB\-\-nointerleaved\fR is set .TP \fB\-f\fR, \fB\-\-force\-broken\fR obsolete option (to be deleted in the future) @@ -60,7 +59,10 @@ update to the latest revision) .TP \fB\-\-interleaved\fR -fetch and checkout projects in parallel (experimental) +fetch and checkout projects in parallel (default) +.TP +\fB\-\-no\-interleaved\fR +fetch and checkout projects in phases .TP \fB\-n\fR, \fB\-\-network\-only\fR fetch only, don't update working tree @@ -149,6 +151,16 @@ .TP \fB\-\-no\-repo\-verify\fR do not verify repo source code +.SS post\-sync hooks: +.TP +\fB\-\-no\-verify\fR +Do not run the post\-sync hook. +.TP +\fB\-\-verify\fR +Run the post\-sync hook without prompting. +.TP +\fB\-\-ignore\-hooks\fR +Do not abort if post\-sync hooks fail. .PP Run `repo help smartsync` to view the detailed manual. .SH DETAILS diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/man/repo-sync.1 new/git-repo-2.59/man/repo-sync.1 --- old/git-repo-2.58/man/repo-sync.1 2025-08-15 01:35:26.000000000 +0200 +++ new/git-repo-2.59/man/repo-sync.1 2025-10-20 20:28:21.000000000 +0200 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man. -.TH REPO "1" "June 2025" "repo sync" "Repo Manual" +.TH REPO "1" "August 2025" "repo sync" "Repo Manual" .SH NAME repo \- repo sync - manual page for repo sync .SH SYNOPSIS @@ -20,12 +20,11 @@ .TP \fB\-\-jobs\-network\fR=\fI\,JOBS\/\fR number of network jobs to run in parallel (defaults to -\fB\-\-jobs\fR or 1). Ignored when \fB\-\-interleaved\fR is set +\fB\-\-jobs\fR or 1). Ignored unless \fB\-\-no\-interleaved\fR is set .TP \fB\-\-jobs\-checkout\fR=\fI\,JOBS\/\fR number of local checkout jobs to run in parallel -(defaults to \fB\-\-jobs\fR or 8). Ignored when \fB\-\-interleaved\fR -is set +(defaults to \fB\-\-jobs\fR or 8). Ignored unless \fB\-\-nointerleaved\fR is set .TP \fB\-f\fR, \fB\-\-force\-broken\fR obsolete option (to be deleted in the future) @@ -60,7 +59,10 @@ update to the latest revision) .TP \fB\-\-interleaved\fR -fetch and checkout projects in parallel (experimental) +fetch and checkout projects in parallel (default) +.TP +\fB\-\-no\-interleaved\fR +fetch and checkout projects in phases .TP \fB\-n\fR, \fB\-\-network\-only\fR fetch only, don't update working tree @@ -156,6 +158,16 @@ .TP \fB\-\-no\-repo\-verify\fR do not verify repo source code +.SS post\-sync hooks: +.TP +\fB\-\-no\-verify\fR +Do not run the post\-sync hook. +.TP +\fB\-\-verify\fR +Run the post\-sync hook without prompting. +.TP +\fB\-\-ignore\-hooks\fR +Do not abort if post\-sync hooks fail. .PP Run `repo help sync` to view the detailed manual. .SH DETAILS diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/project.py new/git-repo-2.59/project.py --- old/git-repo-2.58/project.py 2025-08-15 01:35:26.000000000 +0200 +++ new/git-repo-2.59/project.py 2025-10-20 20:28:21.000000000 +0200 @@ -642,10 +642,6 @@ # project containing repo hooks. self.enabled_repo_hooks = [] - # This will be updated later if the project has submodules and - # if they will be synced. - self.has_subprojects = False - def RelPath(self, local=True): """Return the path for the project relative to a manifest. @@ -1563,8 +1559,8 @@ # TODO(https://git-scm.com/docs/git-worktree#_bugs): Re-evaluate if # submodules can be init when using worktrees once its support is # complete. - if self.has_subprojects and not self.use_git_worktrees: - self._InitSubmodules() + if self.parent and not self.use_git_worktrees: + self._InitSubmodule() all_refs = self.bare_ref.all self.CleanPublishedCache(all_refs) revid = self.GetRevisionId(all_refs) @@ -1593,6 +1589,9 @@ self._FastForward(revid) self._CopyAndLinkFiles() + def _dorebase(): + self._Rebase(upstream="@{upstream}") + def _dosubmodules(): self._SyncSubmodules(quiet=True) @@ -1684,19 +1683,24 @@ if pub: not_merged = self._revlist(not_rev(revid), pub) if not_merged: - if upstream_gain and not force_rebase: - # The user has published this branch and some of those - # commits are not yet merged upstream. We do not want - # to rewrite the published commits so we punt. - fail( - LocalSyncFail( - "branch %s is published (but not merged) and is " - "now %d commits behind. Fix this manually or rerun " - "with the --rebase option to force a rebase." - % (branch.name, len(upstream_gain)), - project=self.name, + if upstream_gain: + if force_rebase: + # Try to rebase local published but not merged changes + # on top of the upstream changes. + syncbuf.later1(self, _dorebase, not verbose) + else: + # The user has published this branch and some of those + # commits are not yet merged upstream. We do not want + # to rewrite the published commits so we punt. + fail( + LocalSyncFail( + "branch %s is published (but not merged) and " + "is now %d commits behind. Fix this manually " + "or rerun with the --rebase option to force a " + "rebase." % (branch.name, len(upstream_gain)), + project=self.name, + ) ) - ) return syncbuf.later1(self, _doff, not verbose) return @@ -2359,8 +2363,6 @@ ) result.append(subproject) result.extend(subproject.GetDerivedSubprojects()) - if result: - self.has_subprojects = True return result def EnableRepositoryExtension(self, key, value="true", version=1): @@ -2411,7 +2413,9 @@ # throws an error. revs = [f"{self.revisionExpr}^0"] upstream_rev = None - if self.upstream: + + # Only check upstream when using superproject. + if self.upstream and self.manifest.manifestProject.use_superproject: upstream_rev = self.GetRemote().ToLocal(self.upstream) revs.append(upstream_rev) @@ -2423,7 +2427,9 @@ log_as_error=False, ) - if self.upstream: + # Only verify upstream relationship for superproject scenarios + # without affecting plain usage. + if self.upstream and self.manifest.manifestProject.use_superproject: self.bare_git.merge_base( "--is-ancestor", self.revisionExpr, @@ -3026,16 +3032,39 @@ project=self.name, ) - def _InitSubmodules(self, quiet=True): - """Initialize the submodules for the project.""" + def _InitSubmodule(self, quiet=True): + """Initialize the submodule.""" cmd = ["submodule", "init"] if quiet: cmd.append("-q") - if GitCommand(self, cmd).Wait() != 0: - raise GitError( - f"{self.name} submodule init", - project=self.name, + cmd.extend(["--", self.worktree]) + max_retries = 3 + base_delay_secs = 1 + jitter_ratio = 1 / 3 + for attempt in range(max_retries): + git_cmd = GitCommand( + None, + cmd, + cwd=self.parent.worktree, + capture_stdout=True, + capture_stderr=True, ) + if git_cmd.Wait() == 0: + return + error = git_cmd.stderr or git_cmd.stdout + if "lock" in error: + delay = base_delay_secs * (2**attempt) + delay += random.uniform(0, delay * jitter_ratio) + logger.warning( + f"Attempt {attempt+1}/{max_retries}: " + + f"git {' '.join(cmd)} failed." + + f" Error: {error}." + + f" Sleeping {delay:.2f}s before retrying." + ) + time.sleep(delay) + else: + break + git_cmd.VerifyCommand() def _Rebase(self, upstream, onto=None): cmd = ["rebase"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/pyproject.toml new/git-repo-2.59/pyproject.toml --- old/git-repo-2.58/pyproject.toml 2025-08-15 01:35:26.000000000 +0200 +++ new/git-repo-2.59/pyproject.toml 2025-10-20 20:28:21.000000000 +0200 @@ -1,4 +1,4 @@ -# Copyright 2023 The Android Open Source Project +# Copyright (C) 2023 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/release/check-metadata.py new/git-repo-2.59/release/check-metadata.py --- old/git-repo-2.58/release/check-metadata.py 1970-01-01 01:00:00.000000000 +0100 +++ new/git-repo-2.59/release/check-metadata.py 2025-10-20 20:28:21.000000000 +0200 @@ -0,0 +1,152 @@ +#!/usr/bin/env python3 +# Copyright (C) 2025 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Helper tool to check various metadata (e.g. licensing) in source files.""" + +import argparse +from pathlib import Path +import re +import sys + +import util + + +_FILE_HEADER_RE = re.compile( + r"""# Copyright \(C\) 20[0-9]{2} The Android Open Source Project +# +# Licensed under the Apache License, Version 2\.0 \(the "License"\); +# you may not use this file except in compliance with the License\. +# You may obtain a copy of the License at +# +# http://www\.apache\.org/licenses/LICENSE-2\.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied\. +# See the License for the specific language governing permissions and +# limitations under the License\. +""" +) + + +def check_license(path: Path, lines: list[str]) -> bool: + """Check license header.""" + # Enforce licensing on configs & scripts. + if not ( + path.suffix in (".bash", ".cfg", ".ini", ".py", ".toml") + or lines[0] in ("#!/bin/bash", "#!/bin/sh", "#!/usr/bin/env python3") + ): + return True + + # Extract the file header. + header_lines = [] + for line in lines: + if line.startswith("#"): + header_lines.append(line) + else: + break + if not header_lines: + print( + f"error: {path.relative_to(util.TOPDIR)}: " + "missing file header (copyright+licensing)", + file=sys.stderr, + ) + return False + + # Skip the shebang. + if header_lines[0].startswith("#!"): + header_lines.pop(0) + + # If this file is imported into the tree, then leave it be. + if header_lines[0] == "# DO NOT EDIT THIS FILE": + return True + + header = "".join(f"{x}\n" for x in header_lines) + if not _FILE_HEADER_RE.match(header): + print( + f"error: {path.relative_to(util.TOPDIR)}: " + "file header incorrectly formatted", + file=sys.stderr, + ) + print( + "".join(f"> {x}\n" for x in header_lines), end="", file=sys.stderr + ) + return False + + return True + + +def check_path(opts: argparse.Namespace, path: Path) -> bool: + """Check a single path.""" + data = path.read_text(encoding="utf-8") + lines = data.splitlines() + # NB: Use list comprehension and not a generator so we run all the checks. + return all( + [ + check_license(path, lines), + ] + ) + + +def check_paths(opts: argparse.Namespace, paths: list[Path]) -> bool: + """Check all the paths.""" + # NB: Use list comprehension and not a generator so we check all paths. + return all([check_path(opts, x) for x in paths]) + + +def find_files(opts: argparse.Namespace) -> list[Path]: + """Find all the files in the source tree.""" + result = util.run( + opts, + ["git", "ls-tree", "-r", "-z", "--name-only", "HEAD"], + cwd=util.TOPDIR, + capture_output=True, + encoding="utf-8", + ) + return [util.TOPDIR / x for x in result.stdout.split("\0")[:-1]] + + +def get_parser() -> argparse.ArgumentParser: + """Get a CLI parser.""" + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "-n", + "--dry-run", + dest="dryrun", + action="store_true", + help="show everything that would be done", + ) + parser.add_argument( + "paths", + nargs="*", + help="the paths to scan", + ) + return parser + + +def main(argv: list[str]) -> int: + """The main func!""" + parser = get_parser() + opts = parser.parse_args(argv) + + paths = opts.paths + if not opts.paths: + paths = find_files(opts) + + return 0 if check_paths(opts, paths) else 1 + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/release/util.py new/git-repo-2.59/release/util.py --- old/git-repo-2.58/release/util.py 2025-08-15 01:35:26.000000000 +0200 +++ new/git-repo-2.59/release/util.py 2025-10-20 20:28:21.000000000 +0200 @@ -14,7 +14,7 @@ """Random utility code for release tools.""" -import os +from pathlib import Path import re import shlex import subprocess @@ -24,8 +24,9 @@ assert sys.version_info >= (3, 6), "This module requires Python 3.6+" -TOPDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -HOMEDIR = os.path.expanduser("~") +THIS_FILE = Path(__file__).resolve() +TOPDIR = THIS_FILE.parent.parent +HOMEDIR = Path("~").expanduser() # These are the release keys we sign with. @@ -54,7 +55,7 @@ def import_release_key(opts): """Import the public key of the official release repo signing key.""" # Extract the key from our repo launcher. - launcher = getattr(opts, "launcher", os.path.join(TOPDIR, "repo")) + launcher = getattr(opts, "launcher", TOPDIR / "repo") print(f'Importing keys from "{launcher}" launcher script') with open(launcher, encoding="utf-8") as fp: data = fp.read() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/repo new/git-repo-2.59/repo --- old/git-repo-2.58/repo 2025-08-15 01:35:26.000000000 +0200 +++ new/git-repo-2.59/repo 2025-10-20 20:28:21.000000000 +0200 @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# # Copyright (C) 2008 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/run_tests new/git-repo-2.59/run_tests --- old/git-repo-2.58/run_tests 2025-08-15 01:35:26.000000000 +0200 +++ new/git-repo-2.59/run_tests 2025-10-20 20:28:21.000000000 +0200 @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright 2019 The Android Open Source Project +# Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ import functools import os +import shlex import shutil import subprocess import sys @@ -26,6 +27,11 @@ ROOT_DIR = os.path.dirname(os.path.realpath(__file__)) +def log_cmd(cmd: str, argv: list[str]) -> None: + """Log a debug message to make history easier to track.""" + print("+", cmd, shlex.join(argv), file=sys.stderr) + + @functools.lru_cache() def is_ci() -> bool: """Whether we're running in our CI system.""" @@ -37,6 +43,7 @@ if is_ci(): argv = ["-m", "not skip_cq"] + argv + log_cmd("pytest", argv) return subprocess.run( [sys.executable, "-m", "pytest"] + argv, check=False, @@ -49,6 +56,7 @@ if is_ci(): argv = ["-m", "not skip_cq"] + argv + log_cmd("[vpython 3.8] pytest", argv) try: return subprocess.run( [ @@ -77,8 +85,10 @@ "release/update-hooks", "release/update-manpages", ] + argv = ["--diff", "--check", ROOT_DIR] + extra_programs + log_cmd("black", argv) return subprocess.run( - [sys.executable, "-m", "black", "--check", ROOT_DIR] + extra_programs, + [sys.executable, "-m", "black"] + argv, check=False, cwd=ROOT_DIR, ).returncode @@ -86,8 +96,10 @@ def run_flake8(): """Returns the exit code from flake8.""" + argv = [ROOT_DIR] + log_cmd("flake8", argv) return subprocess.run( - [sys.executable, "-m", "flake8", ROOT_DIR], + [sys.executable, "-m", "flake8"] + argv, check=False, cwd=ROOT_DIR, ).returncode @@ -95,8 +107,21 @@ def run_isort(): """Returns the exit code from isort.""" + argv = ["--check", ROOT_DIR] + log_cmd("isort", argv) + return subprocess.run( + [sys.executable, "-m", "isort"] + argv, + check=False, + cwd=ROOT_DIR, + ).returncode + + +def run_check_metadata(): + """Returns the exit code from check-metadata.""" + argv = [] + log_cmd("release/check-metadata.py", argv) return subprocess.run( - [sys.executable, "-m", "isort", "--check", ROOT_DIR], + [sys.executable, "release/check-metadata.py"] + argv, check=False, cwd=ROOT_DIR, ).returncode @@ -109,8 +134,10 @@ print("update-manpages: help2man not found; skipping test") return 0 + argv = ["--check"] + log_cmd("release/update-manpages", argv) return subprocess.run( - [sys.executable, "release/update-manpages", "--check"], + [sys.executable, "release/update-manpages"] + argv, check=False, cwd=ROOT_DIR, ).returncode @@ -124,6 +151,7 @@ run_black, run_flake8, run_isort, + run_check_metadata, run_update_manpages, ) # Run all the tests all the time to get full feedback. Don't exit on the diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/setup.py new/git-repo-2.59/setup.py --- old/git-repo-2.58/setup.py 2025-08-15 01:35:26.000000000 +0200 +++ new/git-repo-2.59/setup.py 2025-10-20 20:28:21.000000000 +0200 @@ -1,7 +1,7 @@ #!/usr/bin/env python3 -# Copyright 2019 The Android Open Source Project +# Copyright (C) 2019 The Android Open Source Project # -# Licensed under the Apache License, Version 2.0 (the 'License"); +# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/subcmds/forall.py new/git-repo-2.59/subcmds/forall.py --- old/git-repo-2.58/subcmds/forall.py 2025-08-15 01:35:26.000000000 +0200 +++ new/git-repo-2.59/subcmds/forall.py 2025-10-20 20:28:21.000000000 +0200 @@ -133,7 +133,7 @@ @staticmethod def _cmd_option(option, _opt_str, _value, parser): - setattr(parser.values, option.dest or "command", list(parser.rargs)) + setattr(parser.values, option.dest, list(parser.rargs)) while parser.rargs: del parser.rargs[0] @@ -161,6 +161,7 @@ p.add_option( "-c", "--command", + dest="command", help="command (and arguments) to execute", action="callback", callback=self._cmd_option, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/subcmds/sync.py new/git-repo-2.59/subcmds/sync.py --- old/git-repo-2.58/subcmds/sync.py 2025-08-15 01:35:26.000000000 +0200 +++ new/git-repo-2.59/subcmds/sync.py 2025-10-20 20:28:21.000000000 +0200 @@ -975,9 +975,6 @@ sync_event.set() sync_progress_thread.join() - self._fetch_times.Save() - self._local_sync_state.Save() - if not self.outer_client.manifest.IsArchive: self._GCProjects(projects, opt, err_event) @@ -1003,53 +1000,58 @@ to_fetch.extend(all_projects) to_fetch.sort(key=self._fetch_times.Get, reverse=True) - result = self._Fetch(to_fetch, opt, err_event, ssh_proxy, errors) - success = result.success - fetched = result.projects - if not success: - err_event.set() - - if opt.network_only: - # Bail out now; the rest touches the working tree. - if err_event.is_set(): - e = SyncError( - "error: Exited sync due to fetch errors.", - aggregate_errors=errors, - ) - - logger.error(e) - raise e - return _FetchMainResult([]) - - # Iteratively fetch missing and/or nested unregistered submodules. - previously_missing_set = set() - while True: - self._ReloadManifest(None, manifest) - all_projects = self.GetProjects( - args, - missing_ok=True, - submodules_ok=opt.fetch_submodules, - manifest=manifest, - all_manifests=not opt.this_manifest_only, - ) - missing = [] - for project in all_projects: - if project.gitdir not in fetched: - missing.append(project) - if not missing: - break - # Stop us from non-stopped fetching actually-missing repos: If set - # of missing repos has not been changed from last fetch, we break. - missing_set = {p.name for p in missing} - if previously_missing_set == missing_set: - break - previously_missing_set = missing_set - result = self._Fetch(missing, opt, err_event, ssh_proxy, errors) + try: + result = self._Fetch(to_fetch, opt, err_event, ssh_proxy, errors) success = result.success - new_fetched = result.projects + fetched = result.projects if not success: err_event.set() - fetched.update(new_fetched) + + if opt.network_only: + # Bail out now; the rest touches the working tree. + if err_event.is_set(): + e = SyncError( + "error: Exited sync due to fetch errors.", + aggregate_errors=errors, + ) + + logger.error(e) + raise e + return _FetchMainResult([]) + + # Iteratively fetch missing and/or nested unregistered submodules. + previously_missing_set = set() + while True: + self._ReloadManifest(None, manifest) + all_projects = self.GetProjects( + args, + missing_ok=True, + submodules_ok=opt.fetch_submodules, + manifest=manifest, + all_manifests=not opt.this_manifest_only, + ) + missing = [] + for project in all_projects: + if project.gitdir not in fetched: + missing.append(project) + if not missing: + break + # Stop us from non-stopped fetching actually-missing repos: If + # set of missing repos has not been changed from last fetch, we + # break. + missing_set = {p.name for p in missing} + if previously_missing_set == missing_set: + break + previously_missing_set = missing_set + result = self._Fetch(missing, opt, err_event, ssh_proxy, errors) + success = result.success + new_fetched = result.projects + if not success: + err_event.set() + fetched.update(new_fetched) + finally: + self._fetch_times.Save() + self._local_sync_state.Save() return _FetchMainResult(all_projects) @@ -2491,107 +2493,120 @@ sync_event = _threading.Event() sync_progress_thread = self._CreateSyncProgressThread(pm, sync_event) - with multiprocessing.Manager() as manager, ssh.ProxyManager( - manager - ) as ssh_proxy: - ssh_proxy.sock() - with self.ParallelContext(): - self.get_parallel_context()["ssh_proxy"] = ssh_proxy - # TODO(gavinmak): Use multprocessing.Queue instead of dict. - self.get_parallel_context()[ - "sync_dict" - ] = multiprocessing.Manager().dict() - sync_progress_thread.start() + try: + with multiprocessing.Manager() as manager, ssh.ProxyManager( + manager + ) as ssh_proxy: + ssh_proxy.sock() + with self.ParallelContext(): + self.get_parallel_context()["ssh_proxy"] = ssh_proxy + # TODO(gavinmak): Use multprocessing.Queue instead of dict. + self.get_parallel_context()[ + "sync_dict" + ] = multiprocessing.Manager().dict() + sync_progress_thread.start() - try: - # Outer loop for dynamic project discovery. This continues - # until no unsynced projects remain. - while True: - projects_to_sync = [ - p - for p in project_list - if p.relpath not in finished_relpaths - ] - if not projects_to_sync: - break - - pending_relpaths = {p.relpath for p in projects_to_sync} - if previously_pending_relpaths == pending_relpaths: - stalled_projects_str = "\n".join( - f" - {path}" - for path in sorted(list(pending_relpaths)) - ) - logger.error( - "The following projects failed and could not " - "be synced:\n%s", - stalled_projects_str, - ) - err_event.set() - break - previously_pending_relpaths = pending_relpaths - - self.get_parallel_context()[ - "projects" - ] = projects_to_sync - project_index_map = { - p: i for i, p in enumerate(projects_to_sync) - } - - # Inner loop to process projects in a hierarchical - # order. This iterates through levels of project - # dependencies (e.g. 'foo' then 'foo/bar'). All projects - # in one level can be processed in parallel, but we must - # wait for a level to complete before starting the next. - for level_projects in _SafeCheckoutOrder( - projects_to_sync - ): - if not level_projects: - continue - - objdir_project_map = collections.defaultdict(list) - for p in level_projects: - objdir_project_map[p.objdir].append( - project_index_map[p] + try: + # Outer loop for dynamic project discovery. This + # continues until no unsynced projects remain. + while True: + projects_to_sync = [ + p + for p in project_list + if p.relpath not in finished_relpaths + ] + if not projects_to_sync: + break + + pending_relpaths = { + p.relpath for p in projects_to_sync + } + if previously_pending_relpaths == pending_relpaths: + stalled_projects_str = "\n".join( + f" - {path}" + for path in sorted(list(pending_relpaths)) + ) + logger.error( + "The following projects failed and could " + "not be synced:\n%s", + stalled_projects_str, ) - - work_items = list(objdir_project_map.values()) - if not work_items: - continue - - jobs = max(1, min(opt.jobs, len(work_items))) - callback = functools.partial( - self._ProcessSyncInterleavedResults, - finished_relpaths, - err_event, - errors, - opt, - ) - if not self.ExecuteInParallel( - jobs, - functools.partial(self._SyncProjectList, opt), - work_items, - callback=callback, - output=pm, - chunksize=1, - initializer=self.InitWorker, - ): err_event.set() + break + previously_pending_relpaths = pending_relpaths - if err_event.is_set() and opt.fail_fast: - raise SyncFailFastError(aggregate_errors=errors) + self.get_parallel_context()[ + "projects" + ] = projects_to_sync + project_index_map = { + p: i for i, p in enumerate(projects_to_sync) + } + + # Inner loop to process projects in a hierarchical + # order. This iterates through levels of project + # dependencies (e.g. 'foo' then 'foo/bar'). All + # projects in one level can be processed in + # parallel, but we must wait for a level to complete + # before starting the next. + for level_projects in _SafeCheckoutOrder( + projects_to_sync + ): + if not level_projects: + continue - self._ReloadManifest(None, manifest) - project_list = self.GetProjects( - args, - missing_ok=True, - submodules_ok=opt.fetch_submodules, - manifest=manifest, - all_manifests=not opt.this_manifest_only, - ) - pm.update_total(len(project_list)) - finally: - sync_event.set() - sync_progress_thread.join() + objdir_project_map = collections.defaultdict( + list + ) + for p in level_projects: + objdir_project_map[p.objdir].append( + project_index_map[p] + ) + + work_items = list(objdir_project_map.values()) + if not work_items: + continue + + jobs = max(1, min(opt.jobs, len(work_items))) + callback = functools.partial( + self._ProcessSyncInterleavedResults, + finished_relpaths, + err_event, + errors, + opt, + ) + if not self.ExecuteInParallel( + jobs, + functools.partial( + self._SyncProjectList, opt + ), + work_items, + callback=callback, + output=pm, + chunksize=1, + initializer=self.InitWorker, + ): + err_event.set() + + if err_event.is_set() and opt.fail_fast: + raise SyncFailFastError( + aggregate_errors=errors + ) + + self._ReloadManifest(None, manifest) + project_list = self.GetProjects( + args, + missing_ok=True, + submodules_ok=opt.fetch_submodules, + manifest=manifest, + all_manifests=not opt.this_manifest_only, + ) + pm.update_total(len(project_list)) + finally: + sync_event.set() + sync_progress_thread.join() + finally: + self._fetch_times.Save() + self._local_sync_state.Save() pm.end() @@ -2695,17 +2710,19 @@ self._saved = {} def Save(self): - if self._saved is None: + if not self._seen: return + self._Load() + for name, t in self._seen.items(): # Keep a moving average across the previous/current sync runs. old = self._saved.get(name, t) - self._seen[name] = (self._ALPHA * t) + ((1 - self._ALPHA) * old) + self._saved[name] = (self._ALPHA * t) + ((1 - self._ALPHA) * old) try: with open(self._path, "w") as f: - json.dump(self._seen, f, indent=2) + json.dump(self._saved, f, indent=2) except (OSError, TypeError): platform_utils.remove(self._path, missing_ok=True) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/tests/conftest.py new/git-repo-2.59/tests/conftest.py --- old/git-repo-2.58/tests/conftest.py 2025-08-15 01:35:26.000000000 +0200 +++ new/git-repo-2.59/tests/conftest.py 2025-10-20 20:28:21.000000000 +0200 @@ -1,4 +1,4 @@ -# Copyright 2022 The Android Open Source Project +# Copyright (C) 2022 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/tests/test_error.py new/git-repo-2.59/tests/test_error.py --- old/git-repo-2.58/tests/test_error.py 2025-08-15 01:35:26.000000000 +0200 +++ new/git-repo-2.59/tests/test_error.py 2025-10-20 20:28:21.000000000 +0200 @@ -1,4 +1,4 @@ -# Copyright 2021 The Android Open Source Project +# Copyright (C) 2021 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/tests/test_git_command.py new/git-repo-2.59/tests/test_git_command.py --- old/git-repo-2.58/tests/test_git_command.py 2025-08-15 01:35:26.000000000 +0200 +++ new/git-repo-2.59/tests/test_git_command.py 2025-10-20 20:28:21.000000000 +0200 @@ -1,4 +1,4 @@ -# Copyright 2019 The Android Open Source Project +# Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/tests/test_platform_utils.py new/git-repo-2.59/tests/test_platform_utils.py --- old/git-repo-2.58/tests/test_platform_utils.py 2025-08-15 01:35:26.000000000 +0200 +++ new/git-repo-2.59/tests/test_platform_utils.py 2025-10-20 20:28:21.000000000 +0200 @@ -1,4 +1,4 @@ -# Copyright 2021 The Android Open Source Project +# Copyright (C) 2021 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/tests/test_repo_trace.py new/git-repo-2.59/tests/test_repo_trace.py --- old/git-repo-2.58/tests/test_repo_trace.py 2025-08-15 01:35:26.000000000 +0200 +++ new/git-repo-2.59/tests/test_repo_trace.py 2025-10-20 20:28:21.000000000 +0200 @@ -1,4 +1,4 @@ -# Copyright 2022 The Android Open Source Project +# Copyright (C) 2022 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/tests/test_ssh.py new/git-repo-2.59/tests/test_ssh.py --- old/git-repo-2.58/tests/test_ssh.py 2025-08-15 01:35:26.000000000 +0200 +++ new/git-repo-2.59/tests/test_ssh.py 2025-10-20 20:28:21.000000000 +0200 @@ -1,4 +1,4 @@ -# Copyright 2019 The Android Open Source Project +# Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/tests/test_subcmds.py new/git-repo-2.59/tests/test_subcmds.py --- old/git-repo-2.58/tests/test_subcmds.py 2025-08-15 01:35:26.000000000 +0200 +++ new/git-repo-2.59/tests/test_subcmds.py 2025-10-20 20:28:21.000000000 +0200 @@ -94,7 +94,12 @@ """Block redundant dest= arguments.""" def _check_dest(opt): - if opt.dest is None or not opt._long_opts: + """Check the dest= setting.""" + # If the destination is not set, nothing to check. + # If long options are not set, then there's no implicit destination. + # If callback is used, then a destination might be needed because + # optparse cannot assume a value is always stored. + if opt.dest is None or not opt._long_opts or opt.callback: return long = opt._long_opts[0] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/tests/test_subcmds_sync.py new/git-repo-2.59/tests/test_subcmds_sync.py --- old/git-repo-2.58/tests/test_subcmds_sync.py 2025-08-15 01:35:26.000000000 +0200 +++ new/git-repo-2.59/tests/test_subcmds_sync.py 2025-10-20 20:28:21.000000000 +0200 @@ -681,6 +681,9 @@ # Mock _GetCurrentBranchOnly for worker tests. mock.patch.object(sync.Sync, "_GetCurrentBranchOnly").start() + self.cmd._fetch_times = mock.Mock() + self.cmd._local_sync_state = mock.Mock() + def tearDown(self): """Clean up resources.""" shutil.rmtree(self.repodir) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/tests/test_update_manpages.py new/git-repo-2.59/tests/test_update_manpages.py --- old/git-repo-2.58/tests/test_update_manpages.py 2025-08-15 01:35:26.000000000 +0200 +++ new/git-repo-2.59/tests/test_update_manpages.py 2025-10-20 20:28:21.000000000 +0200 @@ -1,4 +1,4 @@ -# Copyright 2022 The Android Open Source Project +# Copyright (C) 2022 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/git-repo-2.58/tox.ini new/git-repo-2.59/tox.ini --- old/git-repo-2.58/tox.ini 2025-08-15 01:35:26.000000000 +0200 +++ new/git-repo-2.59/tox.ini 2025-10-20 20:28:21.000000000 +0200 @@ -1,4 +1,4 @@ -# Copyright 2019 The Android Open Source Project +# Copyright (C) 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. ++++++ git-repo.obsinfo ++++++ --- /var/tmp/diff_new_pack.zjke7k/_old 2025-11-13 17:28:50.474857748 +0100 +++ /var/tmp/diff_new_pack.zjke7k/_new 2025-11-13 17:28:50.482858088 +0100 @@ -1,5 +1,5 @@ name: git-repo -version: 2.58 -mtime: 1755214526 -commit: 38d2fe11b9df521727fcca23c9dac086ce8378d3 +version: 2.59 +mtime: 1760984901 +commit: 1afe96a7e997ce7748f066b206a85ac648f7a87c
