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