Skip to content
Blockchain Association of Cameroon
BAC-L1-FOUND-1164 h

Git, GitHub and open-source contribution

The submission channel for all Academy coursework, and the skill that makes a developer employable in a protocol ecosystem.

  • Keep a readable history: branches, messages, controlled rewriting
  • Open a pull request and respond to review
  • Resolve a merge conflict without losing work

All the Academy's coursework is submitted through Git and GitHub, and all the large open protocols — Polkadot, Cosmos, Ethereum and their tools — are developed the same way: you copy the repository, work on a branch, propose a pull request, answer review, resolve conflicts. This workshop teaches you that flow end to end, starting with what a Git history really is (a chain of snapshots linked by hashes, like the blocks of a blockchain; the hash and chain-explorer tools in the first lesson show it) and what can be rewritten without danger. Your browser cannot run Git: you will learn to read histories, conflicts and reviews as console excerpts, before making real moves on your machine in the independent lab.

Lessons 1/4 · 25 min

Git history: commits, branches and controlled rewriting

A starting question

Two students work on the same file. One sends it to the other by messaging; the other replies with "final version 2". Three days later nobody knows which version is right. Git solves this problem: it keeps the history of each change, who made it, when, and why.

What a commit is

A commit is a snapshot of all the project's files at a given moment, with: an author, a date, a message, and a link to the parent commit(s). Git identifies each commit by a hash computed from all this content, including the parent's hash. Historically this hash is a SHA-1; Git can also work with SHA-256 (see the note below).

As with a blockchain, changing an old commit changes its hash, hence that of all its descendants. In the hash tool, change one letter of a text and watch: the hash is completely different. In the chain explorer, edit block 2: all later ones "break". It is the same idea.

Two honest differences from a blockchain:

  • There is no consensus. Everyone owns a full copy and can rewrite their local history. The project's "truth" is whatever the team agreed to treat as the reference repository (usually the main branch of the main repository).
  • A Git hash identifies content; it does not prove who wrote it (except through commit signing, see lesson 4).

The three areas

working directory  --git add-->  index (staging area)  --git commit-->  repository (history)
  • git status shows what changed and what is ready to be committed.
  • git add file chooses what enters the next commit (one commit = one idea, not everything you touched).
  • git commit creates the snapshot.

Branches: labels that move

A branch is simply a pointer to a commit. Creating a branch copies nothing:

git switch -c feature/fees       # create and move to the branch
git log --oneline --graph --decorate --all

Example output:

* 9f3c2ab (HEAD -> feature/fees) Add fee estimate helper
* 41d7e90 Fix typo in README
| * c0a11fe (origin/main, main) Update CI node version
|/
* 7b2e5d1 Initial import

Reading: from the initial commit 7b2e5d1, main advanced by one commit (c0a11fe) and feature/fees by two. HEAD is the branch you are on. origin/main is what you know of main on the server as of the last git fetch.

Good commit messages

A message is read months later by someone searching why a line exists. A widespread convention (not a law):

Reject negative amounts in transfer()

A negative amount credited the sender. Validate amount > 0
and add tests for -1 and 0.

A short imperative subject (about 50 characters), a blank line, then the why. A message like "fix" or "WIP" teaches nothing. A commit mixing three subjects cannot be undone cleanly.

Controlled rewriting

Git lets you rewrite history: git commit --amend (the last commit), git rebase -i HEAD~3 (combine, rename, reorder three commits). Every rewrite creates new commits with new hashes. Golden rule:

  • Rewriting is safe as long as the commits are yours alone (not yet pushed, or on your pull-request branch that nobody else uses). To push after rewriting: git push --force-with-lease (more cautious than --force: it refuses if the remote branch has moved since your last fetch, for instance because someone else pushed to it; an automatic git fetch by your editor can however update your view and defeat this protection).
  • Rewriting is forbidden on a shared branch (main): your colleagues built on it.
  • To undo a commit already shared, use git revert <hash>: it creates a new commit that cancels the old one, rewriting nothing.

Safety net: git reflog lists the places HEAD has been, even after a rewrite, which generally lets you recover a "lost" commit for some time. git reset --hard, however, permanently discards uncommitted changes.

What this does NOT guarantee

  • A clean history is not correct code: it says what changed, not that it works.
  • Git does not prove the author's identity: a commit's name and e-mail are fields anyone can write.
  • git revert does not erase the trace of a secret or a mistake: the old commit stays in history.

Recap

A commit = a snapshot linked to its parent by a hash; a branch = a pointer. Rewrite only what is yours alone, and use revert for what is shared.

Interactive tool

SHA-256 digest

Type some text: the digest updates on every keystroke. Compare with a second text to see the avalanche effect.

Digest (64 hex characters)

…

Digest of the second text

…

Differing characters are underlined and bold. Text is UTF-8 encoded with no space or newline added.

Interactive tool

Blockchain explorer (toy)

Five blocks linked by their digests. Edit a block's data: its digest changes, the next block's link breaks, and every later block becomes invalid.

Building the chain (mining 5 blocks)…

This is a toy. A block's hash is SHA-256 of "index|previous hash|data|nonce"; difficulty is only two leading hex zeros (about 256 attempts). A real chain requires astronomically more work and a network of nodes that each verify the chain.

Lessons 2/4 · 25 min

Merge, rebase and conflicts without losing work

The problem

Amina and Paul each edit a tool's configuration file. Amina sets fees to 15 on her branch; Paul, after talking with the team, sets them to 12 on main. When Amina wants to bring in main, Git cannot guess who is right. It stops and asks: this is a conflict. It is not a failure; it is Git refusing to choose for you.

Two ways to integrate

Merge: git merge main (from your branch) creates a merge commit that has two parents. History keeps the real shape of the work, with its forks. If your branch has not diverged, Git simply moves the pointer forward (fast-forward), with no merge commit.

Rebase: git rebase main replays your commits one by one on top of the latest commit of main. History becomes a straight line, but your commits receive new hashes: it is a rewrite (lesson 1). You therefore rebase your own unshared commits, never those of a branch others use.

Projects choose their policy (merge, rebase, "squash" into a single commit): read the CONTRIBUTING file before choosing.

Anatomy of a conflict

After git merge main, Git prints:

Auto-merging config.toml
CONFLICT (content): Merge conflict in config.toml
Automatic merge failed; fix conflicts and then commit the result.

And the file contains:

<<<<<<< HEAD
fee = 15
=======
fee = 12
>>>>>>> main
timeout = 30
  • Between <<<<<<< HEAD and =======: your version (the branch you are on).
  • Between ======= and >>>>>>> main: the version coming from the other branch.
  • timeout = 30 is outside the markers: Git merged it without difficulty.

Resolving: decide, edit, commit

Reminder: the team decided 12 (discussion in the ticket). The steps:

  1. Decide which value is right. Git cannot decide; do not take "mine" by reflex.
  2. Edit the file so it contains the intended result, with no marker at all:
fee = 12
timeout = 30
  1. git add config.toml to mark the conflict as resolved.
  2. git merge --continue (or git commit) to finish the merge. In a rebase, it would be git rebase --continue.
  3. Run the tests. A merge with no markers is not necessarily correct (see below).

To undo everything and return to the earlier state: git merge --abort (or git rebase --abort). It is the emergency exit.

Not losing work

  • Before a risky merge, commit (git commit) or set aside (git stash) your changes.
  • Create a backup branch: git branch backup-before-merge; it costs nothing.
  • After resolving, re-read with git diff and run the tests.
  • If you are lost: git status says exactly what state you are in and what to do next. Then git merge --abort.

A semantic conflict: no markers, but wrong

Amina renames the function compute_fee to estimate_fee on her branch. Paul adds on main a new call to compute_fee in another file. Git merges without conflict, since the changed lines differ. But the code now calls a function that no longer exists: the program fails. A successful textual merge does not guarantee a correct program, hence the "run the tests" step.

Worked example

Two branches modify the same 200-line file: B changes 10 lines, C changes 4 (Git always merges two branches at a time). If 3 consecutive lines of C are also modified by B, Git produces a single conflict block of 3 lines; if those 3 lines are separated by unchanged lines, there is one block per group. Beware: changes to adjacent lines (line 2 on one side, line 3 on the other) are already a conflict, even with no overlap; at least one unchanged line is needed between two modified areas for Git to merge them alone (behaviour observed with Git 2.43 on default settings). A conflict is a block of lines, not a line.

What this does NOT guarantee

  • Resolving a conflict is not the team's agreement: if you ignore the ticket's decision, you create a silent bug.
  • git merge --abort does not recover work you yourself discarded with reset --hard.
  • A clean rebase says nothing about the state of the tests.

Recap

A conflict calls for a human decision. Decide, edit with no markers, add, continue, test; --abort is always possible; back up before risking.

Lessons 3/4 · 25 min

Fork, pull request and review

The problem

You want to fix a typo in the documentation of a repository that is not yours. You are not allowed to write to it directly, and fortunately so: anyone could otherwise break the project. The standard flow lets you propose a change that maintainers examine before accepting.

The flow, step by step

  1. Fork: on GitHub, you create your copy of the repository (the fork). You can write to it.
  2. Clone: you copy your fork to your machine.
  3. Upstream remote: you declare the original repository as a second remote, to follow it.
  4. Branch: you work on a dedicated branch, never directly on main.
  5. Push: you push the branch to your fork.
  6. Pull request (PR): on GitHub, you ask for your branch to be integrated into the original repository.
  7. Review: maintainers comment; you answer and fix; they accept or not.
git clone <url-of-your-fork>
cd repo
git remote add upstream <url-of-the-original-repo>
git remote -v                          # origin = your fork, upstream = the original
git fetch upstream
git switch -c docs/fix-typo-install    # dedicated branch, meaningful name
# ... edit, then:
git add docs/install.md
git commit -m "Fix typo in install instructions"
git push -u origin docs/fix-typo-install

Then, on GitHub, open the PR: base = the original repository's main branch, compare = your branch in your fork.

A good pull request

  • A single subject, small. The bigger a PR, the slower it is to review and the easier to reject.
  • A clear title and a description: what, why, how verified. If it answers a ticket, write Closes #123: when merged into the default branch, GitHub closes ticket 123 automatically.
  • The automatic checks (CI) must pass. They are described in workflow files; see the GitHub Actions documentation.
  • Read `CONTRIBUTING` before writing: style, tests, message format, sometimes a Signed-off-by line (git commit -s) or a contribution agreement.
  • For a big change, open a ticket first to ask whether the project wants it.
  • A draft PR signals "not ready for review yet".

Answering a review

The reviewer writes: "Can you add a test for the amount = 0 case? And rename tmp?". Good conduct:

  1. Read each comment and decide: I apply it, or I politely explain why not.
  2. Make the fixes on the same branch and push: the PR updates itself. Do not close the PR to open a new one.
  3. Answer each thread ("done in 3f2a9c1" or an explanation); mark "resolved" only what really is.
  4. If the project asks for a clean history, combine the fixes with an interactive rebase then git push --force-with-lease — only on your PR branch.
  5. Request review again once the points are handled.

A review criticises the code, not the person. A PR being refused is not a judgment on you: maintainers must preserve the project's coherence. If a disagreement persists, ask, do not argue.

Keeping your fork up to date

While your PR is in review, main moves on. To stay current:

git fetch upstream
git rebase upstream/main        # or: git merge upstream/main, depending on project policy
git push --force-with-lease     # if you rebased your PR branch

Worked example: a PR that is too big

A PR changes 1,200 lines in 14 files, for three subjects. A reviewer reading 200 lines per sitting will need 6 sittings and may no longer remember the beginning by the end. Split into three PRs of 400 lines on one subject each, each PR is reviewed in about 2 sittings and can be accepted or refused separately. The total reading is the same; the risk and the delay are not.

What this does NOT guarantee

  • An approved PR is not a correct PR: human review misses errors. Tests are another safety net.
  • A green CI proves the planned checks pass, not that no bug exists.
  • Opening a PR gives no right to its acceptance or to a quick answer: maintainers are often volunteers.

Recap

Fork, dedicated branch, small and clear PR, review answers on the same branch, fork kept up to date. A review is an exchange about the code.

Lessons 4/4 · 25 min

Contributing to an open protocol, and never publishing a secret

The problem

A motivated student wants to contribute to a large protocol repository. On day one they open ten PRs to "reformat the code". All are closed. Why? Because they neither read the rules nor asked what the project needed. Contributing is a relationship as much as code.

Where to start

Large protocol repositories are public, for example those of Polkadot SDK or Cosmos SDK. Reasonable first contributions are modest:

  • Fix a documentation typo or a dead link.
  • Improve or translate a guide (if the project accepts translations).
  • Reproduce a reported bug and write a test case.
  • Take a ticket marked for beginners (often "good first issue"), after commenting that you are taking it, so two people do not do the same thing.

Before proposing anything: read the README, CONTRIBUTING, the code of conduct, the licence. The licence says under what conditions your contribution is published and reused; by contributing you generally agree it will be under the project's licence. Never send code you have no right to publish (an employer's code, copied without a suitable licence).

