- 1. Basic Operations
- 2. Squashing and Rebasing
- 3. Writing Good Commit messages
- 4. Creating Pull Requests
- 5. Sources
Go to https://github.com/bitcoin/bitcoin
and click on the Fork
button in the upper right corner.
This will create the <username>/bitcoin
project.
To clone the forked project, the command below can be used. This will allow you to work on the code and to open pull requests.
git clone https://github.com/<username>/bitcoin.git [<directory>]
The directory
argument is optional. If no one is provided, the name of the repository will be used (in this case bitcoin
).
If the intention is just to review or study the code, you can clone the original repository.
git clone https://github.com/bitcoin/bitcoin.git [<directory>]
If you intend to open pull requests and contribute to the original code, you also need to add the original repository.
Otherwise, this step can be skipped.
git remote add upstream https://github.com/bitcoin/bitcoin.git
To propose a new feature, a new branch must be used to develop it. This can be done with the command:
git checkout -b new_branch
Switched to a new branch ‘new_branch’
Specifying -b
causes a new branch to be created just like when git-branch
is called and then checked out.
If switching to an existing branch, this parameter must be omitted.
After making the changes, you can check the files that have been changed with git status
. This command is used to examine the current state of the repository and can be used to confirm a git add promotion.
The commit can be done with the following commands:
git add .
git commit -m "First commit"
git push -u origin new_branch
The git add
command adds a change in the working directory to the staging area. It tells Git that you want to include updates to a particular file in the next commit. However, git add doesn't really affect the repository in any significant way—changes are not actually recorded until you run git commit
.
If you want to add specific files to the staging area, you can use git add <filename>
.
The git reset
command is used to undo a git add. The git commit
command is then used to commit a snapshot of the staging directory to the repository's commit history.
You can also use git commit -a -m "First commit"
.Specifying -a
automatically stages all files that git already knows about.
The git push
is essential for a complete collaborative Git workflow. It is utilized to send the committed changes to remote repositories for collaboration.
The -u
argument sets up the association between the current branch and the remote one (defined in the step 1.1.3). You only need to use -u
once.
Alternatively, you can use the –set-upstream
option that is equivalent to the -u
option.
Once you push the changes to the repository, the Compare & pull request
button will appear in GitHub in the initial page.
Open a pull request by clicking this button. This allows the repository maintainers to review the contribution. From here, they can merge it if it's good, or they can request some changes.
GitHub exposes PRs as branches on the upstream repository with pull/<pr_number>/head
. So they can be retrieved with following command:
git fetch origin pull/<pr_number>/head && git checkout FETCH_HEAD
The review message usually contains ACK
if the reviewers agree with the changes, NACK
if not.
An ACK
is usually followed by a description of how the review was done.
Concept ACK
means the reviewer agrees with the objective of the change, but has not looked at or tested the code.
Approach ACK
means agreement with both the objective and the approach used in the change.
Code review ACK
means the code has been reviewed but not yet tested.
Tested ACK
(or tACK
) means the code was tested and the reviewer agrees with the change.
Some reviews contain a concise description of the tests performed, such as the PR 23065 - comment. It is good practice for new contributors.
When giving an ACK
, specify the commits reviewed by appending the commit hash of the HEAD commit, for example, tACK 94b6c8d
.
The command git tag
shows all existing tags.
You can switch to a specific tag with the following command, which is useful when testing a version.
git checkout tags/v0.21.0
Keeping commits organized is a good practice. Usually after opening a PR, reviewers will make suggestions and the code will need to be changed. So, rather than adding new commits indiscriminately, git rebase
can be used to organize these new commits in a coherent way.
If the --amend
option is used, the changes will be squashed into the most recent commit.
This is the fastest way to merge two commits into one.
git commit -a --amend
Amending only works for the most recent commit. But it's common to need to fix an older commit.
There is a tool to do this: the interactive rebase, which can be used in two ways: passing HEAD~n
as parameters, where n
is the last n
commits that are about to be rebased. Or passing the commit hash as parameter.
git rebase -i HEAD~3
# or
git rebase -i 1be0581
This will open the default text editor with something like this:
pick 8d3fc77 Add test.cpp
pick 2a73a77 Change net.h
pick 0b9d0bb Add net.h
# Rebase f5f19fb..0b9d0bb onto f5f19fb (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# f, fixup <commit> = like "squash", but it discards the commit's log message.
When you run git rebase -i, you get an editor session listing all of the commits that are being rebased and a number of options for what you can do to them. The default choice is pick
.
pick
maintains the commit in your history.
reword
allows you to change a commit message, perhaps to fix a typo or add additional commentary.
squash
merges multiple commits into one.
fixup
is like squash
, but discard this commit's log message.
You can reorder commits by moving them around in the file.
To merge commits, change pick
to fixup
for the items to be merged.
pick 8d3fc77 Add test.cpp
fixup 2a73a77 Change net.h
pick 0b9d0bb Add net.h
After saving and closing the text editor, git will remove all these commits from its history and then execute each line, one at a time. Lines with the pick
command will be kept. Those with fixup
or squash
will be squashed.
The squashing can also be performed in a more automated manner by using the --autosquash
option of git rebase
in combination with the --fixup
option of git commit
:
git commit -a --fixup HEAD^
git rebase -i --autosquash HEAD~3
This automatically generates the rebase script with commits reordered and actions set up.
HEAD^
refers to the first immediate parent of the tip of the current branch. HEAD^
is short for HEAD^1
. This is usually used on merge commits.
HEAD ~ 3
gets the last 3 commits. This value can be changed to an arbitrary number of commits.
When developing new features, it is common to need to fetch new commits from bitcoin:master
and rewrite the local master with the upstream's master.
To do this, the upstream repository must have already been configured, as shown in step 1.1.3.
The commands below execute this merge.
$ git fetch upstream
$ git rebase upstream/master
To push the update to the master, it may be necessary to force the push with --force
(or -f
).
$ git push origin master --force
Good commit messages are one of most important aspects for a project's maintainability. They allow new contributors to retrieve the context of a change and understand what has changed and why.
Therefore, a developer should not neglect good practices when writing commit messages. Some of them will be presented below.
From the git commit
manpage:
Though not required, it’s a good idea to begin the commit message with a single short (less than 50 character) line summarizing the change, followed by a blank line and then a more thorough description. The text up to the first blank line in a commit message is treated as the commit title, and that title is used throughout Git.
Firstly, not every commit requires both a subject and a body. Sometimes a single line is fine, especially when the change is so simple that no further context is necessary. For example:
Some small improvements to release notes
This message can be found in the commit 9f9ffe5.
Nothing more needs be said; if the reader wonders what the improvements were, she can simply take a look at the change itself, i.e. use git show
or git diff
or git log -p
.
If the commit message is simple, just add the -m
option to git commit
:
$ git commit -m "Some small improvements to release notes"
However, when a commit deserves a little explanation and context, it's better to write a body. For example:
doc: Stop nixing `-` in manual pages
The version replacement here is not working anyway, not just that but it
is actively harmful as it removes all `-` from the text. So remove that
line. See discussion in #22681.
Commit messages with bodies are not so easy to write with the -m
option. It's better to write in a proper text editor.
Note that there is a blank line between the title and the body.
This entire message will be shown in the result of git log
command.
git log --oneline
prints out just the subject line and git shortlog
groups commits by user, again showing just the subject line for concision.
50 characters is not a hard limit, just a rule of thumb. Keeping subject lines at this length ensures that they are readable, and forces the author to think for a moment about the most concise way to explain what’s going on.
GitHub’s UI will warn users if they exceed the 50 character limit and will truncate any subject line longer than 72 characters with an ellipsis.
So shoot for 50 characters, but consider 72 the hard limit.
Note that some of the last commits prior to v22 follow this rule:
$ git log --oneline
a0988140b (HEAD, tag: v22.0, origin/22.x) Merge bitcoin/bitcoin#22921: Some small improvements to release notes
9f9ffe5bb Some small improvements to release notes
f75615ebd doc: Manual pages update for 22.0 final
afbee409b build: Bump version to 22.0 final
03f142278 Merge bitcoin/bitcoin#22857: [22.x] Backports
fbf498d26 Merge bitcoin/bitcoin#22920: doc: Move 22.0 release notes from wiki
d44797241 doc: Move 22.0 release notes from wiki
303bc8a06 guix/prelude: Override VERSION with FORCE_VERSION
0640bf5c8 doc: mention bech32m/BIP350 in doc/descriptors.md
86de56776 (tag: v22.0rc3) doc: Manual pages update for rc3
c1c79f4c8 doc: Stop nixing `-` in manual pages
f95b655ba Improve doc/i2p.md regarding I2P router options/versions
59d4afc27 build: Bump version to 22.0rc3
This is as simple as it sounds. Begin all subject lines with a capital letter.
Note that most of the above commit messages start with a capital letter, except when prefixing the area affected by the commit. Valid areas are:
consensus
for changes to consensus critical codedoc
for changes to the documentationqt
orgui
for changes to bitcoin-qtlog
for changes to log messagesmining
for changes to the mining codenet
orp2p
for changes to the peer-to-peer network coderefactor
for structural changes that do not change behaviorrpc
,rest
orzmq
for changes to the RPC, REST or ZMQ APIsscript
for changes to the scripts and toolstest
,qa
orci
for changes to the unit tests, QA tests or CI codeutil
orlib
for changes to the utils or librarieswallet
for changes to the wallet codebuild
for changes to the GNU Autotools or reproducible builds
Trailing punctuation is unnecessary in subject lines. Besides, space is precious when you’re trying to keep them at 50 characters or less.
This can be seen in the above commit message headers.
Imperative mood just means “spoken or written as if giving a command or instruction”. A few examples:
- Remove extra \r from all.SHA256SUMS line ending
- Add a note that codesigners need to rebuild after tagging
- Make all.SHA256SUMS rather than codesigned.SHA256SUMS
The use of the imperative is important only in the subject line. This restriction can be relaxed when writing the body.
Git never wraps text automatically. When writing the body of a commit message, mind its right margin, and wrap text manually.
The recommendation is to do this at 72 characters, so that Git has plenty of room to indent text while still keeping everything under 80 characters overall.
The commit eb0b56b from Bitcoin Core is a great example of explaining what changed and why:
commit eb0b56b19017ab5c16c745e6da39c53126924ed6
Author: Pieter Wuille <pieter.wuille@gmail.com>
Date: Fri Aug 1 22:57:55 2014 +0200
Simplify serialize.h's exception handling
Remove the 'state' and 'exceptmask' from serialize.h's stream
implementations, as well as related methods.
As exceptmask always included 'failbit', and setstate was always
called with bits = failbit, all it did was immediately raise an
exception. Get rid of those variables, and replace the setstate
with direct exception throwing (which also removes some dead
code).
As a result, good() is never reached after a failure (there are
only 2 calls, one of which is in tests), and can just be replaced
by !eof().
fail(), clear(n) and exceptions() are just never called. Delete
them.
Take a look at the full diff and just think how much time the author is saving fellow and future committers by taking the time to provide this context here and now. If he didn’t, it would probably be lost forever.
The important thing here is to focus on making clear the reasons why you made the change in the first place—the way things worked before the change (and what was wrong with that), the way they work now, and why you decided to solve it the way you did.
It is important that the PR has a clear objective. This makes the review easier and reduces the risk of changes introducing bugs.
If the PR is too long, consider breaking it down into smaller ones.
A good example of this is PR 17728.
The PR title and description is the first thing reviewers will see. Therefore, it is important that they present clarity and objectivity so that the reviewer can easily understand the PR motivations.
The PR title should use the prefixes presented in section 1.3.3. They are described in Bitcoin Core's contributing guide. The title of PR 22732, for example, uses the prefix net
.
The same recommendation for the commit description applies here. PR text should focus on why, not what. Summarizing what was done is okay, but explaining the motivations and the problem that the RP solves is crucial.
The text of PR 22791, for example, explains the problem (how the bug was introduced) and the proposed solution.
Including step-by-step instructions on how to review and test the PR in the text is also interesting.
A PR can contain multiple commits. It is easier for the reviewer to analyze each change separately.
A good strategy for deciding the granularity of each commit is by functionality. For example, one changes the component, the other updates the documentation about this component, and another implements the tests for this component.
A good example of this strategy is PR 23065.
d96b000e9 (HEAD) Make GUI UTXO lock/unlock persistent
077154fe6 Add release note for lockunspent change
719ae927d Update lockunspent tests for lock persistence
f13fc1629 Allow lockunspent to store the lock in the wallet DB
c52789365 Allow locked UTXOs to be store in the wallet database
The 5 commits that make up this PR are logically divided. c52789365
implements the proposed solution. f13fc1629
updates the RPC. 719ae927d
updates the tests. 077154fe6
adds release note and d96b000e9
implements the same solution in GUI.
Separating commits this way is a good practice and makes it a lot easier for reviewers.
When adding a new feature, such as a new RPC, a test should be added.
If the change occurs at the component level, unit testing is likely to be more appropriate. If it's at the resource level (something users interact with), then functional testing fits the case.
An example of a PR that does this is the PR 14667, which added a new RPC and functional test for it.
How to Contribute Pull Requests to Bitcoin Core
How to create a pull request in GitHub
How to Review Pull Requests in Bitcoin Core
How to update a forked repo with git rebase
How to Write a Git Commit Message