Skip to content
John Mertz edited this page Oct 11, 2024 · 2 revisions

Clients

Because most MailCleaner development will be done while logged on to a MailCleaner VM in a development environment, it is assumed that you'll be using the git command. If you use a graphical client or an IDE, you may need to look up instructions for some actions using some of the keywords found here.

Terminology

The terminology and commands mentioned here are correct for the basic git commandline tool and GitHub. Other clients or repository hosting providers can sometimes use different terminology, handle things slightly differently, or provide simplified functions which could reduce multiple commands into one.

'origin' - this refers to the location that all changes are pushed and pulled from, without any special instructions. Unless development is being done completely locally, it refers to the GitHub repository that you are committing your changes to directly. This should be your Fork on GitHub, since you will not have permission to push to the official repository.

'upstream' - this refers to the canonical/authoritative repository where changes will eventually be merged and from which you'll want to pull in changes that other contributors have provided. This should be the official upstream repository.

'remote' - a remote repository is any other than the one on your local system. Unless development is being done completely locally, it can refer to your GitHub fork, the main GitHub repository, or any other version of the repository which is hosted elsewhere (eg. another contributor's repository).

'branch' - a branch is a specific version or working state of the Git tree which is tracked to allow for independent development without influencing other branches. A branch will inherit all of history of the branch that it was based upon, but will then diverge with each subsequent change on it or the parent branch.

'master' - is the primary branch which all other branches should diverge from and then try to have their changes merged into.

'pull request' - this is a GitHub-specific term, often called a 'merge request' on other platforms. This is an action, usually performed via the GitHub website or an official GitHub client where you request that changes that you have made to a one branch be pulled/merged into another. Most often, this is requesting that work you have done on a branch in your local repository be absorbed into the upstream/master branch so that it becomes an official part of the product.

'rebase' - the process of taking all of the changes that have been applied since a branch diverged and layering them on top of other changes that have since been committed to the branch you are rebasing on. This is generally done when there is progress on both the parent branch and the working branch. You MUST rebase your working branch on the whatever branch you want to merge it into before opening a pull request. If there are conflicts between your branch and the upstream branch, they will have to be resolved before a pull request is opened.

'commit' - a set of changes which are logged with a description and which are capable of being pulled or pushed to/from another branch. These will be logged in the Git history. It is preferred that you make many small commits where each represents and can be (and is) described as one or two discrete changes. A complex new feature should generally be composed of several small commits which get merged at once.

Forking

To contribute to our repository, you must create your own fork:

  1. On GitHub: navigate to the main repository, then click Fork and accept.

  2. a) When working on a non-MailCleaner machine (eg. your personal computer):

  • you should first clone your repository: git clone git@github.com:MyUsername/MailCleaner.git

  • change to the repository directory: cd MailCleaner

  • then add our remote with the name 'upstream': git remote add upstream https://github.com/MailCleaner/MailCleaner.git

    b) When working on a MailCleaner machine:

  • the repository already exists, change to it: cd /usr/mailcleaner

  • rename the current 'origin' to 'upstream': git remote rename origin upstream

  • add your fork as the new 'origin': git remote add origin https://github.com/MyUsername/MailCleaner.git

Given the steps above, your origin will be your fork, which you will actually be able to have write access to. Since the default remote that git push/pull/fetch commands will use is origin, this will simplify several of the command listed in this document.

You .git/config file should look like:

[core]
	repositoryformatversion = 0
	filemode = true
	bare = false
	logallrefupdates = true
[remote "upstream"]
	url = https://github.com/MailCleaner/MailCleaner.git
	fetch = +refs/heads/*:refs/remotes/upstream/*
[remote "origin"]
	url = https://github.com/MyUsername/MailCleaner.git
	fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
	remote = origin
	merge = refs/heads/main

Opening pull requests

When you open GitHub to either your fork or the main project after pushing recent changes, you will see a banner asking if you would like to contribute those changes, like:

MyUsername:branch-name had recent pushes 1 minute ago. **Compare & pull request**

Clicking that will guide you through opening a Pull Request.

MailCleaner requires review and merging by an approved maintainer, so you will need to sit and wait. If the pull request is related to an open issue, please mention the pull request number in that issue.

Rebasing

Once a pull request, or any other contribution has been merged, your own local branches will all be behind the upstream master branch. Before pushing new commits, it is essential that you 'rebase' your branches on the current upstream 'master' branch to ensure that there are no conflicts. From GitHub you will see something like:

This branch is 1 commit behind MailCleaner:master

Clicking 'Fetch upstream' will automatically resolve this on that remote end, then you will need to git pull the changes locally.

Alternatively, you can 'rebase' the branch locally and then push that along with any other commits:

git pull --rebase upstream master
git push

Best practices

Use SSH key authentication

Especially if you have have 2FA set up in GitHub (recommended), you should use SSH key authentication, when possible. Ideally you should use a password-protected private key on the off chance that you accidentally commit it to the repository.

You can generate a SSH key with:

ssh-keygen

and follow the prompts. Then, in GitHub, click on your avatar, then click SSH and GPG keys, New SSH Key and paste the content of the new public key (usually ~/.ssh/id_{rsa,ed25519}.pub).

If you did give the key a password, you can activate it in a SSH agent with:

eval `ssh-agent`
ssh-add

then enter the password. Communication can then be done with GitHub via SSH by changing the .git/config so that the URLs look like:

	url = git@github.com:MyUsername/MailCleaner.git

GPG sign your commits

If you don't already have a GPG key, you can generate one with:

gpg --gen-key

and follow the prompts. After it is done, the last line will look like:

sub   4096R/B0832523 2024-01-01

Take note of the string after the / (B0832523 in this example). Then, in GitHub, click on your avatar, then click SSH and GPG keys, New GPG Key and paste the content of the new public key (usually ~/.ssh/id_{rsa,ed25519}.pub).

To activate the GPG agent in order to be able to sign messages, you can run:

eval $(keychain --eval --agents gpg B0832523)

where the last argument is the string you noted earlier. This will prompt for the password. You can then require signing by adding this to the .git/config:

[commit]
        gpgSign = true

When you perform a git commit it will then attempt to sign the commit with your key to verify it's origin. GitHub will compare this with the key on file and add a verification icon next to your commits.

Push early and push often

While working locally, even if your changes are experimental and not ready for production, it is still best to push it to your remote branch with each incremental commit. If your local branch doesn't exist on the remote yet, you can create it with the first push, like:

git push --set-upstream origin/my_branch

This will let others see your progress so far and will ensure that your work is not lost if you accidentally delete your development environment. Early commits will not be merged until you request that they be, so you can continue to add small commits to your experimental branch on your remote repository until you are ready.

Always rebase

Unless you are certain that there have been no changes to the relevant upstream remote repository, you should always start each coding session by rebasing your current branch on the remote branch that it is based on as described above.

Having mixed commits between each can be difficult to resolve and we will probably ask you to rebase and push before we accept any pull request.

Always work from a branch

Although it is possible for you to work from master on your fork and then make pull requests from there, it is not recommend that you do this. If you decide to make any other unrelated commits, that would then need to be done on a branch that is based prior to any of your unmerged commits.

When a pull request is open, the request is to merge whatever commits exist on that branch at the time that it is approved. So, if you open a pull request from your master branch to our master branch, you will not be able to merge in additional unrelated commits. We will not accept unrelated commits in the same pull request since it will make for a confusing Issues history.

The first steps when you want to start working on a new feature or issue should always be:

git checkout master
git pull --rebase upstream master
git push
git checkout -b FeatureXYZ

That is, make sure that you are on the 'master' branch, rebase on our master branch to make sure you are up to date, push those changes to your remote to make sure it is up-to-date (identical to our 'master'), then check out a branch with a short, meaningful name.

Use meaningful branch names

It is best to pick short, meaningful names for your branches. This is not a requirement, but it will make your life easier. It will prevent you from having a collision with a branch that already exists and will help you return to working on it if you switch away for any period of time.

In the event that your branch name is not unique enough, and it turns out that one already exists, you will need to specify a different upstream branch name, like:

git checkout dublicate-name
git push --set-upstream origin MyUsername:unique-name

These relationships become intuitive.

Administrator Shortcuts

User Shortcuts

Developer Shortcuts

Expand ▶ Pages above to view the Table of Contents for the article you are already reading, or to browse additional topics. You can also search for keywords in the Wiki.

Clone this wiki locally