For this lab, we'll practice using GitHub.
The first thing I have to do is Fork the Spoon-Knife repository to my own repository. This basically means that all the code inside that repository is copied into my GitHub account.
Next I have to clone it to my own local machine. This means that the software can become available for my own computer to use.
I go into Git Bash, create a 'temp' folder for this lab, and copy the SSH key.
I then typed:
git clone git@github.com:Blackweda/Spoon-Knife.git while inside /temp folder.
I got this response:
Sigh... nothing ever seems to work right the first time in this course... I googled the problem and came to this website: https://help.github.com/articles/error-permission-denied-publickey/
Took a few steps:
Apparently I have no identity, so I'll need to generate my own keys:
https://help.github.com/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent/
I followed the steps and made ssh keys that are saved. Then I went back and did:
ssh-add ~/.ssh/id_rsa
Inside the /temp folder. It asked for my passphrase and then added the keys!
Now let's try this all with git cloning again.
Again the same problem... but I took a look at the command I entered and made a few changes...
As you can see... I changed: git@github.com -> https://github.com
and I also changed: :Blackweda -> /Blackweda
NOW WE CAN GET BACK TO WORK...
I go into the Spoon-Knife/ folder and git branch to make sure I'm on the master branch inside the cloned repository using Git Bash.
Added new file:
Created new branch and checked both branches:
Then changed master back to its original version:
Because we are merging two branches that have different commit histories, git will use a recursive merge strategy, and create a new commit merge commit that connects these two branches.
Going through all the branches, I can see that the merged branch (name-and-food) has files from both of the other branches, while each the other branches do not contain the files from the other branches.
'name' has no food.txt
'food' has no Wayne.txt
Also when creating different versions of animals.txt, I noticed that my jungle-animals.txt had the same file as my water-animals.txt and the original animals.txt. So this means that they are all pointing to the same commit I suppose. I checked all three with 'git show' and I was correct.
I've realized through making some errors that having all three branches point to the same commit means that any changes in the branches will be seen by all branches because the commit is the same. Unless we commit the files personally inside a branch, all branches see changes.
While I was going through the whole process, I had the hardest time with the animals / water-animals / jungle-animals part...
Initially I kept pointing them all to animals, and then changing the animals.txt without actually adding and committing them each time. This essentially made all branch versions of animals.txt point to the most recent version of animals.txt that was edited.
I discovered I had to 'add' and 'commit' before 'checkout' so that the animals.txt version was local to that branch.
The squashing just added the commit messages into another commit message, similar to merging branches, but you are merging commit messages instead.. and the rebasing meant that I had to make the changes to animals.txt all over again to make the new merge successful, as it was undoing the previous merging process and repeating the entire process for rebasing the new single commit.
Adding the gh-pages wasn't too hard, just had to setup in my GitHub account and check for the site through the 'Settings' option.
=========================================================================
Entire Instructions:
Git Branches
In this lab you will review working with branches, and learn how to use the special
gh-pages
branch on Github to host static web content.
1. Review: creating branches
Every git repo begins with a single branch named
master
. When you commit, the
master
branch is automatically updated to point to the most recent commit.
Recall from class that we can create a new branch by doing the following:
git checkout -b new-branch
The
-b
flag indicates that you want to first create
the branch if it does not already exist, then switch to it and check it
out to your working directory. You could also have written it like
this:
git checkout -b new-branch master
In this case, we are explicitly specifying that we want
new-branch
to be created and point to the same commit to which
master
is pointing. Because we're already on the
master
branch, we don't have to do this--git will assume you want to use the current commit. We could also have written it like this:
git checkout -b new-branch HEAD
Here we explicitly specify that we want to use the latest commit on the current branch, also known as
HEAD
.
If we wanted to have
new-branch
get created pointing
to something other than the latest commit, we can specify that commit
instead. For example, if we want it to point back to two commits from
the current one (i.e., go back in time), we'd do this:
git checkout -b new-branch HEAD~2
As you switch between branches, if you ever get confused about which branch you're on, use
git status
or
git branch
, both of which will indicate the current branch.
2. Resetting a Branch
Sometimes it's necessary to switch the commit to which a branch is pointing. For example, if you accidentally commit things on
master
vs. a new branch, and need to move
master
back to a commit in the same history as a remote repository (i.e., if you want to
git pull upstream master
to get new updates).
Let's walk through an example:
- On Github, fork the Spoon-Knife repository at https://github.com/octocat/Spoon-Knife
- Clone this repository to your local computer
- Confirm that you are on the
master
branch (i.e., use git branch
)
- Create a new file called
name.txt
with your first name in it
- Use git to
add
and commit
the new name.txt
file.
- Confirm that you have 1 new commit on the
master
branch (i.e., use git log
)
At this point we have a new commit on the master branch, but decide
that we should have done this work on a new branch. Let's fix things so
master is back where it should be, and we instead have a new branch:
- Create a new branch called
name
by doing git checkout -b name
- Confirm that you are now on the
name
branch
- Confirm that
name
and master
both point to the same commit (i.e., use git show name
and git show master
)
We can now move our
master
branch back one commit, and leave our
name
branch where it is. We'll do that using the
-B
flag (note the capital), which will
create or reset a branch:
-
git checkout -B master HEAD~1
- Confirm that
name
and master
both point to different commits (i.e., use git show name
and git show master
)
3. Merging
Next, let's experiment with merging. We'll start by creating a third branch:
git checkout -b fav-food master
This will create a
fav-food
branch which is pointing to the same commit as
master
. Confirm that you now have 3 branches (
master, name, fav-food
) using
git branch
.
On the
fav-food
branch, add a new file
food.txt
with a list of your favourite foods. When you're done,
add
and
commit
this file to the
fav-food
branch.
Let's now try combining our
name
and
fav-food
branches. If you're ever unsure about a merge, you can always try
doing it on a new branch and see how it goes. Let's try doing our merge
on a branch called
name-and-food
:
- Create a fourth branch that points to the same commit as
name
: git checkout -b name-and-food name
- Merge the
fav-food
branch into name-and-food
. When you merge, you always switch to (i.e., checkout
) the branch into which you want to merge first, then merge
the other branch in: git merge fav-food
. Because we are merging two branches that have different commit histories, git will use a recursive merge strategy, and create a new commit merge commit that connects these two branches.
- Confirm that you now have a new merge commit on the
name-and-food
branch (i.e., use git log
).
- Confirm that the
name-and-food
branch contains both the name.txt
and food.txt
files, created on the earlier branches.
- Try switching back to your
name
branch, confirm that you don't have a food.txt
file.
- Try switching back to your
fav-food
branch, confirm that you don't have a name.txt
file.
4. Merge Conflicts
Git can automatically merge most things without any help. In the
previous case, we added two separate files, and git had no trouble
combining them. What if we had made changes to the same file?
Let's try an experiment that will simulate two separate lines of development on the same file:
- Create a new branch called
animals
that points to the same commit as master
.
- Create a new file called
animals.txt
and add the names of three farm animals, one per line. For example, your animals.txt
might look like this:
horse
cow
chicken
When you're done, commit the new
animals.txt
file to your
animals
branch. Now let's start two separate changes that both work on
animals.txt
:
- Create a new branch called
water-animals
that points to the same commit as animals
.
- On the
water-animals
branch, edit animals.txt
to add 3 water animals. When you're done, commit your changes to the water-animals
branch. For example, your animals.txt
might look like this:
horse
cow
chicken
whale
seahorse
dolphin
Let's repeat this process, but this time we'll focus on animals that live in jungles:
- Create a new branch called
jungle-animals
that points to the same commit as animals
.
- On the
jungle-animals
branch, edit animals.txt
to add 3 jungle animals. When you're done, commit your changes to the water-animals
branch. For example, your animals.txt
might look like this:
horse
cow
chicken
monkey
python
bird of paradise
Now let's merge our
water-animals
branch into
animals
:
- Switch to your
animals
branch
- Merge
water-animals
into animals
(i.e., git merge water-animals
)
- Confirm that your
animals
branch now contains the changes you made to animals.txt
Because our
water-animals
branch was ahead of our
animals
branch by 1 commit, git was able to do this merge using the
fast-forward merge algorithm, which simply moves the branch ahead to align with the other branch. If you use
git log
you'll notice that there is no
merge commit this time.
Next, let's merge our
jungle-animals
branch into
animals
as well. Since both of these branches touch the same lines of the same
file, this won't work automatically, and we'll have to fix it manually:
- Switch to your
animals
branch
- Merge
jungle-animals
into animals
(i.e., git merge jungle-animals
)
Git will respond and indicate that there was an issue merging
animals.txt
:
Auto-merging animals.txt
CONFLICT (content): Merge conflict in animals.txt
Automatic merge failed; fix conflicts and then commit the result.
Let's fix the merge conflict. Confirm that we are mid-way through a merge and have a merge conflict using
git status
. You'll see something like this:
On branch animals
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: animals.txt
no changes added to commit (use "git add" and/or "git commit -a")
Open the
animals.txt
file. Here is what mine looks like:
horse
cow
chicken
<<<<<<< HEAD
whale
seahorse
dolphin
=======
monkey
python
bird of paradise
>>>>>>> jungle-animals
Notice the presence of
<<<<<<< HEAD
,
=======
, and
>>>>>>> jungle-animals
. These are
conflict markers and show the different versions of the lines in question. Because we are merging
into animals
, it is our
HEAD
, and everything between
<<<<<<< HEAD
and
=======
is what is on this branch. Everything between
=======
and
>>>>>>> jungle-animals
is what is on the
jungle-animals
branch. Because both branches edit the
same lines of the same file, git needs us to resolve the conflict. We have a few options:
- Use what is in
HEAD
, and erase the lines from jungle-animals
- Do the opposite and use what is in
jungle-animals
, and erase what is in HEAD
- Combine the two sets of changes into one change
In this case, we just need to combine the entries into a single,
longer list. He can simply remove the conflict markers and save the
file. Here's what mine looks like when I'm done:
horse
cow
chicken
whale
seahorse
dolphin
monkey
python
bird of paradise
Now we can
add
and
commit
this conflict resolution in order to finish our merge. When we're done, we'll have a new
merge commit that combines the changes from our two branches into
animals
.
5. Rebasing, Squashing
At this point our
animals
branch is where we want it, in
terms of content; but it's a bit messy in terms of how we got there.
Sometimes before we share (i.e.,
git push
) branches to
share with colleagues, we want to clean up our history. Wouldn't it be
nice if we could take all the different commits we made, and combine
them into a single commit that achieved the same result? We can, and
it's called a
rebase.
First, a few warnings. Unlike a merge, which always keeps your
history intact, a rebase will alter your commit history. You should
never do this to a branch that has been shared with other developers,
since it will erase and re-create commits, which makes it impossible for
others to collaborate with you on those commits. A rebase is only
something you should do
before you share your commits with others.
Let's practice a rebase on our
animals
branch, and
squash our separate commits into one single commit:
- Switch to your
animals
branch
- Start an interactive rebase:
git rebase -i master
This will open your editor and show you all of the commits on
animals
that are ahead of
master
. Mine looks like this:
pick 436a838 Adding animals.txt
pick 595f37e Added water animals
pick 3d7af87 Add jungle animals
# Rebase 21826a9..3b4d451 onto 21826a9 (3 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
The comments at the bottom tell us what our options are. We can
pick a commit to include it,
squash a commit to combine it with the previous commit (like using
git commit --amend
),
fixup a commit to squash and throw away the commit message, etc.
In our case, let's modify things to include the first commit, and
squash the next two into it. To do so, edit the commit message like
so, then save and exit your editor:
pick 436a838 Adding animals.txt
squash 595f37e Added water animals
squash 3d7af87 Add jungle animals
Git responds with the same merge conflict we had previously:
error: could not apply 3d7af87... Add jungle animals
When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".
Could not apply 3d7af871d2677c64399c674f6a5937b9bbc48852... Add jungle animals
If you run
git status
you'll see this:
interactive rebase in progress; onto 21826a9
Last commands done (3 commands done):
squash 595f37e Added water animals
squash 3d7af87 Add jungle animals
(see more in file .git/rebase-merge/done)
No commands remaining.
You are currently rebasing branch 'animals' on '21826a9'.
(fix conflicts and then run "git rebase --continue")
(use "git rebase --skip" to skip this patch)
(use "git rebase --abort" to check out the original branch)
Unmerged paths:
(use "git reset HEAD <file>..." to unstage)
(use "git add <file>..." to mark resolution)
both modified: animals.txt
no changes added to commit (use "git add" and/or "git commit -a")
The reason that our rebase is failing is because git is
replaying our commits one-by-one on top of
master
, and as it does so, it's hitting a commit that is changing the same lines as a previous one. Once again, edit your
animals.txt
file to combine the merge conflict:
horse
cow
chicken
<<<<<<< HEAD
whale
seahorse
dolphin
=======
monkey
python
bird of paradise
>>>>>>> 3d7af87... Add jungle animals
Should become:
horse
cow
chicken
whale
seahorse
dolphin
monkey
python
bird of paradise
When you're done,
git add animals.txt
to signal to git that you've resolve the conflict. Then, you can tell git to
continue running the rebase (i.e., do the next commit in your list):
git rebase --continue
. Finally, git finishes replaying all our commits, and gives us a chance to alter our new commit message:
# This is a combination of 3 commits.
# This is the 1st commit message:
Adding animals.txt
# This is the commit message #2:
Added water animals
# This is the commit message #3:
Add jungle animals
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date: Thu Feb 2 13:01:46 2017 -0500
#
# interactive rebase in progress; onto 21826a9
# Last commands done (3 commands done):
# squash 595f37e Added water animals
# squash 3d7af87 Add jungle animals
# No commands remaining.
# You are currently rebasing branch 'animals' on '21826a9'.
#
# Changes to be committed:
# new file: animals.txt
#
You can change it, or leave it as is. Save and exit your editor. In
the end, I have a single commit that combines all of my other commits
into one:
$ git log
commit afc5bad7f651be478c69c4e117102bfeb183323c
Author: David Humphrey (:humph) david.humphrey@senecacollege.ca <david.humphrey@senecacollege.ca>
Date: Thu Feb 2 13:01:46 2017 -0500
Adding animals.txt
Added water animals
Add jungle animals
6. The gh-pages branch
For our final experiment, let's learn how to use Github's special
gh-pages
branch to host static web content.
First, let's convert our
animals.txt
file to HTML. Open the file and modify it so it's a proper HTML document, something like this:
<!doctype html>
<title>Learning about gh-pages</title>
<body>
<ul>
<li>horse
<li>cow
<li>chicken
<li>whale
<li>seahorse
<li>dolphin
<li>monkey
<li>python
<li>bird of paradise
</ul>
Next,
commit
your change, then use
git mv
to rename
animals.txt
to
animals.html
. Make sure you
commit
that rename change as well.
Now let's create a new branch named
gh-pages
that points to our current commit on
animals
:
git checkout -b gh-pages animals
Next,
push
your new
gh-pages
branch to Github and your forked repo:
git push origin gh-pages
Lastly, we can visit our hosted page at
http://{username}.github.io/Spoon-Knife/animals.html. Mine is at
https://humphd.github.io/Spoon-Knife/animals.html. There are more instructions
here.
NOTE: it can take a few minutes for your
gh-pages
branch to get published.