Skip to main content

Make git logs easier for use in scenarios when communicating the progress of a project to non-experts

Project description

Lumpy Log - Prettified Git Logs

Make git logs easier for use in scenarios when communicating the progress of a project to non-experts.

Features

  • Generates readable markdown reports from Git commit history
  • Processes test output (TAP format) and creates test documentation
  • Multi-folder organization with unified index

📚 See Also:

Installation

From PyPI (when published)

pip install lumpy-log

For Development

# Clone the repository
git clone https://github.com/UTCSheffield/lumpy_log.git
cd lumpy_log

# Install in editable mode
pip install -e .

Usage

As a CLI Command

After installation, you can use the lumpy-log command:

Generate Git Commit Logs

# Process current directory repository
lumpy-log log

# Process a specific repository
lumpy-log log -i /path/to/repo

# Process with options
lumpy-log log -i /path/to/repo -o devlog --verbose --force

# Backwards compatible (defaults to log command)
lumpy-log -i /path/to/repo

Process Test Results

Lumpy Log can process test output in TAP (Test Anything Protocol) format and create markdown documentation alongside your commit logs.

Install pytest-tap plugin:

pip install pytest-tap

Bash/Linux/macOS:

# Pipe test output directly
pytest --tap | lumpy-log test

# Or save to file first
pytest --tap > test_output.txt
lumpy-log test --input test_output.txt

Windows cmd.exe or PowerShell:

REM Pipe test output directly
py -m pytest --tap | lumpy-log test

REM Or save to file first
py -m pytest --tap > test_output.txt
lumpy-log test --input test_output.txt

REM Include raw output for debugging
py -m pytest --tap | lumpy-log test --raw-output

Test results are saved to output/tests/ with timestamp filenames (e.g., 20260118_1430.md), and the index is automatically updated to include both commits and test results.

Rebuild Index

If you manually modify or reorganize commit/test files, you can regenerate the index:

# Rebuild with default order (oldest first - development log style)
lumpy-log rebuild

# Rebuild with changelog order (newest first)
lumpy-log rebuild --changelog

As a Python Module

You can also run it as a module:

python -m lumpy_log -i /path/to/repo -o output

Command-line Options

Log Command (Git Commits)

  • -i, --repo: Path to the local Git repository (default: current directory)
  • -o, --outputfolder: Output folder for generated files (default: devlog)
  • -f, --fromcommit: Start from this commit
  • -t, --tocommit: End at this commit
  • -a, --allbranches: Include all branches
  • -v, --verbose: Verbose output
  • -b, --branch: Specific branch to process
  • --force: Force overwrite existing files
  • -d, --dryrun: Dry run - don't write files
  • -n, --no-obsidian-index: Don't generate index.md

Test Command (Test Results)

  • -o, --outputfolder: Output folder for test results (default: devlog)
  • --input: Input file with test output (if not specified, reads from stdin)
  • -v, --verbose: Verbose output
  • --raw-output: Include raw test output in the report

Rebuild Command (Regenerate Index)

Rebuilds the unified index.md from existing commits and test results without re-processing git history or re-running tests.

# Rebuild index with default order (oldest first)
lumpy-log rebuild

# Rebuild with changelog order (newest first)
lumpy-log rebuild --changelog

# Rebuild from custom output folder
lumpy-log rebuild -o /path/to/output
  • -o, --outputfolder: Output folder containing commits/ and tests/ (default: devlog)
  • -v, --verbose: Verbose output
  • --changelog: Use changelog order (newest first) instead of default (oldest first)

Output Structure

Lumpy Log organizes output into subdirectories:

devlog/
├── index.md              # Unified index with commits and test results
├── commits/              # Git commit markdown files
│   ├── 20260118_1430_abc1234.md
│   └── 20260118_1500_def5678.md
└── tests/                # Test result markdown files
    ├── 20260118_1430.md
    └── 20260118_1500.md

Ignoring Files (.lumpyignore)

Lumpy Log respects a repository-level .lumpyignore file using the same syntax as .gitignore (git wildmatch patterns). By default, it ignores Markdown files (*.md) so documentation changes don't flood the logs. Add additional patterns to .lumpyignore at your repo root to skip files or folders.

Example .lumpyignore:

# Ignore Markdown (default)
*.md

# Ignore generated docs and build artifacts
docs/
dist/
*.tmp

Running Tests

To run the tests, use the following command:

pytest

Example Output

Commit : Refactor verbose logging conditions in ChangeLump methods for clarity

By "Mr Eggleton" on 2026-01-18

