-
Notifications
You must be signed in to change notification settings - Fork 336
How we use version control
Our new (as of October 2015) Git branching model is heavily influenced by Git Flow, and this blog post.
The core development repository is Graphene. Code always flows
downward from Graphene to chain repositories. Porting code from a chain repository to Graphene requires
cherry-pick
, and is to be avoided.
-
$CHAIN/master
: Most recent common ancestor of$CHAIN
andgraphene/master
-
$CHAIN/$CHAIN
: Production branch. Currently active nodes are advised to run this. -
$CHAIN/staging
: Staging branch. Should be helping Advanced users may run this to help us test new features. -
$CHAIN/hftest
: Hardfork testing. Hardfork with earlier inclusion date, thus, it is different from the main chain and will be thrown away when testing is finished. Such branches will not be created until further infrastructure improvements have been made, i.e.: GUI improvements to show when you're on a non-production chain, hardforking the reported chain ID, accepting transactions with multiple chain ID to allow us to import the tx flow from the main chain.
-
graphene/master
: New chains are advised to start with this commit, then configureconfig.hpp
. -
graphene/develop
: Staging branch for new features not yet ready forgraphene/master
. -
graphene/stable
: Most recent common ancestor of$CHAIN/master
across all chains. Preferred basis for patches, since patches written against this should apply to any chain.
-
graphene/fork-123
: HF Commits which will change consensus go in here. -
graphene/api-123
: Non-HF Commits which add API calls go here. -
graphene/wallet-123
: Non-HF Commits which add wallet features go here. -
graphene/opt-123
: Non-HF Commits which optimize existing code go here. Resulting code should be semantically equivalent but faster. -
graphene/bugfix-123
: Non-HF Commits which fix a specific local bug. -
graphene/cleanup-123
: Non-HF Commits which refactor, neaten, or fix architectural issues. May be semantically equivalent. -
graphene/feature-123
: Possibly-HF Commits which don't fit well in the above categories. The issue should describe exactly what it does.
Some changes are so minor as to not require an issue, e.g. changes to logging. These should still be done against stable
where possible (or the earliest commit they depend on,
where not possible) and merged. In practice you may develop such patches against master
or one of the chain branches; then rebase against stable
before pushing.
-
graphene/log-blackswan
: Changes to logging of something (in this case, black swan).
-
$CHAIN/master
should never be force-pushed. -
$CHAIN/$CHAIN
should never be force-pushed. - Ideally,
graphene/master
should never be force-pushed; exceptions to this policy may be made on a case-by-case basis. -
graphene/stable
is always maintained as the MRCA of all$CHAIN/master
. Since we have a policy to never force-push$CHAIN/master
, this effectively meansgraphene/stable
will never be force-pushed. -
graphene/develop
should rarely be force-pushed, but it may be force-pushed to kill patches that are prematurely merged. - Individual patch branches may be force-pushed at any time, at the discretion of the developer or team working on the patch.
- A tag is made of
$CHAIN/$CHAIN
when a new "production" release is created. - A corresponding tag should be made on
graphene/master
based on the MRCA of$CHAIN/$CHAIN
andgraphene/master
.
- All pull requests should be against
graphene/stable
, unless they contain code specific to some chain.
- Each chain-specific
master
branch should always be an ancestor ofgraphene/master
. - A chain-specific
master
branch should not include any commit that has not been merged into its production branch. - If we have a situation where a commit from
graphene/master
can never be ported to some chain, the above policies imply that chain'smaster
will become stuck. In this case, the commit should be merged to the production branch and immediately reverted. This action documents our conscious decision not to put this particular patch on this particular chain, and should hopefully keep the patch from being inadvertently merged later.
- Two developers must review every consensus-breaking change before it moves into
graphene/master
. - Two developers should review every patch before it moves into
graphene/master
. - One of the reviewers may be the author of the change.
- This policy is designed to encourage you to take off your "writer hat" and put on your "critic/reviewer hat." If this was a patch from an unfamiliar community contributor, would you accept it? Can you understand what the patch does and check its correctness based only on its commit message and diff? Does it break any existing tests, or need new tests to be written? Is it stylistically sloppy -- trailing whitespace, multiple unrelated changes in a single patch, mixing bugfixes and features, or overly verbose debug logging?
- Having multiple people look at a patch reduces the probability it will contain bugs.
Each of these scenarios starts out with "The Backstory" specifying what situation you're in and what you're trying to do, and then a concise explanation and list of commands telling you how to get to where you need to go.
The Backstory: I want to work on bitshares
and graphene
in the same local repository.
-
This is why git remotes were invented. A git remote is simply an alias to a URL where a repository lives. Create git remotes as follows:
git remote add graphene [email protected]:cryptonomex/graphene git fetch graphene git remote add bts2 [email protected]:bitshares/bitshares-2 git fetch bts2 git remote add muse [email protected]:peertracksinc/muse git fetch muse
-
Git tip: The
git fetch
command locally mirrors all the branches on the specified remote into remote-tracking branches such asbts2/bitshares
. These remote-tracking branches are automatically updated by futuregit fetch
operations, so at any point in time thebts2/
namespace collectively represents the state of thebts2
node the last time you did agit fetch bts2
. -
Git tip: If you created your repository with
git clone
, then theorigin
remote is set up as the remote you originally specified. Anywhere you specifyorigin
in your normalgit
workflow, you can specify an arbitrary remote name. -
Git tip: The URL for a remote can be changed with
git remote set-url
. This may be useful e.g. when moving from a privately hosted repository on a local filesystem or Gitlab, to a public repository on Github. -
Git tip: Remotes may also be
http
orhttps
URL's, paths on the local filesystem, orgit+ssh
to run over ssh.
The Backstory: I want to add an API call.
-
Create an issue in the Graphene repo. This assumes the issue you created was issue number 123.
git fetch graphene git checkout graphene/stable -b api-123 notepad libraries/app/api.cpp git add libraries/app/api.cpp git commit -m "The API is better now" git push graphene HEAD:api-123
-
Git tip: The
-b
command creates a branch. A branch is simply a named pointer to a commit, which automatically advances when you create a new commit withgit commit
. Creating local branches for specific tasks and deleting them when you're done is often a useful workflow.
The Backstory: I want to add the API feature I created in Graphene (see previous item) to BitShares.
-
First, self-review. Take off your "patch author hat" and put on your "critic/reviewer hat". If this was a patch from an unfamiliar community contributor, would you accept it? Can you understand what the patch does and check its correctness based only on its commit message and diff? Does it break any existing tests, or need new tests to be written? Is it stylistically sloppy -- trailing whitespace, multiple unrelated changes in a single patch, mixing bugfixes and features, or overly verbose debug logging?
-
Merge it into
graphene/develop
:git fetch graphene git checkout graphene/develop -b develop git merge graphene/api-123 git push graphene HEAD:develop
-
Merging your feature into
graphene/develop
is a signal that you've completed your self-review and it's as ready for production as you can make it on your own. It's time for other developers to take a look at it. -
When it passes code review, it's ready to be merged to
graphene/master
:git fetch graphene git checkout graphene/master -b master git merge graphene/api-123 git push graphene HEAD:master
-
Anything in
graphene/master
can be merged into chain branches:git fetch bts2 git checkout bts2/master -b master git merge graphene/api-123 git push graphene HEAD:master
The Backstory: I'm not sure what I should put in $CHAIN/master
.
This can actually be mechanically determined:
git merge-base origin/develop bts2/bitshares
The Backstory: So what's the deal with graphene/stable
then?
-
It's the most recent common ancestor of
graphene/master
and all other chains, i.e.:git merge-base --octopus graphene/master bts2/bitshares muse/muse otherchain/otherchain ...
-
If the current
stable
commit is different from the output of the above command,stable
should be updated.
The Backstory: OK, I screwed up. Something bad happens like api.cpp
does not compile, or running it formats the user's disk.
I already made a commit. What do I do?
-
If you have not merged your commit to master you can rewrite the commit.
git commit --amend git push -f origin HEAD:api-123
The Backstory: OK, I screwed up and made two commits. What do I do?
- If you have not merged your commits to master you can rewrite the commits. Let's say you created a new API call that doesn't compile.
Your history looks like this:
```
commit 2c5d85c0ceaef022bdd783720503cfe11a493782
Author: me <me@localhost>
Fix the API call
commit 5b457a9f305f585bda6c5b2dcd40c12235b8f2bc
Author: me <me@localhost>
Create a new API call that doesn't compile
commit a4382631cb3f3343e18c5894dcc818e7f3497b06
Author: coredev <coredev@coredevbox>
This is somebody else's code
```
The middle commit is useless because it does not compile.
What you really want to do is combine the last two commits into a single patch.
The two commits you want to combine need not be adjacent in your history (although
they are adjacent in this example).
You can combine commits using the interactive mode of git rebase
:
```
git rebase -i a43826
```
This will provide you with an editor containing a list of commits since the listed commit, like this:
```
pick 5b457a9 Create a new API call that doesn't compile
pick 2c5d85c Fix the API call
```
If the commits you are combining are not adjacent in the history, you will need to reorder your commits so that they are adjacent; simply cut and paste the second commit immediately below the first commit.
Once your two commits are adjacent, you can change the "pick" for the second commit into "squash", like this:
```
pick 5b457a9 Create a new API call that doesn't compile
squash 2c5d85c Fix the API call
```
Then save and quit the editor. A new editor prompt will appear asking you to create a message for the new commit, already containing the messages of squashed commits. (If you would like the combined commit to have the same message as the first commit, you can use the "fixup" command instead of "squash".) Interactive rebase is very powerful.
Rebase works by throwing away your commits and replacing them with different commits. That means you need to force-push your commits:
git push -f origin HEAD:api-123
If you want to fix a mistake that has been merged, you just need to create a new commit.
git checkout bad-commit
notepad libraries/app/api.cpp
git add libraries/app/api.cpp
git commit -m "api.cpp now compiles"
git push origin HEAD:api-123
First, find the merge base:
git merge-base HEAD origin/master
Any commit on or before the merge-base
has been merged and should not be replaced.
Extra whitespace at the ends of lines should not go into any commit.
git rebase --whitespace=fix origin/stable
If you want to keep yourself from writing such commits in the first place, git
comes with
a handy pre-commit hook which will yell if you attempt to write such a commit. Try this command:
cp .git/hooks/pre-commit.sample .git/hooks/pre-commit
The Backstory: I want to write a patch api-456
which depends on
bugfix-123
and wallet-345
which haven't made it into stable
yet.
git checkout graphene/stable -b api-456
git merge graphene/bugfix-123
git merge graphene/wallet-345
# write your patch...
git add libraries/api/api.cpp
git commit -m "Now the API is even better"
The Backstory: I did an API improvement, then I fixed a bug. These are two unrelated things and should be in two different branches. However my history looks like this:
commit 2c5d85c0ceaef022bdd783720503cfe11a493782
Author: me <me@localhost>
Fix a bug
commit 5b457a9f305f585bda6c5b2dcd40c12235b8f2bc
Author: me <me@localhost>
Create a new API call
commit a4382631cb3f3343e18c5894dcc818e7f3497b06
Author: coredev <coredev@coredevbox>
This is graphene/stable
First we create a new bugfix
branch with just the bugfix commit based on graphene/stable
and cherry-picking the commit:
git checkout graphene/stable -b bugfix-456
git cherry-pick 2c5d85
git push -f graphene HEAD:bugfix-456
Then we fix the API branch by backing up to 5b457
:
git checkout api-123
git reset --hard 5b457
git push -f graphene HEAD:api-123
-
You shouldn't do this if your code has not been merged to
master
! If your code has been merged, well, now the bugfix spuriously depends on the API call. Living with the spurious dependency is easier than cherry-picking into master. -
If you're worried that this violates the "avoid cherry-picking" guideline of this blog post, you should read the post more carefully. In particular, the title states that "If you cherry pick, your branch model is wrong." In this case, you broke the branching model when you made the bugfix commit without creating a new branch from stable; now you need to cherry-pick. The need to cherry-pick to get back to a more policy-compliant state is an indication that you did something wrong (namely, breaking the branching model).
-
This should only be a very minor problem. Project policy states that the
api-
branches anddevelop
can be reset at any time, so even if you've pushed commits like this, they can still be rewound. -
If it has been merged to
master
it is a slightly less minor problem, but still not very significant in the scheme of things. We should still be able to avoid many cases of this happening, as a branch should not pass review if it creates this kind of dependency across multiple unrelated commits.