git2svn 0.5.2__tar.gz → 0.6.0__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.
Files changed (42) hide show
  1. {git2svn-0.5.2/git2svn.egg-info → git2svn-0.6.0}/PKG-INFO +1 -1
  2. {git2svn-0.5.2 → git2svn-0.6.0}/git2svn/__init__.py +2 -0
  3. {git2svn-0.5.2 → git2svn-0.6.0}/git2svn/_version.py +2 -2
  4. {git2svn-0.5.2 → git2svn-0.6.0}/git2svn/cli.py +20 -1
  5. git2svn-0.6.0/git2svn/colors.py +104 -0
  6. {git2svn-0.5.2 → git2svn-0.6.0}/git2svn/core.py +45 -28
  7. {git2svn-0.5.2 → git2svn-0.6.0/git2svn.egg-info}/PKG-INFO +1 -1
  8. {git2svn-0.5.2 → git2svn-0.6.0}/git2svn.egg-info/SOURCES.txt +1 -0
  9. {git2svn-0.5.2 → git2svn-0.6.0}/git2svn.egg-info/scm_file_list.json +1 -0
  10. {git2svn-0.5.2 → git2svn-0.6.0}/tests/test_git2svn.py +53 -1
  11. {git2svn-0.5.2 → git2svn-0.6.0}/.githooks/pre-commit +0 -0
  12. {git2svn-0.5.2 → git2svn-0.6.0}/.github/dependabot.yml +0 -0
  13. {git2svn-0.5.2 → git2svn-0.6.0}/.github/workflows/ci.yml +0 -0
  14. {git2svn-0.5.2 → git2svn-0.6.0}/.github/workflows/release.yml +0 -0
  15. {git2svn-0.5.2 → git2svn-0.6.0}/.gitignore +0 -0
  16. {git2svn-0.5.2 → git2svn-0.6.0}/.pre-commit-config.yaml +0 -0
  17. {git2svn-0.5.2 → git2svn-0.6.0}/CONTRIBUTING.md +0 -0
  18. {git2svn-0.5.2 → git2svn-0.6.0}/LICENSE.md +0 -0
  19. {git2svn-0.5.2 → git2svn-0.6.0}/README.md +0 -0
  20. {git2svn-0.5.2 → git2svn-0.6.0}/docs/arc42/README.md +0 -0
  21. {git2svn-0.5.2 → git2svn-0.6.0}/docs/arc42/adrs.md +0 -0
  22. {git2svn-0.5.2 → git2svn-0.6.0}/docs/arc42/concepts.md +0 -0
  23. {git2svn-0.5.2 → git2svn-0.6.0}/docs/req42.md +0 -0
  24. {git2svn-0.5.2 → git2svn-0.6.0}/docs/user-guide/README.md +0 -0
  25. {git2svn-0.5.2 → git2svn-0.6.0}/docs/user-guide/commands.md +0 -0
  26. {git2svn-0.5.2 → git2svn-0.6.0}/docs/user-guide/integration-workflow.md +0 -0
  27. {git2svn-0.5.2 → git2svn-0.6.0}/docs/user-guide/troubleshooting.md +0 -0
  28. {git2svn-0.5.2 → git2svn-0.6.0}/git2svn/__main__.py +0 -0
  29. {git2svn-0.5.2 → git2svn-0.6.0}/git2svn/eol.py +0 -0
  30. {git2svn-0.5.2 → git2svn-0.6.0}/git2svn/git.py +0 -0
  31. {git2svn-0.5.2 → git2svn-0.6.0}/git2svn/patcher.py +0 -0
  32. {git2svn-0.5.2 → git2svn-0.6.0}/git2svn/py.typed +0 -0
  33. {git2svn-0.5.2 → git2svn-0.6.0}/git2svn/state.py +0 -0
  34. {git2svn-0.5.2 → git2svn-0.6.0}/git2svn/svn.py +0 -0
  35. {git2svn-0.5.2 → git2svn-0.6.0}/git2svn.egg-info/dependency_links.txt +0 -0
  36. {git2svn-0.5.2 → git2svn-0.6.0}/git2svn.egg-info/entry_points.txt +0 -0
  37. {git2svn-0.5.2 → git2svn-0.6.0}/git2svn.egg-info/requires.txt +0 -0
  38. {git2svn-0.5.2 → git2svn-0.6.0}/git2svn.egg-info/top_level.txt +0 -0
  39. {git2svn-0.5.2 → git2svn-0.6.0}/git2svn.py +0 -0
  40. {git2svn-0.5.2 → git2svn-0.6.0}/pyproject.toml +0 -0
  41. {git2svn-0.5.2 → git2svn-0.6.0}/setup.cfg +0 -0
  42. {git2svn-0.5.2 → git2svn-0.6.0}/tests/test_e2e.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: git2svn
3
- Version: 0.5.2
3
+ Version: 0.6.0
4
4
  Summary: CLI utility to synchronize changes from a local Git repository to a local SVN working copy
5
5
  Author: Simon
6
6
  License: Apache-2.0
@@ -6,6 +6,7 @@ to a local Subversion (SVN) working copy.
6
6
  from __future__ import annotations
7
7
 
8
8
  from .cli import build_parser, main, parse_cli_args
9
+ from .colors import TerminalColor
9
10
  from .core import Synchronizer
10
11
  from .eol import detect_file_eol, normalize_file_eol
11
12
  from .git import FileChange, GitRepo, parse_name_status, parse_name_status_z, parse_ref_arguments
@@ -43,6 +44,7 @@ __all__ = [
43
44
  "SvnLockError",
44
45
  "SvnOutOfDateError",
45
46
  "Patcher",
47
+ "TerminalColor",
46
48
  "FileChange",
47
49
  "parse_ref_arguments",
48
50
  "parse_name_status",
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.5.2'
22
- __version_tuple__ = version_tuple = (0, 5, 2)
21
+ __version__ = version = '0.6.0'
22
+ __version_tuple__ = version_tuple = (0, 6, 0)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -67,6 +67,12 @@ def build_parser() -> argparse.ArgumentParser:
67
67
  default=argparse.SUPPRESS,
68
68
  help="Enable verbose output",
69
69
  )
70
+ common_parser.add_argument(
71
+ "--color",
72
+ choices=["auto", "always", "never"],
73
+ default=argparse.SUPPRESS,
74
+ help="Control colored output (auto, always, never; default: git config git2svn.color or auto)",
75
+ )
70
76
 
