Introduction to Git & Gerrit for CrOS contributors

This guide provides a brief guide to creating Git commits and getting them reviewed on Gerrit, for those who have not used either system before. It assumes that you’ve already got a local copy of the repository you want to modify, and have set up your Gerrit credentials. For more details on the topics that this guide skims over (such as the commit queue and the review process) see the Contributing Guide.

Terms

We’ll be talking about two related but separate systems here:

Git is a version control system, in many ways similar to SVN, except that Git repositories do not need to be hosted on one central server; instead, you have a local copy which can pull from many servers, called remotes. It's strongly recommended to read this introductory presentation for more in-depth information including an explanation of the concepts and workflows.

Gerrit is a Git remote which hosts the code and makes it easier to manage code reviews. Many different projects host instances for their code, but mostly I’ll be referring to the Chromium Gerrit instance when I mention it here. For more details, see this introductory presentation that goes into more detail about the concepts and workflows.

Make and commit some changes

In your local copy of your repository, make some changes, then run git status. Here’s some sample output (in this example we'll be committing some files in the chip/max32660 directory):

$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)

        modified:   foo.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        chip/max32660/

no changes added to commit (use "git add" and/or "git commit -a")

This output tells us that a file that Git already tracks (foo.txt) has been modified, and a directory has been created that Git hasn’t seen before (chip/max32660/). We need to create a commit that contains these changes. To do that, we first tell Git what we want to include in this commit:

$ git add chip/max32660/build.mk
$ git add chip/max32660/max32660.h

Now, when we run git status, we see that some changes have been added. (In Git terms, we say that they are staged.)

$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)

        modified:   chip/max32660/build.mk
        modified:   chip/max32660/max32660.h

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)

        modified:   foo.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        chip/max32660/foo.h
        chip/max32660/foo.c
        ...

no changes added to commit (use "git add" and/or "git commit -a")

We’re almost ready to make a commit, but notice that we’re currently on the branch called master. Most changes end up on master eventually, but for now we want to put them on a different local branch (often called a feature branch) while they’re in review. So, let’s create one called max32660-basics and switch to it:

$ git checkout -b max32660-basics
$ git status
On branch max32660-basics
...

Now we can make the commit. When you run this command, a text editor will appear:

$ git commit

In the editor, add a message for your commit. See the Commit messages section of the Contributing Guide for suggestions on what to put in it. (No need to add the Change-Id line shown there, Gerrit will do that for you.)

When you’re done, save the file and close your editor. The commit has now been made. (You can see it in git log.)

Uploading your branch as a changelist

Now you’ve got a branch with a commit in it, and you want to upload it to Gerrit for review. Gerrit’s unit of code review is called a changelist (often abbreviated to CL), and you can create one from your branch. First, let’s make sure that you have Gerrit set up as a remote.

Check your repository’s remotes

If you downloaded the repository by using git clone or repo, you probably already have Gerrit set up as a remote. Check with the git remote -v command, and note the name of the remote (cros in this case):

$ git remote -v
cros    https://chromium.googlesource.com/chromiumos/platform/ec (fetch)
cros    https://chromium.googlesource.com/chromiumos/platform/ec (push)

If you don’t see a suitable remote, you can add one (replacing platform/ec with the appropriate path for the repository you're working in):

$ git remote add cros https://chromium.googlesource.com/chromiumos/platform/ec

Uploading the code

First, check that you’re on your feature branch by running git status and checking the first line. (If you’re on the wrong branch, use git checkout <branch name> to get back to the right one.)

You‘ll need the repo command for this step. repo is a tool that makes it easier to manage multiple related Git repositories, but we’ll just be using it to upload our change and run presubmit checks. If you don't have it, you can get it by installing depot_tools.

First, set your branch’s upstream. This tells repo what version of the code it should compare your current state with. You’ll probably want the following command (assuming that your remote is called cros):

$ git branch --set-upstream-to=cros/master

Now you can tell repo to upload your change:

$ repo upload . --br=max32660-basics

When asked whether to run the checks, enter yes. If the checks find any problems, they will tell you about them and the change will not be uploaded. Once you‘ve fixed them you can run the command again. Sometimes it will warn about things that can’t be fixed given the circumstances, in which case add --no-verify to the command (though you should probably amend your commit message to explain why).

Adding reviewers

Whichever method you use, you should see some output like this if all goes well:

Upload project src/platform/ec/ to remote branch refs/heads/master:
  branch max32660-basics ( 1 commit, Wed Jun 5 12:14:47 2019 -0700):
         7d244dd1 Add basic support for MAX32660
to https://chrome-internal-review.googlesource.com (y/N)? y

remote: Processing changes: refs: 1, new: 1, done
remote:
remote: SUCCESS
remote:
remote:   https://chrome-internal-review.googlesource.com/c/chromeos/platform/ec/+/1234567 Add basic support for MAX32660 [NEW]
remote:
To https://chrome-internal-review.googlesource.com/chromeos/platform/ec
 * [new branch]      max32660-basics -> refs/for/master

Here, the URL https://chrome-internal-review.googlesource.com/c/chromeos/platform/ec/+/1234567 is the one for your change. Open it in your browser to see the change.

Now, if you’re signed in to Gerrit, you can request that one or more other Gerrit users review the CL. Click “Reply” and add the reviewers you want to the “Reviewers” line.

Updating your changelist

Once you’ve sent your CL for review, your reviewers will probably leave comments on it, suggesting improvements. Once you’ve made the changes they asked for, you need to update the CL in Gerrit.

Modifying your commit

To do this, we need to modify the commit you created the CL from. First, let’s check that the commit has the Change ID line that Gerrit uses to identify it. Run git log and look for a line beginning Change-Id: in the message. With that verified, let’s go ahead and modify the commit.

Amending

If you haven’t committed your recent changes yet, this is really simple. Use git add as before to stage the changes, but then instead of creating a new commit, change the existing one:

$ git commit --amend

The commit message editor will appear again, prefilled with your existing message. If you want to change the commit message, do so now (but make sure to leave the Change-Id: line intact). Then save and close the editor as before.

Rebasing

If you’ve already made additional commits on the branch, all is not lost. We can combine them together after the fact, using a rebase:

$ git rebase -i master

(Yes, that should be master, even though you’re on a branch, as we’re telling the rebase command to look at all the commits between this branch head and the master branch.)

A text editor should appear, prepopulated with a list of commits, something like the following:

pick 7d244dd12 Add basic support for MAX32660
pick 2e0a4ea43 Fix some review comments
pick 46d8c0a28 Fix some more comments

# Rebase 29e7e6eec..46d8c0a28 onto 29e7e6eec (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

The first line will be your original commit, which we want to combine everything in to. To do that, simply replace pick with fixup on all the lines below it. Then, save and close the editor. Git will go through and do the combining.

(If you want to change the commit message too, just run git commit --amend at this point.)

Uploading again

Now that you’ve modified the commit, re-upload it using the same repo upload command you used before.

Once the upload is done, open your CL in Gerrit again. You should see that it now has another patch set. A patch set is Gerrit’s name for a version of a changelist, and Gerrit allows you (and your reviewers) to see what you’ve changed between patch sets.

Respond to reviewer comments

To make it easier to see what’s going on, reply to your reviewers’ comments. This is very important to make it easier for both you and your reviewers to keep track of what's happening. If you’ve done what they asked for, click “Done” and Gerrit will mark the comment as resolved. Otherwise you can reply explaining why you think their suggestion won’t work or isn’t a good idea. Once you’re done, click the blue “Reply” button at the top of the page to send your responses.

Submitting the CL

To be submitted, your CL must have a Code-Review +2 from at least one reviewer, and Verified +1 from anybody (including yourself). Once it has those, reply to the CL adding Commit-Queue +2 to start the submission process. Assuming that the tests pass, you will get an email some time later telling you that the CL has been submitted.

Getting ready to start again

You probably want to build on the code that you just submitted in your next CL. To do this, you’ll need to update your local copy of the master branch to include the changes that have just been merged. Checkout master, then tell repo to synchronise your repositories:

$ git checkout master
$ repo sync

(If you’ve got other stuff going on in your Chromium OS tree, you might find it helpful to split repo sync into two stages.)

Rinse and repeat

If you run git log now, your CL should appear somewhere in the list. (It might be quite far down depending on how busy the repository is and how long it’s been since it was submitted.) Now you can create another feature branch and start again, building on the changes you’ve already submitted.