mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-13 04:30:28 +00:00
Merge remote-tracking branch 'Thecave3/rerere-doc' into integration_2026_w28
doc: add a Git guide (commit signing, branch management, rerere) (#202) Adds doc/git-guide.md, a single entry point for the practical Git knowledge needed to contribute to OAI: commit signing setup (DCO + verified commits), branch management (including fixup commits and --autosquash), submodules, recovering from mistakes, and reusing conflict resolutions with git rerere. This started as a rerere-only guide. Review discussion (@luispereira106, @rorsc, @sgarg00) converged on a broader scope: the Git how-tos are scattered across the documentation (signing in CONTRIBUTING.md, branch management in code-style-contrib.md), and configuring commit signing in particular was reported as hard to get right. This PR consolidates the how-tos in one guide while the policy documents stay authoritative for the rules. Single commit (review rounds squashed per the linear-history policy): - New doc/git-guide.md with sections on commit signing (moved from CONTRIBUTING.md, led by a copy-pasteable SSH recipe), branch management (moved from code-style-contrib.md; git switch, fixup/--autosquash, --fixup=amend:), submodules (unintended pointer-update pitfall), recovering from mistakes (restore/reset/reflog), and git rerere. - CONTRIBUTING.md keeps the normative DCO/Verified requirements and links to the guide; code-style-contrib.md keeps the workflow policy and links to the guide; doc/README.md lists the guide under Developer tools. - Cross-references (not moved, since they are coupled to their own docs): clang-format.md pre-commit hook, GET_SOURCES.md branch/tag model, doc_best_practices.md docs: commit prefix. - Small typo fix in the T tracer addconsoletrace.md documentation. Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org> Reviewed-by: Francesco Mani <email@francescomani.it> Reviewed-by: Shubhika Garg <shubhika.garg@openairinterface.org>
This commit is contained in:
@@ -133,6 +133,9 @@ The other SDRs (AW2S, LimeSDR, ...) have no READMEs.
|
||||
## Developer tools
|
||||
|
||||
- [code-style-contrib.md](./code-style-contrib.md): overall working practices, code style, and review process
|
||||
- [git-guide.md](./git-guide.md): Git how-tos — commit signing setup, branch
|
||||
management, submodules, recovering from mistakes, reusing conflict
|
||||
resolutions (rerere)
|
||||
- [cross-compile.md](./cross-compile.md): how to cross-compile OAI for ARM
|
||||
- [clang-format.md](./clang-format.md): how to format the code. See also the
|
||||
next entry for an error detection tool.
|
||||
|
||||
@@ -118,43 +118,12 @@ e.g., `v3.0`. We target to make releases bi-yearly.
|
||||
|
||||
### How to manage your own branch
|
||||
|
||||
Before starting to work, please make sure to branch off the latest `develop`
|
||||
branch. Make commits as appropriate.
|
||||
```bash
|
||||
$ git fetch origin
|
||||
$ git checkout develop
|
||||
$ git checkout -b my-new-feature # name as appropriate
|
||||
$ git add -p # add changes for change set 1, use `-p` to review what to include
|
||||
$ git commit # in the editor, describe your changes
|
||||
$ git add -p # add changes for change set 2
|
||||
$ git commit # in the editor, describe your changes
|
||||
```
|
||||
|
||||
Again, commit message should take multiple lines; after the initial title, a
|
||||
blank line should follow. Read the `DISCUSSION` section in `man git commit` for
|
||||
more information.
|
||||
|
||||
If your development takes longer, make sure to synchronize regularly with
|
||||
`origin/develop` using `git rebase`:
|
||||
```bash
|
||||
$ git fetch origin
|
||||
$ git rebase -i origin/develop
|
||||
```
|
||||
|
||||
If you do logical changes, you should not have to resolve the same conflicts
|
||||
over and over again. Note that if you jumped over multiple develop tags, you
|
||||
can also rebase in intermediate steps, in case you fear the differences might
|
||||
be too big.
|
||||
```
|
||||
$ git rebase -i 2023.w38
|
||||
$ git rebase -i 2023.w41
|
||||
$ git rebase -i develop
|
||||
```
|
||||
|
||||
Once you rebased, push the changes to the remote
|
||||
```
|
||||
$ git push origin my-new-feature --force-with-lease # force with lease let's you only overwrite what you also have locally in origin/my-new-feature
|
||||
```
|
||||
Branch off the latest `develop` branch before starting to work, keep your
|
||||
branch synchronized with `origin/develop` through regular rebases, and push
|
||||
with `--force-with-lease` after rebasing. The step-by-step commands — including
|
||||
how to rebase over multiple develop tags in intermediate steps and how to avoid
|
||||
resolving the same conflicts repeatedly with `git rerere` — are in the
|
||||
[branch management section of the Git guide](./git-guide.md#managing-your-own-branch).
|
||||
|
||||
### Use of git commit trailers
|
||||
|
||||
|
||||
390
doc/git-guide.md
Normal file
390
doc/git-guide.md
Normal file
@@ -0,0 +1,390 @@
|
||||
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
|
||||
|
||||
# Git guide
|
||||
|
||||
This guide collects the practical Git knowledge needed to contribute to OAI in
|
||||
one place: how to set up commit signing, how to manage and synchronize a
|
||||
feature branch, how to handle submodules, how to recover from common mistakes,
|
||||
and how to avoid resolving the same merge conflicts over and over. It is a
|
||||
how-to companion to the contribution *requirements*, which are
|
||||
defined in [CONTRIBUTING.md](../CONTRIBUTING.md) (CLA, DCO, verified commits)
|
||||
and [code-style-contrib.md](./code-style-contrib.md) (workflow, commit, and
|
||||
review policy).
|
||||
|
||||
[[_TOC_]]
|
||||
|
||||
## Setting up commit signing
|
||||
|
||||
Every commit in a pull request must pass two independent CI checks, described
|
||||
in [CONTRIBUTING.md](../CONTRIBUTING.md#commit-guidelines):
|
||||
|
||||
1. **[Developer Certificate of Origin (DCO)](https://en.wikipedia.org/wiki/Developer_Certificate_of_Origin)**:
|
||||
the commit message carries a `Signed-off-by:` trailer.
|
||||
2. **[Verified commits](https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification)**:
|
||||
the commit is cryptographically signed with an SSH or GPG key.
|
||||
|
||||
These are two different mechanisms: the sign-off is a line of text you add with
|
||||
`git commit -s`, the signature is created automatically by Git once signing is
|
||||
configured. You need both.
|
||||
|
||||
### Quick setup (SSH signing)
|
||||
|
||||
```bash
|
||||
# 1. Generate a key pair (skip if you already have one)
|
||||
ssh-keygen -t ed25519 -C "<your email>"
|
||||
|
||||
# 2. Configure Git to sign every commit with it
|
||||
git config --global user.name "<Your Name>"
|
||||
git config --global user.email "<your email>"
|
||||
git config --global gpg.format ssh
|
||||
git config --global user.signingkey ~/.ssh/id_ed25519.pub
|
||||
git config --global commit.gpgsign true
|
||||
```
|
||||
|
||||
> **NOTE:**
|
||||
> `--global` writes to `~/.gitconfig` and applies to every repository on the
|
||||
> machine. When working on a shared server (or with different identities in
|
||||
> different clones), drop `--global` to store the same settings in the current
|
||||
> repository's `.git/config` only.
|
||||
|
||||
Then print the public key with `cat ~/.ssh/id_ed25519.pub` and paste it into
|
||||
your GitHub account under *Settings → SSH and GPG keys → New SSH key*, choosing
|
||||
the key type **Signing Key**.
|
||||
|
||||
> **NOTE:**
|
||||
> Adding an SSH key for repository access does not automatically enable commit
|
||||
> signing. The key must also be added under GitHub's Signing Keys settings.
|
||||
|
||||
For commits to show as *Verified* on GitHub:
|
||||
|
||||
- your `git config user.email` must match an email of your GitHub account,
|
||||
- that email must be [verified in your GitHub account](https://docs.github.com/en/account-and-profile/how-tos/email-preferences/verifying-your-email-address),
|
||||
- and it must be the email address used for the CLA (see
|
||||
[CONTRIBUTING.md](../CONTRIBUTING.md)).
|
||||
|
||||
If you prefer GPG over SSH, set `gpg.format` to `openpgp` and `user.signingkey`
|
||||
to your GPG key ID instead; see the [GitHub documentation on signing
|
||||
commits](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits)
|
||||
for the full walkthrough of both methods.
|
||||
|
||||
### Signing off your commits (DCO)
|
||||
|
||||
The `Signed-off-by:` trailer is added with the `-s`/`--signoff` flag:
|
||||
|
||||
```bash
|
||||
git commit -s # new commit
|
||||
git commit --amend -s --no-edit # add the trailer to the last commit
|
||||
```
|
||||
|
||||
It must read `Signed-off-by: Full Name <email-for-cla>`. See the
|
||||
[commit trailers section](./code-style-contrib.md#use-of-git-commit-trailers)
|
||||
of the contribution guidelines for this and other trailers.
|
||||
|
||||
### Verifying signed commits
|
||||
|
||||
You can verify that commits are properly signed locally using:
|
||||
|
||||
```bash
|
||||
git log --show-signature
|
||||
```
|
||||
|
||||
GitHub should also display a *Verified* badge next to signed commits once the
|
||||
signing key has been correctly configured in your account.
|
||||
|
||||
For SSH commit signing, local Git verification may require an
|
||||
`allowed_signers` file. This is only used for local verification in Git and is
|
||||
not required by GitHub. If you see errors such as:
|
||||
|
||||
```text
|
||||
No principal matched
|
||||
Can't check signature
|
||||
error: gpg.ssh.allowedSignersFile needs to be configured
|
||||
```
|
||||
|
||||
create the file, add your signing identity, and enable it in your Git config:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.config/git
|
||||
echo "user@example.com ssh-ed25519 AAAACexamplekeystringhere" > ~/.config/git/allowed_signers
|
||||
git config gpg.ssh.allowedSignersFile ~/.config/git/allowed_signers
|
||||
```
|
||||
|
||||
> **NOTE:**
|
||||
> This is only for local Git signature verification and does not affect GitHub,
|
||||
> or remote repository behavior.
|
||||
|
||||
## Managing your own branch
|
||||
|
||||
The general development branch, and the target of every contribution, is
|
||||
`develop`; see [GET_SOURCES.md](./GET_SOURCES.md) for the branch and tag model
|
||||
(weekly `YYYY.wXX` tags, `vX.Y` releases). The rules for what a branch should
|
||||
look like — linear history, small self-contained logical commits, commit
|
||||
messages that explain *why* — are policy and live in
|
||||
[code-style-contrib.md](./code-style-contrib.md#workflow).
|
||||
|
||||
Before starting to work, please make sure to branch off the latest `develop`
|
||||
branch. Make commits as appropriate.
|
||||
|
||||
```bash
|
||||
git fetch origin
|
||||
git checkout develop
|
||||
git checkout -b my-new-feature # name as appropriate
|
||||
git add -p # add changes for change set 1, use `-p` to review what to include
|
||||
git commit -s # in the editor, describe your changes
|
||||
git add -p # add changes for change set 2
|
||||
git commit -s # in the editor, describe your changes
|
||||
```
|
||||
|
||||
Recent Git versions also offer `git switch` as a clearer alternative to
|
||||
`git checkout` for branch operations: `git switch develop` changes branch,
|
||||
`git switch -c my-new-feature` creates one.
|
||||
|
||||
Commit messages should take multiple lines; after the initial title, a blank
|
||||
line should follow. Read the `DISCUSSION` section in `man git commit` for more
|
||||
information. For documentation-only commits, prefix the title with `docs:`
|
||||
(see [doc_best_practices.md](./doc_best_practices.md)).
|
||||
|
||||
Code must be formatted with clang-format; an optional pre-commit hook can
|
||||
check this automatically at every commit — see
|
||||
[clang-format.md](./clang-format.md) for its installation and how to combine
|
||||
it with `git add -p`/`git stash -p`.
|
||||
|
||||
If your development takes longer, make sure to synchronize regularly with
|
||||
`origin/develop` using `git rebase`:
|
||||
|
||||
```bash
|
||||
git fetch origin
|
||||
git rebase -i origin/develop
|
||||
```
|
||||
|
||||
If you do logical changes, you should not have to resolve the same conflicts
|
||||
over and over again. If the same conflicts do keep reappearing, e.g., when
|
||||
maintaining a long-lived fork, consider enabling
|
||||
[`git rerere`](#reusing-conflict-resolutions-with-git-rerere). Note that if
|
||||
you jumped over multiple develop tags, you can also rebase in intermediate
|
||||
steps, in case you fear the differences might be too big.
|
||||
|
||||
```bash
|
||||
git rebase -i 2023.w38
|
||||
git rebase -i 2023.w41
|
||||
git rebase -i develop
|
||||
```
|
||||
|
||||
Once you rebased, push the changes to the remote:
|
||||
|
||||
```bash
|
||||
git push origin my-new-feature --force-with-lease # force with lease lets you only overwrite what you also have locally in origin/my-new-feature
|
||||
```
|
||||
|
||||
### Fixing up earlier commits
|
||||
|
||||
The [workflow policy](./code-style-contrib.md#workflow) asks for a history
|
||||
without "clean up" commits: when review or testing reveals a problem in an
|
||||
earlier commit of your branch, fold the fix into that commit instead of
|
||||
appending a `Fix bug` commit on top. Git automates this with fixup commits and
|
||||
`--autosquash`:
|
||||
|
||||
```bash
|
||||
git add -p # stage the fix
|
||||
git commit --fixup=<commit> # creates a commit titled "fixup! <original title>"
|
||||
git rebase -i --autosquash origin/develop # moves it after <commit> and squashes the two
|
||||
```
|
||||
|
||||
During the `--autosquash` rebase, Git pre-arranges the todo list so each
|
||||
`fixup!` commit is squashed into the commit it references; you normally just
|
||||
accept it. The result is the same clean history as if the fix had been part of
|
||||
the original commit.
|
||||
|
||||
A handy variant is `git commit --fixup=amend:<commit>`, which folds in the fix
|
||||
and also rewrites the commit message: during the `--autosquash` rebase the
|
||||
editor opens pre-filled with the original message, ready to be edited into the
|
||||
new one.
|
||||
|
||||
## Working with submodules
|
||||
|
||||
Parts of the tree are Git submodules. After cloning, and after every branch
|
||||
switch or pull, make sure they match the superproject:
|
||||
|
||||
```bash
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
A recurring review problem is the *unintended submodule pointer update*: a
|
||||
submodule whose checked-out commit differs from what the superproject records
|
||||
shows up in `git status` as `modified: <path> (new commits)`, and a broad
|
||||
`git add .`, `git add -A`, or `git commit -a` silently records the new pointer
|
||||
in your commit. To avoid it:
|
||||
|
||||
- review `git status` before committing and stage files explicitly (e.g. with
|
||||
`git add -p`) rather than adding everything;
|
||||
- if a pointer change was staged by accident, unstage it with
|
||||
`git restore --staged <path>` and realign the submodule with
|
||||
`git submodule update --init <path>`.
|
||||
|
||||
Only commit a submodule pointer change when updating that submodule is the
|
||||
purpose of the commit, and say so in the commit message.
|
||||
|
||||
## Recovering from mistakes
|
||||
|
||||
To unstage a file that was added by accident (the changes stay in your working
|
||||
tree), or to throw away local changes to a file:
|
||||
|
||||
```bash
|
||||
git restore --staged <file> # unstage; keeps the modifications
|
||||
git restore <file> # discard unstaged modifications - cannot be undone
|
||||
```
|
||||
|
||||
`git reset` moves the current branch to another commit and differs in what it
|
||||
does to your files:
|
||||
|
||||
```bash
|
||||
git reset --soft HEAD~1 # undo the last commit, keep its changes staged (e.g. to re-split it)
|
||||
git reset --hard <commit> # make branch, index and working tree identical to <commit>
|
||||
```
|
||||
|
||||
> **Warning:** `git reset --hard` discards all uncommitted changes; there is no
|
||||
> way to recover them.
|
||||
|
||||
Committed work is much harder to lose than it seems: `git reflog` records every
|
||||
position of `HEAD` (commits, rebases, resets, checkouts) for a retention period
|
||||
of at least 30 days, even for commits no branch points to anymore. If a rebase
|
||||
or reset went wrong, find the last good state and reset back to it:
|
||||
|
||||
```bash
|
||||
git reflog # e.g.: e75076172 HEAD@{5}: commit: doc: add git rerere guide
|
||||
git reset --hard 'HEAD@{5}' # return the branch to that state
|
||||
```
|
||||
|
||||
## Reusing conflict resolutions with git rerere
|
||||
|
||||
The `develop` branch is updated roughly once a week. Feature branches that live
|
||||
for more than a few days therefore have to be re-synced with `develop`
|
||||
repeatedly, and the same merge conflicts tend to reappear at every sync - often
|
||||
in the same scheduler, PHY, or RRC files that several contributors touch at
|
||||
once. Resolving the identical conflict by hand every week is error-prone and
|
||||
wastes time.
|
||||
|
||||
Git ships a built-in feature for exactly this situation: `rerere`, short for
|
||||
**reuse recorded resolution**. Once enabled, Git remembers how you resolved a
|
||||
given conflict and replays that resolution automatically the next time the same
|
||||
conflict appears.
|
||||
|
||||
This section explains how to enable and use it. It is a local developer
|
||||
convenience: nothing about it changes the repository, the history you push, or
|
||||
the contribution workflow.
|
||||
|
||||
### What it does
|
||||
|
||||
When a conflict occurs, `rerere` records the conflicted hunk (the *preimage*).
|
||||
After you resolve it, `rerere` records your resolution (the *postimage*), keyed
|
||||
by a hash of the preimage. The next time a conflict with the same preimage shows
|
||||
up - in a later rebase, a later merge, or even another branch - Git reapplies
|
||||
your recorded resolution instead of presenting the conflict again.
|
||||
|
||||
The data lives in `.git/rr-cache/` inside your local clone. It is never part of
|
||||
any commit and is never pushed.
|
||||
|
||||
### Enabling it
|
||||
|
||||
Enable it once, globally, so it applies to every repository on your machine:
|
||||
|
||||
```bash
|
||||
git config --global rerere.enabled true
|
||||
git config --global rerere.autoupdate true
|
||||
```
|
||||
|
||||
`rerere.autoupdate` stages a replayed resolution automatically. Without it, the
|
||||
resolution is still written into your working tree, but you have to `git add`
|
||||
the file yourself.
|
||||
|
||||
### Typical flow
|
||||
|
||||
The first time you hit a conflict after enabling `rerere`, resolve it exactly as
|
||||
you always have:
|
||||
|
||||
```bash
|
||||
# during a rebase or a merge that conflicts
|
||||
git status # rerere reports which paths it is recording
|
||||
# edit the conflicted files, remove the markers
|
||||
git add <resolved-files>
|
||||
git rebase --continue # or: git commit, for a merge
|
||||
```
|
||||
|
||||
That resolution is now recorded. The next time the same conflict appears, Git
|
||||
resolves it for you. With `autoupdate` on, the file is already staged and you can
|
||||
go straight to:
|
||||
|
||||
```bash
|
||||
git rebase --continue # or git commit
|
||||
```
|
||||
|
||||
Always review the replayed result before continuing - see *Caveats* below.
|
||||
|
||||
### Inspecting and undoing recorded resolutions
|
||||
|
||||
```bash
|
||||
git rerere status # paths with a recorded preimage in the current operation
|
||||
git rerere diff # the resolution rerere is applying
|
||||
git rerere forget <path> # discard a recorded resolution (e.g. a wrong one)
|
||||
```
|
||||
|
||||
`git rerere forget` is the escape hatch when you recorded a bad resolution: it
|
||||
drops the cached entry for that path so the next conflict is presented fresh.
|
||||
|
||||
### Seeding from existing history
|
||||
|
||||
If your branch already contains **merge commits** whose conflicts you resolved
|
||||
before enabling `rerere`, you can backfill the cache so those resolutions are
|
||||
available immediately. Git ships a helper for this in `contrib/`:
|
||||
|
||||
```bash
|
||||
sh /path/to/git/contrib/rerere-train.sh origin/develop..HEAD
|
||||
```
|
||||
|
||||
It replays the merge commits in the given range, reconstructs each conflict, and
|
||||
records the resolution found in the merge commit.
|
||||
|
||||
> **Note:** this only works for resolutions captured in merge commits. A purely
|
||||
> linear (rebased) history has no merge commits to learn from, so there is
|
||||
> nothing to backfill - `rerere` will simply start recording from your next
|
||||
> conflict onward.
|
||||
|
||||
### Sharing the cache (optional)
|
||||
|
||||
The cache is local. If you work across several machines, or want a team to share
|
||||
resolutions for the same recurring conflicts, copy the directory:
|
||||
|
||||
```bash
|
||||
rsync -a ~/work/oai-A/.git/rr-cache/ ~/work/oai-B/.git/rr-cache/
|
||||
```
|
||||
|
||||
There is no built-in push/pull for the cache; treat it as an ordinary directory
|
||||
to sync.
|
||||
|
||||
### Caveats and good practice
|
||||
|
||||
- `rerere` matches on the **exact** conflicting text. If `develop` changed the
|
||||
lines surrounding your change, the preimage differs and the conflict is
|
||||
presented as new. This is expected - the resolution is still recorded for the
|
||||
next identical occurrence.
|
||||
- A replayed resolution is only as correct as the original. When the code around
|
||||
a conflict has evolved, an old resolution can apply cleanly yet be wrong.
|
||||
**Review every replayed resolution and build/test before continuing.**
|
||||
- `rerere` reduces repeated manual work; it does not change which branch
|
||||
strategy you use. It helps both when rebasing onto `develop` and when merging
|
||||
`develop` into a feature branch. Remember that branches intended for
|
||||
contribution must have a linear history without merge commits (see the
|
||||
[workflow policy](./code-style-contrib.md#workflow)); a fork can of course
|
||||
carry merge commits if that is convenient for its development.
|
||||
|
||||
## See also
|
||||
|
||||
- [CONTRIBUTING.md](../CONTRIBUTING.md) - CLA, DCO, and licensing requirements.
|
||||
- [code-style-contrib.md](./code-style-contrib.md) - workflow, commit, and
|
||||
review policy, including commit trailers.
|
||||
- [GET_SOURCES.md](./GET_SOURCES.md) - branches, tags, and how to obtain the
|
||||
sources.
|
||||
- [clang-format.md](./clang-format.md) - code formatting and its Git
|
||||
integration (pre-commit hook).
|
||||
- The [Git Book](https://git-scm.com/book/en/v2) and the
|
||||
[`git rerere` manual](https://git-scm.com/docs/git-rerere)
|
||||
Reference in New Issue
Block a user