Skip to content

Latest commit

 

History

History
345 lines (233 loc) · 22.2 KB

GitHub.md

File metadata and controls

345 lines (233 loc) · 22.2 KB

Introduction to GitHub

This is a quick introduction to Git and GitHub.

We strongly encourage you to look up guides as you need them for advanced features, as Git is very powerful but hard to take in all at once.

Set up Git, and create a repository

You will use Git throughout the course for version control, including exams and homeworks.

Git is a version control system for tracking changes in files and coordinating work on those files among multiple people. It is primarily used for source code management in software development, but it can be used to keep track of changes in any set of files.

First, you need an account on GitHub, the site that will host the code of your project. If you don't already have a GitHub account, here are instructions on how to create one. Using your real name in your GitHub profile will make debugging easier for the staff in case you run into issues with the class, but this is not required.

You then need to install Git if you do not already have it.

ℹ️ On macOS or Linux, Git may already be preinstalled on your system. You can check this by running git --version.

ℹ️ On Windows, when installing Git, select "Use Git from the Windows Command Prompt" when asked about adjusting your PATH; if you choose one of the other options, whenever this bootcamp talks about "a command line", open Git Bash instead. Or use the Windows Subsystem for Linux.

ℹ️ If you are asked to generate a new SSH key and add it to the ssh-agent, please follow GitHub's instructions.

GitHub provides setup instructions to install Git on your computer.

⚠️ In the next step, when doing a "git commit", you will probably open vim, a command-line editor that is somewhat hard to use. To be able to write content, press i. This will enable the editing mode. You can write your commit message. Once your are done with it, press ESCAPE, write :x and then press ENTER to save your changes and quit. Alternatively, you can change the editor Git uses by doing git config --global core.editor <editor> where on Linux and Mac you might use nano for the editor, and on Windows you might use notepad. You can also use fancier editors such as Visual Studio Code. If you entered vim but just want to quit to use nano, press ESCAPE, write :q! then press ENTER to quit.

Now open a command line and go to where your code is. You can create a new basic app, or even just a few text files to learn; git is not just for code, you can also use it to track anything whose changes you'd like to track, such as code documentation or even cooking recipes..

To track a folder using Git, execute the following commands within the folder:

git init   # Initialize a new repository
git add .  # Add all the files in the current folder to the repository
git commit # Create a first commit

The commit you just created contains all of the changes you made; since this is your first commit, those changes are the creation of files.

ℹ️ Usually, creating a git repository is one of the first things you do when starting a project. Therefore, the initial commit is in general small, or it only contains the files generated by your IDE/framework. It is even created for you by some frameworks.

Ignoring files

Git sees every file in your working copy as one of three things:

  1. tracked - a file which has been previously staged or committed;
  2. untracked - a file which has not been staged or committed; or
  3. ignored - a file which Git has been explicitly told to ignore.

Ignored files are usually build artifacts and machine generated files that can be derived from your repository source or should otherwise not be committed. In the particular case of this part of the project, these generated files are the .class files.

Git uses a special file named .gitignore to list all the ignored files. Note that this is not a file extension; the name must be exactly .gitignore and nothing else. It should be at the root of your repository. There is no explicit ignore command: you must edit the .gitignore file by hand when you have new files that you wish to ignore.

Note that unlike the files it ignores the .gitignore file itself must be tracked.

Usually, your typical .gitignore file will exclude all build artifacts (.class, .jar, .so, ...), as well as project dependencies that you manage via a specific build tool (SBT, Gradle, NPM, ...), local IDE configuration (.eclipse, .idea, project.iml, ...) and, most important if your repository is public, secrets (NEVER upload a private key or API secret on a public facing git repository - thousands of people do that and get hacked, even by automatic bots that look for API keys on GitHub).

ℹ️ Most of the huge libraries and frameworks provide typical .gitignore files you can use in your projects. Github maintains a collection of such files on this repo, feel free to use them!

