After having had almost the same discussion twice about how to work with git in creating pull requests, I guess it's time to write it all down so I can refer people to this.
In short
This is a "how to" on how to submit changes to the code of other people. For instance for fixing issues in Flarum or in any extension, provided these are hosted on Github.
Requirements
As each of you use different setups I won't go into detail about the things you need beforehand:
- A git client, the github mac and windows applications can handle most of the below automatically. This how-to does not apply to them.
- A github.com account.
- Patience.
Disclaimer
- If you're not sure how to start after reading this how-to, please get advice over at gitter.
- Be careful what directories on your machine you use for these git actions, the best thing to do is create a completely new one up front.
- If at any point something goes wrong, erase your directory and fork. Start over.
Terms
- Version Control Software/vcs: this is an application that tracks changes. Git is a vcs and stores changes you've done on your machine and once you submit them to the repository, these changes are tracked there as well.
- Repository/repo: this is the collection of files that make a package (either an extension or for instance the Flarum core). A repo holds the files but also the changes done to those files over time.
- Remote: the remote is the central server that stores all changes and files. Any client in the world (if they have access) can send in changes to this remote repository.
- Cloning: when cloning a repository you are actually loading an exact copy from the remote onto your local machine. Cloning is used as a first step to create a local folder to work with.
- Committing: is saving the changes you've made into the clone - the local repository. While committing you can add messages, select specific files per commit and refer to issues.
- Pulling: with pulling you load all changes from the remote towards your local machine, every change will be rerun over your local files. For instance if line 8 of file x changed in the remote to have a enter/newline since the last time you pulled, this change will be applied to file x locally as well. In access terms this can be seen as reading.
- Pushing: means you are sending your changes to the repository. These changes are then saved to be used with/from other clients. In access terms this can be seen as writing.
- Fork: a fork is a copy of the original repository, while maintaining the relation of parent-child between them. So if you clone the flarum/core repository, github will understand your fork is a child. Forks are used to create pull requests and can be created under any namespace even your own username.
- Branching: the default branch is called "master". A branch is like a set of changes that move away from the master to allow for radical or separated changes. It allows you to work on different tasks, each branch can be loaded with the changes applied in that branch.
How to make a fork
The first step in contributing is to create a fork. Visit the github page of the repository you'd like to contribute to, for instance flarum/core and click on fork in the top right of the screen:
- if you are member of any github group you are provided with a popup to choose under which namespace/group you want to fork, let's click your own username for now
- if you are not a member of any github group your fork will be created under your own username
Let's go for my username "luceos" as the example from now on. I've forked flarum/core, flarum is the namespace (group or username) of the repository. The fork is now created under your own username with a replaced namespace, so flarum became luceos in my example. The forked repository therefor now lives under luceos/core.
Access rights
Because the fork is under your namespace you have full access to it (read and write). The original repository is under flarum/core, which you - as you can understand - have only read access to (hello open source). Pull requests are a "necessary" evil because it allows people to contribute without requiring write access to the original repository. Would you simply try to write (aka push) to the original repository without being a developer of that team, you would receive a "not allowed" error from github (/ the remote).
Onto the fun part; cloning
So you have git client and you have decided which folder/directory on your local machine you will "infest" with files. Well done.
From now on everything explained with the git client is with a ssh/terminal ssh client. If you use a windows client, you need to check documentation if something is hard to find.
You will now first clone your fork. To do so run:
git clone git@github.com:luceos/core.git .
Where the last .
implies the directory to clone the files into. You can also choose to target a directory specifically, for instance:
git clone git@github.com:luceos/core.git /home/luceos/repos/flarum-core
From now on we assume the cloned repository lives in the above provided example /home/luceos/repos/flarum-core
Staying up to date; upstream
Move to the directory you cloned into. Let's check the remote's configured:
git remote -v
In our example it will show:
origin git@github.com:luceos/core.git (fetch)
origin git@github.com:luceos/core.git (push)
This output might be different if you use a git UI client, it will show both an "origin" and a "flarum".
As you can see we can fetch/pull and push to the remote called "origin". This is the default remote. So any command you run without specifying the remote name in your command, will use origin.
Unless your git client already added the original remote repository (probably called flarum), we will now add the upstream. The conventions imply using that name for the original repository.
git remote add --mirror=fetch upstream git@github.com:flarum/core.git
You have now added another remote with which you keep your local repository up-to-date.
Let's try this out:
git pull upstream master
You've now pulled all the latest changes from the original remote repository. Do this regularly and always do this before starting making changes!
These changes from upstream are now only available locally. So let's update our fork too:
git push origin master
Making changes; branching & committing
At this point you can start doing changes, you first however need to think what you are going to change and create a branch for it that tells the fork/repository that you will move away from the master.
Let's assume I'd like to change the meta description field in a template:
git checkout -b meta_description
For the branch name I specifically choose something that clearly explains to me what I was about to do.
At this point I would open my favorite editor and make the changes needed. At every moment I can see what branch I am in and what changes are open using:
git status
Which shows the branch and the state of modified files (m = modified, d = deleted) and also files that are not yet known to our repository.
It's very wise to commit your changes at regular intervals. We will commit these changes by adding the files to commit and adding a message:
git commit -am 'modified the meta description in the forum'
The -am
will commit all open changes -a
(ignores unknown/unadded files) and allows to add a commit message -m
.
If you only want to commit specific files, you would use:
git add core/views/app.blade.php
Running git status
again will show that you have files ready to commit, let's do so manually:
git commit -m 'modified the meta description in the forum'
You can also select the changes from within files to select, use git add -p
. See the help for more information on such commands git help
.
If at any time you want to start on a different task, make sure you stored your changes either by committing it or by resetting the changes after moving back to the master branch:
git checkout master
If you want to reset all changes:
git reset --hard HEAD
Don't forget to pull upstream regularly and especially before starting a new task.
Submitting your commits; pushing & pull request
Your commits are now stored on your local machine, which is no help at all so far, except for you.
We will now submit these to your fork:
git push origin meta_description
In this example, "origin" is the remote name for our fork repository on github and "meta_description" is the name of our current branch where we made the necessary changes we wanted.
Git will now tell you a new branch has been created on the remote, which is very good.
Visit your github fork page and you will see a message "create pull request" mentioning your branch name "meta_description", follow that procedure it's not hard at all.
Once your pull request has been merged with the original repository you will see an option in the pull request page to delete the branch. Please do so. Your local branch will not be deleted.
If you want to continue development, switch back to the master branch and reset/stash (git help stash
) any changes before pulling upstream changes and pushing them to your fork to keep it up-to-date:
git checkout master
git pull upstream master
git push origin master
Some notes
- While your pull request is open, any changes to push to the branch mentioned in the pull request will be included.
- If you encounter "merge conflicts" you will need to open the mentioned file and search for lines with
>>>
that start from the left side of the code, you will need to fix the differences between version in that file and commit & push it again.
- If you encounter a "please stash changes" this means you have files that have changes that have not yet been committed, either commit them or reset them using
git checkout -- <file>
Thanks for taking the time to read this Chinese wall of text. Good luck and fire away with any questions you have.