blob: 9678b933fd198eaf29fdad150af5c5801ed4532a [file] [log] [blame] [view]
# Workflow tips and questions
## Workflow tips
This is a list of tips that should help you be more productive when working on fuchsia.
### Install Gerrit Monitor
Install the [Gerrit Monitor](https://chrome.google.com/webstore/detail/gerrit-monitor/leakcdjcdifiihdgalplgkghidmfafoh)
Chrome extension to have in the Chrome toolbar the list of CLs requiring your
attention.
### Enable three-way diffs in Git
By default Git uses two-way diffs when presenting conflicts. It does not
display what the original text was before the conflict, which makes it [hard to
solves some conflicts](https://stackoverflow.com/questions/4129049/why-is-a-3-way-merge-advantageous-over-a-2-way-merge).
You can configure Git to show the original text by enabling three-way diffs:
```git config --global merge.conflictstyle diff3```
### Enable fuchsia-specific git commands
Add `$FUCHSIA_DIR/scripts/git` to your PATH to be able to use fuchsia-specific git
commands such as `git fuchsia-review [<commit ref>]`, which opens the current
or given commit in gerrit.
## Questions and answers
You are encouraged to add your own questions (and answers) here!
### Q: Is there a standard Git workflow for Fuchsia?
There are a wide variety of workflows used by the Fuchsia team. A daily
workflow to get you started is as follows:
```shell
$ jiri update -gc
# Start a new feature "myfeature" from the current stable commit
$ git checkout -b myfeature JIRI_HEAD
# Do work, making changes, etc.
$ git commit
# Upload your work to Gerrit for review
$ jiri upload
# OR
$ git push origin HEAD:refs/for/master
```
Congratulations, you made your first Gerrit change!
Suppose you want to start new work while you wait for review of "myfeature":
```shell
# Start a new independent line of work while waiting for review:
$ git checkout -b otherfeature JIRI_HEAD
# OR
# Start a derivative line of work while waiting for review:
$ git checkout -b otherfeature
```
When you want to update "myfeature" and you've been working on an
"independent" line of work:
```shell
# Commit any present dirty work, then, switch to "myfeature":
$ git checkout myfeature
# Make any relevant edits to the code, then:
$ git commit --amend
# Now upload the new patchset to Gerrit:
$ jiri upload
# OR
$ git push origin HEAD:refs/for/master
```
When you want to update "myfeature" because you got some review comments, and
you are using a "derivative" line of work:
```shell
# Now you get a review comment that needs a change in "myfeature"
# Commit your present work, if you aren't finished, maybe use a work-in-progress change:
$ git commit -a -m "work in progress"
# Start a rebase operation, so you can edit your first change:
$ git rebase -i JIRI_HEAD
# Replace "pick" with "edit" on the change you need to update and save and close the file
# Make the relevant code changes, then:
$ git add . && git rebase --continue
# You may need to make additional "rebase" steps if your edits need integration
# with later commits For each case, look at "git status" to see what files are
# in conflict, and make the relevant adjustments. The rebase is complete when
# git reports "Successfully rebased and updated ...." If you made a "work in
# progress" change and want to unwind that commit:
$ git reset HEAD
# Now you can upload your modified changes to Gerrit:
$ jiri upload
# OR
$ git push origin HEAD:refs/for/master
```
When you see "merge conflict" in Gerrit because your change can't cleanly be
integrated with "master":
```shell
# Checkout the branch for the change you need to update (e.g. "myfeature"):
$ git checkout myfeature
# Update your git repository:
$ git fetch
# Update your branch:
$ git rebase origin/master
# Fixup and continue the rebase as necessary, until you see "Successfully rebased ..."
# Then upload your newly updated code:
$ jiri upload
# OR
$ git push origin HEAD:refs/for/master
```
When you've been working for more than a day, and you need to "sync your
code" with upstream (you generally want to do this at least once per day):
```
# Commit any in-progress work, then
# Checkout the stable upstream you last sync'd
$ git checkout JIRI_HEAD
# Update your local repository (this will include updates for prebuilts, third
# party repositories, and so on):
$ jiri update -gc
# Now to switch back to, and update your working branch (e.g. "myfeature"):
$ git checkout myfeature
# Updating "myfeature" with the latest stable code:
$ git rebase JIRI_HEAD
# Perform fixups and "git rebase --continue" until you get to "Successfully rebased ..."
```
You can find more information on parts of workflows below.
You can find more information on general git workflows in [gitworkflows(7)](https://github.com/git/git/blob/master/Documentation/gitworkflows.txt).
You can find more information on git in general at [git-scm.com/doc](https://git-scm.com/doc).
#### Rebasing
Update all projects simultaneously, and rebase your work branch on `JIRI_HEAD`:
```shell
$ jiri update -gc -rebase-untracked
$ git checkout <my_branch>
$ git rebase JIRI_HEAD
```
The `git rebase` to `JIRI_HEAD` should be done in *each* repo where you have
ongoing work. It's not needed for repos you haven't touched.
#### Uploading a new patch set (snapshot) of a change
You'll need to *upload* a patch set to
[Gerrit](https://fuchsia-review.googlesource.com/) to have it reviewed by
others. We do this with `jiri upload`.
Gerrit uses an auto-generated metadata marker in the CL description to figure
out which Gerrit review thread to upload a patch to, such as: `Change-Id:
I681125d950205fa7654e4a8ac0b3fee7985f5a4f`
This is different from a git commit's SHA hash, and can be considered stable
during review, as you make edits to your changes and commits. Use the same
Change-Id for a given review (in case you are
[squashing](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History) multiple
commits).
If you've made changes and want to upload a new patch set, then (assuming that
this is the latest change in your branch; use `git log` to find out) you can do
something like:
```shell
$ git commit -a --amend
# -a for all uncommitted files, --amend to amend latest commit
$ jiri upload
```
#### Resolving merge conflicts
Attempt a rebase:
```shell
$ git fetch origin && git rebase origin/master
# Resolve conflicts as needed...
$ jiri upload
```
But read below about how a `git rebase` can negatively interact with `jiri
update`.
#### Stashing
You can save all uncommitted changes aside, and re-apply them at a later time.
This is often useful when you're starting out with Git.
```shell
$ git stash # uncommitted changes will go away
# do stuff
$ git stash pop # uncommitted changes will come back
```
### Q: I use **fx** and **jiri** a lot. How are they related?
A: They are not related.
[`jiri`](https://fuchsia.googlesource.com/jiri/+/master/) is a wrapper around
git that provides support for managing more than one git repository in sync
(the Fuchsia code base is composed of many git repositories), as well as
synchronizing a set of prebuilt artifacts, such as those found in
`//prebuilt`.
[`fx`](/scripts/fx) is a
convenience wrapper around many tools built in the Fuchsia tree, and helps
with many daily workflow tasks, such as building, running tests, consuming
logs, connecting to shells on devices, and many other operations.
### Q: Will a git rebase to origin/master mess up my jiri-updated (i.e. synchronized) view of the repository?
A: Yes, unless jiri is configured to sync the rebased repository/petal to HEAD
instead of the globally integrated version. This is not the case if you use the
current/new default bootstrap setup, which tracks global integration for all
repos, but may be the case if you set up your checkout in the past or used `fx
set-petal X`.
When working at petal X (accomplished with `fx set-petal X`), `jiri update` will
rebase the local branches in repo X onto HEAD of origin/master. But other
petals' repos will be synced to specific revisions that may be behind HEAD of
their origin/master.
Our continuous integration system (specifically rollers) makes a new revision of
a petal available to other petals only after testing that the new revision
doesn't break other petals. `jiri update` will always leave other petals synced
to these successfully-tested revisions. But a git rebase to origin/master for a
petal may advance that repo beyond the tested revision, which has the potential
to introduce breaking changes. The result may be that you can build for a
certain petal, but not for other petals (e.g., correctly build garnet, but not
be able to build topaz).
If you have a particular commit that you want jiri to honor, download its
`jiri.update` file and feed it to `jiri update`.
### Q: What if I need an atomic commit across git repositories?
A: Can't, sorry. Try to arrange your CLs to not break each petal during a
transition (i.e., do a [soft
transition](working_across_petals.md#soft-transitions-preferred)). But sometimes
you will necessarily break things; aim to minimize the duration of breakage
(i.e., a [hard transition](working_across_petals.md#hard-transitions)).
Example scenario: I have an interface defined in stem, and it is implemented in
another petal. If I change the interface, am I doomed to break other petals?
Yes. But you can "babysit" the rollers so that the breakage range is minimized.
The gotcha with babysitting is that others may *also* be babysitting a breakage,
and you may end up babysitting for longer than you had intended.
Alternatively, you *could* do something as follows:
1. Introduce a new interface in `lower` that is a copy of the original
interface.
1. Wait for `lower-roller` to roll into `upper`, or roll yourself by updating
the file `upper/manifest`.
1. Change `upper` to use the new clone interface that maintains the old
contract.
1. Change `lower` such that the original interface’s contract is modified to
the new, desired form.
1. Wait for `lower-roller`, or roll yourself.
1. Change `upper` to use the original interface name, now with its new
contract. Make any changes required.
1. Delete the clone interface in `lower`.
### Q: How do I do parallel builds from a single set of sources?
Note: this answer is subject to change/breakage shortly after authorship.
Let's assume you want to produce four builds:
* a "bringup" product for x64
* a "workstation" product for x64
* a "core" product for vim2
* a "workstation" product for vim2
First, one must build Zircon, as the Zircon build directory is shared across
Fuchsia build targets. It doesn't matter at this stage which product/board
combination you pick, we just need to start building Zircon.
```shell
# We start with bringup, because it's small, but it doesn't matter which you start with:
$ fx set bringup.x64 out/bringup.x64
$ fx --dir=out/bringup.x64 build
```
Now you have Zircon built, you can start building several other builds concurrently:
```shell
$ fx set workstation.x64 out/workstation.x64
$ fx --dir out/workstation.x64 build > workstation.x64.build.log &
$ fx set core.arm64 out/core.vim2
$ fx --dir out/core.vim2 build > core.vim2.build.log &
$ fx set workstation.arm64 out/workstation.vim2
$ fx --dir out/workstation.vim2 build > workstation.vim2.build.log &
```
You can reference each of these builds while running `fx` tools by passing
`--dir` to your fx command, e.g. to run `fx serve` using the vim2 workstation
product, you would use:
```shell
$ fx --dir out/workstation.vim2 serve
```
You can also change which build directory is your current default by using `fx use`:
```shell
$ fx use out/core.vim2
```
### Q: What if I want to build at a previous snapshot across the repos?
A: You'll need to `jiri update` against a *jiri snapshot file*, an XML file that
captures the state of each repo tracked by jiri.
### Q: How can I get a build that works for a particular fuchsia.git commit?
A: `fx sync-from-stem` will do this. It uses `jiri` under the hood. However,
instead of syncing fuchsia.git and dependencies to match the current integration
repo, it instead finds the integration commit that matches *currently checked
out* fuchsia.git, and syncs integration and dependencies to match that
fuchsia.git commit.
Put another way, fuchsia.git will be untouched, and everything else is synced to
match. This can be useful to bisect within fuchsia.git.
### Q: I'm building on Mac, how to do I stop getting spammed with 'incoming network connection' notifications?
A: You'll want to run `fx setup-macos`, which registers all the relevant Fuchsia
tools with the MacOS Application Firewall.
### Q: When/how do I make a soft vs hard transition when changing APIs?
See [this section](working_across_petals.md#hard-and-soft-transitions) about hard
and soft transitions.
### Q: How do I update a FIDL protocol?
A: The preferred method for updating a FIDL protocol is to use a *soft
transition*. In order for a soft transition to work, you need to create an
intermediate state that supports both the old and new versions of the protocol.
Use the following steps to execute a soft transition:
1. Modify the FIDL definition in the Stem repository to support both the old
and new protocol elements. Before landing the change, trigger the *global
integration* tryjobs to validate that step 2 will succeed.
1. Publish the Stem repository, either by waiting for the daily automatic
publication or by manually publishing the repository.
1. Update all the clients to use the new protocol elements.
1. Publish all the clients.
1. Remove the old protocol elements from the FIDL definition in the Stem
repository.
1. Publish the Stem repository, typically by waiting for the daily automatic
publication.
### Q: How do I coordinate changes across multiple Petals?
A: Coordinating an atomic change across multiple Petals (or between the Stem
repository and one or more Petals) requires performing a *hard transition*.
Use the following steps to execute a hard transition:
1. Prepare changes to all affected repositories. If all of these repositories
are part of the Platform Source Tree:
1. Upload CLs containing the changes to fuchsia-review.googlesource.com.
1. Upload another CL that modifies the *global integration* repository to
reference the git revisions from your CLs. Perform a "dry run" of the
commit queue for this CL.
1. Notify the team stating your intention to execute a hard transition.
1. Land all the changes in the affected repositories. This step will break
local integration in these repositories but will not break global
integration because the changes have not been published yet.
1. Land a change in the *global integration* repository that references the new
versions of the affected repositories. This change will publish the new
version of all the affected repositories and should not break global
integration. This change should unbreak local integration in the affected
repositories.
### Q: How do I bisect history to track down when something changed?
A: To bisect history, perform the following steps:
1. Bisect the history in the configuration repository, which contains the
revision history of global integration, before and after the observable
change. The result of this bisect will be a single change to configuration
repository, presumably that includes the publication of one or more
repositories or prebuilt packages.
1. If the change to the configuration repository is a publication of a single
repository, bisect the history of that repository before and after the
publication of global integration. The result of this bisect should be the
revision at which the behavior changed.
1. If the change to the configuration repository is a publication of prebuilt
packages, switch to the source tree from which the prebuilt packages were
created. Consult the documentation for that repository regarding how to
bisect changes in that repository.
1. If the change to the configuration repository is a publication of multiple
repositories, bisecting history becomes complicated because the two
repositories have likely been changed in concert and you will need to
traverse their history in concert. Consider studying the history of the
repositories to understand why they were published together.