Git and GitHub
Soon you are going to take your first step in using Git. If you would like to see a quick overview of the full Git landscape before jumping in, watch the video below.
Install SourceTree which is Git + a GUI for Git.
Suppose you want to create a repository in an empty directory things
Windows: Click File
→ Clone/New…
. Click on Create
button.
Mac: New...
→ Create New Repository
.
Enter the location of the directory (Windows version shown below) and click Create
.
Go to the things
folder and observe how a hidden folder .git
has been created.
Note: If you are on Windows, you might have to configure Windows Explorer to show hidden files.
Open a Git Bash Terminal.
If you installed SourceTree, you can click the Terminal
button to open a GitBash terminal.
Navigate to the things
directory.
Use the command git init
which should initialize the repo.
$ git init
Initialized empty Git repository in c:/repos/things/.git/
You can use the command ls -a
to view all files, which should show the .git
directory that was created by the previous command.
$ ls -a
. .. .git
You can also use the git status
command to check the status of the newly-created repo. It should respond with something like the bellow
git status
⤵️
# On branch master
#
# Initial commit
#
nothing to commit (create/copy files and use "git add" to track)
Create an empty repo.
Create a file named fruits.txt
in the working directory and add some dummy text to it.
Working directory: The directory the repo is based in is called the working directory.
Observe how the file is detected by Git.
The file is shown as ‘unstaged’
You can use the git status
command to check the status of the working directory.
git status
⤵️
# On branch master
#
# Initial commit
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# a.txt
nothing added to commit but untracked files present (use "git add" to track)
Although git has detected the file in the working directory, it will not do anything with the file unless you tell it to. Suppose we want to commit the current state of the file. First, we should stage the file.
Commit: Saving the current state of the working folder into the Git revision history.
Stage: Instructing Git to prepare a file for committing.
Select the fruits.txt
and click on the Stage Selected
button
fruits.txt
should appear in the Staged files
panel now.’
You can use the stage
or the add
command (they are synonyms, add
is the more popular choice) to stage files.
git add fruits.txt
git status
⤵️
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
# new file: fruits.txt
#
Next, we can commit the staged version of fruits.txt
Click the Commit
button, enter a commit message e.g. add fruits.txt
in to the text box, and click Commit
Use the commit
command to commit. The -m
switch is used to specify the commit message.
git commit -m "add fruits.txt"
You can use the log
command to see the commit history
git log
⤵️
commit 8fd30a6910efb28bb258cd01be93e481caeab846
Author: … < … @... >
Date: Wed Jul 5 16:06:28 2017 +0800
Add fruits.txt
Note the existence of something called the master
branch. Git allows you to have multiple branches (i.e. it is a way to evolve the content in parallel) and Git creates a default branch named master
on which the commits
go on by default.
Do some changes to fruits.txt
(e.g. add some text and delete some text). Stage the changes, and commit the changes using the same steps we followed before. You should end up with something like this.
Next, add two more files colors.txt
and shapes.txt
to the same working directory. Add a third commit to record the current state of the working directory.
Add a file names temp.txt
to the things
repo you created. Suppose we don’t want this file to be revision controlled by Git. Let’s instruct Git to ignore temp.txt
The file should be currently listed under Unstaged files
. Right-click it and choose Ignore…
. Choose Ignore exact filename(s)
and click OK
.
Observe that a file named .gitignore
has been created in the working directory root and has the following line in it.
temp.text
Create a file named .gitignore
in the working directory root and add the following line in it.
temp.text
The .gitignore
file tells Git which files to ignore when tracking revision history. That file itself can be either revision controlled or ignored.
- To version control it (the more common choice – which allows you to track how the
.gitignore
file changed over time), simply commit it as you would commit any other file. - To ignore it, follow the same steps we followed above when we set Git to ignore the
temp.txt
file.
Tag
Let's tag a commit in a local repo you have (e.g. the sampelrepo-things
repo)
Right-click on the commit (in the graphical revision graph) you want to tag and choose Tag…
Specify the tag name e.g. v1.0
and click Add Tag
.
The added tag will appear in the revision graph view.
To add a tag to the current commit as v1.0
,
git tag –a v1.0
To view tags
git tag
To learn how to add a tag to a past commit, go to the ‘Git Basics – Tagging’ page of the git-scm book and refer the ‘Tagging Later’ section.
Checkout
Git can show you what changed in each commit.
To see which files changed in a commit, click on the commit. To see what changed in a specific file in that commit, click on the file name.
git show < part-of-commit-hash >
Example:
git show 251b4cf
⤵️
commit 5bc0e30635a754908dbdd3d2d833756cc4b52ef3
Author: … < … >
Date: Sat Jul 8 16:50:27 2017 +0800
fruits.txt: replace banana with berries
diff --git a/fruits.txt b/fruits.txt
index 15b57f7..17f4528 100644
--- a/fruits.txt
+++ b/fruits.txt
@@ -1,3 +1,3 @@
apples
-bananas
+berries
cherries
Git can also show you the difference between two points in the history of the repo.
Select the two points you want to compare using Ctrl+Click
.
The same method can be used to compare the current state of the working directory (which might have uncommitted changes) to a point in the history.
The diff
command can be used to view the differences between two points of the history.
git diff
: shows the changes (uncommitted) since the last commitgit diff 0023cdd..fcd6199
: shows the changes between the points indicated by by commit hashesgit diff v1.0..HEAD
: shows changes that happened from the commit tagged asv1.0
to the most recent commit.
Git can load a specific version of the history to the working directory. Note that if you have uncommitted changes in the working directory, you need to stash them first to prevent them from being overwritten.
Double-click the commit you want to load to the working directory, or right-click on that commit and choose Checkout...
.
Click OK
to the warning about ‘detached HEAD’ (similar to below).
The specified version is now loaded to the working folder, as indicated by the HEAD
label. HEAD
is a reference to the currently checked out commit.
If you checkout a commit that come before the commit in which you added the .gitignore
file, Git will now show ignored fiels as ‘unstaged modifications’ because at that stage Git hasn’t been told to ignore those files.
To go back to the latest commit, double-click it.
Use the checkout <commit-identifier>
command to change the working directory to the state it was in at a specific past commit.
git checkout v1.0
: loads the state as at commit taggedv1.0
git checkout 0023cdd
: loads the state as at commit with the hash0023cdd
git checkout HEAD~2
: loads the state that is 2 commits behind the most recent commit
For now, you can ignore the warning about ‘detached HEAD’.
Use the checkout <branch-name>
to go back to the most recent commit of the current branch (the default branch in git is named master
)
git checkout master
Stash
You can use the git's stash feature to temporarily shelve (or stash) changes you've made to your working copy so that you can work on something else, and then come back and re-apply the stashed changes later on. -- adapted from this
Follow this article from SourceTree creators. Note the GUI shown in the article is slightly outdated but you should be able to map it to the current GUI.
Follow this article from Atlassian.
Clone
Clone the sample repo samplerepo-things to your computer.
Note that the URL of the Github project is different form the URL you need to clone a repo in that Github project. e.g.
Github project URL: https://github.com/nus-cs2103-AY1718S1/samplerepo-things
Git repo URL: https://github.com/nus-cs2103-AY1718S1/samplerepo-things.git
(note the .git
at the end)
File
→ Clone / New…
and provide the URL of the repo and the destination directory.
You can use the clone
command to clone a repo.
Follow instructions given here.
Pull
Clone the sample repo as explained in
Delete the last two commits to simulate cloning the repo 2 commits ago.
Clone
🏆 Can clone a remote repo
Clone the sample repo samplerepo-things to your computer.
Note that the URL of the Github project is different form the URL you need to clone a repo in that Github project. e.g.
Github project URL: https://github.com/nus-cs2103-AY1718S1/samplerepo-things
Git repo URL: https://github.com/nus-cs2103-AY1718S1/samplerepo-things.git
(note the .git
at the end)
File
→ Clone / New…
and provide the URL of the repo and the destination directory.
You can use the clone
command to clone a repo.
Follow instructions given here.
Right-click the target commit (i.e. the commit that is 2 commits behind the tip) and choose Reset current branch to this commit
.
Choose the Hard - …
option and click OK
.
This is what you will see.
Note the following (cross refer the screenshot above):
(a) The local repo is now at this commit, marked by the master
label.
(b) origin/master
shows what is the latest commit in the master
branch in the remote repo.
Use the reset
command to delete commits at the tip of the revision history.
git reset --hard HEAD~2
Now, your local repo state is exactly how it would be if you had cloned the repo 2 commits ago, as if somebody has added two more commits to the remote repo since you cloned it. To get those commits to your local repo (i.e. to sync your local repo with upstream repo) you can do a pull.
Click the Pull
button in the main menu, choose origin
and master
in the next dialog, and click OK
.
Now you should see something like this where master
and origin\master
are both pointing the same commit.
git pull origin
Push
-
Fork the samplerepo-things to your GitHub account:
Navigate to the on GitHub and click on the button on the top-right corner.
-
Clone the fork (not the original) to your computer.
-
Create some commits in your repo.
-
Push the new commits to your fork on GitHub
Click the Push
button on the main menu, ensure the settings are as follows in the next dialog, and click the Push
button on the dialog.
Use the command git push origin master
. Enter Github username and password when prompted.
Branch
0. Observe that you are normally in the branch called master
. For this, you can take any repo you have on your computer (e.g. a clone of the samplerepo-things).
git status
⤵️
on branch master
1. Start a branch named feature1
and switch to the new branch.
Click on the Branch
button on the main menu. In the next dialog, enter the branch name and click Create Branch
Note how the feature1
is indicated as the current branch.
You can use the branch
command to create a new branch and the checkout
command to switch to a specific branch.
git branch feature1
git checkout feature1
One-step shortcut to create a branch and switch to it at the same time:
git checkout –b feature1
2. Create some commits in the new branch.Just commit as per normal. Commits you add while on a certain branch will become part of that branch.
3. Switch to master
branch. Note how the changes you did in the feature1
branch are no longer in the working directory.
Double-click the master
branch
git checkout master
4. Add a commit to the master branch. Let’s imagine it’s a bug fix.
5. Switch back to the feature1
branch(similar to step 3).
6. Merge the master
branch to the feature1
branch, giving an end-result like the below. Also note how Git has created a merge commit.
Right-click on the master
branch and choose merge master into the current branch
. Click OK
in the next dialog.
git merge master
Observe how the changes you did in the master
branch (i.e. the imaginary bug fix) is now available even when you are in the feature1
branch.
7. Add another commit to the feature1
branch.
8. Switch to the master
branch and add one more commit.
9. Merge feature1
to the master branch, giving and end-result like this:
Right-click on the feature1
branch and choose Merge...
.
git merge feature1
10. Create a new branch called add-countries
, switch to it, and add some commits to it(similar to steps 1-2 above). You should have something like this now:
11. Go back to the master
branch and merge the add-countries
branch onto the master
branch(similar to steps 8-9 above). While you might expect to see something
like the below,
... you are likely to see something like this instead:
That is because Git does a fast forward merge if possible. Seeing that the master
branch has not changed since you started the add-countries
branch, Git has decided it is simpler
to just put the commits of the add-countries
branch in front of the master
branch, without going into the trouble of creating an extra merge commit.
It is possible to force Git to create a merge commit even if fast forwarding is possible.
Tick the box shown below when you merge a branch:
Use the --no-ff
switch (short for no fast forward):
git merge --no-ff add-countries
Merge Conflicts
1. Start a branch named fix1
in a local repo. Create a commit that adds a line with some text to one of the files.
2. Switch back to master
branch. Create a commit with a conflicting change i.e. it adds a line with some different text in the exact location the previous line was added.
3. Try to merge the fix1
branch onto the master
branch. Git will pause mid-way during the merge and report a merge conflict. If you open the conflicted file, you will see something like this:
COLORS
------
blue
<<<<<<< HEAD
black
=======
green
>>>>>>> fix1
red
white
4. Observe how the conflicted part is marked between a line starting with <<<<<<<
and a line starting with >>>>>>>
, separated by another line starting with
=======
.
This is the conflicting part that is coming from the master
branch:
<<<<<<< HEAD
black
=======
This is the conflicting part that is coming from the fix1
branch:
=======
green
>>>>>>> fix1
5. Resolve the conflict by editing the file. Let us assume you want to keep both lines in the merged version. You can modify the file to be like this:
COLORS
------
blue
black
green
red
white
6. Stage the changes, and commit.
Create PRs
1. Fork the samplerepo-pr-practice onto your GitHub account. Clone it onto your computer.
2. Create a branch named add-intro
in your clone. Add a couple of commits which adds/modifies an Introduction section to the README.md. Example:
# Introduction
Creating Pull Requsts (PRs) is needed when using RCS in a multi-person projects. This repo can be used to practice creating PRs.
3. Push the add-intro
branch to your fork.
git push origin add-intro
4. Create a Pull Request from the add-intro
branch in your fork to the master
branch of the same fork (i.e. your-user-name/samplerepo-pr-practice
, not se-edu/samplerepo-pr-practice
),
as described below.
4a. Go to the GitHub page of your fork (i.e. https://github.com/{your_username}/samplerepo-pr-practice
), click on the Pull Requests
tab, and then click on New Pull Request
button.
4b. Select base fork
and head fork
as follows:
base fork
: your own fork (i.e.{your user name}/samplerepo-pr-practice
, NOTnus-cs2103-AY1718S1/samplerepo-pr-practice
)head fork
: your own fork.
The base fork is where changes should be applied. The head fork contains the changes you would like to be applied.
4c. (1) Set the base branch to master
and head branch to add-intro
, (2) confirm the diff contains the changes you propose to merge in this PR (i.e. confirm that you did not accidentally include extra commits in the branch),
and (3) click the Create pull request
button.
4d. (1) Set PR name, (2) set PR description, and (3) Click the Create pull request
button.
A common newbie mistake when creating branch-based PRs is to mix commits of one PR with another. To learn how to avoid that mistake, you are encouraged to continue and create another PR as explained below.
5. In your local repo, create a new branch add-summary
off the master
branch.
When creating the new branch, it is very important that you switch back to the master
branch first. If not, the new branch will be created off the current branch add-intro
. And that is how you end up having commits
of the first PR in the second PR as well.
6. Add a commit in the add-summary
branch that adds a Summary section to the README.md
, in exactly the same place you added the Introduction section earlier.
7. Push the add-summary
to your fork and create a new PR similar to before.
Manage PRs
1. Go to GitHub page of your fork and review the add-intro
PR you created previously in Tools → Git & GitHub → Create PRs to simulate the PR being reviewed
by another developer, as explained below. Note that some features available to PR reviewers will be unavailable to you because you are also the author of the PR.
1a. Go to the respective PR page and click on the Files changed
tab. Hover over the line you want to comment on and click on the +
icon that appears on the left margin. That should create a text box for you to enter your
comment.
1b. Enter some dummy comment and click on Start a review
button.
1c. Add a few more comments in other places of the code.
1d. Click on the Review Changes
button, enter an overall comment, and click on the Submit review
button.
2. Update the PR to simulate revising the code based on reviewer comments. Add some more commits to the add-intro
branch and push the new commits to the fork. Observe how the PR is updated automatically to reflect
the new code.
3. Merge the PR. Go to the GitHub page of the respective PR, scroll to the bottom of the Conversation
tab, and click on the Merge pull request
button, followed by the Confirm merge
button.
You should see a Pull request successfully merged and closed
message after the PR is merged.
4. Sync the local repo with the remote repo. Because of the merge you did on the GitHub, the master
branch of your fork is now ahead of your local repo by one commit. To sync the local repo with the remote repo, pull
the master
branch to the local repo.
git checkout master
git pull origin master
Observe how the add-intro
branch is now merged to the master
branch in your local repo as well.
5. De-conflict the add-summary
PR. Note that GitHub page for the add-summary
PR is now showing a conflict (when you scroll to the bottom of that page, you should see a message This branch has conflicts that must be resolved
).
You can resolve it locally and update the PR accordingly, as explained below.
5a. Switch to the add-summary
branch. To make that branch up-to-date with the master
branch, merge the master
branch to it, which will surface the merge conflict. Resolve it and complete the merge.
5b. Push the updated add-summary
branch to the fork. That will remove the 'merge conflicts' warning in the GitHub page of the PR.
6. Merge the add-summary
PR using the GitHub interface, similar to how you merged the previous PR.
Note that you could have merged the add-summary
branch to the master
branch locally before pushing it to GitHub. In that case, the PR will be merged on GitHub automatically to reflect that the branch has been merged
already.
Forking Workflow
-
One member: set up the team org and the team repo.
- Create a GitHub organization for your team. The org name is up to you. We'll refer to this organization as team org from now on.
- Add a team called
developers
to your team org. - Add your team members to the
developers
team. - Fork se-edu/samplerepo-workflow-practice to your team org. We'll refer to this as the team repo.
- Add the forked repo to the
developers
team. Give write access.
-
Each team member: create PRs via own fork
- Fork that repo from your team org to your own GitHub account.
- Create a PR to add a file
yourName.md
(e.g.jonhDoe.md
) containing a brief resume of yourself (branch → commit → push → create PR)
-
For each PR: review, update, and merge.
- A team member (not the PR author): Review the PR by adding comments (can be just dummy comments).
- PR author: Update the PR by pushing more commits to it, to simulate updating the PR based on review comments.
- Another team member: Merge the PR using the GitHub interface.
- All members: Sync your local repo (and your fork) with upstream repo. In this case, your upstream repo is the repo in your team org.
-
Create conflicting PRs.
- Each team member: Create a PR to add yourself under the
Team Members
section in theREADME.md
. - One member: in the
master
branch, remove John Doe and Jane Doe from theREADME.md
, commit, and push to the main repo.
- Each team member: Create a PR to add yourself under the
-
Merge conflicting PRs one at a time. Before merging a PR, you’ll have to resolve conflicts. Steps:
- [Optional] A member can inform the PR author (by posting a comment) that there is a conflict in the PR.
- PR author: Pull the
master
branch from the repo in your team org. Merge the pulledmaster
branch to your PR branch. Resolve the merge conflict that crops up during the merge. Push the updated PR branch to your fork. - Another member or the PR author: When GitHub does not indicate a conflict anymore, you can go ahead and merge the PR.