"changelump.py" was Modified

    # Abstracts out lineIsComment so we can  print the results
    def _lineIsComment(self, i):
        line = self.lines[i]
        if(self.verbose):
            print(self.lang.name, "self.lang.comment_structure",self.lang.comment_structure)
        comment_structure = self.lang.comment_structure

        begin = comment_structure.get("begin")
        end = comment_structure.get("end")
        single = comment_structure.get("single")

        # Multiline comments: treat lines with both begin and end as comment,
        # and any line inside unmatched begin/end pairs as comment.
        if begin:
            try:
                beginmatches = re.findall(begin, line)
                endmatches = re.findall(end, line)

                # If both markers appear on the same line, it's a comment line.
                if len(beginmatches) and len(endmatches):
                    return True
                
                # If this line is inside an open multiline comment, it's a comment.
                if self._in_multiline_comment(i, begin, end):
                    return True
            except Exception as Err:
                print(type(Err), Err)
                print(self.lang.comment_family, comment_structure)

        # Single-line comments
        if single:
            try:
                if re.search(single, line.strip()):
                    return True
            except Exception as Err:
                print("Single", type(Err), Err)
                print(self.lang.comment_family, comment_structure["single"])

        return False
    @property
    def code(self):    
        start = self.start 
        if(self.commentStart is not None):
            start = self.commentStart     

        #code = ""self.source+"\n"+
        code = ("\n".join(self.lines[start: self.end+1]))
        if self.verbose:
            print("code", code)
        return code
    def extendOverComments(self):
        if self.verbose:
            print("extendOverComments", "self.start", self.start)
        j = self.start
        while(j > 0 and self.lineIsComment(j-1)):
            j -= 1
            self.commentStart = j
    def lineIsComment(self, i):
        blineIsComment = self._lineIsComment(i)
        if self.verbose:
            print("lineIsComment", blineIsComment, self.lines[i])
        return blineIsComment
    def inLump(self,i):
        inLump = (self.start <= i and i <= self.end)
    
        if self.verbose:
            print("inLump", "self.start", self.start,"i", i, "inLump",inLump)
        return inLump
        """Return True if line i is inside an unmatched multiline comment block."""
        try:
            # Check if begin and end delimiters are the same (symmetric like """)
            # Strip common regex anchors to compare the actual delimiter strings
            begin_stripped = begin_re.strip('^$\\s')
            end_stripped = end_re.strip('^$\\s')
            symmetric = (begin_stripped == end_stripped)
            
            in_comment = False
            for idx in range(0, i + 1):
                s = self.lines[idx]
                
                if symmetric:
                    # For symmetric delimiters (like """ in Python), each occurrence
                    # toggles the comment state: first one opens, second one closes, etc.
                    # Example: """comment""" means we enter on first """, exit on second
                    matches = re.findall(begin_re, s)
                    for _ in matches:
                        in_comment = not in_comment  # Flip True->False or False->True
                else:
                    # For asymmetric delimiters, track depth
                    begins = len(re.findall(begin_re, s))
                    ends = len(re.findall(end_re, s))
                    
                    # Process begins first, then ends
                    if not in_comment and begins > 0:
                        in_comment = True
                    if in_comment and ends > 0:
                        in_comment = False
                    
            
            return in_comment
        except Exception as Err:
            if self.verbose:
                print("_in_multiline_comment error", type(Err), Err)
            return False

Test Results : 2026-01-20 13:30:12

Format: tap

  • Tests Run: 113
  • Passed: 113 ✅
  • Failed: 0
  • Skipped: 0

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

lumpy_log-0.1.3.tar.gz (33.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

lumpy_log-0.1.3-py3-none-any.whl (27.1 kB view details)

Uploaded Python 3

File details

Details for the file lumpy_log-0.1.3.tar.gz.

File metadata

  • Download URL: lumpy_log-0.1.3.tar.gz
  • Upload date:
  • Size: 33.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.1

File hashes

Hashes for lumpy_log-0.1.3.tar.gz
Algorithm Hash digest
SHA256 067f13d3c59cc48b9c12f0ee73484969cf6c81ddd8cd208216495d9b8cf5e9d7
MD5 7c52df8ffc4213a60fab63923de2d2ab
BLAKE2b-256 3167e19948b25ed529f5333e64994392319bb766ab72b86021962a50349680df

See more details on using hashes here.

File details

Details for the file lumpy_log-0.1.3-py3-none-any.whl.

File metadata

  • Download URL: lumpy_log-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 27.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.1

File hashes

Hashes for lumpy_log-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 45248094d9a83dadcaccdfcef6dd06811a595bacaf114832abd77e51def76e66
MD5 ff01dd6d3c780b287e6581b77eb31387
BLAKE2b-256 37f610bad5d12432828cda8b3604b77e988741df3c28911e044760269da38d8d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page