71
77
  parser = argparse.ArgumentParser(
72
78
  prog="git2svn",
@@ -435,8 +441,21 @@ def main(argv: Optional[List[str]] = None) -> int:
435
441
  print(f"Error: '{svn_dir}' does not appear to be an SVN working copy (no .svn found).", file=sys.stderr)
436
442
  return 1
437
443
 
444
+ # Color mode: CLI --color -> git config git2svn.color -> "auto"
445
+ color_mode = getattr(args, "color", None)
446
+ if not color_mode:
447
+ config_color = git_repo.get_config("git2svn.color")
448
+ color_mode = config_color if config_color in ("auto", "always", "never") else "auto"
449
+
438
450
  patcher = Patcher(svn_dir, dry_run=dry_run)
439
- sync_mgr = Synchronizer(git_repo, svn_workspace, patcher, dry_run=dry_run, auto_update=auto_update)
451
+ sync_mgr = Synchronizer(
452
+ git_repo,
453
+ svn_workspace,
454
+ patcher,
455
+ dry_run=dry_run,
456
+ auto_update=auto_update,
457
+ color_mode=color_mode,
458
+ )
440
459
 
441
460
  # Fallback to git2svn.defaultRange if ref1 is omitted
442
461
  ref1 = getattr(args, "ref1", None)
@@ -0,0 +1,104 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import sys
5
+
6
+
7
+ class TerminalColor:
8
+ """ANSI terminal color formatting with auto/always/never detection and NO_COLOR support."""
9
+
10
+ # ANSI Styles & Colors
11
+ RESET = "\033[0m"
12
+ BOLD = "\033[1m"
13
+ DIM = "\033[2m"
14
+
15
+ RED = "\033[31m"
16
+ GREEN = "\033[32m"
17
+ YELLOW = "\033[33m"
18
+ BLUE = "\033[34m"
19
+ MAGENTA = "\033[35m"
20
+ CYAN = "\033[36m"
21
+ GRAY = "\033[90m"
22
+
23
+ BOLD_RED = "\033[1;31m"
24
+ BOLD_GREEN = "\033[1;32m"
25
+ BOLD_YELLOW = "\033[1;33m"
26
+ BOLD_CYAN = "\033[1;36m"
27
+
28
+ def __init__(self, mode: str = "auto"):
29
+ self.mode = mode.lower()
30
+ self.enabled = self._resolve_enabled()
31
+
32
+ def _resolve_enabled(self) -> bool:
33
+ if self.mode == "always":
34
+ return True
35
+ if self.mode == "never":
36
+ return False
37
+
38
+ # Mode is "auto"
39
+ # 1. Respect NO_COLOR standard (https://no-color.org/)
40
+ if "NO_COLOR" in os.environ and os.environ["NO_COLOR"] != "":
41
+ return False
42
+
43
+ # 2. Check for dumb terminals
44
+ if os.environ.get("TERM") == "dumb":
45
+ return False
46
+
47
+ # 3. Check if stdout is an interactive TTY
48
+ try:
49
+ return sys.stdout.isatty()
50
+ except Exception:
51
+ return False
52
+
53
+ def colorize(self, text: str, ansi_code: str) -> str:
54
+ """Wrap text in ANSI color escape codes if coloring is enabled."""
55
+ if not self.enabled:
56
+ return text
57
+ return f"{ansi_code}{text}{self.RESET}"
58
+
59
+ # Style helpers
60
+ def bold(self, text: str) -> str:
61
+ return self.colorize(text, self.BOLD)
62
+
63
+ def dim(self, text: str) -> str:
64
+ return self.colorize(text, self.DIM)
65
+
66
+ def red(self, text: str) -> str:
67
+ return self.colorize(text, self.RED)
68
+
69
+ def green(self, text: str) -> str:
70
+ return self.colorize(text, self.GREEN)
71
+
72
+ def yellow(self, text: str) -> str:
73
+ return self.colorize(text, self.YELLOW)
74
+
75
+ def cyan(self, text: str) -> str:
76
+ return self.colorize(text, self.CYAN)
77
+
78
+ def gray(self, text: str) -> str:
79
+ return self.colorize(text, self.GRAY)
80
+
81
+ def bold_red(self, text: str) -> str:
82
+ return self.colorize(text, self.BOLD_RED)
83
+
84
+ def bold_green(self, text: str) -> str:
85
+ return self.colorize(text, self.BOLD_GREEN)
86
+
87
+ def bold_yellow(self, text: str) -> str:
88
+ return self.colorize(text, self.BOLD_YELLOW)
89
+
90
+ def bold_cyan(self, text: str) -> str:
91
+ return self.colorize(text, self.BOLD_CYAN)
92
+
93
+ # Semantic badges
94
+ def ok(self, text: str = "OK") -> str:
95
+ return self.bold_green(f"✔ {text}")
96
+
97
+ def clean(self, text: str = "Clean") -> str:
98
+ return self.bold_green(f"✔ {text}")
99
+
100
+ def warn(self, text: str = "Warning") -> str:
101
+ return self.bold_yellow(f"⚠ {text}")
102
+
103
+ def error(self, text: str = "Error") -> str:
104
+ return self.bold_red(f"✖ {text}")
@@ -7,6 +7,7 @@ import sys
7
7
  from pathlib import Path
8
8
  from typing import Dict, List, Optional
9
9
 
10
+ from .colors import TerminalColor
10
11
  from .eol import detect_file_eol, normalize_file_eol
11
12
  from .git import FileChange, GitRepo, parse_ref_arguments
12
13
  from .patcher import Patcher
@@ -32,12 +33,14 @@ class Synchronizer:
32
33
  patcher: Patcher,
33
34
  dry_run: bool = False,
34
35
  auto_update: bool = False,
36
+ color_mode: str = "auto",
35
37
  ):
36
38
  self.git = git_repo
37
39
  self.svn = svn_workspace
38
40
  self.patcher = patcher
39
41
  self.dry_run = dry_run
40
42
  self.auto_update = auto_update
43
+ self.color = TerminalColor(color_mode)
41
44
 
42
45
  def show_identity_banner(self, target_ref_spec: str) -> None:
43
46
  """Display identity banner showing active Git and SVN target branches/URLs."""
