APPDEV SourceControl 060922 1617
APPDEV SourceControl 060922 1617
Commits
A commit is the Git operation which records changes to the local repository. Commits are tracked by Git as a series or history of changes to the
repo. Though this operation is simple, there are many things we can consider in order to maximize the benefits of this design.
With each commit a developer can attach a message describing the changes being introduced. This features provides the opportunity for
storing documentation of the changes to the system with the source code itself. A good commit message describes why the changes under com
mit are being introduced. This is fundamentally different, and far more useful to future developers, than describing what changed. What chang
ed can easily be seen by evaluating the diff between two commits which is supported by most third-party Git clients and hosted Git
repositories such as GitHub. A commit message that describes the why of the change focuses on what the user is now capable of doing and/or
how the change fits in to a larger scope of work.
CONSIDER Adding a more detailed explanation of the changes in the commit message
There is no practical limit on the length of a commit message so there is no need to be brief. The expected format for a message is to provide
a short, 70 characters or less, summary as the first line, followed by a blank line, followed by a more in depth description of the changes. Some
third-party Git clients provide two fields to facilitate this approach.
Users were being redirected to the home page after login, which is
less useful
than redirecting to the page they had originally requested before being
redirected to the login form.
This format can be critical in some git clients and tools as the blank line is used to differentiate between the summary and body of the message.
There are cases where it is useful to be able associate a change introduced by a single commit to a larger set of work such as a Pull Request
or the documentation captured in a project management tool. To facilitate this it can be very useful to include a ticketing system identifier in the
commit message. This should be done at the head of the summary, in the same format used for Pull Requests.
...
Since your commit summary already references the ticket number, it is redundant to simply repeat the ticket title.
Each commit should capture a small cohesive set of changes. Keeping commits small and focused allows developers establish a history of
changes that describe how the work was implemented, and easily revert changes if necessary. Additionally, small and focused commits
provide the opportunity to document each change with a detailed message that describes why the change is being introduced. Sometimes an
atomic change must be large by its nature; this is an acceptable tradeoff, but it should be a conscious decision.
It’s exponentially more difficult to understand the functional changes introduced by a commit when the commit includes whitespace,
reformatting and clean up work. When displaying a diff of two commits, remote repositories such as GitHub or local clients such as Source
Tree have no way to differentiate the meaningful changes from the trivial. Keeping formatting changes separate, allows developers to view a
set of changes commit by commit and know right away which ones to focus on.
In situations where a change should be included with previously committed work (), consider using the --amend flag. This flag will effectively
add the change to the previous commit by replacing the previous commit with a new one that incorporates both sets of changes.
Git’s Interactive Rebase feature essentially allows the developer to rewrite the commit history of a given branch. This can be useful in cases
where multiple commits should be reordered, combined, reordered so that they can be combined, or commit messages need to be clarified.
Interactive Rebase is an effective tool that developers can use to ensure their commit history is clean, concise and tells a story of how a
feature was implemented.
Git Hooks allow developers to automate repetitive tasks for each commit. This functionality can be used to automate any number of tasks. One
example of a useful automation is using a git hook to prepend the ticket number to each commit message based on a branch naming
convention. Other examples include enforcing spellcheck requirements on changes and linting of code.
Branching
It is important to maintain a branching strategy that is used consistently across a team and a project. There are numerous published strategies
for accomplishing this including git flow. DWH’s default branching strategy is a variant of this. Create feature branches off of the head of test
branch (for short-lived, single-sprint work), or the head of prod branch (for hotfixes); if a large feature must unavoidably span multiple sprints,
use feature flags to control access to the feature (and CONSIDER breaking it into smaller sprint-sized chunks). This keeps all changes related
to a single feature on the same branch and minimizes the need to synchronize multiple higher-level branches. We typically discourage
branching off of feature branches to minimize the potential for conflict. Additionally, feature branches make it easier to review all changes
related to a specific ticket, gives developers the freedom to experiment without destabilizing the time, and ensures the stability of the
integration branch at all times facilitating continuous deployment.
[Ticket Identifier]
[Ticket Identifier]-[Description]
The Ticket Identifier is the integer identifier for the ticket being developed against and is established by Jira as well. The optional Descr
iption is a very brief description of the work written in all lower case and separated by hyphens (this is often helpful when a feature spans
multiple applications, requiring multiple distinct Pull Requests within each of them for proper integration). Examples:
ab1234
ab1234-update-client-package
This format allows anyone to easily identify the corresponding ticket for the work being done on a branch. The descriptive text quickly conveys
what is being done and the use of lower case text and hyphens allow the branch name to be typed efficiently.
There are two ways to join two or more development histories together with git. Merge incorporates the changes from one branch in to another by
replaying all of the commits that took place since the two branches diverged. The changes are recorded as a single new commit on the
destination branch. Rebase incorporates the changes from one branch in to another by replaying all of the commits individually as new commits
on the destination branch. These two strategies both have trade offs that are important to consider.
If a developer has local commits on a branch that have not been pushed to the remote repository and changes from the remote need to be
pulled to update the local branch, consider executing a pull with rebase (git pull --rebase). A pull with rebase will replay the local
commits that haven’t been pushed after pulling down the latest commits from the remote repository. This will help maintain a clean local
commit history rather than introducing an unnecessary merge commit.
DO NOT Rebase a branch once it has been submitted for a pull request
As mentioned previously, rebasing effectively rewrites history. Rebasing a branch that has been pushed will require a forced push to update
the remote branch. This is fine as long as the remote repository is controlled by you. However, if the code has been submitted for a pull
request, the reviewers will need to re-review the code since a force-push effectively undid the reviewers efforts.
DO Rebase locally
Interactive Rebasing provides the opportunity to clean up commit history, reorganize commits and adjust commit messaging. Developers
should feel free to take advantage of rebase operations locally until their branch has been pushed to a remote repository.
In the rare case where a developer finds themselves in need of executing a forced push, use the --force-with-lease option as a safety
net. --force-with-lease will verify that no other changes will be overridden and lost prior to executing the force.
You should be pushing to your fork instead. The main repository should not be littered with branches whose purpose is unclear to the team.
DO NOT Push to any main branches in the main repository (test/prod), even as a repo maintainer
This approach effectively bypasses the code review process and sets you (and the team) up for failure.
Tools
There are many third-party tools available to help maximize the benefits of git. Though we encourage familiarity with using git from the command
line, the third-party tools listed below add extremely useful functionality to our git workflows.
Both of these tools provide 3-way merge capability and are very useful when working through merge conflicts.
Git Bash provides a bash environment on Windows while Posh-Git adds Git context information to a Powershell console. Hub wraps Git with
github-specific functionality.
See also