-
Notifications
You must be signed in to change notification settings - Fork 48
The Basics of GitHub
Before you start, you'll need to do a few things if you haven't already:
- Make a GitHub account
-
Download GitHub Desktop and sign in to your account
- You don't need GitHub Desktop and can use terminal commands instead, but if you're in a position where you're reading this tutorial, I cannot recommend it enough and will assume you'll be using it.
If you'd like to exclusively use the command line, or just want an alternative tutorial, I'd recommend Lunos's guide.
This is the section for if you're starting a new project totally from scratch. If you've decided to use git after already getting a project set up and potentially having already made changes to it, start with Using An Existing Project instead.
Note also that once you're done making a fork, you'll have to go through the steps in the INSTALL.md
of whatever repository you forked to finish getting it set up and ready to compile.
Now that you have a GitHub account, navigate to the repository you would like to base your work on. If you're on this tutorial page, that's likely either pokeemerald or pokeemerald-expansion. Once there, click the "Fork" button in the top right. Fill in a repo name and description, and then click "Create Fork". This example image is making one for Pokefirered as I already have a pokeemerald-expansion fork and can't make a new one it, but the steps are the same. Once the loading bar is finished, you'll have a fork!
A fork is your personal copy of this repository. You're in control of it and can change whatever you want about it without affecting the repository you forked. A fork is a new repository that shares code (and also visibility settings, though this likely doesn't matter to you) with the original repository, known as the "upstream" repository. Forking a repository means that your fork stays connected with the upstream repository; think of it like you're establishing a new town (your fork) down the river from a major city (the upstream repo). Much like the original repo is referred to as the "upstream" repo, your repo is referred to as "downstream" from the original repo. You may see contributors to pokeemerald-expansion or any of these other repos talking about how a change "will affect downstream users" or something similar, and that means they're thinking about you!
Much like the town and the city being connected by a river, your fork and the upstream repo are also connected. You'll get a message at the top of your repo that keeps you updated with how many commits ahead of the base repo you are (these are changes you're adding that aren't in the base repo), and also how many commits behind you are (these are changes to the base repo that you haven't added yet). Having a "commits behind" message generally means it's time to update your repo with the upstream changes if you'd like to stay up to date. See Updating Your Fork for how to do that.
The commit message will look something like this:
The main benefits of forking for our purposes are that it's very easy to update with new contributions the repository receives, and it's very easy to submit your own contributions to the upstream repository. Updating will be a very manual process otherwise, and contributing to the upstream repository will be impossible if you decide to later on.
Now that you've got your own copy of the repository hosted on your GitHub, you'll want a copy on your machine so you can make changes to it and build your game. A very important concept that most new GitHub users struggle with is that your local copy is just a copy, and the remote copy hosted on GitHub is the master copy. It doesn't matter if you delete your entire file structure on your local machine, your project is still safe on GitHub. You can get your repository set up on 7 different computers, and the master copy is still safe on GitHub. Your local copy is just a means of accessing your repo to do things with it. The most common commands that let you interact with the remote copy of your repo will be covered in Making Changes To Your Repo: Commit, Push, and Pull, though some others are listed in Overview of Useful Git Commands.
To get a copy of your remote repo hosted on GitHub onto your machine, called "cloning", open up GitHub Desktop and click on the "Add" button underneath Current Repository at the top right. Choose "Clone an existing repository," then choose the fork you just made from the options menu. Specify a local path to whatever folder you'd like to build the repository in, and then click "Clone." Once it's finished, you'll have a local copy of the fork you just made in that location.
The three most basic and important ways that the local and remote copies of your repo interact with one another are through these commands.
Making a "commit" essentially marks a checkpoint in your work; any changes you've made since the last commit (or from the upstream repository if this is your first commit) will be grouped together as a "commit". Git is primarily about version management, and commits are the foundation on which that versioning is built.
Say you make a change to your repo every day for six months, and you find a bug on month 6 that's occurring somewhere you don't think you've touched for two months. How do you find the cause? With git, it's as simple as checking through your history of commits to figure out which commit introduced the bug, and then fixing whatever the issue turns out to be there. It doesn't necessarily make fixing the bugs easier, but it makes figuring out what introduced them MUCH easier. This uses a different command called git checkout
that I'll cover in Overview of Useful Git Commands.
For now, the key takeaway is that making commits is super important. Make them more often than you think you need to when starting out. A good rule of thumb is that every complete feature change should get a commit, as should anything that you've confirmed is working. Add a new overworld sprite successfully? New commit. New Pokemon? New commit. New item? New AI behaviour? New feature? All commits.
I've made new commits just to update comments before. Having too many is not really an issue. Having too few certainly can be.
To make a new commit, open up GitHub Desktop. I generally just leave GitHub Desktop open all the time while I'm working on my project. If you've made any changes since the last commit, they'll be displayed for you. Here's an example.
The left panel shows all changed files, the right panel shows all changes in the currently selected file, and the bottom left has boxes where you enter the summary (what label the commit will have on GitHub) and description (any extra notes you might need, though entirely optional).
If you want to discard all of your changes, you can right click on the "X changed files" message and then "Discard all changes". If you want to discard your changes to a particular file, you can right click that file in the changed files list and only discard its changes instead.
If you're happy with your commit, then click the blue "Commit to " button in the bottom left. You'll only have one branch by default, which will be the name listed. Using multiple branches will be covered in the Branches section.
If you decide you actually don't want your commit to get pushed to your remote repo after you've already made it (this happens most commonly after finishing resolving merge conflicts and finding you still broke your codebase), no need to worry! You can go into the History tab, right click the offending commit, and select "Undo commit".
You can only do this before pushing, as after pushing you'll have to either revert the commit (make a new commit undoing the prior commit) or reset to a particular commit (obliterate any history past a certain point you'd like to restart from), which will be covered in the Overview of Useful Git Commands section.
In short, pushing is how you move new commits from the local copy of your repository to the remote copy of your repository. Generally, your workflow with git will involve making a commit for the new changes you've done once you're happy with them, and then immediately pushing them to the remote repository hosted on GitHub. Remember, the remote repo hosted on GitHub is the master copy, your local one is just a clone. If you don't move commits from your local copy to the remote copy, your project won't have the new changes you've made.
If at any time you have local commits that have yet to be pushed to your remote repository, such as after making a new commit or performing a merge, GitHub Desktop will prompt you to Push those channels with a blue button on the right panel. If you've got more than one commit waiting to be pushed, GItHub Desktop will also tell you how many commits you'll be pushing. While working on your own this will likely be one at a time in most cases, though after merging feature branches or upstream updates you can push several at a time. Nothing to worry about.
While pushing moves new commits from your local copy to your remote copy, pulling moves commits from the remote copy to your local copy. The "Fetch origin" button in the top right of GitHub Desktop will change to prompt you if there are new changes in the remote repo that aren't in your local copy that you might want to grab, and clicking that button will pull them for you. This most often happens in cases where you're working on multiple branches, or where you're working on multiple machines.
Let's say you have two computers, 1 and 2, and a new commit. You make the commit on computer 1 while you're at work, push it to the remote, and shut off the computer to go home. Once at home, you fire up computer 2 and want to continue working on the project. You'll first have to pull that new commit you made at work from the remote repo to your local repo, using the button in the top right. Nothing too scary.
Merging is also technically pulling, though I'll cover merging in dedicated sections for Updating Your Fork, Merging Feature Branches, and Merge Conflicts.
Once you're finished this section, you can go back and read What's A Fork and Making Changes To Your Repo: Commit, Push, and Pull for details on how to use GitHub, and then continue reading from here. This section is only to cover the steps that are different between creating a new fork from an existing repo, and adding your already created project that is not a GitHub repo to GitHub.
If you followed the instructions in whatever decomp you using's INSTALL.md
file, which you should have if you've gotten this far, then in doing so you'll have been instructed to git clone
the repository onto your machine, even if you didn't realize it at the time. This is great because you've effectively already got a copy of the repository on your computer ready to go. You just need to tell GitHub that you want to treat that as its own repository that you own, as a fork of the decomp repository. This is much simpler than it might sound.
Open up GitHub Desktop and click on the Current repository dropdown on the top left, which might be blank if you haven't made any repositories yet. Then click the Add dropdown, and select "Add existing repository."
Then you'll need to provide it the filepath to your folder. The easiest way to do this is just click Choose, navigate to where the project folder you'd like to add as a repository is, select it, and click "Select Folder". I'll be adding a base pokeemerald repository in this example, as I've already got pokeemerald-expansion setup.
Once you've provided the filepath, click "Add repository". GitHub Desktop will likely need to think for a while while it adds everything, but because this folder is already a cloned version of the original repository, it'll sort it out without issue. Once it's finished you'll get a list of all the file changes, which will be all of them as this is the first time GitHub is seeing your copy of this repository, and a prompt at the bottom left that says you don't have access and suggests you make a fork. This is good! We want a fork. Go read What's A Fork if you don't believe me.
Click "Create a fork", and in the following dialogue select "Fork this repository". Then select "For my own purposes" in the "How are you planning to use this fork" dialogue, and click continue. That's it, you're done! You'll then need to commit all of the changes you've made before using Git all at once. You can read how to make a commit in the Making Changes To Your Repo: Commit, Push, and Pull section.
As mentioned earlier, updating your fork with upstream changes is a lot more automatic with git than without. You'll still run into Merge Conflicts at some point, so feel free to read that section too.
If GitHub has detected that your fork is out of date and shows you as being some number of commits behind, that means you might want to update your fork. If GitHub knows you can do this update without any conflicts, it's got ways to automate it for you. You can click on the "Sync fork" button in the top right of your repo's main page on GitHub itself to update your fork from there with anything that's changed with the main repository.
Alternatively, or if the "Sync fork" option is greyed out as there will be merge conflicts, you can run git merge upstream/main
from your terminal. Updating from the terminal will be by far the most common way of doing so. If there are merge conflicts, git will helpfully print out where they occur in the terminal for you to deal with. See the Merge Conflicts section for details.
When you find a feature branch either on our Feature Branch Wiki or on pret's Tutorials and Simple Modifications Wiki or somewhere else altogether that you'd like to use, you've got two general approaches.
The first is copying over all of the changes listed in that feature branch's commit history manually into the appropriate files, and committing them to your repo yourself. This is useful for very small branches or potentially branches that have a lot of conflicts as you'll be manually resolving them by pasting in the code you want to keep and deleting whatever sections you don't want, or branches that you want to make sure you understand what the code is doing or where it's going better. If you do this method, make a commit once you're finished copying everything over and you've tested that it worked, and then push as you would normally.
The second is by using the pull command to have git merge it into your repository for you, and then resolving and potential conflicts that arise. This section will briefly go through how to do this, as the former approach is self explanatory.
First, you need to add a connection between your repo and the repo that hosts the feature branch you'd like to use, so git knows where to get everything. You do this using git remote add <SomeLabel> <SomeURL>
If you want to pull my start menu clock branch for example, you'd run git remote add Pawkkie https://github.com/Pawkkie/pokeemerald-expansion
. I should note that <SomeLabel>
can be whatever you'd like, it's just a label you can use to refer to that specific repo. Generally making labels match the author's name is a good idea, as authors frequently have several branches in the same repo and it saves you running this command every time or forgetting what label you've attached to a specific person.
Second, you need to pull the particular branch you want from that repository, using git pull <SomeLabel> <BranchName>
. In the same example of my start menu clock branch, this would look like git pull Pawkkie start-menu-clock
. The branch names are listed on the author's repo page in a dropdown list just below the repo title for you to reference.
If you want to add an additional feature branch from the same author, you don't need to run the first command again as you've already established a connection between your repo and theirs. For example if you also wanted my ability changer branch after merging the start menu clock, you could just run git pull Pawkkie ability-changer
without having to do the git remote add
again.
Note that once you've pulled the branch this way, you may have Merge Conflicts to solve. Once you've solved them (if there were any), you'll then need to push your changes to your remote repo as you would normally. GitHub Desktop will prompt you to accept the merge if there are no conflicts and warn you if there are, and once you accept them and make the commit you'll be good to go.
Note also that if you want to remove the connection for whatever reason, you can run git remote rm <SomeUrl>
or git remote rm <SomeLabel>
, both work.
Merge conflicts are what happens when git can't automatically figure out how to put two codebases together. They're exceedingly common, and if you're pulling new feature branches or updating your repo with upstream changes you'll likely run into them at some point. There's no one-size-fits-all solution to resolving merge conflicts, and questions around resolving them are some of the most common fielded in any of the romhacking Discord servers.
This section will not cover actually solving the conflicts individually as they could be anything, but it will cover how to set yourself up to solve them yourself in brief.
Once you attempt to pull or merge something that generates conflicts, your terminal will tell you that they happened and what files they happened in. This is useful to give you a quick heads up for how extensive the conflicts might be, or at least how wide ranging. Here's a section of an especially bad case just for demonstration.
Each of the CONFLICT lines indicate a conflict in a particular file.
There's no need to panic in cases like this, all your work is safe. Your master copy is on the remote, remember? Nothing will happen to it until you push something, and you can't push anything if there are conflicts. The worst thing that happens is you have to cancel the merge, or you think you resolve the merge, commit it, build the game to test and learn that it actually broke something, and have to get rid of the commit before pushing it. Even if you do merge in dozens of bad changes and commit them by accident that are commands to bail you out, though this is less common. Check [Overview of Useful Git Commands] for details.
We'll look at how you can check what these conflicts are in a second, but first check out GitHub Desktop. It may take a second to load all the changes, and then it'll give you some popups.
Much like what the terminal said, this is a list of files that have conflicts in them. Assuming you're using VSCode, you can open it up and click on the Version Control tab to go through the details. Note that if you decide to bail at this point, you can just click the "Abort merge" button in GitHub Desktop, and it'll get rid of everything for you.
You can see a list of all the files that have conflicts, and then a list of all the files that have changes as a result of the merge but don't have conflicts. This screenshot is just a section of the list of conflicts. If you click on one of the listed files VSCode will take you there, and you can use the arrows in the top right to move up and down between individual conflicts. You can also click the "Resolve in Merge Editor" button, which gives you a pane on the left of the incoming changes, a pane on the right of your current codebase, and a pane at the bottom showing your merged version, which is super useful.
You'll then need to do the actual merge conflict resolution, which will vary enormously depending on what you're doing. Once you're done, you can tab back over to GitHub Desktop, which will tell you that all your conflicts have been resolved and you're ready to merge.
Before doing so, I'd recommend recompiling the game, as just because your merge conflicts are resolved doesn't mean the game will compile correctly. If it doesn't, fix the compiler's errors first, then go back to GitHub Desktop. It'll prompt you to accept the merge, which you do; then it'll list all of the changes you made to fix the compiler, which you'll have to commit (I usually use a summary message like "Fixing compiler errors for recent merge" or something) as well. Then push everything, and you're good to!
From GitHub's documentation, "Branches allow you to develop features, fix bugs, or safely experiment with new ideas in a contained area of your repository. You always create a branch from an existing branch. Typically, you might create a new branch from the default branch of your repository."
Branches are especially useful if you want to work on something that will likely take several commits to put together completely without messing up your main project before it's finished. You can make a new branch, do the interim commits and development there, and then merge it into your main branch when you're finished.
Branches are also the way you can use Pull Requests to contribute to the upstream repository you're working off of if you like. You make a new branch in your repository that's based off of the appropriate branch of the upstream repository, add in your new feature, and then submit a pull request to that upstream repository using the new branch with the feature you're adding isolated.
If this sounds a bit advanced or over your head, don't worry about it. You never need to use branches, and if contributing is something you're interested in and you can't figure out how to set up a pull request properly, just ask in the appropriate community you're looking to contribute to.
This section will very briefly cover how you can make a branch and how you can merge changes from one branch into another.
The middle dropdown at the top of GitHub Desktop is where you can switch between branches or make new ones. Your "currently checked out" branch is the branch that your local copy is currently using; if you switch between branches, your local repository will overwrite and change its contents to match the new branch.
Remember, your local copy is not the master copy, the remote on GitHub is. Flipping between branches is totally safe, you can always go back to your main branch.
Whenever you make a new branch, the popup will indicate that you need to name it and that it'll be based on whatever your currently checked out branch is. If you want to make a branch to develop a feature, you should base it off of your project's main branch.
If you want to make a branch to contribute to the upstream repository in a pull request, you'll want to base it on the appropriate branch of that repository; for example, pull requests for pokeemerald-expansion only go to the master branch if they're bugfixes, otherwise they go to upcoming. Make sure your new branch is based on the appropriate one.
Merging across branches is as simple as running git merge <BranchName>
, where BranchName is the branch you'd like to merge into your currently checked out branch. For example, if I had a separate branch called new-feature to work on a new feature that's now finished, I would switch back to my main branch, and then enter git merge new-feature
to merge the changes over. Then I'd push those changes, and I could delete the new-feature branch if I wanted to now that I'm done with it.
Note that for our purposes, if you're making changes to two branches in parallel, you want to be smart about merging like this. Say I have an isolated branch for my custom sprites called new-sprites, and my main release branch called release. I work on release almost all the time, and only do sprite changes in new-sprites, just to keep them isolated from all of my engine work.
In this case, you'd want to merge release into new-sprites every time you made changes to it, so new-sprites stays up to date. If you merge updates from upstream into release, for example, you don't need to resolve that merge again for new-sprites, you can just merge new-sprites with your release branch. Merging branches can go both ways.
This is not meant to be an exhaustive list of all git commands, nor is it meant to be holistic documentation of the ones presented. This is just meant to let you know that these commands exist, what they're used for, and provide an example of the command you can run. Reading further documentation if you're interested in more niche, detailed, or varied uses is strongly recommended.
Please read the [Making Changes To Your Repo: Commit, Push, and Pull] for details on those commands, as they're fundamental for using git at all.
Commit
git commit -a -m "<Commit Label>"
- Adds and commits a change
Push
git push
- Pushes commits from local repo to remote repo
Pull
git pull
- Pulls commits from remote repo to local repo
Checkout
git checkout <Branch>
- Lets you check out Branch in your local repository. You can switch back to your working branch afterwards.
- This is extremely useful when trying to identify which commit introduced a bug. If you
git checkout <Commit#>
, your local repo will load exactly the state the repo was in at the time you made that commit. You can then recompile the game and test if the bug still exists; if it does, you need to go back to an older commit and check again. Doing this iteratively will eventually identify exactly the commit that introduced the issue, so you have a much narrower scope of where to look to try to fix it.
Cherry Pick
git cherry-pick <Commit#>
- Lets you pull individual commits
- Has extensive alternate forms where you can specify a branch or a list of commits within a branch etc. Google around for more detailed documentation, it's out of scope for an introductory guide.
Revert
git revert <Commit#>
- Adds a new commit undoing all of the changes in <Commit#>
Reset
git reset --hard <Commit#>
- Be careful with this one
- Resets your project to the exact state it was at Commit#, and obliterates the history and existence of any commits in between. If you're working on a solo project, this is usually not a big concern, I've used this personally a few times especially starting out. This is a huge deal in collaborative projects, if you're working on the same repo as others and are all contributing and you're using this guide you need to do more research beyond this bullet point.
- Generally needs to be followed up with
git push -f origin <BranchName>
, which will generally also require you to enter your password
Error: The details of how and why this error works are beyond the scope of this guide, but it essentially requires you to change your merge settings.
Fix:
Run git config --global pull.rebase false
to turn rebase merging off. This is a pretty common error when you're merging for the first time ever, and only needs to be run once. Once you've run this command, run the pull or merge command you ran previously again.