File: README.md
Linguist
This library is used on GitHub.com to detect blob languages, ignore binary or vendored files, suppress generated files in diffs, and generate language breakdown graphs.
Documentation
- How Linguist works
- Change Linguist's behaviour with overrides
- Troubleshooting
- Contributing guidelines
Installation
Install the gem:
gem install github-linguistDependencies
Linguist is a Ruby library so you will need a recent version of Ruby installed.
There are known problems with the macOS/Xcode supplied version of Ruby that causes problems installing some of the dependencies.
Accordingly, we highly recommend you install a version of Ruby using Homebrew, rbenv, rvm, ruby-build, asdf or other packaging system, before attempting to install Linguist and the dependencies.
Linguist uses charlock_holmes for character encoding and rugged for libgit2 bindings for Ruby.
These components have their own dependencies.
You may need to install missing dependencies before you can install Linguist.
For example, on macOS with Homebrew:
brew install cmake pkg-config icu4cOn Ubuntu:
sudo apt-get install build-essential cmake pkg-config libicu-dev zlib1g-dev libcurl4-openssl-dev libssl-dev ruby-devUsage
Application usage
Linguist can be used in your application as follows:
require 'rugged'
require 'linguist'
repo = Rugged::Repository.new('.')
project = Linguist::Repository.new(repo, repo.head.target_id)
project.language #=> "Ruby"
project.languages #=> { "Ruby" => 119387 }Command line usage
The github-linguist executable operates in two distinct modes:
- Git Repository mode - Analyzes an entire Git repository (when given a directory path or no path)
- Single file mode - Analyzes a specific file (when given a file path)
Git Repository
A repository's languages stats can be assessed from the command line using the github-linguist executable.
Without any options, github-linguist will output the language breakdown by percentage and file size.
cd /path-to-repository
github-linguistYou can try running github-linguist on the root directory in this repository itself:
$ github-linguist
66.84% 264519 Ruby
24.68% 97685 C
6.57% 25999 Go
1.29% 5098 Lex
0.32% 1257 Shell
0.31% 1212 DockerfileAdditional options
`--rev REV`
The --rev REV flag will change the git revision being analyzed to any gitrevisions(1) compatible revision you specify.
This is useful to analyze the makeup of a repo as of a certain tag, or in a certain branch.
For example, here is the popular Jekyll open source project.
$ github-linguist jekyll
70.64% 709959 Ruby
23.04% 231555 Gherkin
3.80% 38178 JavaScript
1.19% 11943 HTML
0.79% 7900 Shell
0.23% 2279 Dockerfile
0.13% 1344 Earthly
0.10% 1019 CSS
0.06% 606 SCSS
0.02% 234 CoffeeScript
0.01% 90 HackAnd here is Jekyll's published website, from the gh-pages branch inside their repository.
$ github-linguist jekyll --rev origin/gh-pages
100.00% 2568354 HTML`--breakdown`
The --breakdown or -b flag will additionally show the breakdown of files by language.
You can try running github-linguist on the root directory in this repository itself:
$ github-linguist --breakdown
66.84% 264519 Ruby
24.68% 97685 C
6.57% 25999 Go
1.29% 5098 Lex
0.32% 1257 Shell
0.31% 1212 Dockerfile
Ruby:
Gemfile
Rakefile
bin/git-linguist
bin/github-linguist
ext/linguist/extconf.rb
github-linguist.gemspec
lib/linguist.rb
โฆ`--strategies`
The --strategies or -s flag will show the language detection strategy used for each file. This is useful for understanding how Linguist determined the language of specific files. Note that unless the --json flag is specified, this flag will set the --breakdown flag implicitly.
You can try running github-linguist on the root directory in this repository itself with the strategies flag:
$ github-linguist --breakdown --strategies
66.84% 264519 Ruby
24.68% 97685 C
6.57% 25999 Go
1.29% 5098 Lex
0.32% 1257 Shell
0.31% 1212 Dockerfile
Ruby:
Gemfile [Filename]
Rakefile [Filename]
bin/git-linguist [Extension]
bin/github-linguist [Extension]
lib/linguist.rb [Extension]
โฆIf a file's language is affected by .gitattributes, the strategy will show the original detection method along with a note indicating whether the gitattributes setting changed the result or confirmed it.
For instance, if you had the following .gitattributes overrides in your repo:
*.ts linguist-language=JavaScript
*.js linguist-language=JavaScriptthe output of Linguist would be something like this:
100.00% 217 JavaScript
JavaScript:
demo.ts [Heuristics (overridden by .gitattributes)]
demo.js [Extension (confirmed by .gitattributes)]`--json`
The --json or -j flag output the data into JSON format.
$ github-linguist --json
{"Dockerfile":{"size":1212,"percentage":"0.31"},"Ruby":{"size":264519,"percentage":"66.84"},"C":{"size":97685,"percentage":"24.68"},"Lex":{"size":5098,"percentage":"1.29"},"Shell":{"size":1257,"percentage":"0.32"},"Go":{"size":25999,"percentage":"6.57"}}This option can be used in conjunction with --breakdown to get a full list of files along with the size and percentage data.
$ github-linguist --breakdown --json
{"Dockerfile":{"size":1212,"percentage":"0.31","files":["Dockerfile","tools/grammars/Dockerfile"]},"Ruby":{"size":264519,"percentage":"66.84","files":["Gemfile","Rakefile","bin/git-linguist","bin/github-linguist","ext/linguist/extconf.rb","github-linguist.gemspec","lib/linguist.rb",...]}}NB. The --strategies flag has no effect, when the --json flag is present.
Single file
Alternatively you can find stats for a single file using the github-linguist executable.
You can try running github-linguist on files in this repository itself:
$ github-linguist grammars.yml
grammars.yml: 884 lines (884 sloc)
type: Text
mime type: text/x-yaml
language: YAMLAdditional options
`--breakdown`
This flag has no effect in Single file mode.
`--strategies`
When using the --strategies or -s flag with a single file, you can see which detection method was used:
$ github-linguist --strategies lib/linguist.rb
lib/linguist.rb: 105 lines (96 sloc)
type: Text
mime type: application/x-ruby
language: Ruby
strategy: ExtensionIf a file's language is affected by .gitattributes, the strategy will show whether the gitattributes setting changed the result or confirmed it:
In this fictitious example, it says "confirmed by .gitattributes" since the detection process (using the Filename strategy) would have given the same output as the override:
.devcontainer/devcontainer.json: 27 lines (27 sloc)
type: Text
mime type: application/json
language: JSON with Comments
strategy: Filename (confirmed by .gitattributes)In this other fictitious example, it says "overridden by .gitattributes" since the gitattributes setting changes the detected language to something different:
test.rb: 13 lines (11 sloc)
type: Text
mime type: application/x-ruby
language: Java
strategy: Extension (overridden by .gitattributes)Here, the .rb file would normally be detected as Ruby by the Extension strategy, but .gitattributes overrides it to be detected as Java instead.
`--json`
Using the --json flag will give you the output for a single file in JSON format:
$ github-linguist --strategies --json lib/linguist.rb
{"lib/linguist.rb":{"lines":105,"sloc":96,"type":"Text","mime_type":"application/x-ruby","language":"Ruby","large":false,"generated":false,"vendored":false}}NB. The --strategies has no effect, when the --json flag is present.
Docker
If you have Docker installed you can either build or use
our pre-built images and run Linguist within a container:
$ docker run --rm -v $(pwd):$(pwd):Z -w $(pwd) -t ghcr.io/github-linguist/linguist:latest
66.84% 264519 Ruby
24.68% 97685 C
6.57% 25999 Go
1.29% 5098 Lex
0.32% 1257 Shell
0.31% 1212 DockerfileBuilding the image
$ docker build -t linguist .
$ docker run --rm -v $(pwd):$(pwd):Z -w $(pwd) -t linguist
66.84% 264519 Ruby
24.68% 97685 C
6.57% 25999 Go
1.29% 5098 Lex
0.32% 1257 Shell
0.31% 1212 Dockerfile
$ docker run --rm -v $(pwd):$(pwd) -w $(pwd) -t linguist github-linguist --breakdown
66.84% 264519 Ruby
24.68% 97685 C
6.57% 25999 Go
1.29% 5098 Lex
0.32% 1257 Shell
0.31% 1212 Dockerfile
Ruby:
Gemfile
Rakefile
bin/git-linguist
bin/github-linguist
ext/linguist/extconf.rb
github-linguist.gemspec
lib/linguist.rb
โฆContributing
Please check out our contributing guidelines.
License
The language grammars included in this gem are covered by their repositories' respective licenses.vendor/README.md lists the repository for each grammar.
All other files are covered by the MIT license, see LICENSE.
File: docs/how-linguist-works.md
How Linguist works
Linguist takes the list of languages it knows from languages.yml and uses a number of methods to try and determine the language used by each file, and the overall repository breakdown.
Linguist starts by going through all the files in a repository and excludes all files that it determines to be binary data, vendored code, generated code, documentation, or are defined as data (e.g. SQL) or prose (e.g. Markdown) languages, whilst taking into account any overrides.
If an explicit language override has been used, that language is used for the matching files.
The language of each remaining file is then determined using the following strategies, in order, with each step either identifying the precise language or reducing the number of likely languages passed down to the next strategy:
- Vim or Emacs modeline,
- commonly used filename,
- shell shebang,
- file extension,
- XML header,
- man page section,
- heuristics,
- naรฏve Bayesian classification
The result of this analysis is used to produce the language stats bar which displays the languages percentages for the files in the repository.
The percentages are calculated based on the bytes of code for each language as reported by the List Languages API.
How Linguist works on GitHub.com
When you push changes to a repository on GitHub.com,
a low priority background job is enqueued to analyze the default branch of your repository as explained above.
The results of this analysis are cached for the lifetime of your repository and are only updated when the repository is updated.
As this analysis is performed by a low priority background job, it can take a while, particularly during busy periods, for your language statistics bar to reflect your changes.
File: docs/overrides.md
Overrides
Linguist supports a number of different custom override strategies for language definitions and file paths.
Using gitattributes
Add a .gitattributes file to your project and use standard git-style path matchers for the files you want to override using the linguist-documentation, linguist-language, linguist-vendored, linguist-generated and linguist-detectable attributes..gitattributes will be used to determine language statistics and will be used to syntax highlight files.
You can also manually set syntax highlighting using Vim or Emacs modelines.
When testing with a local installation of Linguist, take note that the added attributes will not take effect until the .gitattributes file is committed to your repository.
File and folder paths inside .gitattributes are calculated relative to the position of the .gitattributes file.
# Example of a `.gitattributes` file which reclassifies `.rb` files as Java:
*.rb linguist-language=Java
# Replace any whitespace in the language name with hyphens:
*.glyphs linguist-language=OpenStep-Property-List
# Language names are case-insensitive and may be specified using an alias.
# So, the following three lines are all functionally equivalent:
*.es linguist-language=js
*.es linguist-language=JS
*.es linguist-language=JAVASCRIPTSummary
| Git attribute | Defined in | Effect on file |
|---|---|---|
linguist-detectable |
languages.yml |
Included in stats, even if language's type is data or prose |
linguist-documentation |
documentation.yml |
Excluded from stats |
linguist-generated |
generated.rb |
Excluded from stats, hidden in diffs |
linguist-language=name |
languages.yml |
Highlighted and classified as name |
linguist-vendored |
vendor.yml |
Excluded from stats |
Detectable
By default only languages of type programming or markup in languages.yml are included in the language statistics.
Languages of a different type in languages.yml are not "detectable" by default, causing them not to be included in the language statistics,
but can be made detectable as shown below. Languages that are not yet mentioned in languages.yml will not be included in the language
statistics, even if you specify something like *.mycola linguist-language=MyCoolLang linguist-detectable in the .gitattributes file.
Use the linguist-detectable attribute to mark or unmark paths as detectable:
*.kicad_pcb linguist-detectable
*.sch linguist-detectable
tools/export_bom.py -linguist-detectableDocumentation
Just like vendored files, Linguist excludes documentation files from your project's language stats.documentation.yml lists common documentation paths and excludes them from the language statistics for your repository.
Use the linguist-documentation attribute to mark or unmark paths as documentation:
# Apply override to all files in the directory
project-docs/* linguist-documentation
# Apply override to a specific file
docs/formatter.rb -linguist-documentation
# Apply override to all files and directories in the directory
ano-dir/** linguist-documentationGenerated code
Not all plain text files are true source files.
Generated files like minified JavaScript and compiled CoffeeScript can be detected and excluded from language stats.
As an added bonus, unlike vendored and documentation files, these files are suppressed in diffs.generated.rb lists common generated paths and excludes them from the language statistics of your repository.
Use the linguist-generated attribute to mark or unmark paths as generated.
Api.elm linguist-generatedVendored code
Checking code you didn't write, such as JavaScript libraries, into your git repo is a common practice, but this often inflates your project's language stats and may even cause your project to be labeled as another language.
By default, Linguist treats all of the paths defined in vendor.yml as vendored and therefore doesn't include them in the language statistics for a repository.
Use the linguist-vendored attribute to vendor or un-vendor paths:
# Apply override to all files in the directory
special-vendored-path/* linguist-vendored
# Apply override to a specific file
jquery.js -linguist-vendored
# Apply override to all files and directories in the directory
ano-dir/** linguist-vendoredUsing Emacs or Vim modelines
If you do not want to use .gitattributes to override the syntax highlighting used on GitHub.com, you can use Vim or Emacs style modelines to set the language for a single file.
Modelines can be placed anywhere within a file and are respected when determining how to syntax-highlight a file on GitHub.com
Vim
# Some examples of various styles:
vim: syntax=java
vim: set syntax=ruby:
vim: set filetype=prolog:
vim: set ft=cpp:Emacs
-*- mode: php; -*-
-*- c++ -*-File: docs/README.md
Linguist Documentation
- How Linguist works
- Change Linguist's behaviour with overrides
- Troubleshooting
- Contributing guidelines
- Releasing Linguist (Only applicable to GitHub staff)
File: docs/releasing.md
Releasing
This is the procedure for making a new release of Linguist. The entire process needs to be performed by a member of GitHub staff.
Create a branch for the release:
git checkout -b release-vxx.xx.xxMake sure your local dependencies are up to date:
rm Gemfile.lock && script/bootstrapIf the grammar submodules have not been updated recently, update them:
git submodule update --remote.
If any submodules are updated:- update the
grammars.yml:script/grammar-compiler update -f - confirm the updated grammars still compile and no new errors have been introduced and none have gone missing:
bundle exec rake check_grammars - verify and fix any problems identified above
- commit all changes:
git commit -a - update the license cache:
bundle exec licensed cache -c vendor/licenses/config.yml - double check no license problems found:
bundle exec licensed status -c vendor/licenses/config.yml - verify and fix any problems identified above
- commit all changes:
git commit -a
- update the
Ensure that samples are updated:
bundle exec rake samplesEnsure that tests are green:
bundle exec rake testBuild a test gem
GEM_VERSION=$(git describe --tags 2>/dev/null | sed 's/-/./g' | sed 's/v//') bundle exec rake build_gemTest the test gem:
- Bump the Gemfile and Gemfile.lock versions for an app which relies on this gem
- Install the new gem locally
- Test behavior locally, branch deploy, whatever needs to happen
Bump gem version in
lib/linguist/VERSION, like this.Make a PR to
github/linguist, like this.Build a local gem:
bundle exec rake build_gemMerge the
github/linguistPRTag and push:
git tag vx.xx.xx; git push --tagsCreate a GitHub release with the pushed tag (https://github.com/github/linguist/releases/new) and populate it using the "Generate release notes" button.
Build a grammars tarball (
./script/build-grammars-tarball) and attach it to the GitHub releasePush to rubygems.pkg.github.com --
gem push --key github --host https://rubygems.pkg.github.com/github github-linguist-3.0.0.gem. See Working with the RubyGems registry for more details.Push to rubygems.org --
gem push github-linguist-3.0.0.gemUpdate and deploy the following repositories to use the new gem in production:
github/github- if backporting, label for backporting to the latest version of GitHub Enterprise Server only.github/treelights- this only needs the Linguist version updated to pull the compiled grammars from the Linguist release. If backporting, label for backporting to the latest version of GitHub Enterprise Server only.
Note: syntax highlighting changes won't take effect until the updated
github/treelightsrepo has been deployed.
File: docs/troubleshooting.md
Troubleshooting
My repository is detected as the wrong language
If the language stats bar is reporting a language that you don't expect:
- Click on the name of the language in the stats bar to see a list of the files that are identified as that language.
Keep in mind this performs a search so the code search restrictions may result in files identified in the language statistics not appearing in the search results.
Installing Linguist locally and running it from the command line will give you accurate results. - If you see files that you didn't write in the search results, consider moving the files into one of the paths for vendored code, or use the manual overrides feature to ignore them.
- If the files are misclassified, search for open issues to see if anyone else has already reported the issue.
Any information you can add, especially links to public repositories, is helpful.
You can also use the manual overrides feature to correctly classify them in your repository. - If there are no reported issues of this misclassification, open an issue and include a link to the repository or a sample of the code that is being misclassified.
Keep in mind that the repository language stats are only updated when you push changes, and the results are cached for the lifetime of your repository.
If you have not made any changes to your repository in a while, you may find pushing another change will correct the stats.
When I click on a language present in the language stats bar, I get a message saying "Your search did not match any code"
There are a few reasons that could lead to this outcome:
- If the repository implements an override via the
linguist-languageattribute, it won't be taken into account in GitHub's search results since GitHub search relies on an internal library which doesn't support overrides at the moment. - GitHub's search internal library might not be using the latest version of Linguist which means that files could be detected differently in search compared to Linguist (see also the note at the end of this section).
- It could be that files are associated to a language that is part of a group. This means that they are counted as the parent language in the language stats bar, but as the actual language in search. For instance, a file ending with
.f90is considered to be "Fortran" in the stats bar, but "Fortran Free Form" in search. - Finally, this can be caused by code search limitations that are unrelated to Linguist.
My C/C++/Objective-C `.h` header file is detected as the wrong language
Correctly detecting the language for the C-family .h header files is tough because Linguist detects the languages of files in isolation when analysing repositories and these header files, especially the smaller ones, can be used across all three languages without using any language-specific content.
To try and reduce the number of false positives and keep some degree of predictability for users, Linguist will assume all .h header files are C by default and will only identify a file as C++ or Objective-C if the content matches the specific heuristic for that language.
This will mean you will need to implement an override for some of your header files if you wish for them to be classified as C++ or Objective-C if they do not contain language-specific content.
My repository isn't showing my language
Linguist does not consider vendored code, generated code, documentation, or data (e.g. SQL) or prose (e.g. Markdown) languages (as defined by the type attribute in languages.yml) when calculating the repository language statistics.
If the language statistics bar is not showing your language at all, it could be for a few reasons:
- Linguist doesn't know about your language.
- The extension you have chosen is not associated with your language in
languages.yml. - All the files in your repository fall into one of the categories listed above that Linguist excludes by default.
If Linguist doesn't know about the language or the extension you're using, consider contributing to Linguist by opening a pull request to add support for your language or extension.
For everything else, you can use the manual overrides feature to tell Linguist to include your files in the language statistics.
There's a problem with the syntax highlighting of a file
Linguist detects the language of a file but the actual syntax-highlighting is powered by a set of language grammars which are included in this project as a set of submodules as listed here.
If you experience an issue with the syntax-highlighting on GitHub, please report the issue to the upstream grammar repository, not here.
Grammars are updated every time we build the Linguist gem so upstream bug fixes are automatically incorporated as they are fixed.
I get an error when using Linguist on a directory that is not a Git repository
Linguist only works on Git repositories and individual files. Its primary use is on GitHub.com which uses bare
repositories and thus changes need to be committed as individual files don't show on the filesystem.
As a work around you could initialise a temporary Git repository in your directory as demonstrated in this
script.
Alternatively you can run Linguist on individual files, see here.
I am unable to install Linguist on macOS
There are several known issues with the version of Ruby shipped with macOS that cause problems when it comes to installing the charlock-holmes gem which is a Linguist dependency.
As this is a problem with the Ruby shipped by Apple and not Linguist or charlock-holmes, we recommend you install a version of Ruby using Homebrew, rbenv, rvm, ruby-build, asdf or other packaging system, before attempting to install Linguist.
My Linguist PR has been merged but GitHub doesn't reflect my changes
Changes to Linguist will only appear on GitHub when a new Linguist release is made and this is deployed to GitHub.com.
There is no set release timeframe, but the intention is at least once every three-to-four months so the latest version ships with each new major version of GitHub Enterprise Server.
A PR like this will be created for the release with checkmarks for each stage of the release process.
All syntax highlighting grammars will also be updated in all major and minor releases.
Grammars will only be updated in patch releases if the patch release is specifically for that language and it requires a grammar update to address the issue.
Note: New languages will not appear in GitHub's search results for some time after the pull request has been merged and the new Linguist release deployed to GitHub.com.
This is because GitHub's search uses a separate internal library for language detection which tends to lag behind Linguist by a few weeks to months.