Difference between revisions of "DPS909 & OSD600 Winter 2017 - Lab 4"

From CDOT Wiki
Jump to: navigation, search
(Submission)
Line 438: Line 438:
 
|-
 
|-
 
| 12
 
| 12
|  
+
| Max Fainshtein
|  
+
| https://mfainshtein4.github.io/Spoon-Knife
|  
+
| https://mfainshtein4.github.io/Spoon-Knife/animals.html
 
|-
 
|-
 
| 13
 
| 13

Revision as of 02:22, 8 February 2017

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:

  1. On Github, fork the Spoon-Knife repository at https://github.com/octocat/Spoon-Knife
  2. Clone this repository to your local computer
  3. Confirm that you are on the master branch (i.e., use git branch)
  4. Create a new file called name.txt with your first name in it
  5. Use git to add and commit the new name.txt file.
  6. 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:

  1. Create a new branch called name by doing git checkout -b name
  2. Confirm that you are now on the name branch
  3. 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:

  1. git checkout -B master HEAD~1
  2. 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:

  1. Create a fourth branch that points to the same commit as name: git checkout -b name-and-food name
  2. 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.
  3. Confirm that you now have a new merge commit on the name-and-food branch (i.e., use git log).
  4. Confirm that the name-and-food branch contains both the name.txt and food.txt files, created on the earlier branches.
  5. Try switching back to your name branch, confirm that you don't have a food.txt file.
  6. 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:

  1. Create a new branch called animals that points to the same commit as master.
  2. 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:

  1. Create a new branch called water-animals that points to the same commit as animals.
  2. 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:

  1. Create a new branch called jungle-animals that points to the same commit as animals.
  2. 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:

  1. Switch to your animals branch
  2. Merge water-animals into animals (i.e., git merge water-animals)
  3. 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:

  1. Switch to your animals branch
  2. 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:

  1. Switch to your animals branch
  2. 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.

Submission

You will have completed your lab when you have done the following:

  • Completed the steps above to fork the Spoon-Knife repo, and created the various branches, done the merges, rebasing, etc.
  • Added a row to the table below with your name, GitHub repo link to your Spoon-Knife fork, and hosted gh-pages URL to your animals.html file.
  • Written a blog post discussing what you learned about branches, merging, rebasing this week.
# Name Spoon-Knife Repo (URL) Hosted gh-pages Site (URL)
1 Dave Humphrey https://github.com/humphd/Spoon-Knife https://humphd.github.io/Spoon-Knife/animals.html
2 Simon de Almeida https://github.com/simon66/Spoon-Knife https://simon66.github.io/Spoon-Knife/animals.html
3 Kevin Ramsamujh https://github.com/kramsamujh/Spoon-Knife https://kramsamujh.github.io/Spoon-Knife/animals.html
4 Timothy Moy https://github.com/timmoy/Spoon-Knife https://timmoy.github.io/Spoon-Knife/animals.html
5 Christopher Singh https://github.com/cgsingh/Spoon-Knife https://cgsingh.github.io/Spoon-Knife/animals.html
6 John Kim https://github.com/bkim49/Spoon-Knife https://bkim49.github.io/Spoon-Knife/animals.html
7 Ray Gervais https://github.com/raygervais/Spoon-Knife https://raygervais.github.io/Spoon-Knife/animals.html
8 Heetai Park https://github.com/tonypark0403/Spoon-Knife https://tonypark0403.github.io/Spoon-Knife/animals.html
9 Len Isac https://github.com/lkisac/Spoon-Knife https://lkisac.github.io/Spoon-Knife/animals.html
10 Dang Khue Tran https://github.com/dangkhue27/Spoon-Knife https://dangkhue27.github.io/Spoon-Knife/animals.html
11 Peiying Yang https://github.com/peiying16/Spoon-Knife https://peiying16.github.io/Spoon-Knife/animals.html
12 Max Fainshtein https://mfainshtein4.github.io/Spoon-Knife https://mfainshtein4.github.io/Spoon-Knife/animals.html
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36