Skip to content

Commit 005c452

Browse files
authored
Merge pull request #432 from lomnido/feat-clean_sync
sync '--clean', fix bare_repos, additional tests
2 parents 81c299b + 142a11c commit 005c452

31 files changed

+1625
-169
lines changed

tsrc/cleaner.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from pathlib import Path
2+
from typing import List
3+
4+
import cli_ui as ui
5+
6+
from tsrc.executor import Outcome, Task
7+
from tsrc.repo import Repo
8+
9+
10+
class Cleaner(Task[Repo]):
11+
def __init__(
12+
self,
13+
workspace_path: Path,
14+
*,
15+
do_hard_clean: bool = False,
16+
) -> None:
17+
self.workspace_path = workspace_path
18+
self.do_hard_clean = do_hard_clean
19+
20+
def describe_item(self, item: Repo) -> str:
21+
return item.dest
22+
23+
def describe_process_start(self, item: Repo) -> List[ui.Token]:
24+
return ["Cleaning", item.dest]
25+
26+
def describe_process_end(self, item: Repo) -> List[ui.Token]:
27+
return [ui.green, "ok", ui.reset, item.dest]
28+
29+
def process(self, index: int, count: int, repo: Repo) -> Outcome:
30+
"""
31+
Clean each repo so it will be ready for next 'sync'
32+
"""
33+
self.info_count(index, count, "Cleaning", repo.dest)
34+
35+
repo_path = self.workspace_path / repo.dest
36+
self.run_git(repo_path, "clean", "-f", "-d")
37+
if self.do_hard_clean is True:
38+
self.run_git(repo_path, "clean", "-f", "-X", "-d")
39+
40+
return Outcome.empty()

tsrc/cli/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ def repos_from_config(
334334
", ".join(repo_groups),
335335
)
336336
# fmt: on
337-
return manifest.get_repos(groups=repo_groups)
337+
return manifest.get_repos(groups=repo_groups, ignore_if_group_not_found=silent)
338338
else:
339339
# workspace config does not specify clone_all_repos nor
340340
# a list of groups, ask the manifest for the list of default

tsrc/cli/dump_manifest.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def configure_parser(subparser: argparse._SubParsersAction) -> None:
7676
parser.add_argument(
7777
"-U",
7878
"--update-on",
79-
help="Set UPDATE operation mode, by setting the UPDATE source and DESTINATION default to provided UPDATE_AT path to YAML file. Such path must exists", # noqa: E501
79+
help="Set UPDATE operation mode, by setting the UPDATE source and DESTINATION default to provided UPDATE_ON path to YAML file. Such path must exists", # noqa: E501
8080
type=Path,
8181
dest="update_on",
8282
)
@@ -87,22 +87,28 @@ def configure_parser(subparser: argparse._SubParsersAction) -> None:
8787
dest="no_repo_delete",
8888
)
8989
parser.add_argument(
90-
"--sha1-only",
90+
"--sha1-on",
9191
action="store_true",
92-
help="Use SHA1 as only value (with branch if available) for every considered Repo. This is particulary useful when we want to point to exact point of Repos states", # noqa: E501
93-
dest="sha1_only",
92+
help="Explicitly set SHA1 for every considered Repo. This is particulary useful when we want to point to exact commit in Repos", # noqa: E501
93+
dest="sha1_on",
94+
)
95+
parser.add_argument(
96+
"--sha1-off",
97+
action="store_true",
98+
help="Tell dumping mechanism that we do not care about excat Repo commit, as long as it keep 'branch' and 'tag's. This option is ignored, when there is no 'branch' or 'tag'", # noqa: E501
99+
dest="sha1_off",
94100
)
95101
parser.add_argument(
96102
"-X",
97-
"--skip-manifest",
103+
"--skip-manifest-repo",
98104
help="Skip manifest repository if found. If not, it is ignored. For this filter to work, the Workspace needs to be present. And it is only applied after the processing of the Repositories", # noqa: E501
99105
dest="skip_manifest",
100106
default=False,
101107
action="store_true",
102108
)
103109
parser.add_argument(
104110
"-M",
105-
"--only-manifest",
111+
"--only-manifest-repo",
106112
help="Only work with manifest repository if found. If not, the Error is thrown that list of Repositories ends up empty. For this filter to work, the Workspace needs to be present. And it is only applied after the processing of the Repositories", # noqa: E501
107113
dest="only_manifest",
108114
default=False,

tsrc/cli/manifest.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def configure_parser(subparser: argparse._SubParsersAction) -> None:
8484

8585

8686
def run(args: argparse.Namespace) -> None:
87-
gtf = GroupsToFind(args.groups)
87+
gtf = GroupsToFind(args.groups, args.ignore_if_group_not_found)
8888
groups_seen = simulate_get_workspace_with_repos(args)
8989
gtf.found_these(groups_seen)
9090

@@ -129,9 +129,13 @@ def run(args: argparse.Namespace) -> None:
129129
)
130130
if args.manifest_branch:
131131
cfg_update_data = ConfigUpdateData(manifest_branch=args.manifest_branch)
132-
status_header.register_change(
133-
cfg_update_data, [ConfigUpdateType.MANIFEST_BRANCH]
134-
)
132+
if (
133+
status_header.register_change(
134+
cfg_update_data, [ConfigUpdateType.MANIFEST_BRANCH]
135+
)
136+
is False
137+
):
138+
return
135139
status_header.display()
136140
status_collector = StatusCollector(
137141
workspace, ignore_group_item=args.ignore_group_item

tsrc/cli/status.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
ready_tmp_bare_repos,
2222
)
2323
from tsrc.manifest_common_data import ManifestsTypeOfData
24-
from tsrc.pcs_repo import get_deep_manifest_from_local_manifest_pcsrepo
24+
from tsrc.pcs_repo import PCSRepo, get_deep_manifest_from_local_manifest_pcsrepo
2525
from tsrc.repo import Repo
2626
from tsrc.status_endpoint import (
2727
BareStatus,
@@ -102,7 +102,7 @@ def configure_parser(subparser: argparse._SubParsersAction) -> None:
102102

103103

104104
def run(args: argparse.Namespace) -> None:
105-
gtf = GroupsToFind(args.groups)
105+
gtf = GroupsToFind(args.groups, args.ignore_if_group_not_found)
106106
groups_seen = simulate_get_workspace_with_repos(args)
107107
gtf.found_these(groups_seen)
108108

@@ -124,23 +124,23 @@ def run(args: argparse.Namespace) -> None:
124124
)
125125