@@ -522,6 +525,7 @@ class Synchronizer:
522
525
  1 if errors, working copy locked, merge conflicts, or linear violations exist
523
526
  """
524
527
  has_error = False
528
+ c = self.color
525
529
 
526
530
  # 1. Check in-progress replay state
527
531
  state = load_replay_state(self.svn.workspace_dir)
@@ -534,59 +538,69 @@ class Synchronizer:
534
538
  completed = state.get("completed_commits", 0)
535
539
  total = state.get("total_commits", len(remaining) + 1)
536
540
 
537
- print("[Replay In Progress]")
538
- print(f' State : PAUSED (conflict at commit {current_commit[:8]} "{first_msg}")')
541
+ print(c.bold_red("[Replay In Progress]"))
542
+ print(
543
+ f' State : {c.bold_red("PAUSED")} (conflict at commit {c.yellow(current_commit[:8])} "{first_msg}")'
544
+ )
539
545
  print(f" Progress : {completed} of {total} commits applied ({len(remaining)} remaining)")
540
546
 
541
547
  rej_files = find_conflict_artifacts(self.svn.workspace_dir)
542
548
  if rej_files:
543
549
  rel_rejs = [str(r.relative_to(self.svn.workspace_dir)) for r in rej_files]
544
- print(f" Conflicts : {', '.join(rel_rejs)}")
545
- print(" Action : Resolve conflicts and run 'git2svn replay --continue' (or '--abort' / '--skip')\n")
550
+ print(f" Conflicts : {c.bold_red(', '.join(rel_rejs))}")
551
+ action_text = (
552
+ f"Resolve conflicts and run {c.bold_cyan('git2svn replay --continue')} (or '--abort' / '--skip')"
553
+ )
554
+ print(f" Action : {action_text}\n")
546
555
 
547
556
  # 2. Git Workspace Status
548
557
  git_clean = self.git.is_clean()
549
558
  branch = self.git.get_current_branch()
550
559
  head_commit = self.git.get_head_commit()
551
560
  head_subject = self.git.get_head_subject()
552
- head_desc = f'{head_commit} "{head_subject}"' if head_subject else head_commit
561
+ head_desc = f'{c.yellow(head_commit)} "{head_subject}"' if head_subject else c.yellow(head_commit)
553
562
 
554
- print("[Git Workspace]")
563
+ print(c.bold_cyan("[Git Workspace]"))
555
564
  print(f" Repository: {self.git.repo_dir}")
556
- print(f" Branch : {branch} (at {head_desc})")
557
- print(f" Tree : {'Clean' if git_clean else 'Dirty (uncommitted changes present)'}")
565
+ print(f" Branch : {c.bold(branch)} (at {head_desc})")
566
+ if git_clean:
567
+ print(f" Tree : {c.bold_green('Clean')}")
568
+ else:
569
+ print(f" Tree : {c.bold_yellow('Dirty')} (uncommitted changes present)")
558
570
 
559
571
  # 3. SVN Working Copy Status
560
- print("\n[SVN Working Copy]")
572
+ print(f"\n{c.bold_cyan('[SVN Working Copy]')}")
561
573
  print(f" Path : {self.svn.workspace_dir}")
562
574
  svn_info = self.svn.get_info()
563
575
  svn_url = svn_info.get("URL") or svn_info.get("Relative URL") or "unknown"
564
576
  svn_rev = svn_info.get("Revision")
565
- rev_str = f" (r{svn_rev})" if svn_rev else ""
577
+ rev_str = f" ({c.dim(f'r{svn_rev}')})" if svn_rev else ""
566
578
  print(f" Target : {svn_url}{rev_str}")
567
579
 
568
580
  try:
569
581
  svn_clean = self.svn.is_clean()
570
582
  if svn_clean:
571
- print(" Tree : Clean (no uncommitted changes, unlocked)")
583
+ print(f" Tree : {c.bold_green('Clean')} (no uncommitted changes, unlocked)")
572
584
  else:
573
585
  uncommitted = self.svn.get_status_summary()
574
- print(f" Tree : Dirty ({len(uncommitted)} uncommitted changes)")
586
+ print(f" Tree : {c.bold_yellow(f'Dirty ({len(uncommitted)} uncommitted changes)')}")
575
587
  for item in uncommitted[:5]:
576
588
  print(f" {item}")
577
589
  if len(uncommitted) > 5:
578
- print(f" ... and {len(uncommitted) - 5} more")
590
+ print(f" {c.dim(f'... and {len(uncommitted) - 5} more')}")
579
591
  except SvnError as e:
580
592
  has_error = True
581
- print(f" Tree : Error: {e.args[0].splitlines()[0]}")
593
+ print(f" Tree : {c.bold_red(f'Error: {e.args[0].splitlines()[0]}')}")
582
594
 
583
595
  # 4. Synchronization Queue & Default Range
584
596
  default_range = self.git.get_config("git2svn.defaultRange")
585
- print("\n[Synchronization]")
597
+ print(f"\n{c.bold_cyan('[Synchronization]')}")
586
598
  if not default_range:
587
- print(" Range : Not configured (run 'git2svn setup' or 'git config git2svn.defaultRange <range>')")
599
+ print(
600
+ f" Range : {c.dim('Not configured')} (run 'git2svn setup' or 'git config git2svn.defaultRange <range>')"
601
+ )
588
602
  else:
589
- print(f" Range : {default_range}")
603
+ print(f" Range : {c.bold(default_range)}")
590
604
  try:
591
605
  is_single, start_ref, end_ref = parse_ref_arguments(default_range)
592
606
  if is_single:
@@ -595,11 +609,13 @@ class Synchronizer:
595
609
  else:
596
610
  assert end_ref is not None
597
611
  if not self.git.ref_exists(start_ref):
598
- print(f" Warning : Range start ref '{start_ref}' not found in Git.")
612
+ warn_msg = f"Range start ref '{start_ref}' not found in Git."
613
+ print(f" Warning : {c.bold_yellow(warn_msg)}")
599
614
  commits = []
600
615
  merges = []
601
616
  elif not self.git.ref_exists(end_ref):
602
- print(f" Warning : Range end ref '{end_ref}' not found in Git.")
617
+ warn_msg = f"Range end ref '{end_ref}' not found in Git."
618
+ print(f" Warning : {c.bold_yellow(warn_msg)}")
603
619
  commits = []
604
620
  merges = []
605
621
  else:
@@ -608,24 +624,25 @@ class Synchronizer:
608
624
 
609
625
  if merges:
610
626
  has_error = True
611
- print(
612
- f" Linearity : INVALID ({len(merges)} merge commits found in range - linear rebase required)"
613
- )
627
+ inv_msg = f"INVALID ({len(merges)} merge commits found in range - linear rebase required)"
628
+ print(f" Linearity : {c.bold_red(inv_msg)}")
614
629
  else:
615
- print(" Linearity : OK (strictly linear)")
630
+ print(f" Linearity : {c.bold_green('OK')} (strictly linear)")
616
631
 
617
632
  if not commits:
618
- print(" Pending : In sync (0 commits to replay)")
633
+ print(f" Pending : {c.bold_green('In sync')} (0 commits to replay)")
619
634
  else:
620
- print(f" Pending : {len(commits)} commit(s) ready to replay:")
635
+ pending_title = f"{len(commits)} commit(s) ready to replay:"
636
+ print(f" Pending : {c.bold_yellow(pending_title)}")
621
637
  for idx, c_hash in enumerate(commits[:10], start=1):
622
638
  msg = self.git.get_commit_message(c_hash)
623
639
  subject = msg.splitlines()[0] if msg else ""
624
- print(f" {idx}. [{c_hash[:8]}] {subject}")
640
+ print(f" {c.dim(f'{idx}.')} [{c.yellow(c_hash[:8])}] {subject}")
625
641
  if len(commits) > 10:
626
- print(f" ... and {len(commits) - 10} more")
642
+ print(f" {c.dim(f'... and {len(commits) - 10} more')}")
627
643
  except Exception as e:
628
- print(f" Error : Could not parse range '{default_range}': {e}")
644
+ err_msg = f"Could not parse range '{default_range}': {e}"
645
+ print(f" Error : {c.bold_red(err_msg)}")
629
646
  has_error = True
630
647
 
631
648
  if has_error:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: git2svn
3
- Version: 0.5.2
3
+ Version: 0.6.0
4
4
  Summary: CLI utility to synchronize changes from a local Git repository to a local SVN working copy
5
5
  Author: Simon
6
6
  License: Apache-2.0
@@ -21,6 +21,7 @@ git2svn/__init__.py
21
21
  git2svn/__main__.py
22
22
  git2svn/_version.py
23
23
  git2svn/cli.py
24
+ git2svn/colors.py
24
25
  git2svn/core.py
25
26
  git2svn/eol.py
26
27
  git2svn/git.py
@@ -21,6 +21,7 @@
21
21
  "git2svn/__init__.py",
22
22
  "git2svn/__main__.py",
23
23
  "git2svn/cli.py",
24
+ "git2svn/colors.py",
24
25
  "git2svn/core.py",
25
26
  "git2svn/eol.py",
26
27
  "git2svn/git.py",
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env python3
1
+ import os
2
2
  import subprocess
3
3
  import tempfile
4
4
  import unittest
@@ -1263,5 +1263,57 @@ class TestStatus(unittest.TestCase):
1263
1263
  mock_status.assert_called_once()
1264
1264
 
1265
1265
 
1266
+ class TestTerminalColor(unittest.TestCase):
1267
+ def test_color_always(self):
1268
+ color = git2svn.TerminalColor(mode="always")
1269
+ self.assertTrue(color.enabled)
1270
+ self.assertIn("\033[", color.bold_cyan("Test"))
1271
+ self.assertIn("\033[", color.clean())
1272
+ self.assertIn("\033[", color.ok())
1273
+ self.assertIn("\033[", color.warn())
1274
+ self.assertIn("\033[", color.error())
1275
+
1276
+ def test_color_never(self):
1277
+ color = git2svn.TerminalColor(mode="never")
1278
+ self.assertFalse(color.enabled)
1279
+ self.assertEqual(color.bold_cyan("Test"), "Test")
1280
+ self.assertEqual(color.clean(), "✔ Clean")
1281
+
1282
+ def test_color_auto_no_color_env(self):
1283
+ with patch.dict(os.environ, {"NO_COLOR": "1"}):
1284
+ color = git2svn.TerminalColor(mode="auto")
1285
+ self.assertFalse(color.enabled)
1286
+ self.assertEqual(color.bold("Hello"), "Hello")
1287
+
1288
+ def test_color_auto_dumb_term(self):
1289
+ with patch.dict(os.environ, {"TERM": "dumb", "NO_COLOR": ""}):
1290
+ color = git2svn.TerminalColor(mode="auto")
1291
+ self.assertFalse(color.enabled)
1292
+
1293
+ def test_color_cli_and_git_config(self):
1294
+ temp_dir = tempfile.TemporaryDirectory()
1295
+ try:
1296
+ repo_path = Path(temp_dir.name).resolve()
1297
+ (repo_path / ".svn").mkdir()
1298
+ subprocess.run(["git", "init"], cwd=repo_path, check=True, capture_output=True)
1299
+ subprocess.run(["git", "config", "user.name", "Test"], cwd=repo_path, check=True)
1300
+ subprocess.run(["git", "config", "user.email", "test@test.com"], cwd=repo_path, check=True)
1301
+ subprocess.run(["git", "config", "git2svn.color", "always"], cwd=repo_path, check=True)
1302
+
1303
+ with patch("git2svn.cli.Synchronizer") as mock_sync_cls:
1304
+ mock_sync_cls.return_value.status.return_value = 0
1305
+ # When no --color is passed, config 'always' should be used
1306
+ git2svn.main(["--git-dir", str(repo_path), "--svn-dir", str(repo_path), "status"])
1307
+ _, kwargs = mock_sync_cls.call_args
1308
+ self.assertEqual(kwargs.get("color_mode"), "always")
1309
+
1310
+ # When CLI --color never is passed, it overrides git config
1311
+ git2svn.main(["--git-dir", str(repo_path), "--svn-dir", str(repo_path), "--color", "never", "status"])
1312
+ _, kwargs = mock_sync_cls.call_args
1313
+ self.assertEqual(kwargs.get("color_mode"), "never")
1314
+ finally:
1315
+ temp_dir.cleanup()
1316
+
1317
+
1266
1318
  if __name__ == "__main__":
1267
1319
  unittest.main()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes