batchfetch 1.2.5__tar.gz → 1.2.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: batchfetch
3
- Version: 1.2.5
3
+ Version: 1.2.6
4
4
  Summary: Efficiently clone and pull multiple Git repositories.
5
5
  Home-page: https://github.com/jamescherti/batchfetch
6
6
  Author: James Cherti
@@ -89,6 +89,31 @@ options:
89
89
  -v, --verbose Enable verbose mode.
90
90
  ```
91
91
 
92
+ ## Frequently Asked Questions
93
+
94
+ ### What are untracked files?
95
+
96
+ The parent directory of the "path:" value defines the managed directory, where the directory of each path is considered as the managed directory.
97
+
98
+ For example, if the "path:" value is `file/my-project`, the managed directory will be `file/`. Any file within `file/` that is not managed by batchfetch will be considered an untracked file.
99
+
100
+ When *batchfetch* encounters an untracked file, it displays an error message to inform users about paths that are not managed by the system. The message provides clear instructions on how to handle these paths by adding them to the `options.ignore_untracked_paths` list, enabling users to manage untracked files effectively.
101
+
102
+ Here is an example of a *batchfetch.yaml* file that enables *batchfetch* to accept a list of untracked files:
103
+
104
+ ``` yaml
105
+ options:
106
+ ignore_untracked_paths:
107
+ - ./test
108
+ - /absolute/path
109
+ - ../relative/path
110
+
111
+ tasks:
112
+ - git: https://github.com/user/project
113
+ ```
114
+
115
+ By default, *batchfetch.yaml* is the only untracked file that is ignored. The user does not need to add it to the *ignore_untracked_paths* option.
116
+
92
117
  ## License
93
118
 
94
119
  Copyright (C) 2024 [James Cherti](https://www.jamescherti.com)
@@ -69,6 +69,31 @@ options:
69
69
  -v, --verbose Enable verbose mode.
70
70
  ```
71
71
 
72
+ ## Frequently Asked Questions
73
+
74
+ ### What are untracked files?
75
+
76
+ The parent directory of the "path:" value defines the managed directory, where the directory of each path is considered as the managed directory.
77
+
78
+ For example, if the "path:" value is `file/my-project`, the managed directory will be `file/`. Any file within `file/` that is not managed by batchfetch will be considered an untracked file.
79
+
80
+ When *batchfetch* encounters an untracked file, it displays an error message to inform users about paths that are not managed by the system. The message provides clear instructions on how to handle these paths by adding them to the `options.ignore_untracked_paths` list, enabling users to manage untracked files effectively.
81
+
82
+ Here is an example of a *batchfetch.yaml* file that enables *batchfetch* to accept a list of untracked files:
83
+
84
+ ``` yaml
85
+ options:
86
+ ignore_untracked_paths:
87
+ - ./test
88
+ - /absolute/path
89
+ - ../relative/path
90
+
91
+ tasks:
92
+ - git: https://github.com/user/project
93
+ ```
94
+
95
+ By default, *batchfetch.yaml* is the only untracked file that is ignored. The user does not need to add it to the *ignore_untracked_paths* option.
96
+
72
97
  ## License
73
98
 
