Commit and push changes

Last update: 27 Feb 2024 [History] [Edit]

Make a Local Commit

Git commits code in two phases:

  1. You add changed files to a staging area with git add
  2. You commit the staging area with git commit
$ cd ../athena
$ git status  # Always a good idea to see where you are...
branch my-topic
Changes not staged for commit:
 (use "git add <file>..." to update what will be committed)
 (use "git checkout -- <file>..." to discard changes in working directory)

 modified:   python/
 modified:   python/

no changes added to commit (use "git add" and/or "git commit -a")
$ git diff    # ...and what you changed
$ git add python/ python/
$ git status
On branch my-topic
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	modified:   python/
	modified:   python/
$ git commit
... # Editor should open and you write a commit message
    # see next section...

git add can take wildcards and it’s possible to add all changed files and new files automatically with git add -A, but be very careful not to add unwanted files to the repository. Definitely use git status to check what will be/had been staged. Note that git status also helpfully tells you what to do if you want to rollback changes to a file or unstage files.

Tip git reset will unstage changes for you if you did an add of something in error.
git checkout FILE will revert FILE to the original version.
git reset --hard will unstage and discard all local changes.

Tip git status is your friend - consult it often!

Commit messages

Commit messages should be informative yet concise. It is suggested that you write a short one line explanation, followed by a blank line, then some additional longer paragraphs explaining the patch. Use less than 72 characters per line - commit logs get read at the terminal. Reference any bugs you are fixing in the usual Jira notation (e.g., ATLASRECTS-3430) and GitLab will post a comment on the Jira ticket, which is useful in correlating a bug with its fix.

The Linux kernel recommendations state:

For these reasons, the “summary” must be no more than 70-75 characters, and it must describe both what the patch changes, as well as why the patch might be necessary. It is challenging to be both succinct and descriptive, but that is what a well-written summary should do.

Further guidelines are in How to Write a Git Commit Message and 5 Useful Tips For A Better Commit Message.

An example of a good commit message would be:

FooTool: Fixed uninitialised value and leak in alignment tool

It was possible to have the m_alignmentOffset variable
uninitialised in certain situations outlined in Jira.
This patch corrects for that and changes the behaviour
to return StatusCode::FAILURE immediately if the
setup is inconsistent at initialise.

The memory leak from failing to delete the pointer returned
from the foo tool was fixed.

Fixes ATLASRECTS-98765.

The first line should start with a topic prefix that gives an indication where changes were made. This could e.g. be a reconstruction domain, package name or algorithm name depending on the size and scope of changes. The commit message should say why something was changed, not what was changed (so it’s not necessary to list changes by filename).

Tip With git it’s extremely easy to see the code changes alongside the commit message:

$ git log --oneline path/to/mypackage # List only commits
                                      # that changed this path
... # Identify the COMMIT_ID you are interested in
$ git show COMMIT_ID

Alert ATLAS does not use ChangeLog files anymore. The commit message is the new change log. This is why it’s really important that you write it well. If you make a mistake use

git commit --amend

to edit it before you push anything. Software review shifters will be instructed to reject merges with bad commit messages.

Keeping your branch up-to-date

Of course while you are developing, the branch you intend to push your changes to is probably changing too (and it will certainly be changing, if main is your target).

For long-lived developments it is a good idea to periodically update your development branch. This will avoid unpleasant surprises when you open a MR (and you should certainly do this before running your tests, to make sure you are testing against the version of the codebase you intend to merge into).

There are two mains ways to do this, merge and rebase. Broadly speaking merge is safer (especially if you are new to git), but it has some definite downsides: each time you merge from the remote branch, a merge commit is added to your git history. These merge commits will clutter the history, which is something we don’t want to do: the commit history of atlas/athena should be clean, with each commit having a well-written commit message and a well-defined purpose. By default, we squash all commits from MRs, so if you have a bunch of debugging commits and merge commits, they will vanish - replaced by one commit in atlas/athena when your MR is accepted (see this link for more about squashing). Unfortunately however, the very existence of merge commits can make an automatic squash fail. The more merge commits you have in your history, the more likely this becomes.

An alternative is to rebase your commits. Rebasing can be dangerous because you are altering history - you are actually changing the commit hashes of your code changes. You should never violate the Golden Rule of Rebasing - never rebase a branch which is shared with other people (which is public, in other words). However as long as you are working on a local branch, or a branch which only you are touching on your fork, then rebasing is a very good strategy to use.

To rebase, the procedure is:

git fetch upstream         # get latest version from GitLab
git rebase upstream/main # replay your local changes on top of that
git push [--force] origin  # push our branch

Hopefully this just works - if you have conflicts, then you will need to handle them as shown in Resolving Conflicts. The --force push is needed in case your branch had already been pushed to GitLab before the rebase.

Rewriting commit history

There is one other very powerful use of rebasing, which is to clean your commits before opening a MR. Often, when developing you might add some debugging tests, or make some missteps you have to correct. This can lead to a convoluted history, which should not end up in the main repository. One option is, as discussed, to squash the commits into one single commit. For simple developments, this might well be best.

However for more complicated MRs, you might want to have several commits, to make the history more understandable, and to make it possible to cherry-pick or revert part of you MR in the future.

For example, you might want to clean the following cluttered and messy history:

To something appropriate to share with the rest of ATLAS:

To do this, you can do an interactive rebase which allows you to squash several commits into one, rewrite commit messages, drop unneeded commits, etc. See interactive rebasing or the Atlassian documentation. Or, if you use our recommeneded IDE, Visual Studio Code, you can install a plugin GitLens which makes this (and many other git operations) a lot easier:

Here is what the GitLens GUI looks like for the example above. You start with a representation of the current history:

You can then choose what to do with each commit (pick, reword, edit, squash, drop), and can drag them around to change their order. For example, this is what we chose to clean the messy history above:

Push Your Changes

Once your development is finished and tested, copy it from your local repository back to your GitLab fork:

git push --set-upstream origin my-topic

The --set-upstream (or just -u) associates your topic branch with the copy on your fork. So subsequent pushes can be a plain git push.

Your topic branch is now available on GitLab and now you can make a merge request.

Note that pushing to a branch from which a merge request has already been created will update the merge request and also re-trigger the continuous integration pipelines. Therefore please avoid making multiple pushes to the same branch in quick succession, as this can overload the system.

Working with a nightly build

Usually it’s sufficient to work with a branch of Athena, as discussed above:

git fetch upstream
git checkout -b my-topic upstream/[parent_branch] --no-track

but sometimes you want to work with exactly the same code version as in the release you’re working with. In this case you can checkout the nightly tag - the code as it was when the nightly was built.

asetup main,Athena,2020-09-27T2101
git checkout "nightly/main/2020-09-27T2101"
#or alternatively, using the generic environment variables:
asetup main,latest,Athena
git checkout "$AtlasReleaseType/$AtlasBuildBranch/$AtlasBuildStamp"
#For a release build it's different, since the nightly tags are archived after a while.
#There is always a stable tag pointing to the release
asetup 22.0.15,Athena
git checkout release/22.0.15
###make changes
git checkout -b my_topic_branch_from_a_nightly_tag
#You can now make a merge request as usual

This is particularly useful if you don’t intend to make changes but just see the code as it is in a nightly/release build, or if your code is likely to be affected by changes in other packages that you don’t compile.