lintje 0.7.0

Lintje is an opinionated linter for Git.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
# Rules

All the rules Lintje follows are documented on this page. The heading name
matches the rule name, and can be used to [ignore specific rules per
commit](../README.md#ignoring-rules-per-commit).

_Lintje is primarily focussed on supporting the English language, other
languages may not be compatible with every rule currently. Please
[create an issue](https://github.com/tombruijn/lintje/issues) if you run into
any problems._

## What type of rules is Lintje configured for?

Lintje is written to promote communication between people within Git commits.
Write commit subjects and messages meant for other people reading these commits
during reviews and debug sessions 2+ months from now.

It checks for commits like "Fix bug" and instead prefers commits that explain
changes in more detail. Explain why the change was necessary, what alternatives
were considered and why this solution was chosen. This will provide much needed
context to future readers so they understand what kind of constraints the
commit was made under.

Inspiration for Lintje's rules:

- [Git is about communication]https://tomdebruijn.com/posts/git-is-about-communication/
  by Tom de Bruijn.
- [A Note About Git Commit Messages]https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
  by Tim Pope.

Read the rest of this page for the full list of rules Lintje checks on and how
to fix them.

### Git for machines

Lintje does not actively promote machine parsing of commit subjects and
messages for the purposes of generating changelogs automatically.

The audiences of commits and changelogs are different. Commits are written for
people working on a project and changelogs are written for people using the
project. In my opinion a changelog entry should not be based on a Git commit,
but instead be managed with another tool such as
[Changesets](https://github.com/atlassian/changesets), which can also generate
changelogs automatically.

## SubjectLength

The commit's subject is considered too short or too long.

Short commit subjects like "WIP" and "Fix" don't explain the change well
enough. Don't be afraid to dive into a little bit more detail to explain the
change.

The commit's subject should be a maximum of 50 characters long. If the subject
is longer than 50 characters, reword the subject to fit in the maximum subject
length. Use the commit's message body to explain the change in greater detail.

```
# Good
Fix incorrect email validation

# Bad - too short
WIP
wip
Fix

# Bad - too long
One day I woke up and found the solution to this year old bug, the solution...
```

### Notes

- This rule is skipped if a [SubjectCliche]#subjectcliche issue is found.
- Certain characters like emoji are counted as multiple characters in this
  rule. Read more about [Unicode support][unicode].

## SubjectMood

Write commit subjects in the [imperative mood]. The commit is not actively
"fixing" an issue, but it is a "fix" for an issue or it does "add" a feature.

Start the subject with something like "Fix ...", but not "Fixes ...", "Fixed
..." or "Fixing ...".

```
# Good
Fix ...
Test ...
Add ...
Change ...
Update ...
Remove ...

# Bad
Fixes ...
Fixed ...
Fixing ...
Tests ...
Tested ...
Testing ...
Adds ...
Added ...
Adding ...
Changes ...
Changed ...
Changing ...
Updates ...
Updated ...
Updating ...
Removes ...
Removed ...
Removing ...
```

(_Where `...` would describe the change in more detail._)

You are of course free to start the subject with any other words than are
listed under the "Good" examples, as long as the subject uses the imperative
grammatical mood.

## SubjectWhitespace

The commit's subject starts with a whitespace (space, tab, etc). Remove this
leading whitespace from the subject.

```
# Good
Fix incorrect email validation

# Bad
 Fix incorrect email validation
  Fix incorrect email validation
<TAB>Fix incorrect email validation
```

## SubjectCapitalization

The commit's subject doesn't start with a capital letter. Use a capital letter
to start the subject.

```
# Good
Fix incorrect email validation

# Bad
fix incorrect email validation
```

### Notes

- This rule is skipped if a [NeedsRebase]#needsrebase issue is found. To fix
  a NeedsRebase issue the commit needs to be rebased into the commit it's marked
  to fixup or squash, and there will be no need to fix the capitalization.*

## SubjectPunctuation

The commit's subject starts or ends with punctuation. Subjects don't need to
end with punctuation.

It may also be that a subject starts with an emoji, subjects also don't need to
start with an emoji as a prefix of some kind.

```
# Good
Fix incorrect email validation

# Bad
Fix incorrect email validation.
Fix incorrect email validation!
Fix incorrect email validation?
.Fix incorrect email validation
!Fix incorrect email validation
?Fix incorrect email validation
📺 Fix my television
👍 All good
🐞 Fix bug in email validation
```

Sometimes commits contain some tag for some machine to parse, like `[ci skip]`
or `[skip ci]` to avoid building the commit on the CI, and save some resources.
This rule will trigger if this tag is part of the commit's subject. Instead
move the tag to the body of the commit message. It's not relevant for the
subject, and the space can instead be used for describe the change in more
detail.

## SubjectTicketNumber

The commit's subject includes a reference to a ticket or issue. Move this to
the message body.

Invalid subject examples:

```
# Bad
Fix #123
I have fixed #123
I have fixed org/repo#123
I have fixed https://github.com/org/repo#123
JIRA-123
Fix JIRA-123 for good
```

## SubjectPrefix

The commit's subject contains a prefix of some kind. Remove prefixes from the
commit subject and reword the subject to explain the change in more detail.

```
# Good
Fix bad validation for user email validation
Refactor the user email validation
Add email validation to user sign up
Add documentation for the user email validation

# Bad
fix: ...
chore: ...
feat: ...
feature: ...
docs: ...
refactor: ...
FIX: ...
fix!: bug...
fix(scope): ...
fix(scope)!: ...
```

## SubjectBuildTag

The commit's subject contains a "skip ci" build tag. This should be moved to
the message body. The skip CI tag doesn't tell anything about what kind of
change was made. It's metadata only for the CI system.

```
# Bad
// General
Update README [ci skip]
Update README [skip ci]
Update README [no ci]
// AppVeyor
Update README [skip appveyor]
// Azure
Update README [azurepipelines skip]
Update README [skip azurepipelines]
Update README [azpipelines skip]
Update README [skip azpipelines]
Update README [azp skip]
Update README [skip azp]
Update README ***NO_CI***",
// GitHub Actions
Update README [actions skip]
Update README [skip actions]
// Travis
Update README [travis skip]
Update README [skip travis]
Update README [travis ci skip]
Update README [skip travis ci]
Update README [travis-ci skip]
Update README [skip travis-ci]
Update README [travisci skip]
Update README [skip travisci]
// Other custom tags that match the format
[skip me]
[something skip]
[skip changeset]
[changeset skip]
[skip review]
[review skip]
```

## SubjectCliche

The commit's subject is considered to be a cliché, it's overused and adds
little meaning. This rule scans for subjects that only use two words to
describe a change, usually "fix bug" and "update code" types of subjects. The
words in the example below are the words it scans for.

To resolve this issue, expand the subject to explain the change in more detail.
Describe what type of bug was fixed and what type of change was made.

```
# Bad
WIP
Fix
Fix bug
Fixes test
Fixed issue
Fixing build
Add
Add file
Adds files
Added tests
Adding stuff
Update
Update README
Updates files
Updated tests
Updating stuff
Change
Change README
Changes files
Changed tests
Changing stuff
Remove
Remove file
Removes files
Removed tests
Removing stuff
Delete
Delete file
Deletes files
Deleted tests
Deleting stuff
...
```

## MessageEmptyFirstLine

The line in the commit message body after the subject is not empty. If the line
after the subject is not empty, it is considered part of the subject.

This is the preferred format of a Git commit:

```
Subject line

First message line below an empty line.
```

## MessagePresence

The commit's message body is empty or too short. Add a message body to the
commit to elaborate on _why_ the change was necessary, what alternatives were
considered and why this particular implementation was chosen as a solution.

### Notes

- This rule is skipped if a [NeedsRebase]#needsrebase issue is found. To fix
  a NeedsRebase issue the commit needs to be rebased into the commit it's
  marked to fixup or squash, and there will be no need to add a message body.*

## MessageLineLength

The commit's message body has one or more lines that are too long. The maximum
line length in a message body is 72 characters. Split sentences and paragraph
across multiples lines.

Lines that include URLs that start with `http://` or `https://` are excluded
from this rule. Lines that are too long inside code blocks are also ignored,
because it's not always possible to reformat code to fit on a 72 character
line.

    # Good - max 72 characters per line
    Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam
    nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,
    sed diam voluptua.

    # Good - the only too long line includes URL
    Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam.
    Source:
    https://url-to-page-that-is-very-long.org/but-still-valid-for-this-rule.html

    # Good - the only long line is in a code block
    Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam.

    ```
    Example code block with a very long line that will be considered valid!!!!
    ```

    ```md
    Example code block with a very long line that will be considered valid!!!!
    ```

    ``` md
    Example code block with a very long line that will be considered valid!!!!
    ```

    - Valid indented fenced code block inside a list
      ```
      Example code block with a very long line that will be considered valid!
      ```

    # Good - the only long line is in a code block
    Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam.

        Example code block with a very long line that will consider valid!!!!

    # Bad - lines are too long
    Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy aa
    tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.

### Notes

- Certain characters like emoji are counted as multiple characters in this
  rule. Read more about [Unicode support][unicode].

## DiffPresence

The commit is detected as having no file changes, it's an empty commit. Avoid
empty commits in the Git history. Empty commits can be left over after a rebase
after all its changes have been applied already in another commit.

Add the intended files changes to the empty commit, or remove the commit from
the Git history to fix this issue.

## MergeCommit

The commit is detected as a merge commit, which is a commit merging one branch
into another. Prefer rebasing feature branches instead of merging base branches
back into them. These commits don't communicate anything meaningful other than
when a person merged changes locally.

Currently only one scenario triggers this rule: when a remote branch is merged
into a local branch.

```
# Merge commit merging a remote branch into a local branch
Merge branch 'develop' of github.com/org/repo into develop
```

When pulling local changes that would create a merge commit, rebase the local
changes on the remote changes instead.

```
# Checkout the branch to pull updates for
git checkout feature-branch
# Rebase local changes on the remote's changes
git pull --rebase origin feature-branch
```

To avoid making these types of commits I recommend configuring a pull strategy
in Git. Either use the `pull.ff only` or `pull.rebase` strategy.

```
# Block pulls that would create a merge commit
# With this config you'll need to pull rebase the remote changes
git config --global pull.ff only

# Automatically rebase local changes on remote changes when using git pull
git config --global pull.rebase true
```

This rule will also try to trigger on local merge commits in the future.

### Notes about merge commits

Merge commits that merge local branches/tags into the repository's branches do
not fail under this rule, they are currently ignored, but may fail on this rule
in the future.

Merge commits made when merging Pull and Merge requests will not fail on this
rule, these commits are ignored entirely. These commits they communicate when a
Pull/Merge request was accepted and merged into the base branch. This includes
commits made by GitHub's "squash and merge" merge strategy.

## NeedsRebase

The commit is detected as a fixup or squash commit. These commits communicate
the intent to squash them into other commits during the next rebase. These
commits should not be send in for review in Pull Requests, and they should not
be merged into main branches.

```
git checkout feature-branch
git rebase --interactive --autosquash main
```

## BranchNameLength

The branch name is detected as too short. A branch name needs to be at least
four characters.

```
# Good branch names
main
develop
trunk
fix-email-validation

# Bad branch names
foo
wip
fix
bug
```

## BranchNameTicketNumber

The branch name is detected to only contain a ticket number or a prefix and
ticket number. Ticket numbers alone don't communicate much, especially if all
branches are formatted this way. Describe the branch in more detail, in a
couple words, to explain what the change is about. Ticket numbers are accepted,
but not as the only thing in the branch name.

```
# Good branch names
123-email-validation
123_fix_email_validation
123/feature-email-validation
fix-123-email-validation
fix_123_email_validation
fix/123-email-validation
feature-123-email-validation
email-validation-123

# Bad branch names - in any capitalization
123
123-fix
123_fix
123/fix
123-feature
fix-123
fix_123
fix/123
feature-123
JIRA-123
```

## BranchNamePunctuation

The branch name starts or ends with punctuation. Branch names should not use
punctuation this way.

```
# Good branch names
fix-email-validation
fix_email_validation
feature/email-validation

# Bad branch names
fix-bug!
fix-bug.
fix-bug'
fix-bug"
!fix-bug
-fix-bug
_fix-bug
~fix-bug
(JIRA-123)
[JIRA-123]
```

## BranchNameCliche

The branch name is considered to be a cliché, it's overused and adds little
meaning. This rule scans for branch names that only use two words to describe a
change, usually "fix-bug" and "add_test" types of branch names. The words in
the example below are the words it scans for.

To resolve this issue, expand the branch name to explain the change in more
detail. Describe what type of bug was fixed and what type of change was made.

```
# Bad
wip
wip-feature
wip_feature
wip/feature
fix
fix-bug
fixes-test
fixed-issue
fixing-build
add
add-file
adds-files
added-tests
adding-stuff
update
update-readme
updates-files
updated-tests
updating-stuff
change
change-readme
changes-files
changed-tests
changing-stuff
remove
remove-file
removes-files
removed-tests
removing-stuff
delete
delete-file
deletes-files
deleted-tests
deleting-stuff
...
```

<a name="unicode"></a>
## A note about Unicode display width

Not all characters render with the same display width in columns in the
terminal. The `a` character has a width of one column, but an emoji usually is
wider than one column. Lintje's rules will count line length in characters
using their display width. This means a subject can contain 50 characters like
the `a` character, but it cannot contain 50 emoji that are two columns wide.
Such an emoji heavy subject is allowed a maximum of 25 emoji for the
SubjectLength rule.

Not all emoji render with the same width and may break the layout of Lintje's
output, and cause problems with rules that calculate line length. Lintje will
attempt to correct the automatic width calculation of character width, but
some characters may not be calculated correctly. This is a bug.
Please [report any characters with that break the output][issue tracker].

For example, by default the "Green Check Mark" emoji `✅` has a width of two,
while the "Woman Scientist" emoji `👩‍🔬` has a width of four columns.
This is because the second emoji is a combination of the `👩` and `🔬` emoji.
Normally they would both be counted towards the width even though only one
emoji is rendered. This particular scenario is calculated and rendered properly
as two columns in Lintje.

The heart emoji `❤️`, and similar emoji, will only have a width of one column,
this is by design as most other (terminal) apps–in which Lintje will print its
output–will also render it with a width of one column.

[unicode]: #unicode
[issue tracker]: https://github.com/tombruijn/lintje/issues
[imperative mood]: https://en.wikipedia.org/wiki/Imperative_mood