74
99
  Copyright (C) 2024 [James Cherti](https://www.jamescherti.com)
@@ -107,6 +107,7 @@ class TaskBatchFetch(TaskBase):
107
107
  self.global_options_schema: Dict[Any, Any] = {
108
108
  Optional("exec_before"): Or([str], str),
109
109
  Optional("exec_after"): Or([str], str),
110
+ Optional("ignore_untracked_paths"): Or([str], str),
110
111
  }
111
112
 
112
113
  self.task_schema: Dict[Any, Any] = {
@@ -120,6 +121,7 @@ class TaskBatchFetch(TaskBase):
120
121
  self.global_options_values: Dict[str, Any] = {
121
122
  "exec_before": [],
122
123
  "exec_after": [],
124
+ "ignore_untracked_paths": [],
123
125
  }
124
126
 
125
127
  self.task_default_values: Dict[str, Any] = {
@@ -43,7 +43,8 @@ class BatchFetchCli:
43
43
  def __init__(self, max_workers: int, verbose: bool = False):
44
44
  self.cfg: dict = {}
45
45
  self.folder = Path(".")
46
- self.managed_paths: Set[Path] = set()
46
+ self.tracked_paths: Dict[Path, Set[str]] = {}
47
+ self.ignore_untracked_paths: Set[Path] = set()
47
48
  self.verbose = verbose
48
49
  self.max_workers = max_workers
49
50
  self._logger = logging.getLogger(self.__class__.__name__)
@@ -97,6 +98,22 @@ class BatchFetchCli:
97
98
  file=sys.stderr)
98
99
  sys.exit(1)
99
100
 
101
+ self.ignore_untracked_paths.add(Path(path).absolute())
102
+ self.ignore_untracked_paths.add(Path(path).resolve())
103
+ untracked_paths = None
104
+ if "options" in yaml_dict:
105
+ untracked_paths = \
106
+ yaml_dict["options"]["ignore_untracked_paths"]
107
+
108
+ if isinstance(untracked_paths, str):
109
+ untracked_paths = [untracked_paths]
110
+
111
+ if untracked_paths:
112
+ for ignore_untracked_path in untracked_paths:
113
+ self.ignore_untracked_paths.add(
114
+ Path(ignore_untracked_path).absolute()
115
+ )
116
+
100
117
  self._loads(dict(yaml_dict))
101
118
  except OSError as err:
102
119
  raise BatchFetchError(str(err)) from err
@@ -154,7 +171,7 @@ class BatchFetchCli:
154
171
  error = False
155
172
  threads = []
156
173
  num_success = 0
157
- self.managed_paths = set()
174
+ self.tracked_paths = {}
158
175
 
159
176
  executor_update = ThreadPoolExecutor(max_workers=self.max_workers)
160
177
 
@@ -165,7 +182,15 @@ class BatchFetchCli:
165
182
  for task in all_tasks:
166
183
  self.dirs_relative_to_batchfetch.add(str(task["path"]))
167
184
  if not task["delete"]:
168
- self.managed_paths.add(Path(task["path"]).absolute())
185
+ full_path = Path(task["path"]).absolute()
186
+ base_path = full_path.parent
187
+ try:
188
+ self.tracked_paths[base_path]
189
+ except KeyError:
190
+ self.tracked_paths[base_path] = set()
191
+
192
+ self.tracked_paths[base_path].add(full_path.name)
193
+
169
194
  threads.append(executor_update.submit(task.update))
170
195
 
171
196
  for future in as_completed(threads):
@@ -207,6 +232,8 @@ class BatchFetchCli:
207
232
 
208
233
  return False
209
234
  else:
235
+ self._find_untracked_paths()
236
+
210
237
  if num_success == 0:
211
238
  print("Nothing to do.")
212
239
  elif not self.verbose:
@@ -214,11 +241,36 @@ class BatchFetchCli:
214
241
 
215
242
  return True
216
243
 
244
+ def _find_untracked_paths(self):
245
+ "Find the files that are untracked and should be deleted."
246
+ untracked_paths = set()
247
+ for tracked_dir, tracked_filenames in self.tracked_paths.items():
248
+ actual_filenames = {file.name for file in tracked_dir.iterdir()}
249
+ for filename in actual_filenames - tracked_filenames:
250
+ full_path = tracked_dir / filename
251
+ if full_path in self.ignore_untracked_paths:
252
+ continue
253
+
254
+ untracked_paths.add(full_path)
255
+
256
+ if untracked_paths:
257
+ err_str = "The following files need to be deleted:\n"
258
+ for path in untracked_paths:
259
+ err_str += (" - " +
260
+ str(path) +
261
+ ("/" if path.is_dir() else "") +
262
+ "\n")
263
+ err_str += ("The paths above are not managed by batchfetch."
264
+ " To retain them, add them to the "
265
+ "options.ignore_untracked_paths list, using either "
266
+ "relative or absolute paths")
267
+ raise BatchFetchError(err_str)
268
+
217
269
 
218
270
  def parse_args():
219
271
  """Parse the command line arguments."""
220
272
  desc = __doc__
221
- usage = "%(prog)s [--option] [args]"
273
+ usage = "%(prog)s [--option]"
222
274
  parser = argparse.ArgumentParser(description=desc, usage=usage)
223
275
 
224
276
  parser.add_argument("-f",
@@ -255,8 +307,8 @@ def run_batchfetch_procedure(file: Path, args) -> int:
255
307
  errno = 0
256
308
  batchfetch_cli = BatchFetchCli(verbose=args.verbose,
257
309
  max_workers=int(args.jobs))
258
- batchfetch_cli.load(file)
259
310
  os.chdir(file.parent)
311
+ batchfetch_cli.load(file)
260
312
 
261
313
  try:
262
314
  if not batchfetch_cli.run_tasks():
@@ -463,22 +463,22 @@ class BatchFetchGit(TaskBatchFetch):
463
463
  self._run(cmd, cwd=str(self.git_local_dir), env=self.env)
464
464
  self._git_fetch_origin_done = True
465
465
 
466
- def _repo_update_submodules(self):
467
- # This parameter instructs Git to initiate the update
468
- # process for submodules:
469
- # 1. Git fetches the commits specified in the parent
470
- # repository's configuration for each submodule.
471
- # 2. Updates are based solely on the commit pointers stored
472
- # within the parent repository's submodule configuration.
473
- # 3. It does not directly consult the upstream repositories
474
- # of the submodules.
475
- # 4. Submodules are updated to reflect the exact commits
476
- # revision in the parent repository's configuration,
477
- # potentially lagging behind the latest changes made in the
478
- # upstream repositories.
479
- if self.git_local_dir.joinpath(".gitmodules").is_file():
480
- cmd = ["git", "submodule", "update", "--recursive"]
481
- self._run(cmd, cwd=str(self.git_local_dir), env=self.env)
466
+ # def _repo_update_submodules(self):
467
+ # # This parameter instructs Git to initiate the update
468
+ # # process for submodules:
469
+ # # 1. Git fetches the commits specified in the parent
470
+ # # repository's configuration for each submodule.
471
+ # # 2. Updates are based solely on the commit pointers stored
472
+ # # within the parent repository's submodule configuration.
473
+ # # 3. It does not directly consult the upstream repositories
474
+ # # of the submodules.
475
+ # # 4. Submodules are updated to reflect the exact commits
476
+ # # revision in the parent repository's configuration,
477
+ # # potentially lagging behind the latest changes made in the
478
+ # # upstream repositories.
479
+ # if self.git_local_dir.joinpath(".gitmodules").is_file():
480
+ # cmd = ["git", "submodule", "update", "--recursive"]
481
+ # self._run(cmd, cwd=str(self.git_local_dir), env=self.env)
482
482
 
483
483
  def _repo_fix_remote_origin(self):
484
484
  correct_origin_url = self[self.main_key]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: batchfetch
3
- Version: 1.2.5
3
+ Version: 1.2.6
4
4
  Summary: Efficiently clone and pull multiple Git repositories.
5
5
  Home-page: https://github.com/jamescherti/batchfetch
6
6
  Author: James Cherti
@@ -89,6 +89,31 @@ options:
89
89
  -v, --verbose Enable verbose mode.
90
90
  ```
91
91
 
92
+ ## Frequently Asked Questions
93
+
94
+ ### What are untracked files?
95
+
96
+ The parent directory of the "path:" value defines the managed directory, where the directory of each path is considered as the managed directory.
97
+
98
+ For example, if the "path:" value is `file/my-project`, the managed directory will be `file/`. Any file within `file/` that is not managed by batchfetch will be considered an untracked file.
99
+
100
+ When *batchfetch* encounters an untracked file, it displays an error message to inform users about paths that are not managed by the system. The message provides clear instructions on how to handle these paths by adding them to the `options.ignore_untracked_paths` list, enabling users to manage untracked files effectively.
101
+
102
+ Here is an example of a *batchfetch.yaml* file that enables *batchfetch* to accept a list of untracked files:
103
+
104
+ ``` yaml
105
+ options:
106
+ ignore_untracked_paths:
107
+ - ./test
108
+ - /absolute/path
109
+ - ../relative/path
110
+
111
+ tasks:
112
+ - git: https://github.com/user/project
113
+ ```
114
+
115
+ By default, *batchfetch.yaml* is the only untracked file that is ignored. The user does not need to add it to the *ignore_untracked_paths* option.
116
+
92
117
  ## License
93
118
 
94
119
  Copyright (C) 2024 [James Cherti](https://www.jamescherti.com)
@@ -22,7 +22,7 @@ from setuptools import find_packages, setup
22
22
 
23
23
  setup(
24
24
  name="batchfetch",
25
- version="1.2.5",
25
+ version="1.2.6",
26
26
  packages=find_packages(),
27
27
  description="Efficiently clone and pull multiple Git repositories.",
28
28
  license="GPLv3",
File without changes
File without changes