Be brief and precise in a request for help: tool version, command run, full error message, what you already tried.

Proving who wrote a commit

A commit's name and e-mail are freely written; anyone can put a false one. To tie a commit to a key, Git lets you sign it (git commit -S, with a GPG key or, in recent versions, an SSH key). GitHub then shows "Verified" if the signature matches a key registered on your account and the commit's e-mail address is a verified address of that account. The signature proves the key holder created the commit; it does not prove the content is good (back to the workshop on signatures).

The accidentally pushed secret

Scenario: Aïssatou works on a public repository. She adds a .env file containing a test API key and pushes it. Ten minutes later she notices, deletes the file and pushes a new commit. Is it settled? No.

Why deleting is never enough:

  • The file stays in the previous commit, hence in the history anyone can clone.
  • Programs continuously scan public repositories looking for keys; a published key can be found very quickly. Assume it is already copied.
  • Forks, clones and caches may have kept the version.
  • Even if you rewrite history and force-push, copies already made still exist.

Correct order of action:

  1. Revoke or renew the key immediately at the service provider (this is what makes it useless). For a chain wallet key: consider the funds exposed and move them to a new key, without waiting.
  2. Check usage: the service's access logs, the key's activity, to know whether it was used.
  3. Remove the secret from current files and add the pattern to the .gitignore file (.env). Beware: .gitignore does not affect a file Git already tracks; you also need git rm --cached .env so that Git stops tracking it.
  4. Only then, clean the history if needed (for example with the git filter-repo tool then a forced push, warning the team): this is hygiene, not security.
  5. Prevent recurrence: a .env.example file without values, an automatic secret check before commit or in CI, and the host's secret-scanning feature (which detects only known patterns).

Worked example of what the order costs: renewing the key takes 5 minutes; cleaning the history and warning a team of 10 takes an hour. If you clean first, the key stays valid during that hour. The order is not a matter of taste.

What this does NOT guarantee

  • Signing your commits does not protect against theft of the signing key: protect it like an SSH key.
  • A secret-detection tool does not find everything: a key with no recognisable pattern slips through.
  • Contributing to a large repository guarantees neither an answer, nor acceptance, nor a job: it is a way to show verifiable work.

Recap

Start small, read the rules, ask before launching in. A pushed secret is a lost secret: revoke first, clean afterwards.

Guided lab

Let us be honest: your browser cannot run Git or open a real pull request. This lab therefore trains reading and reasoning on short console excerpts made up for the exercise: reading a graph history, resolving a conflicted file, judging a commit message, answering a review correctly. The real work — fork, branch, pull request and peer cross-review by two people on an exercise repository — happens in the independent lab, on your machine, with a GitHub account.

A browser is no substitute for a real terminal: this lab trains reading and reasoning; the real hands-on practice is in the independent lab.

A certified account is required

Graded activities record results in your name and are the basis for a credential. They therefore need a complete certified file and a current consent. Filling in the file does not verify your identity; the Secretariat can verify it afterwards ("Verified" badge).

  1. 1.Read a graph history

    The question shown under this step displays the output of git log --oneline --graph --all and a command to run next. Draw the graph on paper, identify what each pointer designates (HEAD, main, origin/main), then predict the history's state after the command, in particular what changes for the commits' hashes. The hash tool reminds you why changing the parent changes the hash.

    Interactive tool

    SHA-256 digest

    Type some text: the digest updates on every keystroke. Compare with a second text to see the avalanche effect.

    Digest (64 hex characters)

    …

    Digest of the second text

    …

    Differing characters are underlined and bold. Text is UTF-8 encoded with no space or newline added.

  2. 2.Resolve a conflicted file

    The question shown under this step reproduces a file with its conflict markers and a team decision taken in a ticket. Mentally write the intended final content (with no marker at all) then choose the proposal that contains both the right content and the right commands to finish the merge.

  3. 3.Judge a commit message

    The question shown under this step offers four commit messages for the same change. Apply the criteria of the first lesson: short imperative subject, a single idea, a "why" in the body. Choose the one a reviewer will be glad to find in six months.

  4. 4.Answer a review correctly

    The question shown under this step describes an open pull request, two reviewer comments asking for changes, and several ways to react. Choose the one that moves the same PR forward, keeps a clear trace of the answers and respects the rewriting rule of the first lesson.

Security exercise — The accidentally pushed secret

Kevin contributes to a public BAC exercise repository from his fork. To test an API gateway, he creates a .env file and absent-mindedly commits it:

$ git log --oneline
d41e8aa Remove .env file
b19c7f3 Add gateway test script and config
$ git show b19c7f3 -- .env
+API_SECRET=sk_test_FICTITIOUS_0000_NOT_A_REAL_KEY

(The value is fictitious and designates no real service.) He pushed both commits to his fork ten minutes ago; the PR is not yet opened. Kevin thinks: "I deleted the file in the last commit, it's settled." A colleague answers: "Rewrite the history with a force push and it's clean."

Your task: say why both ideas are insufficient, and in which order Kevin must act for the secret to really stop being a danger. Answer the application question shown under this exercise.

Workshop quiz

Eight questions drawn from a larger pool, 70% to pass. It opens once every lab checkpoint is passed. You can retake it: the draw changes.

A certified account is required

Graded activities record results in your name and are the basis for a credential. They therefore need a complete certified file and a current consent. Filling in the file does not verify your identity; the Secretariat can verify it afterwards ("Verified" badge).

Independent lab (outside the site)

Not graded online: do it on your own machine and publish it to your Git repository.

A real contribution, then a cross-review

To do on your machine, outside the site, with a GitHub account (enable two-factor authentication) and Git installed.

Part A — the BAC exercise repository. Follow your trainer's instructions for this repository. (1) Fork and clone your fork. (2) Add the upstream remote and check with git remote -v. (3) Create a dedicated branch. (4) Make two separate commits with good messages, one of which deliberately fixes a small mistake. (5) Rewrite: run git rebase -i HEAD~2 and, in the list the editor opens, replace pick with fixup in front of the second commit (fixup absorbs it into the first and drops its message; squash keeps its message too) — pick 1a2b3c4 Add fee helper then fixup 5d6e7f8 Fix typo — then check with git log --oneline --graph. (6) Cause a conflict (two branches changing the same line), resolve it, git add, --continue, then test. (7) Open the PR with a what / why / how verified description. (8) Review a peer's PR (two reviews per PR are required), then answer the comments received on the same branch.

Part B — a real contribution. Choose an official protocol repository (for example among those in the Academy's documentation registry) and read its CONTRIBUTING. Find one modest improvement: a documentation typo, a dead link or a translation the project accepts. If the project asks for a ticket first, open one. Write a small, clear PR. Do not submit anything automatically generated without having understood and verified it yourself.

Part C — hygiene. Create a test repository, add .env to .gitignore before creating the file, then check with git status that it does not appear. Write a .env.example without values. Never use a real secret for this exercise.

Success criterion: your PR is merged into the exercise repository after review by two peers; you can show the history before and after the rebase, and explain every hash that changed.

Takeaways

  • A commit is a snapshot linked to its parent by a hash; a branch is a pointer. No consensus: everyone can rewrite their local copy.
  • Rewrite only what is yours alone (amend, interactive rebase, push --force-with-lease on your PR branch); on a shared branch, use revert.
  • A conflict calls for a human decision: decide, edit with no markers, add, continue, test; merge --abort is the emergency exit. A merge with no markers can still be wrong.
  • A good PR is small, clear and tied to a ticket; you answer review by pushing to the same branch and answering each thread.
  • A pushed secret is a compromised secret: revoke first, check usage, clean history afterwards, and prevent recurrence.

Common misconceptions

  • Misconception : If I delete a secret file in a new commit, it is gone.

    In fact : It remains in the previous commits of the history. A published secret is compromised: it must be revoked.

  • Misconception : A git push --force is fine as long as my tests pass.

    In fact : Forcing on a shared branch destroys others' work. On your own PR branch, prefer --force-with-lease.

  • Misconception : If Git shows no conflict, the merge is correct.

    In fact : Git compares lines, not the program's meaning: a rename on one side and a new call on the other merge without conflict and break the code. Run the tests.

  • Misconception : A refused pull request means my work is bad.

    In fact : Maintainers preserve the project's coherence. Read the reason, ask if needed, and fix or propose something else.

Glossary

Commit
A snapshot of a project's files with author, date, message and link to the parent commit(s), identified by a hash.
Branch
A movable pointer to a commit, which advances with each new commit made from that branch.
Rebase
An operation that replays commits on top of another starting point; they receive new hashes.
Fork
A personal copy of a hosted repository that you can write to, in order to then propose changes to the original.
Pull request
A request to integrate a branch into a repository, with discussion, review and automatic checks before merging.
Merge conflict
A situation where two branches changed the same lines and Git asks a human to choose the result.