A Practical Guide To Git and GitHub For Windows Users - From - Roberto Vormittag - 2016 - Reiter Consulting
A Practical Guide To Git and GitHub For Windows Users - From - Roberto Vormittag - 2016 - Reiter Consulting
While every precaution has been taken in the preparation of this book, the
publisher and author assume no responsibility for errors or omissions, or for
damages resulting from the use of the information or code contained herein.
~
Introduction
CHAPTER 1
Getting Started
1.1 Definitions
What is Git?
What is GitHub?
CHAPTER 2
Installation and Configuration
CHAPTER 3
Hosting Your Projects on GitHub
3.3 Summary
CHAPTER 4
Project Version Control with Git
CHAPTER 5
Working With Branches
5.3 Merging
5.5 Summary
CHAPTER 6
Collaborating with Others on GitHub
Start a Conversation
Merge the Change
Close the Pull Request
6.7 Summary
CHAPTER 7
More Git Magic
Reset
Rebase
CHAPTER 8
Git Concepts
Centralized Workflow
Feature Branch Workflow
Forking Workflow
8.5 Summary
CHAPTER 9
Centralized Workflow
9.10 Summary
The objective of this book is to make the process of acquiring Git and
GitHub skills fun, easy and quick. The only previous knowledge required
are basic Windows skills and the ability to use a text editor like Notepad
and a Web browser.
You will learn to interact with Git and GitHub in a professional way using
Git Bash, the command-line interface installed with Git For Windows,
which gives Windows users the same power and flexibility available on
Linux and Mac computers.
To make the learning process more lively and interactive I have setup a
companion website with additional resources where you can also post
questions and comments. See Chapter 1 for details.
In this edition I have added an entire new chapter describing in detail how
to implement the Centralized Workflow giving readers the opportunity to
review the techniques learned and apply them to a real-world project.
Chapter 3 has been updated to reflect some changes to the GitHub user
interface.
The "Next Steps" section has now gained a separate space of its own at the
end of the book.
Last but not least many thanks for the support and feedback received from
readers all over the world.
***
CHAPTER 1
Getting Started
In this chapter we will briefly define what Git and GitHub are and
introduce you to the book companion website.
1.1 Definitions
What is Git?
What is GitHub?
There are differences in the way Windows and Linux handle line endings in
text files and other low level details that need to be taken care of during the
installation and configuration process to prevent problems from occurring
later.
In this section we will setup and configure everything you need to work
smoothly with Git and GitHub on Windows, starting with the installation of
Git for Windows on your PC.
Then we will take a tour of the Git Bash command-line interface and learn
some useful commands that will be used throughout the book. We will also
use Git Bash to configure Git including some important Windows-specific
settings.
Next we will setup a GitHub account to host your first repository and
configure Git to connect with your GitHub account in a secure way.
At the end of this section you will have everything in place to start working
on your first project.
2.1 Installing Git for Windows
The first thing to do is to download the Git for Windows installer from the
following website:
https://git-for-windows.github.io/
On the book companion website you will find all the installation
screenshots with the recommended options.
Once the download is complete run the installer. During installation you can
specify a different install location, for example C:/apps/git if like me you
prefer short directory names. Please avoid installing in a location that has
directory names with spaces in the path such as "Program Files" or "My
Documents". I recommend selecting the option to create a desktop icon.
Make sure to select the option "Use Git from Bash only" as this is what
we will be using throughout the book.
When the installation is complete you should find a Git Bash icon on your
desktop, or you can also reach it from the Windows Start button -> All
Programs -> Git -> Git Bash.
Bash is the acronym for "Borne Again Shell" and is the most popular
command-line user interface on Linux systems. Git Bash simulates the
Linux environment in Windows. It is a command-line tool that gives you
much more than just Git. In fact what you get is the power of the Linux
tools on Windows. We will explore some useful Linux commands that you
can run from Git Bash. Do not worry if you have never worked with a
Linux shell before. Each command will be clearly explained with examples.
Git for Windows also installs a Git GUI tool which provides a graphical
user interface for Git. Here however we will concentrate on using the Git
Bash command-line interface (CLI) for a number of reasons:
All Git commands can be issued from the CLI whereas the GUI offers
only a subset.
CLI commands are the same in all platforms (Windows, Mac or Linux)
so what you will learn here you can use everywhere.
Not convinced of the CLI usefulness yet? In the science fiction blockbuster
"Jurassic Park" it is a kid's knowledge of the Unix command line interface
that saves the day and prevent the story heroes from becoming a T-Rex
meal. How about that for motivation?
2.2 An Introduction to Git Bash
In this section you will learn some basic Linux file system navigation
commands that we will be using throughout this book. If you are
comfortable using command-line interfaces and know basic Linux shell
commands you can safely skip this section. If it all sounds new or you need
a refresher read on.
Start the Git Bash console by double-clicking on the desktop icon (you can
also reach it from the Windows Start button -> All Programs -> Git -> Git
Bash).
Once the console is up and running you can start entering commands. The
way it works is very simple: you type in a command, strike the [Enter] key
and the command is executed. You can also paste commands into the
console by hitting the [Insert] key.
The illustration below shows how the console looks like when running. The
cursor will be flashing, waiting for a command to be entered besides the $
sign.
Type the following commands on the console, followed by [Enter]. Type
only the commands after the dollar ($) sign. Ignore the lines starting with #
as these are just comments to explain what the command does:
# creates a folder
$ mkdir myfolder
# change to myfolder
$ cd myfolder
Note: in this book the terms directory and folder are used
interchangeably.
The command pwd prints the name of the current working directory.
Note: The Git Bash prompt will normally show the user name, the
computer name and the current directory in the format user@computer
MINGW32 directory displayed in different colours. MINGW32 stands for
"Minimalist GNU for Windows". GNU is a collection of software tools
which make up the Linux operating system.
This ends our crash course on Linux file system commands. Come back to
this section as often as needed until you are familiar with the commands
and comfortable navigating directories and listing files in your PC using Git
Bash.
Before start using Git we need to run some basic configuration commands
to tell Git who you are, how to handle end-of-line characters in Windows
and which text editor to use by default.
Start Git Bash (if not already running) and enter the Git configuration
commands below. In the user.name and user.email settings replace "Your
Name" and "[email protected]" with your name and email address
(Git will record this information against the changes you will be making to
project files). Be careful to enter the commands exactly as shown. Note that
the --global and --list switches are preceded by two minus (-) signs.
Some Git commands fire up automatically a text editor into which to enter a
comment. Here we have setup Git core.editor configuration to fire up
Notepad since it is the standard editor on Windows systems.
In the last command the --list switch displays all your current Git
configuration settings. Check the output and make sure your name, email
address and the other settings have been entered correctly. If you need to
correct any of the settings just re-enter the respective git config command.
We are now done with Git configuration. In the next section you will set up
a GitHub account to host your first repository.
2.4 Setting Up a GitHub Account
Now that you have Git installed you need a place to host a repository to
share your projects. This place of course is GitHub. To create a free public
GitHub account point your browser to:
https://github.com/
Click on the [Sign Up] button and follow the simple instructions. You will
be asked to enter a username, email address and password. Click on
[Create an account] and select the "Free Plan" which gives you unlimited
public repositories and collaborators. Once the signup is complete you will
be taken to your GitHub home page which will have a URL (Web address)
of the form:
https://github.com/your-user-name
From your GitHub home page you can create and manage repositories and
monitor activity. All functionality is accessible from the GitHub menu
located on the top right-hand corner.
Note: the screenshots provided are current at the time of writing but be
aware that, as with all active websites, the GitHub user interface could
change over time. The underlying functionality however will still be the
same and it should be easy to find. Updates will be posted on the book
website so check it out from time to time.
Above is an illustration of the GitHub menu. The bell icon gives access to
notifications, the plus sign (+) can be used to add a repository and the
avatar icon gives access to your profile and settings. The avatar icon will
be replaced by your picture when you upload one. Now is a good time to
complete your profile.
From the GitHub menu, select Avatar -> Settings. Under Profile you can
upload a picture, add your name, email addresses and other information that
will help other users find or get to know you.
Under Emails you can verify the email address you supplied at registration.
Under Account Settings you can change your username and password.
To go back to your GitHub home page select Avatar -> Your Profile.
Next, type the following command to generate a pair of SSH keys making
sure you replace [email protected] with your GitHub email address
(the one you provided at registration). Pay attention to the command syntax.
$ ssh-keygen -t rsa -b 4096 -C "[email protected]"
After a while ssh-keygen asks you to enter a file in which to save the key.
Accept the default suggestion and press [Enter].
Then it will ask you for a passphrase. Enter your passphrase and press
[Enter].
Once you have confirmed the passphrase, ssh-keygen will tell you where
your private and public keys have been saved with a message similar to the
one below:
The ssh-keygen command creates a .ssh folder under your home directory
and place both the public and private keys there. You can verify this with
the following command:
$ ls .ssh
id_rsa id_rsa.pub
The public and private "keys" are just text files with encrypted content used
by the server to identify your computer. Let's do a simple exercise to
familiarize with the key files.
Exercise
Using Windows Explorer, navigate to the .ssh folder located under your
home directory. You should see both key files as illustrated below:
The id_rsa file is your private key and the id_rsa.pub file is your public
key.
Note: the .pub extension can trick Windows Explorer to believe that your
public key file is a Microsoft Publisher Document. Just disregard the
association and treat it as a simple text file.
Start Notepad or your favourite text editor and open the key files to look at
the content. Be careful NOT to accidentally edit these files - if you do, just
exit the editor without saving the changes.
The private key file starts with the text -----BEGIN RSA PRIVATE KEY----
-
The public key file starts with the text ssh-rsa and ends with your email
address. Keep the public key file open in your text editor. You will soon
need to copy its contents into your GitHub settings.
We now need to run the SSH agent. Important: in the command illustrated
below the `ssh-agent -s` argument following the eval keyword is enclosed
within grave accent quotes - do not confuse them with the single quote
character. The grave accent quote has a special meaning for the Linux shell
and Git Bash. The key for this character is usually located on the top left
corner of a standard Windows keyboard. See illustration below. You must
use the grave accent character when typing this command otherwise it will
not work.
On the Git Bash console enter the following command to run the ssh-agent
utility making sure `ssh-agent -s` is enclosed within grave accent quotes:
The command should output the Agent Process ID (pid) confirming that the
ssh-agent is running. We can now add the private key using the ssh-add
utility. When prompted, enter your passphrase (the one you used in Step 1)
and press [Enter]:
$ ssh-add ~/.ssh/id_rsa
You need now to copy the contents of the public key file and add it to your
GitHub account so that GitHub can authenticate your PC connection
requests from Git.
If you have followed the exercise in Step 1 you should already have the
public key file open in your text editor. Use CTRL-A and CTRL-C to copy
its contents into the clipboard.
From the GitHub menu on the top-right corner of the page select Avatar ->
Settings.
From the Settings page select SSH keys then click on the [New SSH key]
button.
In the "Key" field paste the contents of your public key file id_rsa.pub.
We are almost there... now to the last step: testing the connection.
Step 4. Testing the connection between Git and GitHub
We are ready now to test that your PC can connect to GitHub in a secure
way using SSH. To do that go back to the Git Bash console and type the
following command:
$ ssh -T [email protected]
...
Enter passphrase for key '.ssh/id_rsa':
Hi username! You've successfully authenticated,
but GitHub does not provide shell access.
It will output your public key fingerprint and ask if you want to continue.
Type yes and press [Enter].
It will then ask for your private key passphrase. Type it and press [Enter].
You should get a message similar to the above. If the username in the
message is your GitHub username it means everything has been setup
correctly. Congratulations are in order.
You are ready now to host your very own first Git project on GitHub. This
is the subject covered in the next chapter.
***
CHAPTER 3
Hosting Your Projects on GitHub
After all the hard preparatory work that you have done in the previous
chapters, now comes the fun part. Here you will learn the process of hosting
a project on GitHub. Like the rest of the book this is a very hands-on
chapter where you will be learning by doing. The first thing we need is a
project to experiment with. To introduce it I have to digress a little.
There are several of these alphabets in use, and the most widely known is
the pilot's alphabet beginning with Alpha, Bravo, Charlie (for A, B and C).
For instance flight BA-461 will be spelled by pilots over the radio as
"Bravo Alpha Four Six One".
The Phonetic Website is a compact but complete static website built with
HTML and CSS. This project has been specifically designed for Git and
GitHub training. It is hosted on GitHub and you can download it from the
following URL by clicking on the [Clone or Download -> Download Zip]
button:
https://github.com/robertovormittag/phonetic-website
Note: You do not need to have previous knowledge of HTML and CSS.
Every change to this project in the exercises that follow will be clearly
explained.
Once you have downloaded the ZIP file, extract the contents. You will find
that the project consists of three HTML pages and a "style" folder inside
which there are four CSS stylesheets. You can delete the README.md file
as it is not needed.
File Description
cities.html Phonetic alphabet based on city names
names.html Phonetic alphabet based on people names
pilots.html Pilot's phonetic alphabet
style Folder containing all stylesheets
style/cities.css Stylesheet for cities.html
style/names.css Stylesheet for names.html
style/pilot.css Stylesheet for pilots.html
style/site.css Stylesheet for entire site
To see how the website looks like, open it in your Web browser. This is how
it appears on Internet Explorer:
There are three navigation tabs to switch between the alphabets. At present
it only contains the first three letters of each alphabet. Your task will be to
gradually build the website and complete the alphabets from A to Z.
Now that you have a project to work with you need a repository to host it.
3.2 Hosting Your Project
You will now host the Phonetic Website project you have just downloaded
on GitHub. This is what you need to do step by step:
From the GitHub menu select the Plus (+) icon, then New repository:
Check the box "Initialize this repository with a README file" so that it
will be ready to clone.
You will be redirected to your new repository homepage. Its URL (Web
address) will have the following format:
https://github.com/your-github-username/your-repository-name
Let's explore the GitHub repository page. Each project has the following
tabs:
Code: list of all the files and folders in the project (at the moment you will
see only a single file README.md).
The Code tab is the one that you will be using most. From here you can
browse the project files and directories and also see information on
commits, branches, releases and contributors.
You will also find three buttons on the top-right hand corner:
[Watch]: get notifications for this repository.
You are now ready to clone your GitHub repository locally on your PC.
The GitHub repository you have created in the previous step is your official
project central repository. Developers never work on the central
repository, they clone it instead and work independently on their local
copy, adding files and making changes. Only when the changes have been
thoroughly tested the central repository is updated.
To clone your GitHub repository open Git Bash and run the following
commands making sure you replace "your-name" and "your-repo" with
your GitHub user name and repository name respectively:
$ cd ~
You may be prompted to enter your GitHub username and password. Enter
the information requested and click OK.
Change to the working directory and list the contents (replace your-repo in
the example command below with your repository name):
$ cd your-repo
$ ls -a
.git/ README.md
You should find the README.md file added by GitHub when you created
the repository. The listing also shows a .git directory. This is where Git
stores information about changes in your project files. We will explore the
.git directory in detail in another section, but for now think of it as the local
repository version control database.
You can check the status of the files in your working directory by running
the git status command:
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean
The output message means that there have not been any additions or
changes to the files so far in the working directory and there is nothing to
commit. The git status command detects any uncommitted change in the
working directory and reports it.
Developers usually go about their work in the local repository using the
following routine:
You are now ready to add the source code to the project. Using Windows
Explorer, copy the Phonetic Website files you have downloaded earlier into
the project working directory you have created in the previous step.
Once the copy operation is complete use Git Bash to list again the contents
of the working directory:
$ cd ~/your-repo
$ ls -a
./ ../ .git/
cities.html
names.html
pilots.html
README.md
style/
$ ls -a style
./ ../ cities.css
names.css
pilot.css
site.css
Make sure that all the HTML and CSS files have been copied. Test the
website in a Web browser to verify that everything works. You should be
able to switch between the alphabets by clicking on the navigation tabs.
If you now run git status it will detect the new files:
$ git status
On branch master
Your branch is up-to-date
Untracked files:
(use "git add <file>..."
cities.html
names.html
pilots.html
style/
Git refers to files and folders added to the working directory as untracked
files. Git will not track changes to these files until you add them to the
index. This is what you are going to do next.
To add the new files to the repository first we need to add them to the
index. Think of the index as the list of files that are going to be committed
to the repository. The index is also known as the staging area for the next
commit.
You add files to the index by running the git add command as follows:
The first git add command adds the "style" folder with all its contents to the
index. The second adds all the files with the html extension to the index.
Let's now check the status of the working directory again:
$ git status
On branch master
Your branch is up-to-date
Changes to be committed:
There are no longer untracked files. All the new project files and folders are
now in the staging area (index) ready to be committed.
To commit the files added to the index run the git commit command as
follows:
7 files changed
create mode 100644 cities.html
create mode 100644 names.html
create mode 100644 pilots.html
create mode 100644 style/cities.css
create mode 100644 style/names.css
create mode 100644 style/pilot.css
create mode 100644 style/site.css
The -m flag in the git commit command is used to enter a comment within
double quotes. Commit comments are compulsory and are recorded in the
repository.
Let's check the status of the working directory now:
$ git status
On branch master
Your branch is ahead
of 'origin/master' by 1 commit.
use "git push" to publish
your local commits
nothing to commit,
working directory clean
Git is reporting that the working directory is now clean. There are no
pending changes and nothing to commit. The local repository is however
ahead of the remote central repository on GitHub by 1 commit because we
just committed a new version of the project with the source code in it.
Before we can publish the new version of the project to GitHub, we need to
find out how Git identifies the remote repository. You can do that by
running the git remote command:
$ git remote
origin
The output shows that Git knows about a remote repository called origin.
That is the name Git is using to identify your remote repository on GitHub.
Let's find out more detailed information about it:
$ git remote show origin
* remote origin
Fetch URL:
https://github.com/your-name/your-repo
Push URL:
https://github.com/your-name/your-repo
HEAD branch: master
In the output you should see the URL of your repository on GitHub.
To update the GitHub repository with the new version of the project run the
git push command. You may be prompted for your GitHub username and
password.
Now the central repository has been updated. If you login to your GitHub
account and look at the repository page in the Code tab you will find that
all the source code files of your website project have been uploaded.
Notice that the commit comment "Source code added" appears besides each
file. If you click on the commits link you can see details of the two
commits in the project so far.
Git status should now report that your local and remote repositories are in
sync:
$ git status
On branch master
Your branch is up-to-date
with 'origin/master'.
nothing to commit,
working directory clean
In this chapter you have covered a lot of ground. You started by creating a
repository on GitHub to host your first project. Then you cloned it on your
PC creating the project local working directory to which you added the
source code and committed a new version to the local repository.
Finally you pushed the new version to the remote repository on GitHub.
You have learned that committing a new version of a project to the local
repository is a two-stage process:
1. First you need to add the changes to Git's index (staging area) with
the git add command.
2. Then you run git commit to store the new version into the repository.
In this chapter you will add content to the Phonetic Website project and
learn how to use Git to monitor project history, compare versions, undo
changes and update the central repository on GitHub.
4.1 Implementing a Feature Request
Let's start with the pilot's alphabet. Open pilots.html in Notepad or any
other text editor and locate the following section of code:
Save the changes and test that it works by loading pilots.html on a Web
browser. The new word you have just added should appear in the Pilot's
Alphabet page.
Do the same to add the letter D to the Cities' and Names' Alphabets by
editing cities.html and names.html. For example:
<!-- ALPHABET START -->
<li>Atlanta</li>
<li>Boston</li>
<li>Chicago</li>
<li>Detroit</li>
<!-- ALPHABET END -->
Save the changes and test that you can see the new words when browsing
the website.
4.2 Updating the Local Repository
We want to store the changes made in the previous section as a new version
of the website in the local repository. We have already been through this
process in the previous chapter: stage the changes and then commit.
The reason why Git uses two phases - staging and committing - to update
the repository is that by staging first you can group all related changes
into a single commit and give it a meaningful comment.
Open Git Bash and cd to the Phonetic Website project working directory.
Check the status of the working tree with git status. Note that you must
always run git commands from within the working directory of your
project, otherwise you will get a "not a git repository" error message.
$ cd ~/your-repo
$ git status
modified: cities.html
modified: names.html
modified: pilots.html
Git should flag the HTML files as modified and not staged. It also suggests
to use git add <file> to update the index for the next commit. Let's do it:
$ git status
On branch master
Changes to be committed:
modified: cities.html
modified: names.html
modified: pilots.html
Following the commit operation, the working directory should be clean (i.e.
with no uncommitted changes) and the local repository should be ahead of
the remote repo on GitHub by 1 commit.
$ git status
On branch master
Your branch is ahead of
'origin/master' by 1 commit.
(use "git push" to publish
your local commits)
nothing to commit,
working directory clean
You can check the history of commits in a project by running the git log
command:
$ git log
commit cdc81d848c8554d304ea1e8cd886ccd2ffd51884
Author: Your Name <your email>
Date: Day Month Time
Added letter D
commit dd340395aeb30be571ff7536d686d566adb8b362
Author: Your Name <your email>
Date: Day Month Time
commit 5e4ad5a92a7070d209479a3fa330ac05cc9f3c96
Author: Your Name <your email>
Date: Day Month Time
Initial commit
For each commit it shows the long hash (an alpha-numeric string that is
generated by Git to uniquely identify an object) as well as the commit
author, date and comment.
If the history display takes more than one screen you can scroll down by
striking the [Enter] key. Type q to quit history viewing at any point.
The --oneline switch displays the short hash (the first seven characters of
the long hash). The short hash is sufficient to uniquely identify a commit.
Note that hash values for your project will be different as the algorithm
that computes it takes into account local variables.
The --max-count switch displays only the most recent commits. For
example, the switch --max-count=2 displays the two most recent commits
only.
The --author switch displays only the commits from a specific user.
The --decorate switch adds information about branches and the HEAD
pointer. To understand what that means we need to briefly introduce the
concept of branches in Git.
4.4 What is a Branch?
You can list the branches in the local repository by running the git branch
command. The --remote switch shows the local copy of the branches in the
remote repository on GitHub.
$ git branch
* master
Right now your repository has only a single branch called master both
locally and remotely. When you first setup a repository Git creates the
master branch automatically for you. All Git projects have a master
branch by default. If your Git project were a tree the master branch would
be the trunk. From the trunk it is possible to manually create separate lines
of development as independent branches with their own separate commits.
If you look again at the output of the git log command using the --oneline
and --decorate switches you will see in the output HEAD -> master. It
means that HEAD is currently pointing to the master branch. The contents
of the working directory reflect the last commit on the checked out branch
plus any changes you have made.
It is important at this point to note that there are several versions of a file
in a Git project:
1- The working directory version (the one that you use for editing)
2- The staged version (after you run git add <file> to add the file to the
index for the next commit)
The git diff command shows changes between the working directory, index
and commit versions.
If you have followed the exercises so far, you should at this point have a
clean working directory without any uncommitted changes in the Phonetic
Website project. To explore the capabilities of the git diff command we
need to create some additional versions. We will do that in the following
exercises.
Exercise
Implement the following feature request: add the letter E to the Phonetic
Website. To execute you need to modify the HTML files pilots.html,
cities.html and names.html in the same way as you did when you added the
letter D. For example:
pilots.html
<!-- ALPHABET START -->
<li>Alpha</li>
<li>Bravo</li>
<li>Charlie</li>
<li>Delta</li>
<li>Echo</li>
<!-- ALPHABET END -->
cities.html
<!-- ALPHABET START -->
<li>Atlanta</li>
<li>Boston</li>
<li>Chicago</li>
<li>Detroit</li>
<li>Eldorado</li>
<!-- ALPHABET END -->
names.html
<!-- ALPHABET START -->
<li>Andrew</li>
<li>Brigitte</li>
<li>Charles</li>
<li>David</li>
<li>Eva</li>
<!-- ALPHABET END -->
Browse the website to verify that the changes are correct, check the status
and add the files to the index with git add:
$ git status
On branch master
Changes not staged
modified: cities.html
modified: names.html
modified: pilots.html
Exercise
Now implement another feature request: add the letter F to the Phonetic
Website. For example you can add the words "Foxtrot" to pilots.html,
"Fillmore" to cities.html and "Fred" to names.html.
Browse the website to test the changes. This time do not stage. Check the
status of the working directory:
$ git status
On branch master. . .
Changes to be committed:
modified: cities.html
modified: names.html
modified: pilots.html
modified: cities.html
modified: names.html
modified: pilots.html
As you can see from the output of git status we have now two different
versions of the HTML files. One version contains the staged changes in the
first exercise. The second one contains the unstaged changes we did in the
second exercise. And of course we also have the committed versions as
shown by git log:
We can now use git diff to view the differences between the various
versions. We will take the pilots.html file as an example.
To view the changes in pilots.html that have not been staged type:
@@ -22,6 +22,7 @@
<li>Charlie</li>
<li>Delta</li>
<li>Echo</li>
+ <li>Foxtrot</li>
<!-- ALPHABET END -->
The output shows that the line containing the word Foxtrot has been added
as indicated by the plus (+) sign. That is expected as we have added the
word Foxtrot without staging the change in the second exercise.
To view the changes in pilots.html between the index (staging area) and the
last commit use the --cached switch:
@@ -21,6 +21,7 @@
<li>Bravo</li>
<li>Charlie</li>
<li>Delta</li>
+ <li>Echo</li>
<!-- ALPHABET END -->
The output shows that the line containing the word Echo has been added as
indicated by the plus (+) sign. That is expected since we have staged this
change in the first exercise.
@@ -21,6 +21,8 @@
<li>Bravo</li>
<li>Charlie</li>
<li>Delta</li>
+ <li>Echo</li>
+ <li>Foxtrot</li>
<!-- ALPHABET END -->
The output shows that the lines containing the words Echo and Foxtrot
have been added as indicated by the plus (+) signs. The git diff command
interprets HEAD as the hash of the last commit. The result is expected since
the last commit occurred before we made the changes in the previous two
exercises.
To view the changes between the last commit and the commit before last
type:
@@ -20,6 +20,7 @@
<li>Alpha</li>
<li>Bravo</li>
<li>Charlie</li>
+ <li>Delta</li>
<!-- ALPHABET END -->
The output shows that the line containing the word Delta has been added as
indicated by the plus (+) sign. The git diff command interprets HEAD~1 as
the hash of the commit before last.
You can also use the short hash obtained from git log to view the difference
between any two commits:
@@ -20,6 +20,7 @@
<li>Alpha</li>
<li>Bravo</li>
<li>Charlie</li>
+ <li>Delta</li>
<!-- ALPHABET END -->
In the example above the hashes dd34039 and cdc81d8 identify the before
last and last commits. In your repository these identifiers will be different
so use the hashes you get from running the git log command in your
computer.
We have now covered all the basic use cases. Before moving to the next
section let's commit the pending changes. First commit the letter E change
request which is already staged:
You should now have a clean working directory and a longer history log:
$ git status
One of the reasons to track a project's history is to have the ability to undo
changes. It is a common situation in software development: you change
something, you test the change and it does not quite work the way you
expected. You then decide to revert the code back to the previous version.
With Git you can easily accomplish that.
Open pilots.html in a text editor and delete all the words except "Alpha" so
that the alphabet ends up looking like this:
Save the change and test how the page looks now when browsing the
website. You should have only the letter A left in the Pilot's Alphabet. We
will learn how to recover the lost words using Git undo features. In this
simple case you could just manually add the lost words again to fix the
problem. Suppose however the change involved editing dozens of lines of
code in various parts of the source file. In that case it would be impossible
to correct it manually. In such a situation Git undo features become
invaluable.
$ git status
Browse the website to verify that the words have been recovered. The git
status command should report a clean working directory.
$ git status
The git reset command has removed the file from the index (staging area)
however the unwanted change is still in the working directory. To recover
the lost words, you still need to repeat the process for undoing un-staged
changes and run git checkout to restore the last committed version:
$ git status
The working directory should be now clean. Browse the website to verify
that the words have been recovered.
The --no-edit flag prevents the commit editor to popup. Browse the website
to verify that the unwanted change has gone.
As you can see from the git log output a revert commit was added to
cancel the effect of the unwanted change. Git is designed to never loose
history so it keeps the commit you want to revert and overrides it with a
new one.
We have covered in this section three basic undo change scenarios for a
single file. Later in the book we will learn how to navigate history and get
the whole project back to a previous version. In the next section we will
learn how to give a meaningful name to stable versions of a project.
4.7 Tagging Versions
You can use Git to attach a tag to easily identify a stable version of a project
with the git tag command. The tag can be any arbitrary string but
traditional versioning schemes assign a number sequence starting with zero.
For instance, suppose you want to assign the tag v0.1 to the version
(commit) where you added the letter F to the Phonetic Website project. First
you need to find out what the short hash is for the corresponding commit
from the history log (in my case fccad77) and then type the following
command to tag it:
You can now refer to this version of the website using the assigned tag
instead of the short hash.
$ git status
On branch master
Your branch is ahead of
'origin/master' by 5 commits.
(use "git push" to publish
your local commits)
nothing to commit,
working directory clean
Next we looked at what versions of a file exist in Git and how to view the
differences between versions using the git diff command. Then we learned
how to undo changes under a number of different scenarios using the git
checkout, git reset and git revert commands.
# unstage changes
$ git reset HEAD <file>
Branches are useful when you want to try out changes to a project without
affecting the main code base in master. In this section we are going to create
a separate branch to experiment with deleting, moving and renaming files in
a Git project.
Open Git Bash and change to the Phonetic Website project working
directory. Run the following commands to create a new branch called "test"
and switch to it:
$ git branch
master
* test
The git branch <name> command creates a new branch with the specified
name (in this case test).
The git branch command without any arguments lists all local branches in
the project. A star (*) is placed next to the current branch.
We are now free to make changes without affecting the main code base in
the master branch. To demonstrate this feature we will make some structural
changes and move all the stylesheets of the website up one level:
$ git mv style/*.css ./
The git mv command can be used to move or rename files. The change is
immediately added to the index for the next commit. The git rm command
can be used to delete files from the command line.
Note: you can also use Windows Explorer, an IDE or any other file
management tool to move, rename or delete files in a Git project. Git will
detect the changes just as well. The only difference is that if you use the
git mv and git rm commands the changes will automatically be staged for
you, whereas if you use other tools you will have to stage the changes
manually with git add before you can commit them.
Test the website in a browser. You will find that the Phonetic Website has
lost its styling and both the menu and the alphabet pages are displayed as
simple lists without formatting. This is because the location of the CSS files
has changed and the stylesheet links in the HTML files are broken.
Let's fix this problem. Open each HTML file in turn and locate the <head>
section at the top of the file:
<head>
<title>Pilot's Alphabet</title>
<meta charset="UTF-8">
<link href="style/site.css" rel="stylesheet">
<link href="style/pilot.css" rel="stylesheet">
</head>
Remove the style directory path from each stylesheet link as follows:
<head>
<title>Pilot's Alphabet</title>
<meta charset="UTF-8">
<link href="site.css" rel="stylesheet">
<link href="pilot.css" rel="stylesheet">
</head>
The style directory is now empty and can be removed by entering the
following Git Bash command (or if you prefer you can delete the folder
using Windows Explorer):
$ rmdir style
$ git status
On branch test
Changes to be committed:
renamed:
style/cities.css -> cities.css
modified:
cities.html
renamed:
style/names.css -> names.css
modified:
names.html
renamed:
style/pilot.css -> pilot.css
modified:
pilots.html
renamed:
style/site.css -> site.css
The history of the test branch now shows the new commit:
Note that HEAD is pointing to the last commit in the test branch (HEAD -
> test). The working directory should now be clean:
$ git status
On branch test
nothing to commit,
working directory clean
You can see the extent of the changes you have made on the test branch of
the Phonetic Website project in the previous section by listing the contents
of the working directory:
$ ls -a
All the CSS files have moved one level up and the style directory no longer
exists.
Let's switch back to the master branch using the git checkout command and
list the contents of the working directory again:
$ ls -a
As you can see from the listing, the content of the working directory has
been restored. The style directory is back containing all the CSS files. The
stylesheet references in the HTML code have not changed. Test the website
in a browser to verify that it is working correctly.
Every time you switch branches Git will fetch from the repository
database the contents of the working directory from the last commit on that
branch.
When you run the git checkout <branch> command the HEAD pointer
moves to the last commit of the branch you are checking out, and the
content of the working directory reflects what is pointed to by HEAD.
Below is a graphic illustration of the switch process. In the first picture the
branch feature-2 is the current branch. HEAD is pointing to the last commit
on feature-2:
$ git branch
* master
test
The commits for each branch are safely stored in the project repository
database (the .git directory). No matter what changes you make on another
branch, everything will be restored when you switch back unless you decide
to merge the changes. Merging is the subject covered in the next section.
5.3 Merging
Let's assume we have two new feature requests to implement. The first is to
add the letters G, H, I and the second is to add the letters J, K, L to the
Phonetic Website. We will implement each of these feature requests on
separate branches.
Open Git Bash and change to the Phonetic Website working directory.
Make sure the current branch is master and that the working directory is
clean. Create two new feature branches using the following commands:
$ git status
On branch master
nothing to commit,
working directory clean
$ git branch
g-h-i
j-k-l
* master
test
Exercise
Repeat the same process to add the letter H (e.g. "Hotel", "Houston" and
"Henry") then stage and commit.
Repeat the same process to add the letter I (e.g. "India", "Illinois" and
"Isabel") then stage and commit.
On completion of this exercise the g-h-i branch history should look like the
following:
$ git status
On branch g-h-i
nothing to commit,
working directory clean
We are ready now to merge the changes on to the master branch. First
switch back to master and check the history:
Now browse the Phonetic Website and you will find that it is still on the
letter F. This will change once we have merged the changes from the g-h-i
branch. The git merge command lets you integrate separate timelines of
development into a single branch. To incorporate the feature you have
implemented in the g-h-i branch into master we just need to run the
following command:
Updating...
Fast-forward
cities.html | 3 +++
names.html | 3 +++
pilots.html | 3 +++
3 files changed, 9 insertions(+)
Let's look at the history of the master branch after the merge:
You can see that the commits you made on g-h-i have been integrated. If
you browse the website now you can confirm that the changes have been
incorporated, including the words up to the letter I.
Git does a good job of merging changes in different parts of the same file
automatically. Sometimes a conflict may arise during the merging
operation. That happens when the changes you made collide with the
existing code in the branch you are merging into. In the next section we will
see how to resolve conflicts.
5.4 Resolving Conflicts
We will now implement the second feature request outlined in the previous
section: to add the letters J, K and L to the Phonetic Website. We have
already created a feature branch named j-k-l to make this change so let's
switch to it:
As you can see, the history of the j-k-l branch mirrors the history of the
master branch at the point of split when the branch was created. However
it does not contain the latest changes on master derived from the merge with
the g-h-i branch.
Exercise
Do not worry about the gap caused by the missing G, H and I letters. We
will sort that out when merging the changes on to the master branch.
Browse the website to test the changes, stage and commit with the comment
"Added letter J". Refer to Chapter 4 if you need a refresher.
Repeat the same process to add the letter K (e.g. "Kilo", "Kingston" and
"Kate") then stage and commit.
Repeat the same process to add the letter L (e.g. "Lima", "Lincoln" and
"Laura") then stage and commit.
On completion of the exercise the j-k-l branch history should look like the
following:
On branch j-k-l
nothing to commit,
working directory clean
We are ready now to merge the changes on to the master branch. First
switch back to master and check the history:
If you browse the Phonetic Website now you will find that it is still on the
letter I. Let's merge the feature we implemented on the j-k-l branch and see
what happens:
Auto-merging pilots.html
CONFLICT (content):
Merge conflict in pilots.html
Auto-merging names.html
CONFLICT (content):
Merge conflict in names.html
Auto-merging cities.html
CONFLICT (content):
Merge conflict in cities.html
Automatic merge failed;
fix conflicts and then
commit the result.
The git merge output is flagging conflicts on the three HTML files. That
was to be expected as we made changes in the same lines of code on both
branches. Running git status will tell you that the files have been modified:
$ git status
On branch master
Unmerged paths:
Let's fix pilots.html first. Open it in your favourite text editor. The
conflicting changes will be clearly marked by Git:
<<<<<<< HEAD
<li>Golf</li>
<li>Hotel</li>
<li>India</li>
=======
<li>Juliet</li>
<li>Kilo</li>
<li>Lima</li>
>>>>>>> j-k-l
The section of code between <<<<<<< HEAD and ======= shows the
code in the current branch. The section of code between ======= and
>>>>>>> j-k-l shows the conflicting changes merged from the j-k-l branch.
Resolving the conflict in this case is quite easy. As we want to keep both
changes we just need to delete the Git markings and retain the sequence of
words in alphabetical order.
Open pilots.html in your favourite editor and delete the Git markings so that
the alphabet list ends up looking like this:
<!-- ALPHABET START -->
<li>Alpha</li>
<li>Bravo</li>
<li>Charlie</li>
<li>Delta</li>
<li>Echo</li>
<li>Foxtrot</li>
<li>Golf</li>
<li>Hotel</li>
<li>India</li>
<li>Juliet</li>
<li>Kilo</li>
<li>Lima</li>
<!-- ALPHABET END -->
Delete also the Git markings in cities.html and names.html to resolve the
conflicts and save. Browse the website to test. When all looks good, add the
files to the index and commit as usual:
To conclude let's tag this version as v0.2 and update the remote repository
on GitHub. You may be required to enter your username and password:
Your local and remote repositories are now synchronized and you should
have a clean working tree:
$ git status
On branch master
Your branch is up-to-date
with 'origin/master'.
nothing to commit,
working directory clean
Note that we have pushed changes to the remote repository master branch
only. It is also possible to push local branches to a remote repository with
the command git push origin <branch>. We will be using this technique in
the next chapter when collaborating with open source projects on GitHub.
5.5 Summary
The know-how we have covered so far is enough to get you started working
in real-world Git projects. In the next chapter we will look at how to use
GitHub to collaborate with others in open source projects.
# create a branch
$ git branch <branch name>
GitHub has a social element too. Users have a profile page listing their
repositories and contributions. For instance my profile page is located at:
https://github.com/robertovormittag
You can follow other GitHub users when you land on their profile page by
clicking on the [Follow] button located just below the GitHub menu.
Clicking on one of the user's repository links will take you to that repository
home page, where you can look at the code, issues, pull requests and
activity levels.
On each repository page you will find three buttons located below the
GitHub menu:
Click the [Watch] button to follow the repository and be notified of all
events related to that project, such as comments on a pull request, or an
issue being raised. You can [Unwatch] the project at any time if no longer
interested.
Click on the [Star] button to bookmark the project. You can access your
bookmarked projects by selecting "Your stars" from the GitHub profile
menu.
In the next section we will explore the function of the [Fork] button.
6.2 Forking a Repository
You can contribute to any public repository on GitHub. To do that you first
need to fork the repository. When you fork a repo GitHub creates a copy of
the repository under your own account, and you are free to push changes to
it just like you do with your own remote repositories.
To see how that works I have setup a public repository containing a simple
website project at the following URL:
https://github.com/robertovormittag/open-website
Make sure you are logged in to your GitHub account, then visit the above
URL to get to the repository home page. You can look at the project
description and browse the source code to familiarize with it. When ready
click on the [Fork] button located on the top right-hand corner.
GitHub will create a copy of the forked repository under your account with
the following URL:
https://github.com/your-user-name/open-website
You now have full access to this repository just as if you had created it
yourself and you can start contributing to the project by pushing changes to
it. This is the subject of the next section.
6.3 Making Changes
Once you have forked a project you need to clone it in order to work on
your PC, using your favourite IDE and development tools.
We will now clone the Open Website project you forked in the previous
section. Start Git Bash, change to your home directory and enter the
following command taking care of replacing "your-user-name" with your
GitHub username to clone the open-website fork:
$ cd open-website/
$ ls
$ ls style
site.css
Open index.html with your Web browser to see how the website looks like.
Let's now take a look at the branches and the history of this project. As this
is a shared public project, you will find that a number of commits already
exist. Remember that you can limit the number of commits listed by git log
using the --max-count switch. The following command will display only
the last 5 commits:
$ git branch
* master
The repository has only the master branch. Making changes to the master
branch however would make it difficult for the owner of the project to
manage contributions from several developers. For this reason, developers
contributing to open source projects on GitHub make changes on a separate
branch (called a topic or feature branch) without merging their work.
Once the changes are complete they push the topic branch to GitHub and
propose the changes by opening a pull request to the project owner. We will
use this workflow step-by-step to propose changes to the Open Website
project.
The first thing you need to do is to create a topic branch. You can give the
branch any name you like. The following commands create a topic branch
called "new-para" and switch to it:
You can start now making changes without affecting the main code base in
the master branch. Let's say you simply want to add a new paragraph
element to the home page index.html.
Browse the website to check that you are happy with the changes you made
then stage and commit as usual:
Check again history and status. You should see your new commit in the log
and a clean working directory:
$ git status
On branch new-para
nothing to commit,
working directory clean
The next step is to push the topic branch to your GitHub fork by
entering the commands below. Replace new-para with the name of your
topic branch if you have named it differently. You may be asked to enter
your GitHub username and password:
$ git remote
origin
You are ready to propose your changes. This is the subject of the next
section.
6.4 Opening a Pull Request
In GitHub "opening a pull request" is the way to propose changes you made
to a forked repository in a topic branch.
From your GitHub profile page click on the link to your open-website fork:
Now click on the [Branch] button and you should see listed the topic branch
you pushed in the previous section. Select it and your commit comment
should appear next to index.html.
GitHub will open a pull request page on the project owner account with
information about your proposed change. This page compares the original
master branch with your topic branch and reports any conflict. At the
bottom of the page you can see a summary of the commits and changes that
you have made on the topic branch.
You can enter a title and a comment describing your proposed change.
Providing a good description will help the owner of the project to
understand what your change is all about and decide accordingly.
Enter a title (it defaults to your commit comment) and a description then
click the [Create pull request] button.
GitHub will open the pull request page on the owner's repository
[Conversation] tab. This page is used to record conversations between the
owner and contributors to the project. Here you can enter additional
comments or close the pull request if you change your mind.
Let's see now what happens on the receiving side of a pull request. Suppose
you are the repository owner. When someone opens a pull request
proposing changes to your code, GitHub will send you an email and a
notification with a link to the pull request page where you can check all the
relevant information and take appropriate action.
The pull request page provides three tabs: [Conversation] is where all the
conversation and events that take place during the lifetime of the pull
request are displayed; [Commits] show all commits belonging to this pull
request and [Files changed] show the differences between the original
master version and the topic branch version of the changed files.
You should review all the information on the pull request page and decide
what to do. There are three possible actions you can take: start a
conversation, merge the change or reject the request.
Start a Conversation
You can click on the [Conversation] tab to send a comment to the
contributor if you want to discuss or ask clarifications about the change.
You can also send comments by clicking on the [Files changed] tab,
selecting a specific line of code and clicking on the plus sign [+] to enter
and send a comment.
In case you do not agree with the proposed change you will find a [Close
pull request] button at the bottom of the [Conversation] tab in the pull
request page. By clicking on it the collaborator will be notified and the pull
request closed.
Open source projects can be very active with lots of contributions from
many developers being merged all the time. When your pull request stays
around for some time, your fork can become out of date. In the event that
you want to make more changes you need to synchronize it with the
original project so that you can work on the latest code base. This is the
subject covered in the next section.
6.6 Keeping your Fork Synchronized
In the event that your pull requests are long lived you need to keep your
fork synchronized with the original upstream repository, so you can work
and make changes on the latest version of the code. We will use the Open
Website project fork you created earlier to illustrate how to ensure that your
fork is in sync.
Open Git Bash and change to your local open-website working directory.
Add the original upstream repository (the one from which you have forked)
as a new remote repo. The following command adds the original open-
website repository on my account as a new remote identified by upstream:
$ cd ~/open-website
$ git remote -v
origin
https://github.com/your-user-name/open-website (fetch)
origin
https://github.com/your-user-name/open-website (push)
upstream
https://github.com/robertovormittag/open-website (fetch)
upstream
https://github.com/robertovormittag/open-website (push)
Now fetch the latest code changes from the upstream remote:
* [new branch]
master -> upstream/master
Remote commits to master will be stored in a local branch named
upstream/master. You will need to merge it to your local master branch. The
following command lists both local and remote tracking branches:
$ git branch -a
* master
remotes/origin/HEAD -> origin/master
remotes/origin/master
remotes/upstream/master
To complete the sync, first checkout your local fork master branch:
Then bring your local master branch in line with the upstream repository by
merging the latest changes:
You need to manually resolve any conflicts, then stage and commit. We
covered conflict resolution in Section 5.4.
Your local master branch will now be in sync with the original project. You
can make more changes on a new topic branch or reuse an existing one. If
you want to merge the latest code into an existing topic branch you just
need to check it out and merge from master.
Exercise
Let's add another paragraph to index.html. Test the change and when you
are happy with it stage and commit then push the topic branch to your fork
on GitHub as you did previously.
Go to your forked Open Website project repository page on GitHub and
select the topic branch you have just pushed using the [Branch] button.
Then open another pull request to the owner of the project, as you did in the
previous section.
You can continue repeating this cycle of fetch -> merge -> change -> push
-> open pull request for as long as you want to contribute to a project
ensuring that you are always working on the latest version of the code.
6.7 Summary
GitHub is the largest social coding and open source collaboration platform
today. It allows you to follow the activities of other developers and
bookmark repositories of interest.
To keep your fork synchronized with the original project you need to add
the upstream repository as a remote repo. Once you have done that, you can
keep contributing to the project over time by following a continuous cycle
of fetching the latest code base, merging, making the changes and pushing
your feature branch to GitHub to open a new pull request.
In this chapter we will explore additional commands that are at the heart of
the Git toolset.
All the previous examples used static website projects to illustrate Git
commands in order to create a scenario as close as possible to that of a real-
world project. In this chapter we will use instead simple text files that you
can quickly create and edit from the Git Bash command line to make it
easier to see the effect of Git commands on both the working directory and
commit history.
7.1 Initializing a Local Repository
To initialize a local repository open Git Bash and change to your home
directory. Create a new folder that will serve as the working directory of
your new local repo. In the following example we create a folder named
"alphabets". Change to the new project directory and type the command git
init to initialize the local repository. Here is the full command sequence:
$ cd ~
$ mkdir alphabets
$ cd alphabets
$ git init
The output should confirm that a new Git repository has been initialized.
Now list the contents of the working directory, and you will see that a .git
folder has been setup by Git. This is the repository database which we will
explore later:
$ ls -a
./ ../ .git/
$ git status
On branch master
Initial commit
nothing to commit
Your new local Git repository is ready to use. You can now start adding
files to it.
Tip: To create an empty file from the Git Bash command line use the
Linux touch <filename> command.
The following command sequence creates an empty text file which is then
staged and committed to the new repository on the master branch:
$ touch pilots.txt
Tip: To quickly open a text file in Notepad from the command line type
notepad <filename>.
Tip: To quickly insert a line into a text file from the command line use
the Linux command echo "some text" >> <filename>.
Tip: to quickly display the contents of a text file from the command line
use the Linux command cat <filename>.
$ cat pilots.txt
Alpha
Repeat the above command sequence to insert two more words: "Bravo"
and "Charlie" and add two more commits so that the file contents and
history log of the master branch end up looking like the following:
$ cat pilots.txt
Alpha
Bravo
Charlie
$ git status
On branch master
nothing to commit,
working directory clean
In the next section we will use this local repository to learn how to bring an
entire project back to a previous version.
You have already used the git checkout command to switch branches and
to undo unstaged changes in a file. It can also be used to go back in history
in the form git checkout <commit> where <commit> is the hash (or
identifier) of a commit in the revision history.
This use of the checkout command will cause the files in the working
directory to go back to the state they were when the specified commit
took place. Once in this state, called detached HEAD state, any commit
you make will be discarded as soon as you perform another checkout
operation. To preserve any changes you make in a detached HEAD state
you need to create a branch. The following exercise will illustrate how this
works.
Exercise
Open Git Bash and change to the local repository created in the previous
section. List the log history:
$ cd ~/alphabets
If you run git checkout <hash> passing the hash of the commit when you
inserted the word "Bravo" (in my history it is the commit b02d358, in your
history it will be a different hash) you will see that the pilots.txt file
contents go back to that point in time, as demonstrated by the following
command sequence:
$ git checkout b02d358
$ cat pilots.txt
Alpha
Bravo
The git checkout output warns of the detached HEAD state. Any new
commits made whilst in this state will be lost as soon as you perform
another checkout operation. We will demonstrate this with an experiment.
Let's add a new word to pilots.txt and commit:
$ cat pilots.txt
Alpha
Bravo
Delta
$ cat pilots.txt
Alpha
Bravo
Charlie
To keep the changes you make whilst in a detached HEAD state you
need to create a branch.
* delta
master
Note the git checkout -b delta command. The -b option creates a new
branch (named delta) and immediately switches to it. The current branch
is now delta as you can see from the output of the git branch --all
command. Let's make the same change we did earlier to add the word
"Delta" to pilots.txt and commit:
$ echo "Delta" >> pilots.txt
$ cat pilots.txt
Alpha
Bravo
Delta
Now the Delta change will be preserved even after you switch branches:
$ cat pilots.txt
Alpha
Bravo
Charlie
$ cat pilots.txt
Alpha
Bravo
Delta
To bring the changes into the master branch you just have to merge. In this
case Git will flag a conflict in the third line of pilots.txt as the content
differs between the two branches (we have "Charlie" in master and "Delta"
in delta). Let's switch to master and merge:
$ git checkout master
$ cat pilots.txt
Alpha
Bravo
>>>>>>> HEAD
Charlie
=======
Delta
<<<<<<< delta
We need to resolve the conflict manually. Open pilots.txt with Notepad and
delete the markings added by Git leaving both words ("Charlie" and
"Delta") and then commit:
$ notepad pilots.txt
$ cat pilots.txt
Alpha
Bravo
Charlie
Delta
$ git status
On branch master
nothing to commit,
working directory clean
The commit 3d33cc70 (delta) Delta added is the merge from the delta
branch.
The commit 345ed2b (HEAD -> master) Delta added is the commit
following conflict resolution.
* master
In the next section we will use this repository to examine Git commands
that can modify the commit history of a branch.
7.3 Changing History
There are two Git commands capable of re-writing the commit history of a
branch: git reset and git rebase.
Reset
We will demonstrate what git reset does with an experiment. Open Git
Bash and change to the local "alphabets" repository created earlier. Dump
the log history of the master branch:
Suppose you want to reset the history of commits to the point where you
added the word "Charlie" (hash 5b41f59 in my history log, it will be
something else in your repo). All you have to do is typing the following
command replacing the hash value with the one in your history log:
Now check again the history log and you will find that the last two
commits have been removed. The pilots.txt file contents have gone back
to the point prior to the merging operation we performed in the previous
section:
$ git log --oneline --decorate
$ cat pilots.txt
Alpha
Bravo
Charlie
Rebase
The command git rebase can be used in place of git merge to integrate the
work you have done in separate branches. Whereas the merge operation
preserves history, the rebase operation can modify the target branch
history by inserting intermediary commits.
We will explore the difference between git merge and git rebase with an
exercise.
Exercise
Open Git Bash and change to the local "alphabets" project working
directory created earlier. List the history log of the master branch:
$ cd ~/alphabets
Create a new branch called "cities" and switch to it. Add a file named
"cities.txt" containing the word "Atlanta", then stage and commit. Here is
the full command sequence:
$ touch cities.txt
Now switch back to the master branch. List again the history log:
Let's now build some more history on the master branch. Insert the word
"Delta" to pilots.txt then stage and commit:
Repeat again the above command sequence to add the words "Echo" and
"Foxtrot" to pilots.txt on separate commits so that the history log of the
master branch ends up looking like the following:
Suppose we want to integrate the work we have done on the cities branch
on to the master branch. This time, instead of git merge, we will use git
rebase. Let's see what happens:
Now list the history log of the master branch again and observe the results:
With git merge the commits would have been added to the history of the
master branch at the point of merge, leaving the previous history intact. The
rebase operation instead has replayed the commits we did on the cities
branch on top of the master branch effectively modifying its history as if
we had done all the work sequentially on the master branch.
The git stash command can be used to "stash away" half-baked changes
that you are not prepared to commit yet but want to keep for later.
Open Git Bash and change to the local "alphabets" repository working
directory created earlier. If you have followed all the previous exercises to
completion you should have a clean working directory containing two text
files namely pilots.txt and cities.txt:
$ cd ~/alphabets
$ ls
cities.txt pilots.txt
$ git status
On branch master
nothing to commit,
working directory clean
Make some changes to both files (e.g. by adding new words) then stage but
do not commit, so that git status reports the following:
$ git status
On branch master
Changes to be committed:
modified: cities.txt
modified: pilots.txt
Suppose now you decide to start some other work from a clean working
directory but without loosing the changes you have made so far. You can
simply stash the changes away by typing:
$ git stash
$ git status
On branch master
nothing to commit,
working directory clean
But your changes have not been lost. You can view any modifications
stashed away with the following commands:
cities.txt | 1 +
pilots.txt | 1 +
2 files changed, 2 insertions(+)
When you decide to restore the work you have stashed away you can do so
by entering the command git stash apply:
On branch master
Changes not staged for commit:
modified: cities.txt
modified: pilots.txt
The git init command can be used to initialize a local private repository. We
learned how to use the git checkout <commit> command to go back in
time through the history of a project. This can be useful in a situation where
you want to try an alternative implementation. The changes must be made
on a separate branch to be persistent.
We have used the git reset command to change the commit history of a
branch and the git rebase command as an alternative way to integrate work
done on separate branches. Since both reset and rebase change the commit
history, they must only be used on local private short-lived branches,
never on public branches shared with other users on remote repositories
such as GitHub.
To conclude, we learned to use the git stash command to clean the working
directory and index whilst saving the changes made up to that point. The
saved changes can be viewed and restored at any time.
# Git commands
# deletes a branch
$ git branch -D <branch>
# Linux commands
Start Git Bash and change to the open-website project working directory we
created previously. List the contents of the .git directory:
$ cd ~/open-website
$ ls .git
The listing shows five sub-directories (ending with a forward slash) and a
number of files, one of which is HEAD. As we have previously mentioned,
HEAD contains a pointer to the current branch. You can look at the HEAD
contents by using the Unix cat command:
$ cat .git/HEAD
ref: refs/heads/master
From the output you can see that HEAD is pointing to the master branch.
The refs directory contains references to commits for local and remote
tracking branches. You can use the Linux find command to see the refs
directory tree:
$ find .git/refs
.git/refs
.git/refs/heads
.git/refs/heads/master
.git/refs/remotes
.git/refs/remotes/origin
.git/refs/remotes/origin/HEAD
.git/refs/remotes/origin/master
.git/refs/tags
To see the reference to the most recent commit in the master branch type:
$ cat .git/refs/heads/master
f26bc487f9ba866acd512a85461a2c77d463c3fe
The long alpha-numeric string that you get (the value will be different in
your repository) is the hash of the last commit on master. If you look at the
history of the master branch using the git log command you will find that
the short hash of the most recent commit matches it:
The actual commits are stored in the objects directory. Git stores four types
of objects in this directory: commits, trees (directories), blobs (files) and
tags.
Looking inside the objects directory we will find a folder with the name of
the corresponding hash for each of the objects that make up the version
history for the project.
$ find .git/objects
.git/objects
.git/objects/0d
.git/objects/0d/178985f0b0df73aa1b9dbcfdc1fdc0c472437a
...
...
.git/objects/f2
.git/objects/f2/2bdf8b448b6ca035124c165d9d0d734d95e92f
.git/objects/f2/6bc487f9ba866acd512a85461a2c77d463c3fe
.git/objects/info
.git/objects/pack
You can find out the type of each of the objects listed above using the git
cat-file command with the -t option. You only need to specify the first
seven characters of the object's hash. Let's use the hash of the last commit
in master (use the hash in your repository not the one in the example):
commit
The git cat-file output confirms that it is a commit object. You can look at
its contents using the -p option:
tree 40be475a6f44f18dfbf609fa3abc7662862d8402
parent 4f41c728f8e14ed5ec2f05e911c68001bd997a65
author ...
committer ...
Some comment
Let's take a look at the tree object using the first 7 characters of the hash:
The tree object is pointing to a blob which is the site.css file located inside
the style directory.
It is also possible to see the contents of any of the blob objects. Let's look at
site.css:
/* general */
body {
font-family: "Trebuchet MS", Verdana, sans-serif;
font-size: 16px;
background-color: dimgrey;
color: #696969;
padding: 3px;
}
...
We have followed the trail from HEAD all the way to the last commit
object on master and the associated directory and files. This is how Git
stores the project history and is able to retrieve any previous versions.
Before closing this section let's take a quick tour of some of the other
components of the .git directory.
The config file contains project-specific configuration. The index file is the
staging area where changes are grouped before doing a commit. The hooks
directory contains scripts that are executed before or after a specific Git
command. The info directory contains additional information about the
repository.
The logs directory contains the history of each branch as displayed by the
git log command. And this takes us to the subject of the next section: how
to display a more sophisticated view of the project history.
8.2 A More Sophisticated History View
We have often used the git log command with the --oneline and --decorate
options to view the history of commits in a project. However there is a way
to type less and get more information out of git log by using aliases. An
alias is an alternative name that you can give to a Git command.
Aliases can be setup in the Git configuration file .gitconfig located in your
home directory. Start Git Bash and enter the following commands to locate
it:
$ cd ~
$ ls -a .gitconfig
.gitconfig
The .gitconfig file (as well as all other text files in Git Bash) uses the Unix
end-of-line (EOL) character and will not display correctly on some
Windows editors such as Notepad. In this case, we need to convert the EOL
characters to the Windows format before editing. First make a back up copy
of .gitconfig:
$ cp .gitconfig .gitconfig.bak
Now run the following command to convert the EOL characters to the
Windows format:
$ unix2dos .gitconfig
You can now open .gitconfig using Notepad or any other text editor. The
file contains the configuration information we have entered in Section 2.3.
We will now add an [alias] entry named hist as shown in the example
below. Pay attention to the syntax, including the single quotes and white
spaces:
[user]
name = your name
email = your email
[core]
autocrlf = true
safecrlf = false
editor = notepad
[alias]
hist = log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
Save the change and convert the file back to its original Unix EOL format:
$ dos2unix .gitconfig
The hist entry that you have just configured is a shorthand (alias) for the git
log command using the --pretty option with a specification to format the
output.
To try the new alias change to a Git project working directory and type git
hist:
$ cd ~/projects/alphabets/
$ git hist
* 92c1424 2016-03-23 | Foxtrot added (HEAD -> master) [Author]
* 620a381 2016-03-23 | Echo added [Author]
* f747e7b 2016-03-23 | Delta added [Author]
...
* 30c26be 2016-03-21 | Initial commit [Author]
The git log alias we just created shows the date of the commit as well as the
name of the author. You can still append additional switches such as the --
max-count to limit the output to the more recent commits only:
In a project there are often files that you do not want Git to track because
there is no interest in keeping a version history of them. They can be binary
files produced by a compiler or other temporary files generated
automatically by code editors and other tools. You do not want all this
"noise" in your working directory to go into your project history repository.
To prevent Git from tracking such files and directories all you have to do is
to create a text file named .gitignore in the root of the working directory of
your project. Inside .gitignore you can specify the names of files and
directories you want Git to ignore.
Let's see how this works with a couple of exercises. Suppose we want to
ignore all files with a .bak extension. Start Git Bash and change to the
alphabets project working directory we used earlier:
$ cd ~/alphabets
$ ls
cities.txt pilots.txt
$ cp pilots.txt pilots.bak
$ ls
On branch master
Untracked files:
pilots.bak
To tell Git to stop tracking files with a .bak extension first create a
.gitignore file:
$ touch .gitignore
$ ls -a
Then open .gitignore in a text editor and add *.bak to it. The file should
look like this:
The asterisk (*) is a wildcard character that matches any filename, therefore
any file in this project with the .bak extension will be ignored.
$ git status
On branch master
Untracked files:
.gitignore
As you can see git status is now ignoring *.bak files. It is good practice to
commit the .gitignore file to the repository:
You can also instruct Git to ignore an entire directory. Let's create a new
directory called temp containing two files using the command sequence
below:
$ mkdir temp
$ touch temp/temp.log
$ touch temp/temp.old
$ git status
On branch master
Untracked files:
temp/
Let's instruct Git to stop tracking the temp/ directory. Open .gitignore in a
text editor and add the directory name to the list:
*.bak
temp
Check that Git is no longer tracking the temp directory (and any files and
folders contained in it):
$ git status
On branch master
Changes not staged for commit:
modified: .gitignore
On branch master
nothing to commit,
working directory clean
8.4 Git Workflows
Git is a lot more flexible compared with other version control systems
because of its distributed nature, and can be adapted to a variety of different
ways of organizing how each team member contributes to a project. In this
section we will review the basic Git workflows used in this book.
Centralized Workflow
This workflow also uses a central repo as the official project repository.
Each developer contributes code using the following routine:
Forking Workflow
In the Forking Workflow there is a public remote central repository that acts
as the official repository for the project. Developers in the project fork the
central repo creating a public remote repository of their own, which mirrors
the central repo. Then each developer clones its own fork, starts editing
source code and committing changes locally in a topic branch, following a
similar routine to that used in the Feature Branch workflow.
Once the feature has been implemented, developers push the topic branch
to their own remote fork and initiate a pull request to notify the project
owners that a new feature is ready to be integrated.
The project owners will pull the contributor's changes into their local
repository for testing. If the tests pass, they will merge and push the
changes to the remote central repository master branch. Only the project
owners can push to the central remote repo directly.
Centralized, Feature Branch and Forking are just some of the workflows
that are possible to use when collaborating with other developers in a team-
based project using Git. Often in real-world projects, teams tend to mix and
match aspects of the basic workflows reviewed here to best suit the project
size and team structure.
8.5 Summary
Next, we learned how to customize the output of the git log command to
obtain more information about the history of commits, and how to instruct
Git to ignore files and directories we do not want to track.
# using an alias
$ git <alias>
***
CHAPTER 9
Centralized Workflow
The Centralized Workflow, as we saw in Section 8.4, uses a remote central
repository as the official project repo. In this chapter we will illustrate step
by step how to put together what we have learned in this book to implement
a Centralized Workflow that you can easily adapt and use in real-world
projects with a team of any size.
Production (or Live): the official version of the software that your users
will be running (or visiting, in case of a website).
Development: where the software is deployed while you and your team
are adding new features to make sure that all the changes are well integrated
and work well together.
Note that in this context a feature means either a new piece of functionality
or a bug fix.
Our remote central repository on GitHub will need therefore at least two
branches. By default all Git repositories have a master branch which will
contain the codebase that is deployed to production. We will also need an
additional branch to contain the codebase that is currently under
development and testing, which we will call the dev branch. The codebase
in the dev branch is the one that you will deploy to the local, development,
testing and any other pre-production environment.
Let's now look at how to implement the Centralized Workflow step by step.
9.1 Create the Central Repository
The repository will be created with a single branch, the master branch. As
discussed in the introduction this branch will be used to store the codebase
that is in production.
In the next section we will see how to add the dev branch to the central
repository.
9.2 Add the Development Branch
Once the project remote central repository has been created we need to add
the dev branch which, as discussed in the introduction, will store the
codebase that is under development and testing.
To create the new dev branch in GitHub click on the Code tab of your
repository. Open the branch drop-down and in the input box "Find or create
a branch" type the name of the new branch − dev − and hit enter as in the
following illustration.
The new dev branch will be created from the master branch. In the next step
we will assign push privileges to the remote central repository to the other
members of the team.
9.3 Add Project Collaborators
You need now to define who can contribute code to the project, and each
developer in the team must have a GitHub account to be setup as a
collaborator. To accomplish that go to the repository [Settings] tab and
select Collaborators from the side menu.
Search each collaborator by their GitHub user name, and click on [Add
collaborator]. Each added collaborator will receive a notification from
GitHub that they must accept.
Once the invitation is accepted the collaborator is granted push access to the
central repository. When all collaborators have been setup, your team will
be ready to start coding. First though they need to setup the project locally
on their PC.
9.4 Clone the Remote Repo
Each developer in the team must clone the remote repository on his/her PC
before development starts.
Please refer to Section 3.2 for details on how to clone a remote repository.
Once the cloning operation is complete you can verify the branches in your
local repo with the following command:
$ git branch -a -v
* master 5a1183b some comment
remotes/origin/HEAD -> origin/master
remotes/origin/dev e69374b comment
remotes/origin/master 5a1183b comment
You will find a local master branch already created, which is checked out
by default, alongside a local copy of the remote branches origin/dev and
origin/master.
The local master is a tracking branch that mirrors the master branch in the
remote repository. We have already encountered the concept of tracking
branches in Chapter 6, but let's revisit it here.
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean
The output confirms that you are currently in the master branch and that it
is up-to-date with its tracked remote branch origin/master.
The git branch command with the −vv switch allows you to check which
tracking branches you have in your local repo:
$ git branch -vv
The output of the command tells you that you have only one tracking
branch at the moment (master) which is tracking the remote branch
origin/master. It also shows you the identifier and comment of the last
commit.
Tracking branches are a clever feature of Git making it easy for developers
to keep in synch with the rest of the team, as we will examine shortly.
9.5 Create a Tracking Branch for dev
At this point we already have a tracking branch for the remote master in the
central repository. Each team member must now create a local development
branch that tracks the remote dev branch. You can give the local branch any
name you like. The following command creates a local branch with the
name local_dev:
$ git checkout -b local_dev origin/dev
Note that the above command also makes local_dev the current branch, as
you can see from the output of git status:
$ git status
On branch local_dev
We have now two remote tracking branches one for master and one for dev:
$ git branch −vv
Now the team is ready to start development. Each team member can start
implementing a feature in their local repository. As stated before, everybody
must work on the dev branch until the feature is tested and ready to go live.
In this section we will see how you can keep your local dev branch
synchronized with the rest of the team.
You should commit your code frequently, and regularly check that the
commits in the remote repo match what you have in the local repository. It
is a good idea to have a clean working directory before updating your local
branches, so commit your changes once they have been successfully tested.
You can easily check the commit history of the remote dev branch on
GitHub and compare with your local repository. On GitHub select the
[Code] tab, then select the branch − in this case dev − then click on
Commits to see the commit history for that branch. You will see something
similar to the following illustration:
Now check the history of your local dev branch − refer to Section 4.3 and
Section 8.2 for details.
The above command will update your local copy of the remote branches
(the ones identified by remotes/origin). Next you can run the command git
branch −vv to compare. If your local dev branch is behind make it current
with the git checkout command and merge the changes in the remote dev
branch by executing the following command:
$ git merge origin/dev
In case there are conflicts you need to resolve them. That can happen when
you and a team mate have changed the same source file. For a refresher on
merging and conflict resolution please refer to Section 5.3 and Section 5.4.
At the end of this process your local repo will be in synch with the rest of
the team, and you can carry on with your work. Repeat this process
frequently, at least once a day. You can follow the same steps to keep the
local master branch updated after the software has been released to
production.
9.7 Push Code to the Remote Repo
On branch local_dev
Your branch is ahead of 'origin/dev' by 1 commit.
(use "git push" to publish your local commits)
nothing to commit, working directory clean
What you do next depends on whether your team adopts code reviews or
not. In case you do not need to have your code reviewed by another
member of the team you can simply push your code directly to the remote
dev branch using the following command (you may be prompted to enter
your GitHub credentials):
$ git push origin HEAD:dev
Once the push command has been executed you will be able to see your
new commits listed on the remote dev branch on GitHub.
In case you need to have your code reviewed before it gets merged into the
remote dev branch, follow the alternative procedure described in the next
section.
9.8 Open a Pull Request
You only need to follow the procedure described in this section if your team
adopts code reviews.
Commit your code as described in the previous section but do not push it to
the remote repo.
Create a feature branch out of your local dev branch. You can give it any
name you like, although I would recommend choosing a name that is as
descriptive as possible. The command to use is:
$ git checkout -b readme_update
Push the feature branch to GitHub using the command illustrated below
taking care of replacing "readme_update" with the name of your feature
branch. You may be prompted to enter your GitHub credentials:
$ git push origin readme_update
You can now switch back to your local development branch with the
checkout command:
$ git checkout local_dev
On GitHub click on the [Code] tab of your project repository, then select
the feature branch you have pushed in the previous step. Click on the [New
pull request] button. Select the dev branch as the destination (base) branch.
Enter a description of your changes on the "Open a pull request" page and
then click [Create pull request].
The team members responsible for reviewing your code can now click on
the [Pull requests] tab of the project, look at your code and decide to merge
or ask for modifications. This process is described in detail on Section 6.5.
Once the review process is complete your code will be merged into the dev
branch by the reviewer.
You can now go back to your local repo and carry on working on another
feature.
9.9 Merge to Master
The code in the remote dev branch on GitHub will be used to build, deploy
and test the software during development on a number of pre-production
environments, typically using continuous integration tools such as Jenkins
or Travis CI.
Once the new features have been successfully tested and pushed to the
central repository dev branch the team is ready to deliver the new release.
At this point we must open a pull request on GitHub to merge the code from
the remote dev to the remote master branch which houses the production
codebase. To accomplish that go to the GitHub project repository and select
the [Code] tab, click on branches then click on the [Open pull request]
button on the dev branch.
On the "Open a pull request" page make sure you select master as the base
branch, enter any additional comments as required and click on the [Create
pull request] button. On the "Pull requests" page click on the [Merge pull
request] button to merge the commits in dev into the master branch from
where you can build and deploy the new release to the production
environment, either manually or using your favourite continuous integration
tool.
Now the two remote branches (master and dev) are synchronized, and you
are ready to start working on the next release. Go back to Section 9.6 and
follow the instructions to synchronize your local master and dev branches,
checkout the dev branch and start working on a new feature for the next
release. Keep repeating this cycle during the entire lifetime of your project.
9.10 Summary
Equipped with this knowledge you are now prepared to apply the
Centralized Workflow to a real-world software project whatever the size of
your team and the development methodology you use.
***
Next Steps
Where To Go From Here
Overall Git provides a very rich command set. It is beyond the scope of this
book to cover the whole spectrum, but you are now well equipped to
explore more advanced features at your own pace as you become a more
experienced and confident user.
The full Git command set is detailed in the official documentation website.
Some commands are used only by system administrators or in exotic
workflows, so you may never need them.
You have learned to interact with Git and GitHub from the command-line,
which allows you to work under different operating systems (Git Bash on
Windows as well as the Bash console on Linux and Mac computers). The
commands are exactly the same. The knowledge you have gained enables
you to work with Git and GitHub on real-world projects at a professional
level.
Make sure you keep practicing the exercises in this book until you are
confident that you have mastered the commands and workflows illustrated.
Start by using Git to manage the version history of your own projects and
showcase some of your work on GitHub.
Explore the GitHub showcase page, fork a project that is of interest to you
and start exploring the source code. You will find all sorts of cool projects
there including games, editors, programming languages, databases and a lot
more. Contribute to some of your favourite projects.
Do not forget to visit regularly this book's companion website to get
updates, additional material and to post questions, comments and
suggestions.
I hope you have enjoyed reading this book and that it has helped you in
your learning journey.