Disclaimer
==========

This is a quick and dirty, simplified and slightly modified description
of my own edk2 workflow with git. It expressly defers to any upcoming
git workflow documentation on the wiki. It doesn't try to be generic,
focuses only on GNU/Linux (that's what I use). It will not go into many
details about git; if you are interested, you'll have to research those
concepts on the web yourself.

Also, this is very specific to edk2. Other projects have different
workflows.


Contributor workflow
====================

(01) Create an account on GitHub.

(02) Enable SSH authentication for your account.

       https://help.github.com/articles/generating-an-ssh-key/

     When completing this step, you should end up with a new keypair
     under ~/.ssh/, for example:

       id_rsa_for_github
       id_rsa_for_github.pub

     and the following stanza in your ~/.ssh/config:

       Host github.com
         User          git
         IdentityFile  ~/.ssh/id_rsa_for_github

(03) Fork the following repository on GitHub into your own GitHub
     account, using the GitHub web GUI:

       https://github.com/tianocore/edk2/

     (My personal fork is at

       https://github.com/lersek/edk2/
     )

(04) Clone the official edk2 repository to your local computer:

       cd some-appropriate-directory
       git clone https://github.com/tianocore/edk2.git

(05) Implement the following git settings for your local clone, i.e.,
     while standing in your local edk2 directory (these steps don't need
     customization):

       git config am.keepcr              true
       git config am.signoff             true
       git config cherry-pick.signoff    true
       git config color.diff             true
       git config color.grep             auto
       git config commit.signoff         true
       git config core.abbrev            12
       git config core.pager             cat
       git config core.whitespace        cr-at-eol
       git config diff.algorithm         patience
       git config diff.ini.xfuncname     '^\[[A-Za-z0-9_.,   ]+]'
       git config diff.renames           copies
       git config format.signoff         false
       git config notes.rewriteRef       refs/notes/commits
       git config sendemail.chainreplyto false
       git config sendemail.thread       true

(06) Also implement the following -- they need customization:

       git config sendemail.smtpserver FQDN_OF_YOUR_LOCAL_SMTP_SERVER
       git config user.email           "Your Email Address"
       git config user.name            "Your Name"

(07) Create a file called "tianocore.template" somewhere outside your
     edk2 clone, with the following contents (remove leading
     whitespace). Note that the last line requires customization.

       [empty line]
       [empty line]
       Contributed-under: TianoCore Contribution Agreement 1.0
       Signed-off-by: Your Name <Your Email Address>

(08) Standing in your edk2 clone, implement the following setting
     (requires customization):

       git config commit.template \
         FULL_PATHNAME_OF_FILE_CREATED_IN_LAST_STEP

(09) Open the file

       .git/info/attributes

     (create it if it doesn't exist), and add the following contents
     (with the leading whitespace removed):

       *.efi     -diff
       *.EFI     -diff
       *.bin     -diff
       *.BIN     -diff
       *.raw     -diff
       *.RAW     -diff
       *.bmp     -diff
       *.BMP     -diff
       *.dec     diff=ini
       *.dsc     diff=ini
       *.dsc.inc diff=ini
       *.fdf     diff=ini
       *.fdf.inc diff=ini
       *.inf     diff=ini

(10) Create a file called "edk2.diff.order" somewhere outside your local
     clone, with the following contents (removing the leading
     whitespace):

       *.dec
       *.dsc.inc
       *.dsc
       *.fdf
       *.inf
       *.h
       *.vfr
       *.c

(11) Add your own fork of edk2 that lives on GitHub as a *remote* to
     your local clone:

       git remote add -f --no-tags \
         YOUR_GITHUB_ID \
         [email protected]:YOUR_GITHUB_ID/edk2.git

(12) At this point you are ready to start developing. Refresh your local
     master branch from the upstream master branch:

       git checkout master
       git pull

     The first command is extremely important. You should only run "git
     pull" while you are standing on your local master branch that
     tracks (and never diverges from) the upstream master branch.

     These commands will fetch any new commits from upstream master, and
     fast-forward your local tracking branch to the new HEAD.

(13) Create and check out a topic branch for the feature or bugfix that
     you would like to work on. The topic branch name requires
     customization of course.

       git checkout -b implement_foo_for_bar_v1 master

(14) Make sure you have the build environment set up:

       source edk2setup.sh
       make -C "$EDK_TOOLS_PATH"

(15) Implement the next atomic, logical step in your feature or bugfix.
     Test that it builds and works. You should not cross module (driver,
     library class, library instance) boundaries within a single patch,
     if possible.

(16) Add your changes gradually to the staging area of git (it is called
     the "index"):

       git add -p

     This command will ask you interactively about staging each separate
     hunk. See the manual for "git add".

(17) When done, you can run

       git status

     This will list the files with staged and unstaged changes. You can
     show the diff that is staged for commit:

       git diff --staged

     and also the diff that is not staged yet:

       git diff

(18) If you are happy with the staged changes, run:

       git commit

     This will commit the staged changes to your local branch called
     "implement_foo_for_bar_v1". You created this branch in step (13).

     Before the commit occurs, git will fire up your preferred editor
     (from the $EDITOR variable) for you to edit the commit message. The
     commit message will be primed from the template created in step
     (07) and configured in step (08).

     Above the template, you should add:

     - a subject line, no longer than 74-76 characters, consisting of

       PackageName: ModuleName: summary of changes

     - an empty line

     - one or more paragraphs that describe the changes in the patch. No
       line should be longer than 74 characters.

     - an empty line

     - One or more tags directly above the "Contributed-under" line
       (which comes from the template) that CC the package maintainers
       that are relevant for this specific patch. Consult the
       Maintainers.txt file. For example, if you wrote a patch for
       OvmfPkg, add:

       Cc: Jordan Justen <[email protected]>
       Cc: Laszlo Ersek <[email protected]>

(19) When you have committed the patch, it is best to confirm it adheres
     to the edk2 coding style. Run:

       python BaseTools/Scripts/PatchCheck.py -1

(20) If the command in step (19) reports problems, modify the source
     code accordingly, then go back to step (16) and continue from
     there. However, as a small but important change for step (18), run
     "git commit" with the --amend option:

       git commit --amend

     This will *squash* your fixups into the last commit, and it will
     also let you re-edit the commit message (the PatchCheck.py script
     can also find problems with the commit message format).

     Re-run step (19) as well, to see if your patch is now solid.

(21) Write your next patch. That is, repeat this procedure (goto (15))
     until you are happy with the series -- *each* single one of your
     patches builds and runs (implementing the next atomic, logical step
     in the bugfix or feature), and at the last patch, the feature /
     bugfix is complete.

(22) It is now time to publish your changes for review.

     (At this point, at the latest, it is important to review your full
     series using a git GUI; for example "gitk". Practically, any time
     you are in doubt, and especially before publishing patches, run
     "git status" and "gitk" (or another GUI tool) and go over your
     series.)

     First, we'll push your local branch to your personal repository on
     GitHub, under the same branch name.

       git push YOUR_GITHUB_ID master implement_foo_for_bar_v1

     This command will connect to github using your remote configuration
     added in step (11), employing the SSH authentication configured in
     step (02).

     It will update the master branch in your personal repo on GitHub to
     your local master branch (which in turn follows the official master
     branch, see step (12)).

     It will also push the topic branch you created under step (13).

(23) Now we'll format the patches as email messages, and send them to
     the list. Standing in the root of your edk2 directory, run the
     following (note that the "-O" option needs customization: please
     update the pathname to the file created in step (10)):

       rm -f -- *.patch

       git format-patch                               \
         --notes                                      \
         -O"/fully/qualified/path/to/edk2.diff.order" \
         --cover-letter                               \
         --numbered                                   \
         --subject-prefix="PATCH v1"                  \
         --stat=1000                                  \
         --stat-graph-width=20                        \
       master..implement_foo_for_bar_v1

     This command will generate an email file for each one of your
     commits. The patch files will be numbered. The file numbered 0000
     is a *cover letter*, which you should edit in-place:

     - in the "Subject:" line, summarize the goal of your series,

     - in the message body, describe the changes on a broad level,

     - *reference*, by complete URL, the "implement_foo_for_bar_v1"
       branch in your personal GitHub repo -- the one that you pushed in
       step (22),

     - finally, add *all* of the Cc: tags to the cover letter that you
       used across all of the patches. This will ensure that even if a
       maintainer is involved in reviewing one or two of your patches
       across the series, he or she will get a copy of your cover
       letter, which outlines the full feature or bugfix.

(24) Time to mail-bomb the list! Do the following:

     git send-email                 \
       --suppress-cc=author         \
       --suppress-cc=self           \
       --suppress-cc=cc             \
       --suppress-cc=sob            \
       [email protected] \
       *.patch

     This command might ask you about sending the messages in response
     to another message (identified by Message-Id). Just press Enter
     without entering anything.

     You might want to run the command first with the --dry-run
     parameter prepended.

     The messages will be posted to the list only, and to the
     maintainers that you Cc'd explicitly in the commit messages.

     Once the messages are sent, you can remove the local patch files
     with:

       rm -f -- *.patch

(25) On the list, you will get feedback. In the optimal case, each patch
     will get a Reviewed-by tag (or an Acked-by tag) from at least one
     maintainer that is responsible for the package being touched by
     that patch. If you are lucky, you will also get Tested-by tags from
     some of them.

     Once all tags are in place, one of the maintainers will pick up the
     entire series, update the commit messages to include the above tags
     (Reviewed-by, Acked-by, Tested-by) at the bottom, and commit and
     push your patches to upstream master. If this happens, pop the
     champagne, and goto step (12).

(26) More frequently though, you will get requests for changes for
     *some* of your patches, while *others* of your patches will be
     fine, and garner Reviewed-by, Acked-by, and Tested-by tags. What
     you need to do in this case is:

     - create the next version of your local branch
     - pick up the tags that you got on the list
     - implement the requested changes
     - mark the v2 changes on each patch outside of the commit message
     - push the next version to your personal repo again
     - post the next version to the list

     In the following steps, we'll go through each of these in more
     detail.

(27) Create the next version of your local branch. Run the following
     commands in your edk2 tree:

       git checkout master
       git pull

       git checkout -b \
         implement_foo_for_bar_v2 \
         implement_foo_for_bar_v1

       git rebase master implement_foo_for_bar_v2

     These commands do the following: first they refresh (fast forward)
     your local master branch to the current upstream master.

     Then they fork version 2 of your topic branch off of version 1 of
     the same. Finally version 2 of the topic branch is rebased,
     non-interactively, on top of the refreshed master branch.

     The upshot is that at this point, you have an *identical* version
     of your series on top of *refreshed* master, under a name that
     states *v2*, without touching the original *v1* series.

     Theoretically the last command could run into conflicts, but those
     are unlikely for a low-churn project like edk2, and if you get
     those, you should ask for help on the list. Conflict resolution is
     outside of the scope of this writeup. For now we'll assume that
     your last command completes without errors.

(28) Pick up the tags that you got on the list. Run the following
     command:

       git rebase -i master implement_foo_for_bar_v2

     This will open your $EDITOR with a list of your patches, identified
     by commit hash and subject line, each prefixed with a rebase
     *action*. By default, the rebase action will be "pick".

     You should carefully go through the feedback you received on the
     list for the v1 posting. (An email client that supports threading
     is a hard requirement for this.) For each v1 patch where you
     received a tag (Reviewed-by, Tested-by, Acked-by), *replace* the
     "pick" action with "reword". Be sure not to modify anything else in
     the rebase action list.

     Once you modified these actions, save the file, and quit the
     editor. Git will now rebase your patches again (to the same
     refreshed local master branch), but now it will also stop at each
     patch that you marked "reword", and will let you edit the commit
     message for the patch. This is when you append the tags from the
     mailing list feedback to the very end of the commit message,
     underneath your own Signed-off-by tag. Save the updated commit
     message and quit the editor; git will continue the rebase.

     If you mess up a commit message, don't panic. There are two options
     to bail out. First, you can update the next commit message to an
     empty text file. Git rebase will then stop and expect you to issue
     further commands at the normal shell prompt. This is when you run

       git rebase --abort

     and everything will be exactly like at the end of step (27).

     The second option if you mess up a commit message (and you notice
     too late, i.e., the rebase finishes), is just to repeat step (28),
     and fix up the commit message.

     (There is a third option: the branch can be forcibly reset to a
     chronologically earlier HEAD, which you can collect from the
     reflog. But that is a very sharp tool and not recommended for now.)

     At the end of this step, you will have picked up the feedback tags
     from the list, for each affected patch individually.

(29) Implement the requested changes. For this you run again

       git rebase -i master implement_foo_for_bar_v2

     but this time, you replace the "pick" actions of the affected (= to
     be modified) patches with "edit". My *strong* recommendation is to
     set the "edit" action for exactly one patch in the series, and let
     the rest remain "pick". (There are cases when this is not the best
     advice, but once you get in those situations, you won't need this
     guide.)

     Okay then, git will start the rebase, and it will stop *right
     after* the patch you marked as "edit" is *committed*. Your working
     tree will be clean (no changes relative to the staging index), your
     staging index will also be clean (no changes staged relative to the
     last commit -- which is the patch you marked as "edit").

     At this point you modify the code as necessary, and build it and
     test it. Once satisfied, you run steps (16) and (17). After those
     steps, your working tree will be clean relative to the staging
     index, and the index will have all the necessary changes staged
     relative to the last commit (which you marked as "edit" in the
     rebase action list).

     Now, if you ran

       git commit

     at this point (i.e., step (18) verbatim), then git would *insert*
     the staged changes as a *separate patch* into your series, so
     *don't do that*; that's most likely not your intent. Instead, run

       git commit --amend

     which will squash your staged changes into the patch-to-be-edited.

     Then see steps (19) and (20).

     (I am leaving out some editing action types here, such as: dropping
     a patch entirely, inserting a new patch, reordering patches,
     squashing patches together, and especially splitting up patches
     into smaller patches, and moving hunks between patches. Those are
     completely doable, and constitute the absolute power that git has
     over subversion, but they are definitely beyond these basics.)

     Okay, your patch is fixed up now as the reviewer(s) requested; it
     builds, it runs (at the level expected at this stage into your
     series); PatchCheck.py is happy with it; and you have it committed.
     Time to run:

       git rebase --continue

     This will complete the rebase.

     You can repeat this step (step (29)) as many times as necessary.
     Again, I recommend to run a full rebase per each patch that needs
     an edit.

     A small caveat: if you significantly edit a patch, say, for the v3
     posting, for which you have received a Reviewed-by or Tested-by
     earlier, you are supposed to *drop* these tags, because your
     significant edits render them stale.

(30) Mark the v2 changes on each patch outside of the commit message.
     This step is not strictly required, but it is a *huge* help for
     reviewers and maintainers.

     Each time you finish a full rebase (an iteration of step (29)), you
     should run your git GUI ("gitk" or anything else), and locate the
     patch (by subject) that you just edited in step (29).

     Grab the SHA1 commit hash of that patch, and run:

       git notes edit COMMIT_HASH_OF_THAT_PATCH

     Git will again fire up your text editor, and allow you to attach
     *notes* to the commit. The distinction between a commit message and
     commit notes is that the notes are *ephemeral*. They will be
     included in a special section of the patch email, but they will
     never be included in the commit message itself, on the upstream
     master branch. They are perfect for communicating v1 -> v2 -> v3
     changes, per patch, during the evolution of a given patch series.

     So, please format the notes as follows:

       v2:
       - frobnicate quux [Jordan]
       - perturb xizzy [Laszlo]

     No line should be longer than 72 characters in the notes, and each
     entry should preferably mark who suggested that specific change for
     the patch.

     Save the notes file and quit your editor, git will apply the
     changes. If you need to reedit the note, just repeat this step
     (step (30)).

     Very importantly, every time you complete a rebase, your notes are
     *preserved*, even if you edit the patch itself (code or commit
     message) during the rebase. This is very important for v2 -> v3
     updates, because in that case you can add the v3 section *on top*
     of v2 in the notes!

(31) Push the next version to your personal repo again.

     Practically, repeat step (22), but using the branch name
     "implement_foo_for_bar_v2".

     (It is *very* important that you never ever modify
     "implement_foo_for_bar_v1" after you push it to your personal
     github repo. Namely, this FEATURE_BRANCH_vN kind of branch is
     supposed to reflect your vN mailing list posting precisely. Since
     your mailing list posting is read only (you cannot modify emails
     you sent), you must not modify the corresponding branches in your
     github repo either. If a new version is necessary, you'll post a
     new version, and you'll push a new branch too.)

(32) Post the next version to the list.

     In practice, repeat step (23), with the following modifications:

     - The subject prefix should state

       --subject-prefix="PATCH v2"

     - The commit range given should be

       master..implement_foo_for_bar_v2

     - The cover letter should reference the v2 branch pushed in step
       (31).

     - The cover letter should include or reference (with an URL to the
       mailing list archive) the cover letter of the v1 posting, and
       also summarize the v1->v2 changes.

     Then repeat step (24).

This is it, more or less, for a contributor. Nonetheless, I recommend
reading the rest even to contributors, because it will help them
understand how maintainers are supposed to operate, and how their
actions above assist maintainers in doing their work.


Maintainer workflow
===================

(33) You need the same settings in your edk2 clone as a contributor.
     This includes steps (01) through (11).

(34) You get patches to review, either by CC, or you notice them on the
     list.

     If you can immediately point out problems with (some of) the
     patches, do so. If you are pleased with (some of) the patches,
     respond with your Reviewed-by, per patch. (Or, well, if you like it
     all, to the cover letter.)

     If you agree with a patch, more or less, but lack the expertise to
     review it in depth (possible for patches that target a package that
     you don't maintain), respond with your Acked-by.

(35) When reviewing a v2, v3, ... posting of a series, focus on the
     changes. The contributor is expected to support you in this with:

     - Picking up your Reviewed-by and Acked-by tags from your v1
       review. You can skip re-reviewing those patches in v2, especially
       because step (29) instructs the contributor to drop your earlier
       R-b or A-b if he or she reworks the patch significantly.

     - Listing the relative changes per patch, in the git-notes section.
       Refer to step (30).

     - Summarizing the changes in the v2, v3, ... cover letters. Refer
       to step (32).

(36) Assuming the series has converged (i.e., all patches have gained
     the necessary Reviewed-by and/or Acked-by tags), plus you have been
     "elected" as the lucky maintainer to apply and push the series,
     read on.

     (The following steps are also relevant if you would like to *test*
     the series, or if you would like to review each patch in the series
     against a fully up-to-date, complete codebase, with all the
     precursor patches from the series applied.)

(37) The first attempt at applying the contributor's series is directly
     from emails. For this, you *absolutely* need a mail user agent
     (MUA) that allows you to save patch emails *intact*.

     So save all the patch emails into a dedicated, new folder.

(38) Refresh your local master branch.

       git checkout master
       git pull

     Note that it is *extremely* important to switch to the master
     branch, with the checkout command above, before you run "git pull".

(39) Create an application/testing/review branch, and apply the patches
     from the files you saved in step (37), from within your MUA:

       git checkout -b REVIEW_implement_foo_for_bar_vN master

       git am dedicated_directory/*.eml

     Now, this step can genuinely fail for two reasons. The first reason
     is very obscure and I'm sharing it only for completeness.

     So the first reason is that the patch may create or delete files,
     which implies "/dev/null" filenames in the git diff hunk headers.
     Because of the "core.whitespace" setting in step (05) -- which we
     absolutely need due to the source files using CRLF line terminators
     in the *internal* git representation --, git-am might choke on
     those "/dev/null" lines. This depends on the
     Content-transfer-encoding of the email that is saved in step (37).

     The second reason is that the master branch may have genuinely
     diverged from where it was when the contributor prepared his or her
     patches, on top of then-master. And the patches may no longer apply
     with git-am on top of current master.

     If "git am" above fails for *any reason at all*, immediately issue

       git am --abort

     and proceed to the next step, step (40). Otherwise, if "git am"
     succeeds, skip forward to step (43).

(40) As an alternative to step (39), here we'll grab the contributor's
     patches from his or her personal GitHub repo.

     First add his or her personal repo as a *remote* to your local
     clone (this step only needs to be done once, during all of
     the collaboration with a given contributor):

       git remote add --no-tags \
         HIS_OR_HER_GITHUB_ID \
         https://github.com/HIS_OR_HER_GITHUB_ID/edk2.git

     At this point you should of course use the repo URL that the
     contributor shared in his or her cover letter, in step (23) or --
     for a v2, v3, ... post -- in step (32).

(41) Fetch any new commits and branches from the contributor's repo:

       git fetch HIS_OR_HER_GITHUB_ID

(42) Now, set up a local, non-tracking branch off of the contributor's
     relevant remote branch. You know about the relevant branch again
     from the contributor's steps (23) or (32), i.e., the cover letter.

       git checkout -b --no-track \
         REVIEW_implement_foo_for_bar_vN \
         HIS_OR_HER_GITHUB_ID/implement_foo_for_bar_vN

(43) Rebase the contributor's series -- using your local branch that you
     created either in step (39) or in step (42) -- to the local master
     branch (which you refreshed from upstream master in step (38)):

       git rebase -i master REVIEW_implement_foo_for_bar_vN

     Here you should mark those patches with "reword" that have received
     Reviewed-by, Acked-by, Tested-by tags on the mailing list after the
     contributor's last posting. (Patches that garnered such tags in
     earlier versions are supposed to carry those tags already, due to
     step (28).)

     When rewording the relevant patches, simply append the relevant
     R-b, A-b, T-b tags from the mailing list feedback. (Refer to step
     (28).)

     Now, this rebase has a much better chance to succeed than "git am"
     in step (39), for two reasons again. The first reason is that the
     problem with the /dev/null headers just doesn't exist. The second
     reason is that "git rebase", *unlike* "git am", knows *whence* you
     are rebasing, which helps it immensely in calculating conflict
     resolutions automatically.

     Nonetheless, the rebase might still fail, if meanwhile there have
     been intrusive / conflicting changes on the upstream master branch.
     If that happens, you can try to resolve the conflicts yourself, or
     you can ask the contributor to rebase his or her work on current
     upstream master, and to post it as the next version.

(44) Okay, now you have the contributor's patches on top of your local
     master branch, with all the tags added from the mailing list. Time
     to build-test it! If the build fails, report it to the list, and
     ask the contributor for a new version.

     (The OCD variant of this step is to build-test the contributor's
     series at *each* constituting patch, to enforce bisectability.)

(45) Okay, the build test passes! Maybe you want to runtime test it as
     well. If you do, and it works, you can respond with a Tested-by to
     the entire series on the list, *and* immediately add your own
     Tested-by to the patches as well. Employ step (43) accordingly.

(46) Time to push the patches to upstream master. Take a big breath :),
     and run

       git push origin REVIEW_implement_foo_for_bar_vN:master

     This will *attempt* to push the commits from your local
     REVIEW_implement_foo_for_bar_vN branch -- which is based off of
     your local master branch -- to the main github repo, *and* move the
     upstream master branch forward to the final commit among those.

     If it succeeds, you deserve an alcoholic (or non-alcoholic) drink
     of your choice, you're done.

     If it fails, then the reason is that *another maintainer* executed
     these steps in parallel, and moved forward the upstream master
     branch *after* your step (38), but before your step (46). If github
     accepted your push in this case, that would cause the *other*
     maintainer's push to go lost. So, proceed to the next step.

(47) Repeat the following steps:

     - step (38) -- Refresh your local master branch.

       *Do not forget* the "git checkout master" part in that step!

     - step (43) -- Rebase the contributor's series.

       No changes should be necessary, but if conflicts are found, those
       are due to the fresh commits pushed by the other maintainer,
       mentioned in step (46). The possible remedies are discussed in
       step (43) -- fix up the conflicts yourself, or ask the
       contributor to rebase and post a new version.

     - steps (44) through (46) -- rebuild, optionally retest, try
       pushing again.
_______________________________________________
edk2-devel mailing list
[email protected]
https://lists.01.org/mailman/listinfo/edk2-devel

Reply via email to