Before pushing anything to the internet, you want to make sure there are no secrets. Let's say you have a secret constant in your code, such as an API key for a service, and you'd like to hide it. To do so, you will move it to a new file, and read that file from the code.

For instance, when developing a mobile app, API keys commonly need to be secret. On Android, you would create an XML file (for example keys.xml) in the app/src/main/res/values directory, with a content like this:

<resources>
	<string name="my_api_key">YOUR_API_KEY_GOES_HERE</string>
</resources>

Then, you would add the file to your gitignore, to make sure git doesn't index it, by adding /app/src/main/res/values/keys.xml (or whatever your file is called) to your .gitignore.

Importantly, a file you add to your .gitignore isn't removed automatically. To get rid of it, run git rm --cached <file> (without the <>).

ℹ️ The --cached argument tells Git to stop tracking your file while keeping it in the actual filesystem. Omitting this argument will cause the file to be removed from the filesystem as well.

Remember that git stores all past history. If you have already pushed a commit with a secret key to a public repository, that key is now public and you must disable it and create a new one. There are ways to overwrite the public history, but (1) they are dangerous since you could accidentally lose data, and (2) an automated robot might still have fetched your key in the short time between you pushing the secret and you pushing a modified history. Thus, we will not talk about them here.

If you have not pushed it already but did commit locally, you can change your previous commit. To do so, run git commit --amend. This will open the editor with the same commit message as before. You can leave it as is.

ℹ️ Amending a commit can be very powerful, but it changes the id of the commit. We will see just after how to synchronize multiple git repositories. Usually, it is a bad practice to amend a commit that you already pushed to a server, as it breaks the history. GitHub, for example, doesn't allow you to push amended commits.

Publish your repository on GitHub

You will now publish your code to GitHub. Create a repository on GitHub. You can name it as you please. If GitHub offers you to initialize the repository with a README, don't do it.

Publishing code on GitHub requires two steps:

  1. Add GitHub as a "remote" to your repository, using the following command: git remote add origin https://github.com/your-github-username/your-repository-name.git
  2. Push your commits to GitHub: git push -u origin main

ℹ️ If you make a mistake in the URL when adding the remote, you can change it with git remote set-url origin <the correct URL>

The git push command is used to upload local repository content to a remote repository. Pushing is how you transfer commits from your local repository to a remote repository. Here, origin corresponds to the remote repository you declared using the git remote add origin <url> command. You can have multiple remote repositories, with different names.

ℹ️ You will have to authenticate using your github username and password when you want to interact with the remote. By default, Git will "remember" your credentials for 15 minutes - you can change this time using git config credential.helper cache <timeout in seconds>. Alternatively, you can use a SSH key pair to commit. In that case, you'll need to add your public key to your Github account, and replace the remote url with [email protected]:your-github-username/your-repository-name.git. Take a look at GitHub's help page for more details.

Discussing on GitHub

GitHub has discussions, which are a mix between forum and Q&A site.

ℹ️ If your own repository has no discussions, you might need to enable them in the GitHub repository settings.

We will use GitHub discussions on this very repository as the course forum. Staff members are distinguished by a "maintainer" or "contributor" tag next to their username, but feel free to answer others' questions as well! Teaching is a good way to test your own knowledge and realize what you know and what you still need to learn.

Remember to use the right category for each conversation, and in particular to mark an answer as accepted if it solves your question.

Adding and updating files

To demonstrate the power of Git, let's apply some changes. You can, for example, change the greeting message. Compile your app and run it to validate your changes.

Add the file(s) you just changed:

git add <file relative path>

The git add command adds a change in the working directory to the staging area. It tells Git that you want to include updates to a particular file in the next commit. However, git add doesn't affect the repository by itself; changes are not actually recorded until you commit them.

Commit the changes you just made to your local repository:

git commit # You can write the commit message inline with the parameter "-m <your commit message>"

Commits are like checkpoints you set for yourself. It is a good practice to keep each commit as small and to the point as possible. This is especially important when working on a team, since team members often need to look at what their teammates changed.

When it comes to commit messages, beginners (and non-beginners) tend to write anything they have in mind just to get rid of them. It is actually quite a challenge to write a good commit message. There is no absolute rule, but there are several guidelines on the internet, such as Chris Beams' post on how to write a Git commit message.

Keep in mind his Seven rules of a great Git commit message:

  1. Separate subject from body with a blank line
  2. Limit the subject line to 50 characters
  3. Capitalize the subject line
  4. Do not end the subject line with a period
  5. Use the imperative mood in the subject line
  6. Wrap the body at 72 characters
  7. Use the body to explain what and why vs. how

You can now push your commit to your remote repository:

git push # You only need "-u origin main" the first time you are pushing

Working with multiple Git copies

Git is a useful tool to keep track of your changes, but it's also very powerful when working in teams, to synchronize changes in the code among team members.

As you might have guessed already, a Git repository can have many copies. Up until now, your repository had two copies: the one on your computer, and the one hosted by GitHub. When you start working in teams, each member of the team will have a copy of the repository on their computer. But how can one synchronize their own copy with, for example, the remote? And what happens when you try to push new commits to a remote without having first synchronized your local copy with that remote?

To simulate the effect of you working with multiple people, you will host two copies of the same repository on your computer. Open a second command line in another directory (NOT inside your git repository). There, clone your github repository using git clone https://github.com/your-github-username/your-repository-name.git. Git will create a directory with the name of your repository, containing everything you pushed so far. If you go inside, you will see that git was initialized as well, and git remote -v will show you that your GitHub repository has already been added as the origin remote. We will now refer to the directory just created as the second copy, while the directory in which you worked earlier will be called the first copy.

Getting commits on remote with git pull

First, let's try to change some parts of the code. Go to the second copy and update your app. You can once again change the greeting message, or the weather display. Save the file then commit and push your changes (refer to the begining of the bootcamp if you can't remember how to do it). Then, go to the first copy. As you didn't ask anything to Git, the changes you just made are not yet reflected! To retrieve the changes, open the command line and run git pull. You will now see that the changes you made to the second copy are present in the first one as well.

The git pull command works in a similar fashion than the git push command. If you previously ran git push -u <remote> <branch>, then you can call git pull with no argument. In the other case, you have to specify the remote and branch using git pull <remote> <branch>. Note that when you cloned your repository to create the second copy, Git already linked origin main as the default remote and branch for the main branch, so you can run git pull and git push directly.

Merging different copies

Now, let's see what can happen if you change the code on the two copies at the same time. First, open the first copy and change something in your app. For example, you can change the label of a button or its position. You can now commit and push your updates. Make sure that it has been pushed correctly by checking that you can see your changes on GitHub.

Now, go to the second copy and try to do an other change, in the same file as in the first copy. The changes you did in the first copy are indeed not present yet, as you didn't pull. That's expected, as we want you to see what happens when you do this kind of thing. After making the changes, commit your code, and try to push it: Git should not allow you to push your changes.

It is normal.

Internally, Git maintains a tree with all the commits of the repository. Thus, each commit you create has a parent commit, the latest commit you had before commiting. If you try to push a commit that has the same parent commit as a commit that is already on the remote, this will not work, as it would split the tree in two. To avoid this situation, we will have to merge the changes on the remote and the local changes, to create a new commit combining them, and having two parents.

ℹ️ You can see the history of commits on your copy using git log. git log --parents will also show the hash of the parent commits of each commit in the history! You will indeed see that merge commits have two parents, and that normal commits only have one, usually the commit just before them.

When you try to pull commits from the remote while already having locally unpublished commits, git will automatically try to merge them. Run git pull to see what happens.

There is a chance here that you'll get a merge conflict. This means that a given file was modified in both versions you're trying to merge. As a result, Git doesn't know which changes to keep and which changes to remove, and you'll have to tell it what to do. There are powerful tools out there that can help you resolve merge conflicts.

