Skip to content

Instantly share code, notes, and snippets.

@amerryma
Last active February 14, 2025 17:54
Show Gist options
  • Save amerryma/002d7fd5cd0262f194b6494d63cc4834 to your computer and use it in GitHub Desktop.
Save amerryma/002d7fd5cd0262f194b6494d63cc4834 to your computer and use it in GitHub Desktop.
Counting Lines of Code with GitHub Linguist and Bash

Counting Lines of Code with GitHub Linguist and Bash

If you’re using GitHub Linguist to analyze your project’s code, you may want to take a closer look and see how many lines each individual file contains. GitHub Linguist provides a useful breakdown of the languages and files, but not an easy way to get line counts for each file. Let’s dive into how you can achieve this using a combination of GitHub Linguist, jq, and some classic Bash commands.

Why Count Lines of Code Per File?

Analyzing lines of code (LOC) per file is a useful metric to:

  • Assess Complexity: Identifying files with excessive lines of code might highlight areas that could benefit from refactoring.
  • Identify Hotspots: Knowing where most of the code resides helps focus efforts for documentation or optimization.
  • Track Growth: Monitoring LOC helps gauge how your project is evolving over time.

GitHub Linguist Breakdown

GitHub Linguist can give you a breakdown of your repository’s languages and the files for each language. Running:

github-linguist --breakdown

Provides an output like:

69.06%  62434      TypeScript
25.94%  23452      PLpgSQL
1.61%   1459       JavaScript
...

It also lists the filenames for each language. However, to dig deeper, we want to take these file paths and count the lines for each individual file.

The Script to Count Lines of Code

Here's the solution we’ll use to get the line count for each file listed by GitHub Linguist:

github-linguist --breakdown --json | jq -r '.[] | .files[]' | xargs wc -l 2>/dev/null | sort -n

Let’s break down what each part of this command does:

  1. github-linguist --breakdown --json: This command generates a language breakdown in JSON format, which is easier to parse programmatically. It outputs something like this:

    {
      "JavaScript": {
        "size": 1459,
        "percentage": "1.61",
        "files": [".prettierrc.js", "@app/client/eslint.config.mjs", ...]
      },
      ...
    }
  2. jq -r '.[] | .files[]': jq is a powerful command-line tool for parsing JSON. The command .[] | .files[] extracts all file paths from the JSON output.

    • .[]: Selects each language object (e.g., JavaScript, TypeScript).
    • .files[]: Extracts each filename in the files array for each language.
    • -r: Outputs raw strings without quotes, which makes the list suitable for passing to other commands.
  3. xargs wc -l: xargs takes the list of filenames outputted by jq and passes them to wc -l to count the number of lines for each file.

  4. 2>/dev/null: This part redirects error messages (such as permission denied errors or non-existent files) to /dev/null, keeping the output clean.

  5. sort -n: Finally, sort -n sorts the results numerically by line count, so you can quickly identify which files have the most or least lines of code.

Why Use GitHub Linguist Instead of find?

While it’s common to use tools like find to get a list of files for line counting, GitHub Linguist provides a more sophisticated and focused approach.

If you set up your .gitattributes file properly, you can explicitly mark files as generated or not relevant for analysis:

@shared/sql/src/*.mts linguist-generated=true
@shared/sql/src/index.mts linguist-generated=false
@app/db/schema/supabase.sql linguist-generated=true

See more information about this file here.

This allows GitHub Linguist to automatically skip files that don’t reflect the core work done by developers, such as:

  • Generated files: Files that are automatically generated by tools and do not represent human effort.
  • External dependencies: Skipping folders like node_modules to avoid inflating your codebase statistics.

This gives a more accurate representation of the actual work that was done in the project, focusing only on meaningful contributions.

A Note on Lines of Code as a Metric

It’s essential to highlight that lines of code are not an indicator of a developer’s value or productivity. Not all lines of code are equal—some lines solve complex problems, while others might simply be configuration or boilerplate code. Counting lines of code can help identify areas that need attention, but it should never be used to judge contributors.

In fact, some of the most valuable contributions come in the form of removing unnecessary code, which may not show up positively if we only consider line counts.

Example Output

Running the above command might give you output like:

   15 .prettierrc.js
   45 @app/client/eslint.config.mjs
  120 @app/client/next.config.mjs
  200 @app/client/src/app/page.tsx
  ...

This gives a clear, sorted list of each file along with its respective line count.

Conclusion

By leveraging GitHub Linguist, jq, and simple Bash commands, you can gain deeper insights into your repository’s code at a file level while ignoring irrelevant or generated files. This helps you understand where complexity lies and provides a cleaner picture of your project's growth and structure.

However, it’s crucial to remember that lines of code are just one of many metrics. The quality, maintainability, and impact of code are far more valuable measures. Use these insights to improve your project, not as a tool to evaluate individual contributions.


With these considerations in mind, you can use GitHub Linguist as an effective tool to better understand your codebase while ensuring the metrics reflect actual work done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment