1Coreutils Contribution Guidelines
2
3
4Prerequisites
5=============
6You will need the "git" version control tools.  On Fedora-based
7systems, do "yum install git".  On Debian-based ones install the
8"git-core" package.  Then run "git --version".  If that says it's
9older than version 1.4.4, then you'd do well to get a newer version.
10At worst, just download the latest stable release from
11https://git-scm.com/ and build from source.
12
13For details on building the programs in this package, see the file,
14README-hacking.
15
16
17Use the latest upstream sources
18===============================
19Base any changes you make on the latest upstream sources.  You can get
20a copy of the latest with this command:
21
22    git clone https://git.savannah.gnu.org/git/coreutils.git
23    cd coreutils
24
25That downloads the entire repository, including revision control history
26dating back to 1991.  The repository (the part you download, and which
27resides in coreutils/.git) currently weighs in at about 30MB.  So you
28don't want to download it more often than necessary.  Once downloaded,
29you can get incremental updates by running one of these commands from
30inside your new coreutils/ directory:
31
32If you have made *no* changes:
33    git pull
34
35If you *have* made changes and mistakenly committed them to "master",
36do the following to put your changes on a private branch, "br", and
37to restore master to its unmodified (relative-to-upstream) state:
38    git checkout -b br
39    git checkout master
40    git reset --hard origin
41
42Then "git pull" should work.
43
44
45*Before* you commit changes
46===========================
47
48In this project, we much prefer patches that automatically record
49authorship.  That is important not just to give credit where due, but
50also from a legal standpoint (see below).  To create author-annotated
51patches with git, you must first tell git who you are.  That information
52is best recorded in your ~/.gitconfig file.  Edit that file, creating
53it if needed, and put your name and email address in place of these
54example values:
55
56[user]
57  name = Joe X. User
58  email = joe.user@example.com
59
60
61Your first commit: the quick and dirty way
62==========================================
63First of all, realize that to "commit" a change in git is a purely
64local operation.  It affects only the local repository (the .git/ dir)
65in your current coreutils/ hierarchy.
66
67To try this out, modify a file or two.  If you create a new file, you'll
68need to tell git about it with "git add new-file.c".  Commit all changes
69with "git commit -a".  That prompts you for a log message, which should
70include a one-line summary, a blank line, and ChangeLog-style entries
71for all affected files.  More on that below.
72
73Once your change is committed, you can create a proper patch that includes
74a log message and authorship information as well as any permissions
75changes.  Use this command to save that single, most-recent change set:
76
77  git format-patch --stdout -1 > DIFF
78
79The trouble with this approach is that you've just checked in a change
80(remember, it's only local) on the "master" branch, and that's where new
81changes would normally appear when you pull the latest from "upstream".
82When you "pull" from a remote repository to get the latest, your local
83changes on "master" may well induce conflicts.   For this reason, you
84may want to keep "master" free of any local changes, so that you can
85use it to track unadulterated upstream sources.
86
87However, if your cloned directory is for a one-shot patch submission and
88you're going to remove it right afterwards, then this approach is fine.
89Otherwise, for a more sustainable (and more generally useful, IMHO)
90process, read on about "topic" branches.
91
92
93Make your changes on a private "topic" branch
94=============================================
95So you checked out coreutils like this:
96
97  git clone https://git.savannah.gnu.org/git/coreutils.git
98
99Now, cd into the coreutils/ directory and run:
100
101  git checkout -b my-topic
102
103That creates the my-topic branch and puts you on it.
104To see which branch you're on, type "git branch".
105Right after the clone, you were on "master" (aka the trunk).
106To get back to the trunk, do this:
107
108  git checkout master
109
110Note 1:
111    Be careful to run "git pull" only when on the "master" branch,
112    not when on a branch.  With newer versions of git, you can't cause
113    trouble if you forget, so this is a good reason to ensure you're
114    using 1.5.3.1 or newer.
115
116Note 2:
117    It's best not to try to switch from one branch to another if
118    you have pending (uncommitted) changes.  Sometimes it works,
119    sometimes the checkout will fail, telling you that your local
120    modifications conflict with changes required to switch branches.
121    However, in any case, you will *not* lose your uncommitted changes.
122    Run "git stash" to temporarily hide uncommitted changes in your
123    local directory, restoring a clean working directory.
124
125Anyhow, get back onto your just-created branch:
126
127  git checkout my-topic
128
129Now, modify some file and commit it:
130
131  git commit some-file.c
132
133Personally, no matter what package I'm working on, I find it useful to
134put the ChangeLog entries *only* in the commit log, initially, unless
135I plan to commit/push right away.  Otherwise, I tend to get unnecessary
136merge conflicts with each rebase (see below).  In coreutils, I've gone
137a step further, and no longer maintain an explicit ChangeLog file in
138version control.  Instead, in a git working directory, you can view
139ChangeLog information via "git log".  However, each distribution tarball
140does include a ChangeLog file that is automatically generated from the
141git logs.
142
143So, you've committed a change.  But it's only in your local repository,
144and only on your "my-topic" branch.  Let's say you wait a day, and
145then see that someone else changed something and pushed it to the
146public repository.  Now, you want to update your trunk and "rebase"
147your changes on the branch so that they are once again relative to the
148tip of the trunk.  Currently, your branch is attached to the trunk at
149the next-to-last change set.
150
151First: update the trunk from the public repo:
152[you've first made sure that "git diff" produces no output]
153
154  git checkout master
155  git pull
156
157Now, return to your branch, and "rebase" relative to trunk (master):
158
159  git checkout my-topic
160  git rebase master
161
162If there are no conflicts, this requires no more work from you.
163However, let's say there was one in ChangeLog, since you didn't
164follow my advice and modified it anyway.
165git rebase will tell you there was a conflict and in which
166file, and instruct you to resolve it and then resume with
167"git rebase --continue" once that's done.
168
169So you resolve as usual, by editing ChangeLog (which has the
170usual conflict markers), then type "git rebase --continue".
171That will fail, with a diagnostic telling you to mark
172the file as "conflict resolved" by doing this:
173
174  git add ChangeLog
175
176Then, finally, you can proceed (possibly onto more conflict resolution,
177if there are conflicts in other files):
178
179  git rebase --continue
180
181Once it finishes, your changes on the branch are now relative to
182the tip of the trunk.
183
184Now use git format-patch, as above.
185
186
187Amending the most recent change on your private branch
188======================================================
189Let's say you've just committed a change on your private
190branch, and then realize that something about it is not right.
191It's easy to adjust:
192
193  edit your files # this can include running "git add NEW" or "git rm BAD"
194  git commit --amend -a
195  git format-patch --stdout -1 > your-branch.diff
196
197That replaces the most recent change-set with the revised one.
198
199
200
201Coreutils-specific:
202
203No more ChangeLog files
204=======================
205Do not modify any of the ChangeLog files in coreutils.  Starting in
2062008, the policy changed.  Before, we would insert the exact same text
207(or worse, sometimes slightly differing) into both the ChangeLog file
208and the commit log.  Now we put that information only in the commit log,
209and generate the top-level ChangeLog file from logs at "make dist" time.
210As such, there are strict requirements on the form of the commit log
211messages.
212
213
214Commit log requirements
215=======================
216Your commit log should always start with a one-line summary, the second
217line should be blank, and the remaining lines are usually ChangeLog-style
218entries for all affected files.  However, it's fine -- even recommended --
219to write a few lines of prose describing the change, when the summary
220and ChangeLog entries don't give enough of the big picture.  Omit the
221leading TABs that you're used to seeing in a "real" ChangeLog file, but
222keep the maximum line length at 72 or smaller, so that the generated
223ChangeLog lines, each with its leading TAB, will not exceed 80 columns.
224As for the ChangeLog-style content, please follow these guidelines:
225
226  https://www.gnu.org/prep/standards/standards.html#Change-Logs
227
228Try to make the summary line fit one of the following forms:
229
230  program_name: change-description
231  prog1, prog2: change-description
232  doc: change-description
233  tests: change-description
234  build: change-description
235  maint: change-description
236
237If your commit fixes a bug, try to find the commit that introduced that
238bug.  If you do that, add a note in your new commit log saying something
239like "Introduced by commit v8.12-103-g54cbe6e." and add something like
240[bug introduced in coreutils-8.13] in the corresponding NEWS blurb.
241Assuming you found the bug in commit 54cbe6e6, "git describe 54cbe6e6"
242will print the longer tag-relative string that you'll need.
243Note that we used to use an 8-byte SHA1 prefix like "54cbe6e6", because
244that was automatically rendered as a clickable link by "gitk", but with
245git-1.7.10, the more descriptive version-containing "git describe" format
246that we now require is also highlighted.
247
248
249Curly braces: use judiciously
250=============================
251Omit the curly braces around an "if", "while", "for" etc. body only when
252that body occupies a single line.  In every other case we require the braces.
253This ensures that it is trivially easy to identify a single-*statement* loop:
254each has only one *line* in its body.
255
256Omitting braces with a single-line body is fine:
257
258     while (expr)
259       single_line_stmt ();
260
261However, the moment your loop/if/else body extends onto a second line,
262for whatever reason (even if it's just an added comment), then you should
263add braces.  Otherwise, it would be too easy to insert a statement just
264before that comment (without adding braces), thinking it is already a
265multi-statement loop:
266
267     while (true)
268       /* comment... */      // BAD: multi-line body without braces
269       single_line_stmt ();
270
271Do this instead:
272
273     while (true)
274       {  /* Always put braces around a multi-line body.  */
275         /* explanation... */
276         single_line_stmt ();
277       }
278
279There is one exception: when the second body line is not at the same
280indentation level as the first body line.
281
282     if (expr)
283       error (0, 0, _("a diagnostic that would make this line"
284                      " extend past the 80-column limit"));
285
286It is safe to omit the braces in the code above, since the
287further-indented second body line makes it obvious that this is still
288a single-statement body.
289
290To reiterate, don't do this:
291
292     if (expr)
293       while (expr_2)        // BAD: multi-line body without braces
294         {
295           ...
296         }
297
298Do this, instead:
299
300     if (expr)
301       {
302         while (expr_2)
303           {
304             ...
305           }
306       }
307
308However, there is one exception in the other direction, when even a
309one-line block should have braces.  That occurs when that one-line,
310brace-less block is an "else" block, and the corresponding "then" block
311*does* use braces.  In that case, either put braces around the "else"
312block, or negate the "if"-condition and swap the bodies, putting the
313one-line block first and making the longer, multi-line block be the
314"else" block.
315
316    if (expr)
317      {
318        ...
319        ...
320      }
321    else
322      x = y;    // BAD: braceless "else" with braced "then"
323
324This is preferred, especially when the multi-line body is more than a
325few lines long, because it is easier to read and grasp the semantics of
326an if-then-else block when the simpler block occurs first, rather than
327after the more involved block:
328
329    if (!expr)
330      x = y;                  /* more readable */
331    else
332      {
333        ...
334        ...
335      }
336
337If you'd rather not negate the condition, then add braces:
338
339    if (expr)
340      {
341        ...
342        ...
343      }
344    else
345      {
346        x = y;
347      }
348
349
350Use SPACE-only indentation in all[*] files
351==========================================
352We use space-only indentation in nearly all files.
353If you use Emacs and your coreutils working directory name matches,
354this code enables the right mode:
355
356  ;; In coreutils, indent with spaces everywhere (not TABs).
357  ;; Exceptions: Makefile and ChangeLog modes.
358  (add-hook 'find-file-hook '(lambda ()
359    (if (and buffer-file-name
360             (string-match "/coreutils\\>" (buffer-file-name))
361             (not (string-equal mode-name "Change Log"))
362             (not (string-equal mode-name "Makefile")))
363        (setq indent-tabs-mode nil))))
364
365If you use vim (7+ compiled with autocommands), and coreutils working
366directory name also matches, add the following in ~/.vimrc:
367
368  " Set GNU style indentation, spaces instead of TABs
369  function! CoreutilsIndent()
370      " Check if 'coreutils' is part of the current working directory
371      if match(getcwd(), "coreutils") > 0
372          " The next 3 lines below set the GNU indentation
373          setlocal cinoptions=>4,n-2,{2,^-2,:2,=2,g0,h2,p5,t0,+2,(0,u0,w1,m1
374          setlocal shiftwidth=2
375          setlocal tabstop=8
376          " Coreutils specific, expand TABs with spaces
377          setlocal expandtab
378      endif
379  endfunction
380
381  autocmd BufEnter *.c,*.h call CoreutilsIndent()
382
383[*] Makefile and ChangeLog files are exempt, of course.
384
385
386Send patches to the address listed in --help output
387===================================================
388Please follow the guidelines in the "Sending your patches." section of
389git's own SubmittingPatches:
390
391  https://github.com/git/git/blob/master/Documentation/SubmittingPatches
392
393
394Add documentation
395=================
396If you add a feature or change some user-visible aspect of a program,
397document it.  If you add an option, document it both in --help output
398(i.e., in the usage function that generates the --help output) and in
399doc/*.texi.  The man pages are generated from --help output, so
400you shouldn't need to change anything under man/.  User-visible changes
401are usually documented in NEWS, too.
402
403When writing prose (documentation, comments, log entries), use an
404active voice, not a passive one.  I.e., say "print the frobnozzle",
405not "the frobnozzle will be printed".
406
407Please add comments per the GNU Coding Standard:
408  https://www.gnu.org/prep/standards/html_node/Comments.html
409
410
411Minor syntactic preferences
412===========================
413[I hesitate to write this one down, because it appears to be an
414 acquired taste, at least for native-English speakers.  It seems odd
415 (if not truly backwards) to nearly anyone who doesn't have a strong
416 mathematics background and perhaps a streak of something odd in their
417 character ;-) ]
418In writing arithmetic comparisons, use "<" and "<=" rather than
419">" and ">=".  For some justification, read this:
420  http://www.gelato.unsw.edu.au/archives/git/0505/4507.html
421
422const placement:
423Write "Type const *var", not "const Type *var".
424FIXME: dig up justification
425
426
427Be nice to translators
428======================
429Don't change translatable strings if you can avoid it.
430If you must rearrange individual lines (e.g., in multi-line --help
431strings), extract and create new strings, rather than extracting
432and moving into existing blocks.  This avoids making unnecessary
433work for translators.
434
435
436Add tests
437==========
438Nearly every significant change must be accompanied by a test suite
439addition that exercises it.  If you fix a bug, add at least one test that
440fails without the patch, but that succeeds once your patch is applied.
441If you add a feature, add tests to exercise as much of the new code
442as possible.  If you add a new test file (as opposed to adding a test to
443an existing test file) add the new test file to 'tests/local.mk'.
444Note to run tests/misc/new-test in isolation you can do:
445
446  make check TESTS=tests/misc/new-test SUBDIRS=. VERBOSE=yes
447
448Variables that are significant for tests with their default values are:
449
450  VERBOSE=yes
451  RUN_EXPENSIVE_TESTS=no
452  RUN_VERY_EXPENSIVE_TESTS=no
453  SHELL=/bin/sh
454  NON_ROOT_USERNAME=nobody
455  NON_ROOT_GID=$(id -g $NON_ROOT_USERNAME)
456  COREUTILS_GROUPS=$(id -G)
457
458There are hundreds of tests in the tests/ directories.  You can use
459tests/sample-test as a template, or one of the various Perl-based ones
460in tests/misc.
461
462If writing tests is not your thing, don't worry too much about it,
463but do provide scenarios, input/output pairs, or whatever, along with
464examples of running the tool to demonstrate the new or changed feature,
465and someone else will massage that into a test (writing portable tests
466can be a challenge).
467
468
469Copyright assignment
470====================
471If your change is significant (i.e., if it adds more than ~10 lines),
472then you'll have to have a copyright assignment on file with the FSF.
473Since that involves first an email exchange between you and the FSF,
474and then the exchange (FSF to you, then back) of an actual sheet of paper
475with your signature on it, and finally, some administrative processing
476in Boston, the process can take a few weeks.
477
478The forms to choose from are in gnulib's doc/Copyright/ directory.
479If you want to assign a single change, you should use the file,
480doc/Copyright/request-assign.changes:
481
482    https://www.gnu.org/software/gnulib/Copyright/request-assign.changes
483
484If you would like to assign past and future contributions to a project,
485you'd use doc/Copyright/request-assign.future:
486
487    https://www.gnu.org/software/gnulib/Copyright/request-assign.future
488
489You may make assignments for up to four projects at a time.
490
491In case you're wondering why we bother with all of this, read this:
492
493    https://www.gnu.org/licenses/why-assign.html
494
495
496Run "make syntax-check", or even "make distcheck"
497================================================
498Making either of those targets runs many integrity and
499project-specific policy-conformance tests.  For example, the former
500ensures that you add no trailing blanks and no uses of certain deprecated
501functions.  The latter performs all "syntax-check" tests, and also
502ensures that the build completes with no warnings when using a certain
503set of gcc -W... options.  Don't even bother running "make distcheck"
504unless you have a reasonably up to date installation including recent
505versions of gcc and the linux kernel, and modern GNU tools.
506
507
508Ensure that your changes are indented properly.
509===============================================
510Format the code the way GNU indent does.
511Filtering most source files through "indent --no-tabs" should
512induce no change in indentation.  Try not to add any more.
513
514
515Avoid trailing white space
516==========================
517You may notice that the only trailing blanks in coreutils'
518version-controlled files are in a single directory: tests/pr,
519which contains expected output from various invocations of pr.
520
521Do not add any more trailing blanks anywhere.  While "make syntax-check"
522will alert you if you slip up, it's better to nip any problem in the
523bud, as you're typing.  A good way to help you adapt to this rule is
524to configure your editor to highlight any offending characters in the
525files you edit.  If you use Emacs, customize its font-lock mode
526or use its WhiteSpace mode:
527
528    https://www.emacswiki.org/emacs/WhiteSpace
529
530If you use vim, add this to ~/.vimrc:
531
532    let c_space_errors=1
533    highlight RedundantSpaces ctermbg=red guibg=red
534    match RedundantSpaces /\s\+$\| \+\ze\t/
535
536
537Git can help too, by stopping you from committing any change that would
538add trailing blanks.  The example pre-commit hook contains code to check
539for trailing whitespace and spaces before tabs; enable it by moving it
540to the right place and making sure it is executable:
541
542    mv .git/hooks/pre-commit.sample .git/hooks/pre-commit
543
544With a repository created by git-1.5.6 or older, use this command:
545
546    chmod +x .git/hooks/pre-commit
547
548To manually check for whitespace errors before committing, you can use
549
550    git diff --check
551
552Git also has some settings to enable suitable internal whitespace checks.
553See the manpage for git-apply for details.
554
555
556-------------------------------------------
557
558Miscellaneous useful git commands
559=================================
560
561  * gitk: give a graphical view of the revision graph of the current branch
562  * gitk --all: same, but display all branches
563  * git log: to get most of the same info in text form
564  * git log -p: same as above, but with diffs
565  * git log -p SOME_FILE: same as above, but limit to SOME_FILE
566  * git log -p -2 SOME_FILE: same as above, but print only two deltas
567  * git log -p -1: print the most recently committed change set
568  * git format-patch --stdout -1 > FILE: output the most recently committed
569      change set, in a format suitable to be submitted and/or applied via
570      "git am FILE".
571  * git reset --soft HEAD^: Commit the delta required to restore
572      state to the revision just before HEAD (i.e., next-to-last).
573  * git rebase -i master: run this from on a branch, and it gives
574      you an interface with which you can reorder and modify arbitrary
575      change sets on that branch.
576
577  * if you "misplace" a change set, i.e., via git reset --hard ..., so that
578    it's no longer reachable by any branch, you can use "git fsck" to find
579    its SHA1 and then tag it or cherry-pick it onto an existing branch.
580    For example, run this:
581      git fsck --lost-found HEAD && cd .git/lost-found/commit \
582        && for i in *; do git show $i|grep SOME_IDENTIFYING_STRING \
583        && echo $i; done
584    The "git fsck ..." command creates the .git/lost-found/... hierarchy
585    listing all unreachable objects.  Then the for loop
586    print SHA1s for commits that match via log or patch.
587    For example, say that found 556fbb57216b119155cdda824c98dc579b8121c8,
588    you could run "git show 556fbb57216b119" to examine the change set,
589    or "git checkout -b found 556fbb5721" to give it a branch name.
590    Finally, you might run "git checkout master && git cherry-pick 556fbb5721"
591    to put that change on the tip of "master".
592
593-------------------------------------------
594
595Finding things to do
596====================
597If you don't know where to start, check out the TODO file for projects
598that look like they're at your skill-/interest-level.  Another good
599option is always to improve tests.  You never know what you might
600uncover when you improve test coverage, and even if you don't find
601any bugs your contribution is sure to be appreciated.
602
603A good way to quickly assess current test coverage, for standard
604and root only tests, is to follow these steps (requires lcov to be installed):
605
606  # Do a standard run as the current user
607  make -j$(nproc) coverage
608
609  # Add the root only tests
610  sudo make -j$(nproc) build-coverage NON_ROOT_USERNAME=$USER SUBDIRS=.
611
612  # Generate the report with the combined results
613  make gen-coverage
614
615  # view the HTML report:
616  xdg-open doc/coverage/index.html
617
618========================================================================
619Copyright (C) 2009-2023 Free Software Foundation, Inc.
620
621Permission is granted to copy, distribute and/or modify this document
622under the terms of the GNU Free Documentation License, Version 1.3 or
623any later version published by the Free Software Foundation; with no
624Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
625Texts.  A copy of the license is included in the "GNU Free
626Documentation License" file as part of this distribution.
627