Просмотр исходного кода

Add colorized diff support for show command

Fixes #1741
Jelmer Vernooij 5 месяцев назад
Родитель
Сommit
01ad2f79f5
3 измененных файлов с 79 добавлено и 11 удалено
  1. 3 0
      NEWS
  2. 38 1
      dulwich/cli.py
  3. 38 10
      dulwich/porcelain.py

+ 3 - 0
NEWS

@@ -9,6 +9,9 @@
  * Add ``exist_ok`` parameter to ``add_worktree`` to allow creation with
  * Add ``exist_ok`` parameter to ``add_worktree`` to allow creation with
    existing directories. (Jelmer Vernooij)
    existing directories. (Jelmer Vernooij)
 
 
+ * Add colorized diff support for the ``show`` command with ``--color``
+   argument. (Jelmer Vernooij, #1741)
+
 0.24.1	2025-08-01
 0.24.1	2025-08-01
 
 
  * Require ``typing_extensions`` on Python 3.10.
  * Require ``typing_extensions`` on Python 3.10.

+ 38 - 1
dulwich/cli.py

@@ -973,11 +973,48 @@ class cmd_show(Command):
     def run(self, argv) -> None:
     def run(self, argv) -> None:
         parser = argparse.ArgumentParser()
         parser = argparse.ArgumentParser()
         parser.add_argument("objectish", type=str, nargs="*")
         parser.add_argument("objectish", type=str, nargs="*")
+        parser.add_argument(
+            "--color",
+            choices=["always", "never", "auto"],
+            default="auto",
+            help="Use colored output (requires rich)",
+        )
         args = parser.parse_args(argv)
         args = parser.parse_args(argv)
+
+        # Determine if we should use color
+        def _should_use_color():
+            if args.color == "always":
+                return True
+            elif args.color == "never":
+                return False
+            else:  # auto
+                return sys.stdout.isatty()
+
+        def _create_output_stream(outstream):
+            """Create output stream, optionally with colorization."""
+            if not _should_use_color():
+                return outstream
+
+            from .diff import ColorizedDiffStream
+
+            if not ColorizedDiffStream.is_available():
+                if args.color == "always":
+                    raise ImportError(
+                        "Rich is required for colored output. Install with: pip install 'dulwich[colordiff]'"
+                    )
+                else:
+                    logging.warning(
+                        "Rich not available, disabling colored output. Install with: pip install 'dulwich[colordiff]'"
+                    )
+                    return outstream
+
+            return ColorizedDiffStream(outstream.buffer)
+
         with Repo(".") as repo:
         with Repo(".") as repo:
             config = repo.get_config_stack()
             config = repo.get_config_stack()
             with get_pager(config=config, cmd_name="show") as outstream:
             with get_pager(config=config, cmd_name="show") as outstream:
-                porcelain.show(repo, args.objectish or None, outstream=outstream)
+                output_stream = _create_output_stream(outstream)
+                porcelain.show(repo, args.objectish or None, outstream=output_stream)
 
 
 
 
 class cmd_diff_tree(Command):
 class cmd_diff_tree(Command):

+ 38 - 10
dulwich/porcelain.py

@@ -91,7 +91,7 @@ from contextlib import AbstractContextManager, closing, contextmanager
 from dataclasses import dataclass
 from dataclasses import dataclass
 from io import BytesIO, RawIOBase
 from io import BytesIO, RawIOBase
 from pathlib import Path
 from pathlib import Path
-from typing import Optional, TypeVar, Union, overload
+from typing import BinaryIO, Optional, TypeVar, Union, cast, overload
 
 
 from . import replace_me
 from . import replace_me
 from .archive import tar_stream
 from .archive import tar_stream
@@ -1177,17 +1177,45 @@ def show_commit(repo: RepoPath, commit, decode, outstream=sys.stdout) -> None:
       decode: Function for decoding bytes to unicode string
       decode: Function for decoding bytes to unicode string
       outstream: Stream to write to
       outstream: Stream to write to
     """
     """
+    from .diff import ColorizedDiffStream
+
+    # Create a wrapper for ColorizedDiffStream to handle string/bytes conversion
+    class _StreamWrapper:
+        def __init__(self, stream):
+            self.stream = stream
+
+        def write(self, data):
+            if isinstance(data, str):
+                # Convert string to bytes for ColorizedDiffStream
+                self.stream.write(data.encode("utf-8"))
+            else:
+                self.stream.write(data)
+
     with open_repo_closing(repo) as r:
     with open_repo_closing(repo) as r:
-        print_commit(commit, decode=decode, outstream=outstream)
-        if commit.parents:
-            parent_commit = r[commit.parents[0]]
-            base_tree = parent_commit.tree
+        # Use wrapper for ColorizedDiffStream, direct stream for others
+        if isinstance(outstream, ColorizedDiffStream):
+            wrapped_stream = _StreamWrapper(outstream)
+            print_commit(commit, decode=decode, outstream=wrapped_stream)
+            # Write diff directly to the ColorizedDiffStream as bytes
+            # Type cast since ColorizedDiffStream implements the BinaryIO interface we need
+            write_tree_diff(
+                cast(BinaryIO, outstream),
+                r.object_store,
+                commit.parents[0] if commit.parents else None,
+                commit.tree,
+            )
         else:
         else:
-            base_tree = None
-        diffstream = BytesIO()
-        write_tree_diff(diffstream, r.object_store, base_tree, commit.tree)
-    diffstream.seek(0)
-    outstream.write(commit_decode(commit, diffstream.getvalue()))
+            print_commit(commit, decode=decode, outstream=outstream)
+            if commit.parents:
+                parent_commit = r[commit.parents[0]]
+                base_tree = parent_commit.tree
+            else:
+                base_tree = None
+            # Traditional path: buffer diff and write as decoded text
+            diffstream = BytesIO()
+            write_tree_diff(diffstream, r.object_store, base_tree, commit.tree)
+            diffstream.seek(0)
+            outstream.write(commit_decode(commit, diffstream.getvalue()))
 
 
 
 
 def show_tree(repo: RepoPath, tree, decode, outstream=sys.stdout) -> None:
 def show_tree(repo: RepoPath, tree, decode, outstream=sys.stdout) -> None: