Procházet zdrojové kódy

Add colorized diff support for show command

Fixes #1741
Jelmer Vernooij před 5 měsíci
rodič
revize
01ad2f79f5
3 změnil soubory, kde provedl 79 přidání a 11 odebrání
  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
    existing directories. (Jelmer Vernooij)
 
+ * Add colorized diff support for the ``show`` command with ``--color``
+   argument. (Jelmer Vernooij, #1741)
+
 0.24.1	2025-08-01
 
  * 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:
         parser = argparse.ArgumentParser()
         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)
+
+        # 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:
             config = repo.get_config_stack()
             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):

+ 38 - 10
dulwich/porcelain.py

@@ -91,7 +91,7 @@ from contextlib import AbstractContextManager, closing, contextmanager
 from dataclasses import dataclass
 from io import BytesIO, RawIOBase
 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 .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
       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:
-        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:
-            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: