Working on a project

Here are some guidelines on how to create changes and develop new revisions. These guidelines can be used either for your own work on your own project, or to help contribute your code to the project maintained by somebody else.

Different projects can use different development workflows; therefore, some of the recommendations presented here might not make sense, depending on the workflow that is used for a given project.

Working on a topic branch

Branching in Git has two functions (Chapter 6, Advanced Branching Techniques): as a mediator for the code contributed by developers keeping to the specified level of code stability and maturity (long-running public branches), and as a sandbox for the development of a new idea (short-lived private branches).

The ability to sandbox changes is why it is considered a good practice to create a separate branch for each new task you work on. Such a branch is called a topic branch or a feature branch. Using separate branches makes it possible to switch between tasks easily, and to keep disparate work in progress from interfering with each other.

You should choose short and descriptive names for branches. There are different naming conventions for topic branches; the convention your project uses should be specified in the developer documentation. In general, branches are usually named after a summary of a topic they host, usually all lower-case and with spaces between words replaced by hyphens or underscores (see the git-check-ref-format(1) manpage to know what is forbidden in branch names). Branch names can include slash (be hierarchical).

If you are using an issue tracker, then the branch which fixes a bug, or implements an issue, can have its name prefixed with the identifier (the number) of the ticket describing the issue, for example, 1234-doc_spellcheck. On the other hand, the maintainer, while gathering submissions from other developers, could put these submissions in topic branches named after the initials of the developer and the name of the topic, for example, ad/whitespace-cleanup (this is an example of hierarchical branch name).

It is considered a good practice to delete your branch from your local repository, and also from the upstream repository after you are done with the branch in question to reduce clutter.

Deciding what to base your work on

As a developer, you would be usually working at a given time on some specific topic, be it a bug fix, enhancement or correction to some topic, or a new feature.

Decision about where to start your work on a given topic, and what branch to base your work on, depends on the branching workflow chosen for a project (see Chapter 6, Advanced Branching Techniques for a selection of branchy workflows). This decision also depends on the type of the work you do.

For a topic branch workflow (or a branch-per-feature workflow), you would want to base your work on the oldest and most stable long-running branch that your change is relevant to, and for which you plan to have your changes merged into. This is because, as described in Chapter 6, Advanced Branching Techniques, you should never merge a less stable branch into a more stable branch. The reason behind this best practice rule is to not destabilize branch, as merges bring all the changes.

Different types of changes require a different long-lived branch to be used as a base for a topic branch with those changes, or to put those changes onto. In general, to help developers working on a project, this information should be described in the developer documentation; not everybody needs to be knowledgeable about the branching workflow used by the project.

The following describes what is usually used as a base branch, depending on purpose of changes:

  • Bugfix: In this case the topic branch (the bugfix branch) should be based on the oldest and the most stable branch in which the bug is present. This means, in general, starting with the maintenance branch. If the bug is not present in the maintenance branch, then base the bugfix branch on the stable branch. For a bug that is not present in the stable branch, find the topic branch that introduced it and base your work on top of that topic branch.
  • New feature: In this case the topic branch (the feature branch) should be based on the stable branch, if possible. If the new feature depends on some topic that is not ready for the stable branch, then base your work on that topic (from a topic branch).
  • Corrections and enhancements: To a topic that didn't get merged into the stable branch should be based on the tip of the topic branch being corrected. If the topic in question is not considered published, it's all right to make changes to the steps of the topic, squashing minor corrections in the series (see the section about rewriting history in Chapter 8, Keeping History Clean).

If the project you are contributing to is large enough to have dedicated maintainers for selected parts (subsystems) of the system, you first need to decide which repository and which fork (sometimes named "a tree") to base your work on.

Splitting changes into logically separate steps

Unless your work is really simple and it can be done in a single step (a single commit)—like many of the bug fixes—you should make separate commits for the logically separate changes, one commit per single step. Those commits should be ordered logically.

Following a good practice for a commit message (with an explanation of what you have done—see the next section) could help in deciding when to commit. If your description gets too long and you begin to see that you have two independent changes squished together, that's a sign that you probably need to split your commit to finer grained pieces and use smaller steps.

Remember, however, that it is a matter of balance, of the project conventions, and of the development workflow chosen. Changes should, at minimum, stand on their own. At each step (at each commit) of the implementation of a feature, the code compiles and the program passes the test suite. You should commit early and often. Smaller self-contained revisions are easier to review, and with smaller but complete changes, it is easier to find regression bugs with git bisect (which is described in Chapter 2, Exploring Project History).

Note that you don't necessarily need to come up with the perfect sequence of steps from the start. In the case when you notice that you have entangled the work directory's state, you can make use of the staging area, using an interactive add to disentangle it (this is described in Chapter 3, Developing with Git and Chapter 4, Managing Your Worktree). You can also use an interactive rebase or similar techniques, as shown in Chapter 8, Keeping History Clean, to curate commits into an easy-to-read (and easy-to-bisect) history before publishing.

You should remember that a commit is a place to record your result (or a particular step towards the result), not a place to save the temporary state of your work. If you need to temporarily save the current state before going back to it, use git stash.

Writing a good commit message

A good commit message should include an explanation for change that is detailed enough, so that other developers on the team (including reviewers and the maintainer) can judge if it is a good idea to include the change in the codebase. This good or not decision should not require them to read actual changes to find out what the commit intends to do.

The first line of the commit message should be a short, terse description (around from 50 to 72 characters) with the summary of changes. It should be separated by an empty line from the rest of the commit message, if there is one. This is partially because, in many places, such as in the git log --oneline command output, in the graphical history viewer like gitk, or in the instruction sheet of git rebase --interactive, you would see only this one line of the commit message and you would have to decide about the action with respect to that commit on the basis of this one line. If you have trouble with coming up with a good summary of changes, this might mean that these changes need to be split into smaller steps.

There are various conventions for this summary line of changes. One convention is to prefix the first summary line with area:, where area is an identifier for the general area of the code being modified: a name of the subsystem, of an affected subdirectory, or a filename of a file being changed. If the development is managed via an issue tracker, this summary line can start with something like the [#1234] prefix, where 1234 is the identifier of an issue or a task implemented in the commit. In general, when not sure about what information to include in the commit message, refer to the development documentation, or fall back to the current convention used by other commits in the history.

Note

If you are using Agile development methods, you can look for especially good commit messages during retrospectives, and add them as examples in the developer documentation for the future.

For all but trivial changes, there should be a longer meaningful description, the body of the commit message. There is something that people coming from other version control systems might need to unlearn: namely, not writing a commit message at all or writing it all in one long line. Note that Git would not allow to create a commit with an empty commit message unless forced to with --allow-empty.

The commit message should:

  • Include the rationale for the commit, explain the problem that the commit tries to solve, the why: in other words, it should include description of what is wrong with the current code or the current behavior of the project without the change; this should be self-contained, but it can refer to other sources like the issue tracker (the bug tracker), or other external documents such as articles, wikis, or Common Vulnerabilities and Exposures (CVE).
  • Include a quick summary. In most cases, it should also explain (the how) and justify the way the commit solves the problem. In other words, it should describe why do you think the result with the change is better; this part of the description does not need to explain what the code does, that is largely a task for the code comments.
  • If there was more than one possible solution, include a description of the alternate solutions that were considered but were ultimately discarded, perhaps with links to the discussion or review(s).

It's a good idea to try to make sure that your explanation for changes can be understood without access to any external resources (that is, without an access to the issue tracker, to the Internet, or to the mailing list archive). Instead of just referring to the discussion, or in addition to giving a URL or an issue number, write a summary of the relevant points in the commit message.

One of the possible recommendations to write the commit message is to describe changes in the imperative mood, for example, make foo do bar, as if you are giving orders to the codebase to change its behavior, instead of writing This commit makes ... or [I] changed ....

Here, commit.template and commit message hooks can help in following these practices. See Chapter 10, Customizing and Extending Git, for details (and Chapter 11, Git Administration, for a description of the way to enforce this recommendation).

Preparing changes for submission

Consider rebasing the branch to be submitted on top of the current tip of the base branch. This should make it easier to integrate changes in the future. If your topic branch was based on the development version, or on the other in-flight topic branch (perhaps because it depended on some specific feature), and the branch it was based on got merged into a stable line of development, you should rebase your changes on top of the stable integration branch instead.

The time of rebase is also a chance for a final clean-up of the history; the chance to make submitted changes easier to review. Simply run an interactive rebase, or a patch management tool if you prefer it (see Chapter 8, Keeping History Clean). One caveat: do not rewrite the published history.

Consider testing that your changes merge cleanly, and fix it if they don't, if the fix is possible. Make sure that they would cleanly apply, or cleanly merge into the appropriate integration branch.

Take a last look at your commits to be submitted. Make sure that your changes do not add the commented out (or the ifdef-ed out) code, and it does not include any extra files not related to the purpose of the patch (for example, that they do not include the changes from the next new feature). Review your commit series before submission to ensure accuracy.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset