A helpful tutorial for those wanting to learn how to rebase using Git.
I'm going to be adding a new line. This will be my new first new commit. I'll be using Git Commit Template... hopefully.
Cool. So, the Git template works. Anything commented out won't appear.
Source: https://trunkbaseddevelopment.com/
This writeup allows for an individual to get started with Trunk Based Development, and how our team with implement it. For a full understanding, refer to the link above.
This guide will cover our branching and merging strategy.
A source-control branching model, where developers collaborate on code in a single branch called ‘trunk’, resist any pressure to create other long-lived development branches by employing documented techniques, avoid merge hell, do not break the build, and live happily ever after.
Trunk-based development allows for Continuous Integration and Continuous Delivery (CI/CD) because team members commit their changes to master (the trunk) multiple times a day. This is the main requirement for Continuous Integration. This ensures that the codebase is always releasable on demand and helps with Continuous Delivery.
- You can either do a direct to trunk commit/push (small teams) or a Pull-Request workflow as long as those feature branches are short-lived and the product of a single person.
- Short-lived branches are key. These branches are mainly for code review and build checking (CI). This allows for quality control before changes get merged back into the trunk. This allows for continuous code reviews.
- Feature flags comes in handy with day-to-day development. Allows for hedging on order of releases.
- Build server can verify that commits don't break the build after they land in the Trunk and after they're ready to be merged back into the Trunk. CI tools like Jenkins, TravisCI, and GitLab CI can all be used.
- GitHub-flow is very similar to Trunk based development. There's only one small difference.
- Gitflow is very different
More information regarding GitHub-flow vs Gitflow can be found here.
The word "trunk" in trunk-based development refers to the concept of a growing tree. The fattest and longest part of a tree is the trunk, not the branching radiating from it.
Feature flags is a way to control the capabilities of an application or service in a large decisive way. Depending on the feature, the team might determine that the code is allowed to be merged into the Trunk, but only turn on the feature after certain features are truly merged back into the Trunk.
Flags are toggles. Martin Fowler calls this technique 'Feature Toggles'.
The flag controls something large, like the UI of a component. Our flag could be OneClickPurchasing or ShoppingCart.
Flags can be achieved through arguments. Then, implemented through Java.
For example:
if args.contains ("--withOneClickPurchase") {
purchasingCOmpleting = new OneClickPurchasing();
}
Then, through Config:
bootContainer.addComponent(classFromName(config.get("purchasingCompleting")));
It's important to have CI house your expected flags. This ensures that your tests can adaptable and is meaningful for each of your flag permutations.
Flags that are set at launch time isn't enough. Key for runtime switchable flags is the need for the state to persist. A restart of the application or service should not set that flag choice back to default.
These flags affect the application or service as it is being built.
Pushing code that's turned off into production allows you to turn it on for ephemeral reasons. You want a subset of your users to knowingly or unknowningly try it out.
Flags put into your codebase over time can get forgotten. You want to make sure that the feature is fixed in the toggle state. Once ready, feature toggles should be removed once code is ready to go live.
For more information, refer to here.
Teams with a very high cadence do not need, and cannot use, release branches. They have to release from the trunk.
Most teams do not use a numerical release scheme. Instead, they use a format that references the commit number or date and time. Also, when a bug arises, it's fixed as a feature, as quickly as possible.
Branches can be made retroactively. You can choose the revision in the past to branch from. The outcome is exactly the same as if you had made it at the time.
Either you branch off of master, or you fork an entire repository. These branches are going to come back as a "pull/merge request".
A rule of thumb is the length of life of the branch before it gets merged and deleted. The branch should be short lived and only last a couple of days. For our team, our sprints last two weeks, so they should only last, at most, ten days. In reality, these branches should only be living around 2 to 3 days, if possible.
Also, another rule of thumb is the amount of developers that are allowed to live on a specific branch. The answer: one. If you are pair-programming, two. These short-lived branches aren't shared within the team for general development. They only exist for code reviews. If you find yourself needing code from another branch, the task didn't get broken down far enough.
Short-lived feature branches are real branches and merge is a first class concept. In completing work on short-lived branches, you will need to bring it up to date with master (trunk).
Recap: Merges to the short-lived feature branch are allowed to bring it closer to HEAD of master (trunk). Merges to master (trunk) are allowed only as part of closing out the short-lived feature branch (and just before deleting it).
At some point, these short-lived branches have to be brought back up to date with master (trunk).
Some people do git stash before git pull, and before git stash pop. There's a chance that when you pop your working copy may be in a merge clash situation that has to be resolved before you progress. This way will always result in your change being a single commit, at the HEAD of the branch.
Some people do git rebase. Even with this model, you might encounter a merge conflict and have to resolve it.
For our branching strategy, our team is using trunk-based development. This means that, we will only have one main branch, master (trunk). Any features that we have will branch off of master. These feature branches will be short-lived. A merge request is created, and this will be used to code review. Once branch is approved, the commits can be rebased and squashed.
Once these feature branches are ready to be merged in, the merge request is accepted, and the feature now becomes part of the trunk. Then, the feature branch is deleted.
Commit Early, Commit Often, Perfect Later, Publish Once
Pushes should be reserved for when chunks of logic is complete.
You should:
- Commit early
- Commit often
- No need to compile
- No need for CI
- It's only for versioning
You should commit often because you can't revert back to an earlier point in time if they don't exist. Git is a "time machine". You need reference points to go to an earlier time in the codebase.
This allows you to:
- Get forced pushes securely
- Rebase your commits
git pull --rebase origin master
If your branch is already pushed:
git push -f
Then, sync your source branch:
git fetch origin master:master
At this point:
- Tests are passing
- App is working
- Code is reviewed
You have a couple of ways you can do this: git reset HEAD~3 or git rebase -i HEAD~3
You can either reset HEAD or interactive rebase HEAD. When you reset HEAD, you reset HEAD back to N commits earlier. Then, you would commit under a new message.
Use feature flags/toggles if the feature should be disabled. Then, merge back to source.
git checkout master & git merge branch
You should:
- Commit early & often - Perfect later, publish once philosophy
- Trunk Based development - Used to govern overall
- Delivery frequently - Be prepared to send every single commit
- Deleting branches after merge - Will make commit graph readable
- Continuous Integration - Validates master branch continuously
- Pull/Merge Requests - Can be used to review code and to validate before merging back to master
- Feature Flags - Should be used whenever possible
Use stashing when you have uncommitted changes, but you need to switch branches.
git stash save "Updated README"
git reset --hard HEAD~
git stash apply stash@{0}
You can perform a hard reset with merge.
git reset --merge HEAD~
git checkout -b feature/CP-123
git add README.md
git commit -m "Add more info"
Commit message can be really helpful when debugging or looking at changes months from now. For our team, we are following this commit guide. These are the main points:
- Separate subject from body with a blank line
- Limit the subject line to 50 characters
- Capitalize the subject line
- Do not end the subject line with a period
- Use the imperative mood in the subject line
- Wrap the body at 72 characters
- Use the body to explain what and why vs. how
- When using lists, use dashes
You can use Git Commit Templates to create better commit messages.
Once the file is created:
git config --global commit.template <path to file>/git-commit-template.txt
git config --global commit.cleanup strip
Then, you can use the command git commit. This will bring up an editor that has the commit template. Then, you fill in the relevant information you want.
NOTE: The commit template contains lines with '#'. These lines are ommited when actually committed. For more information, refer to here under the commit.template section.
Rebasing is the process of rewriting the project commit history. This can help to make our commit history cleaner by eliminating all of the unnecessary commits.
For example, if you are working on a feature that adds a new view to a website. You might want to make commits when you have certain aspects completed, styles are correct, debugging some logic, adding documentation, and finshing some tests. These commits help the developer during development, and allows for their code to be constantly code reviewed. However, if these commits were pushed to master, the commit history would become extremely dirty because it would be difficult to discern what features are what. By rebasing, you can squash, or convert every commit into one, all the commits and merge that into master.
There are two main trade-offs with rebasing:
- Safety
- Traceability
For safety, a developer needs to be careful of when they rebase. Rebasing should occur before the feature branch is merged into master. If you start rebasing master, if another developer branches off of a commit that does not exist anymore, changes can be lost.
On the other hand, when rebasing in feature branches, everything is fine because the developer is rebasing their own code. If a developer were to rebase in master, all traceability is lost because commits are being rewritten. If the rebase is done by another developer who did not commit those changes, the "author" of the change is now lost.
These are the resources used to make this guide:
- Trunk Based Development
- Git Flow vs Github Flow
- Feature Flags
- How to Write a Git Commit Message
- Git Interactive Rebase, Squash, Amend and Other Ways of Rewriting History
- Merging vs. Rebasing
- Git Anti Patterns How To Mess Up With Git And Love It Again tought me how to rebase and use Git Templates. Video brought to us by Lemi Orhan Ergin who presented at Devoxx 2017.