4 Git Quickstart
tengel edited this page 2024-03-20 11:55:04 -05:00

Overview

Quick and dirty git contributing to a random project

  • origin is the accepted name for you / your forks
  • upstream is the accepted name for master projects
  • "pull request" (PR) and "merge request" (MR) are two terms for the same technical thing in the webUIs:
    • Github/Gitea use PR
    • GitLab uses MR

"Master" and "Main" are two words which refer to the same thing, logically. Whether your upstream repo uses "Master" or "Main" just depends on that repo's details.

Big picture of what's being done:

  1. Copy ("fork") the project ("upstream") to your private space ("origin") where you have edit capability
  2. Make a feature branch from this copy ("origin") to make your contributions
  3. Request your changes be merged to the project ("upstream") from your fork
  4. Re-sync your origin fork of (1) after (3) is accepted
  5. Remove your origin branch (2) now that it's integrated to upstream (1)

How the upstram projects maintains it's working code varies project by project; some maintainers just use the Master/Main branch only, some use a secondary branch typically named "devel" - there are two conceptual ways to do source control, each project has it's own way of doing things. Look for a document CONTRIBUTING.md in the repo for instructions if it exists.

The below examples use the source control method where Master/Main is never edited directly; a secondary branch named "devel" exists upstream which we branch from and PR/MR against; upstream maintainers then merge devel -> master on their timeframe to make official releases. Not everyone does it this way, learn how the upstream project wants you to do it and follow their instructions for details.

In many cases, git assumes you mean origin when it's not directly specified on the commandline; for example git checkout devel is shorthand for git checkout origin/devel; this is used below to reduce typing fatigue, however you can always use the name of the remote in every command if you like and it helps learning. You must always name the remote if it is not origin, such as git fetch upstream/devel for example to indicate you want that specific upstream remote code, not origin.

Process

Fork the upstream project to your personal space on the webUI to create origin; this is done via clickable links in Github, Gitea, GitLab, etc. There is typically a button in the menubar of the webUI, it varies by software but the idea is the same for all of them - copy their stuff to your space so you can edit.

Pull down (clone) your working copy from your fork (origin), then connect it to the upstream project - these are known as "remotes" in git terminology:

git clone git@<GIT URL>:<ORIGIN>/<project>.git
cd <project dir>
git remote add upstream git@<GIT URL>:<UPSTREAM>/<project>.git

Configure your working clone of origin, name/email are required for commits in git:

git config user.name <my name>
git config user.email <my email>

Switch to the project working branch where you base changes off of:

  • you connect it to your working copy of the branch, not upstream
  • track it for changes, will be used when re-syncing your fork later
git checkout --track origin/devel

Now make your new branch to edit code in prep for submitting back upstream:

  • defaults to branching from current point, origin/devel in this example
  • different source point can be named, just add to end of the command

If you were to imagine what the below does visually, it sort of looks like this in tree form (hence "branch"):

  • origin/master
    • origin/devel
      • origin/mynewbranch
git checkout -b <my new branch>

Edit all the things you need to edit - that might be adding new files, changing existing ones, whatever the case may be. The rule of thumb is to try and keep all changes grouped in logical commits; if you need to do 2 things which are not really related, commit the work as 2 unique commits for example with nice comments. There is a bit of an art to this you will learn over time.

Commit your local edits to your local working copy:

git add file/name.py CHANGELOG.md ...
git commit -m "my fix" file/name.py CHANGELOG.md ...

Push your new working branch to your fork:

  • -u tracks it, in case you have to make more fixes
git push origin <my branch> -u

Use the website to create a PR/MR and wait for feedback etc. This varies by software on the exact details, but there is usually a clickable button on your fork which says "Prepare Pull Request" or "Create Merge Request", where you then pick "from" and "to" to the maintainers:

  • "from" is your origin/mynewbranch
  • "to" is upstream's branch that you started from (upstream/devel e.g.)

Wait for feedback on changes needed to your work, or for your changes to be accepted and merged. Let's assume you receive feedback to fix a typo or whatever.

Make fixes and re-push them, this is why we tracked it above:

git commit -m "typo fix" file/name.py

SOME projects do not like to see little typo fixes, and request that you "squash" the fixups back into one single commit; this is up to the upstream maintainers how they want to run their project, some people like to see every typo fix as a unique commit - it just depends.

IF REQUIRED, squash your fixups back into one single commit and "force-push" it back to your origin; the webUI magically updates itself when the last step is done, so you can check your work by reloading the PR/MR web page:

# get list of commits and find the commit ID BEFORE your above work
git log

# "rebase" your work on that commit ID from the log
git rebase -i <HASH ID>

# an editor window (vim, etc.) appears:
  # change 'pick' on first line to 'reword'
  # change every following line to 'fixup'
  # save-quit, update new commit message

# check your work locally first, should be "clean" now:
git log

# push it back to the webUI in forced mode
git push origin <my branch> -f

Your work is now accepted/merged by upstream. Your last two tasks - assuming you want to keep contributing to this project - are to:

  1. Re-synchronize your origin copy with the upstream merges, then
  2. Delete your feature branch from origin and push the delete back to your private space

This part can be confusing, think of your workstation as a proxy - you pull the changes from upstream to your workstation, merge them locally, then push them to origin to update your copy in the webUI.

Switch to the working branch the project uses that you branched off of:

git checkout devel

Get the latest upstream code (stashes it locally but does not apply yet):

git fetch upstream

Apply the stashed updates to your working copy:

git merge upstream/devel

Push your working copy to your fork, it's now in sync again:

git push origin devel

Delete your branch from working copy, push delete to your fork

  • notice the colon in front of the name of the branch, this is a git-ism for "delete" or "deleted file":

This process step should be "clean" - git is smart, it recognizes from the above steps that the code you worked on has been merged by upstream. If that did not happen, then when you attempt to run the first command below it might issue a warning or error "there are commits still on this branch not merged" (sic - the message varies) to try and help you.

Sometimes you just perform these two steps to throw away a bad branch, the warnings/errors do not always mean it's not a correct action for your needs. We will assume that the changes were accepted and merged here and no warnings/errors:

git branch -d <my branch>
git push origin :<my branch>

This last step is a bonus - sometimes upstream adds new branches after you've forked; if you want to grab those new branches from upstream and add them to your origin, then:

git fetch upstream
git checkout -b <new branch name> upstream/<new branch name>
git push origin <new branch name> -u

This is not generally necessary unless you want that new branch for some special reason.