126126
# DM (if present) + bare DM (if DM and present)
127-
dm = None
127+
dm_pcsr: Union[PCSRepo, None] = None
128128
bare_dm_repos: List[Repo] = []
129129
if args.use_deep_manifest is True:
130-
dm, gtf = get_deep_manifest_from_local_manifest_pcsrepo(
130+
dm_pcsr, gtf = get_deep_manifest_from_local_manifest_pcsrepo(
131131
workspace,
132132
gtf,
133133
)
134-
if dm and args.local_git_only is False:
134+
if dm_pcsr and args.local_git_only is False:
135135
# this require to check remote
136136
bare_dm_repos = prepare_tmp_bare_dm_repos(
137-
workspace, dm, gtf, num_jobs=get_num_jobs(args)
137+
workspace, dm_pcsr, gtf, num_jobs=get_num_jobs(args)
138138
)
139139

140140
wrs = WorkspaceReposSummary(
141141
workspace,
142142
gtf,
143-
dm,
143+
dm_pcsr,
144144
manifest_marker=args.use_manifest_marker,
145145
future_manifest=args.use_future_manifest,
146146
use_same_future_manifest=args.use_same_future_manifest,

tsrc/cli/sync.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,18 @@ def configure_parser(subparser: argparse._SubParsersAction) -> None:
5050
dest="ignore_group_item",
5151
help="ignore group element if it is not found among Manifest's Repos. WARNING: If you end up in need of this option, you have to understand that you end up with useles Manifest. Warnings will be printed for each Group element that is missing, so it may be easier to fix that. Using this option is NOT RECOMMENDED for normal use", # noqa: E501
5252
)
53+
parser.add_argument(
54+
"--clean",
55+
action="store_true",
56+
dest="do_clean",
57+
help="WARNING: you may loose files that are not under the version control. like such files that are ignored by '.gitignore'. sync to clean state, so the next sync can run smoothly. use with care.", # noqa: E501
58+
)
59+
parser.add_argument(
60+
"--hard-clean",
61+
action="store_true",
62+
dest="do_hard_clean",
63+
help="WARNING: you may loose files that are not under the version control and also files ignored by '.gitignore'. sync to clean state, that does not even contain ignored files. use with care.", # noqa: E501
64+
)
5365
parser.add_argument(
5466
"--no-correct-branch",
5567
action="store_false",
@@ -77,6 +89,8 @@ def run(args: argparse.Namespace) -> None:
7789
correct_branch = args.correct_branch
7890
workspace = get_workspace(args)
7991
num_jobs = get_num_jobs(args)
92+
do_clean = args.do_clean
93+
do_hard_clean = args.do_hard_clean
8094

8195
ignore_if_group_not_found: bool = False
8296
report_update_repo_groups: bool = False
@@ -93,10 +107,11 @@ def run(args: argparse.Namespace) -> None:
93107
found_groups = list(
94108
set(groups).intersection(local_manifest.group_list.groups)
95109
)
96-
workspace.update_config_repo_groups(
97-
groups=found_groups, ignore_group_item=args.ignore_group_item
98-
)
99-
report_update_repo_groups = True
110+
if update_config_repo_groups is True:
111+
workspace.update_config_repo_groups(
112+
groups=found_groups, ignore_group_item=args.ignore_group_item
113+
)
114+
report_update_repo_groups = True
100115

101116
if update_config_repo_groups is True:
102117
if args.ignore_if_group_not_found is True:
@@ -112,6 +127,8 @@ def run(args: argparse.Namespace) -> None:
112127
ui.info_2("Leaving repo_groups intact")
113128
else:
114129
ui.info_2("Not updating manifest")
130+
if args.ignore_if_group_not_found is True:
131+
ignore_if_group_not_found = True
115132

116133
workspace.repos = resolve_repos(
117134
workspace,
@@ -123,7 +140,9 @@ def run(args: argparse.Namespace) -> None:
123140
ignore_if_group_not_found=ignore_if_group_not_found,
124141
ignore_group_item=args.ignore_group_item,
125142
)
126-
143+
if len(workspace.repos) == 0:
144+
ui.info_1("Nothing to synchronize, skipping")
145+
return
127146
workspace.clone_missing(num_jobs=num_jobs)
128147
workspace.set_remotes(num_jobs=num_jobs)
129148
workspace.sync(
@@ -132,5 +151,6 @@ def run(args: argparse.Namespace) -> None:
132151
correct_branch=correct_branch,
133152
num_jobs=num_jobs,
134153
)
154+
workspace.clean(do_clean=do_clean, do_hard_clean=do_hard_clean, num_jobs=num_jobs)
135155
workspace.perform_filesystem_operations(ignore_group_item=args.ignore_group_item)
136156
ui.info_1("Workspace synchronized")

tsrc/cloner.py

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -180,31 +180,36 @@ def _choose_remote(self, repo: Repo) -> Remote:
180180

181181
return repo.remotes[0]
182182

183-
def bare_clone_repo(self, repo: Repo) -> None:
183+
def bare_clone_repo(self, repo: Repo) -> Path:
184184
# check if our Repo is bare
185185
repo_path = self.workspace_path / repo.dest
186186
parent = repo_path.parent
187187
parent.mkdir(parents=True, exist_ok=True)
188188
remote = self._choose_remote(repo)
189189
remote_url = remote.url
190190
if Path(str(repo_path) + os.sep + ".git").is_dir():
191-
return
192-
if repo._bare_clone_path:
193-
clone_args = [
194-
"clone",
195-
"--mirror",
196-
str(repo._bare_clone_path),
197-
str(repo_path) + os.sep + ".git",
198-
]
199-
else:
200-
clone_args = [
201-
"clone",
202-
"--mirror",
203-
remote_url,
204-
str(repo_path) + os.sep + ".git",
205-
]
191+
return repo_path
192+
clone_args = [
193+
"clone",
194+
"--mirror",
195+
remote_url,
196+
str(repo_path) + os.sep + ".git",
197+
]
206198

207-
self.run_git(parent, *clone_args)
199+
run_git_captured(parent, *clone_args)
200+
201+
# make sure from this moment on to use 'repo_path'
202+
203+
run_git_captured(
204+
repo_path, "config", "--bool", "core.bare", "false", check=False
205+
)
206+
207+
run_git_captured(repo_path, "remote", "remove", remote.name, check=False)
208+
run_git_captured(
209+
repo_path, "remote", "add", remote.name, remote_url, check=False
210+
)
211+
212+
return repo_path
208213

209214
def bare_set_branch(self, repo: Repo) -> bool:
210215

@@ -244,7 +249,8 @@ def bare_reset_repo(self, repo: Repo) -> None:
244249
def process(self, index: int, count: int, repo: Repo) -> Outcome:
245250

246251
self.info_count(index, count, repo.dest, end="\r")
247-
self.bare_clone_repo(repo)
252+
repo_path = self.bare_clone_repo(repo)
253+
self.run_git(repo_path, "fetch", "--all", "--prune")
248254
if self.bare_set_branch(repo) is True:
249255
self.bare_reset_repo(repo)
250256
# NOTE: not considering submodules (not useful for bare Repo)

tsrc/config_status.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from tsrc.config_data import ConfigUpdateData, ConfigUpdateType
1616
from tsrc.config_status_rc import ConfigStatusReturnCode
1717
from tsrc.config_tools import ConfigTools
18+
from tsrc.errors import Error
1819
from tsrc.status_header_dm import StatusHeaderDisplayMode
1920
from tsrc.workspace import Workspace
2021

@@ -110,6 +111,7 @@ def _manifest_branch_report_issue(
110111
"ignoring",
111112
ui.reset,
112113
)
114+
raise Error("aborting Manifest branch change")
113115
if rc == ConfigStatusReturnCode.CANCEL:
114116
branch_0 = self.workspace.config.manifest_branch_0
115117
if branch == branch_0:

tsrc/dump_manifest.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -584,9 +584,7 @@ def _add_repos_based_on_mris(
584584
rr["branch"] = mri.branch
585585
if mri.tag:
586586
rr["tag"] = mri.tag
587-
if (
588-
not mri.branch and not mri.tag and mri.sha1
589-
) or mdo.sha1_only is True:
587+
if (not mri.branch and not mri.tag and mri.sha1) or mdo.sha1_on is True:
590588
rr["sha1"] = mri.sha1
591589

592590
# TODO: add comment in form of '\n' just to better separate Repos
@@ -718,7 +716,12 @@ def _update_on_update_on_items_on_repo(
718716
and mri.sha1 # noqa: W503
719717
and y[u_i] != mri.sha1 # noqa: W503
720718
)
721-
or (mdo.sha1_only is True and y[u_i] != mri.sha1) # noqa: W503
719+
or (mdo.sha1_on is True and y[u_i] != mri.sha1) # noqa: W503
720+
or (
721+
mdo.sha1_off is False
722+
and (mri.ahead > 0 or mri.behind > 0)
723+
and y[u_i] != mri.sha1
724+
)
722725
):
723726
y[u_i] = mri.sha1
724727
ret_updated = True
@@ -833,7 +836,10 @@ def _update_on_items_on_repo(
833836
if not s_item:
834837
if mri.sha1:
835838
s_item.append("sha1")
836-
if "sha1" not in s_item and mdo.sha1_only is True:
839+
if "sha1" not in s_item and (
840+
mdo.sha1_on is True
841+
or (mdo.sha1_off is False and (mri.behind > 0 or mri.ahead > 0))
842+
):
837843
s_item.append("sha1")
838844

839845
# add these on item
@@ -1145,8 +1151,10 @@ def do_create(
11451151
if items.tag:
11461152
rr["tag"] = items.tag
11471153
if (
1148-
not items.branch and not items.tag and items.sha1
1149-
) or mdo.sha1_only is True:
1154+
(not items.branch and not items.tag and items.sha1)
1155+
or mdo.sha1_on is True
1156+
or (mdo.sha1_off is False and (items.ahead > 0 or items.behind > 0))
1157+
):
11501158
rr["sha1"] = items.sha1
11511159

11521160
y["repos"].append(rr)

tsrc/dump_manifest_args.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,19 @@ def __init__(self, args: argparse.Namespace) -> None:
6767

6868
def _get_manifest_data_options(self) -> ManifestDataOptions:
6969
mdo = ManifestDataOptions()
70-
if self.args.sha1_only is True:
71-
mdo.sha1_only = True
70+
if self.args.sha1_on is True and self.args.sha1_off is True:
71+
raise Exception("'--sha1-on' and '--sha1-off' are mutually exclusive")
72+
elif self.args.sha1_on is True:
73+
mdo.sha1_on = True
74+
elif self.args.sha1_off is True:
75+
mdo.sha1_off = True
7276
if self.args.skip_manifest is True:
7377
mdo.skip_manifest = True
7478
if self.args.only_manifest is True:
7579
mdo.only_manifest = True
7680
if self.args.skip_manifest is True and self.args.only_manifest is True:
7781
raise Exception(
78-
"'--skip-manifest' and '--only-manifest' are mutually exclusive"
82+
"'--skip-manifest-repo' and '--only-manifest-repo' are mutually exclusive"
7983
)
8084
return mdo
8185

0 commit comments

Comments
 (0)