When you have a conflict, Git will list all the files that couln't be merged automatically. Open the file in your favourite text editor to see what happened. In every place where there is a conflict, you will see something like this:

<<<<<<< HEAD

All the code you changed on your local version

=======

All the code that changed on the remote version

>>>>>>> da110d39a9f4494805b6eb5ce1e89ec57d947b04 (<-- a commit hash, obviously different from this example)

What you have to do now is to update the files to get them to be exactly what you want them to be. Here, if you want to keep both changes, you can just remove the <<<<<<< HEAD, ======= and >>>>>>> da110d39a9f4494805b6eb5ce1e89ec57d947b04 parts. In other situations, it might be a bit trickier, and dedicated software (sometimes directly included in your IDE) will help you sort it out.

Once everything sounds good to you, run git add, git commit and git push as usual. You will notice that the git commit message is pre-filled with an automatic merge message. It is good practice to leave it as-is.

ℹ️ Even though you can freely update all the files while merging, it's not a good idea to edit them more than needed to complete the merge. Any change you want to make to the code besides from merging should be done in a new commit. Otherwise, the commit will confuse anyone looking at it, since they will not expect unrelated changes in a merge commit.

ℹ️ For trivial merges, you will sometimes see your editor directly when running git pull. This means that the merge didn't require human intervention, and Git just generated the merge commit for you. You just have to accept to finish the merge.

Branches

ℹ️ The commands in this section are provided as an example, but you don't have to run them. Just read the section carefully and make sure you understand it!

In huge projects, having everyone work on the same code becomes problematic because their changes will often conflict. This is where branches come to the rescue. When you want to develop a new feature, you will create a feature branch, and work there. When your work is done, you will merge it into the main branch, or open a pull request for your changes to be reviewed by members of your team before being approved for merge. You can learn more about this concept here. There are also even more separated variants, like the git flow model, but we will not discuss them here. Let's just see how we can create and switch between branches. For this part, we won't need to use two copies, so you can remove one of them if you want.

The git flow model

The git flow model - nvie

Let's begin with the creation of the branch. Try to find a good name for it, usually a 2-3 words description of what you're trying to do. If you want to add a small Hitchiker's Guide in your app, feature/hitchickers-guide is a decent name. To create it, run git checkout -b feature/hitchickers-guide. This will create a new branch and switch to it. Everything you commit will now be on that branch.

To push the branch to the origin remote, you will have to run git push -u origin feature/hitchickers-guide. Again, the -u origin feature/hitchickers-guide is only needed the first time you want to push your branch.

If you want to go to another branch, type git checkout <branch name>.

When your work is done, you will want to make your changes appear in the main branch. For that, you have a few options.

The first one is to simply merge the branch. To do that, go to the main branch and run git merge <the branch you want to merge>.

The second option is to open a pull request on GitHub. To do so, follow GitHub's instructions. Then go to your repository page and click the Pull Requests button. You will then be able to accept it, which performs the merge for you. The main advantage of this technique is that opening a pull request (or PR for short) opens a discussion in which everyone can review the changes and provide feedback.

Git Wrap-Up

In case of fire

In case of fire - Louis Michel Couture

Git is the most widely used version control system, so there is plenty of documentation on the internet. We have given you a quick intro about how it works, but if you want to learn more, feel free to explore the following links:

A final note: as with all tools, you'll get most of your experience in using git by... actually using it. There are a lot of very powerful features you will find handy in some situations. If you want to do something a bit tricky, like identify which commit introduced a particular bug or regression, don't hesitate and look up in the documentation and try to hack!

ℹ️ If you want to use an advanced Git feature but are not sure about what it could do, create a copy of the entire repository folder first! All of the information Git stores is in a folder named .git in your repository, thus your folder copy will behave exactly as the original one, and you can switch to it if you accidentally break the original one.

That's all for today!