Git Apprentice
Git Apprentice
Git Apprentice
Apprentice
By Jawwad Ahmed & Chris Belanger
Git Apprentice
Chris Belanger
Notice of Rights
All rights reserved. No part of this book or
corresponding materials (such as text, images,
or source code) may be reproduced or
distributed by any means without prior written
permission of the copyright owner.
Notice of Liability
This book and all corresponding materials (such
as source code) are provided on an “as is” basis,
without warranty of any kind, express of
implied, including but not limited to the
warranties of merchantability, fitness for a
particular purpose, and noninfringement. In no
event shall the authors or copyright holders be
liable for any claim, damages or other liability,
whether in action of contract, tort or otherwise,
arising from, out of or in connection with the
software or the use of other dealing in the
software.
Trademarks
All trademarks and registered trademarks
appearing in this book are the property of their
own respective owners.
Dedications
"For Russ and Skip."
— Chris Belanger
— Jawwad Ahmad
About the Author
https://github.com/raywenderlich/gita-
materials/tree/editions/1.0
Forum
We’ve also set up an official forum for the book
at forums.raywenderlich.com. This is a great
place to ask questions about the book or to
submit any errors you may find.
About the Cover
Mastering Git
If you’ve been using Git for a while, you may
choose to start in this section first. If you know
how to do basic staging, committing, merging
and .gitignore operations, then you’ll likely be
able to jump right in here. This section walks
you through concepts such as merge conflicts,
stashes, rebasing, rewriting history, fixing
.gitignore after the fact, and more.
Workflows
This section takes a look at some common Git
workflows, such as the feature branch workflow,
Gitflow, a basic forking workflow, and even a
centralized workflow. Because of the flexibility
of Git, lots of teams have devised interesting
workflows for their teams that work for them
— but this doesn’t mean that there’s a single
right way to manage your development.
Learning by doing
Above all, the best advice I can use is to work
with Git: find ways to use it in your daily
workflows, find ways to contribute to open-
source projects that use Git to manage their
repositories, and don’t be afraid to try some of
the more esoteric Git commands to accomplish
something. There’s little chance you’re going to
screw anything up beyond repair, and most
developers learn best when they inadvertently
back themselves into a technical rabbit-hole —
then figure out how to dig themselves out.
Chris Belanger
Section I: Beginning Git
This section is intended to get newcomers
familiar with Git. It will introduce the basic
concepts that are central to Git, how Git differs
from other version control systems, and the
basic operations of Git like committing,
merging, and pulling.
git clone
https://github.com/<your-
username>/programmer-jokes is the full URL to
the repository you want to clone. Breaking
that down further, https://github.com is the
cloud service that hosts the repository,
<your-username> is the owner of the fork of
this repository, and programmer-jokes is the
name of the repository you want to clone.
cd programmer-jokes
ls -l
You’ll see output similar to the following:
Creating a branch
Branches are, conceptually, copies of the
original contents of the repository. You can
work in a branch without affecting the original
contents of the repository until you are ready to
merge all your work back together again.
git branch
* master
my-joke
git branch
master
* my-joke
The asterisk tells you that you’re now working
securely inside the my-joke branch, and that your
changes won’t affect the master copy of the
repository.
Committing changes
Git’s got your back here. It knows that
sometimes you might add a change, but then
have second thoughts, or you might have other
changes that you want to stage at the same
time. That’s why Git separates the act of staging
files from the act of committing those changes.
What is cloning?
Cloning is exactly what it sounds like: creating a
copy, or clone, of a repository. A Git repository
is nothing terribly special; it’s simply a
directory, containing code, text or other assets,
that tracks its own history. Then there’s a bit of
secure file transfer magic in front of that
directory that lets you sync up changes. That’s
it.
Using GitHub
GitHub, at its most basic level, is really just a big
cloud-based storage solution for repositories,
with account and access management mixed in
with some collaboration tools. But you don’t
need to know about all the features of GitHub to
start working with repositories hosted on
GitHub, as demonstrated in the Git crash course
in the previous chapter.
To start, navigate to
https://github.com/raywenderlich/ideas and log
in with your GitHub username and password. If
you haven’t already set up an account, you can
do so now.
mkdir GitApprentice
ls
cd GitApprentice
git clone
~/MasteringGit $ ls
ideas
~/MasteringGit $ cd ideas
~/MasteringGit/ideas $ ls
LICENSE README.md articles books
videos
So there’s the content from the repository. Well,
the visible content at least. Run the ls command
again with the -a option to show the hidden .git
directory discussed earlier:
~/MasteringGit/ideas $ ls -a
. .git README.md books
.. LICENSE articles videos
cd .git
ls -F
You’ll see the following:
~/GitApprentice/ideas/.git $ ls -F
HEAD config hooks/ info/
objects/ refs/
branches/ description index logs/
packed-refs
Forking
You’ve managed to make a clone of the ideas
repository, but although ideas is a public
repository, the ideas repository currently
belongs to the raywenderlich organization. And
since you’re not a member of the raywenderlich
organization, the access control settings of the
ideas repository mean that you won’t be able to
push any local changes you make back to the
server. Bummer.
cd ..
~/GitApprentice $
rm -rf ideas
Execute ls to be sure the directory is gone:
~/GitApprentice $ ls
~/GitApprentice $
Key points
Cloning creates a local copy of a remote Git
repository.
What is a commit?
As you’ve probably guessed by now, a Git repo is
more than a collection of files; there’s quite a bit
going on beneath the surface to track the
various states of your changes and, even more
importantly, what to do with those changes.
To start, head back to the homepage for your
forked repository at https://github.com/[your-
username]/ideas, and find the little “11
commits” link at the top of the repository page:
- [ ] Hotubbing by tutorials
- [x] Advanced debugging and reverse enginee
ring
- [ ] Animal husbandry by tutorials
- [ ] Beginning tree surgery
- [ ] CVS by tutorials
- [ ] Fortran for fun and profit
- [x] RxSwift by tutorials
- [ ] Mastering git
- [ ] Care and feeding of developers
modified: books/book_ideas.md
Unmodified
Modified
Staged
Changes to be committed:
(use "git restore --staged <file>..." to un
stage)
modified: books/book_ideas.md
Ah, that seems a little better. Git recognizes that
you’ve now placed this change in the staging
area.
Changes to be committed:
(use "git restore --staged <file>..." to un
stage)
modified: books/book_ideas.md
git diff
--- a/books/book_ideas.md
+++ b/books/book_ideas.md
-- [ ] Mastering Git
+- [x] Mastering Git
git add .
Changes to be committed:
(use "git restore --staged <file>..." to un
stage)
modified: books/book_ideas.md
That looks good. There’s nothing left unstaged,
and you’ll just see the changes to
books/book_ideas.md that are ready to commit.
-- [ ] Mastering git
+- [x] Mastering git
+- [ ] Care and feeding of developers
git commit
:wq
Adding directories
You have directories in your project to hold
ideas for books, videos and articles. But it would
be good to have a directory to also store ideas
for written tutorials. So you’ll create a directory
and an idea file, and add those to your
repository.
mkdir tutorials
git status
ideas/LICENSE
ideas/README.md
ideas/articles/clickbait_ideas.md
ideas/articles/live_streaming_ideas.md
ideas/articles/ios_article_ideas.md
ideas/books/book_ideas.md
ideas/videos/content_ideas.md
ideas/videos/platform_ideas.md
.keep files
The solution to making Git recognize a directory
is clearly to put a file inside of it. But what if you
don’t have anything yet to put here, or you want
an empty directory to show up in everyone’s
clone of this project?
cd tutorials
Then create an empty file named .keep, using
the touch command for expediency:
touch .keep
ls -a
cd ..
Untracked files:
(use "git add <file>..." to include in what
will be committed)
tutorials/
Changes to be committed:
(use "git restore --staged <file>..." to un
stage)
new file: tutorials/.keep
commit dbcfe56fa47a1a1547b8268a60e5b67de0489
b95
Author: Chris Belanger <chris@example.com>
Date: Sun Jun 16 06:51:54 2019 -0300
commit c47084959448d2e0b6877832b6bd3ae70f70b
187 (origin/master, origin/HEAD)
Author: Chris Belanger <chris@razeware.com>
Date: Thu Jan 10 10:32:55 2019 -0400
commit 629cc4d309cdcfe508791b09da447c3633448
f07
Author: Chris Belanger <chris@razeware.com>
Date: Thu Jan 10 10:32:17 2019 -0400
commit 57f31b37ea843d1f0692178c99307d96850ec
a57
Author: Chris Belanger <chris@razeware.com>
Date: Fri Jan 11 10:16:13 2019 -0400
Key points
A commit is essentially a snapshot of the
particular state of the set of files in the
repository at a point in time.
images/favicon.ico
images/header.jpg
images/footer.jpg
images/profile.jpg
styles/admin.css
styles/frontend.css
scripts/main.js
scripts/admin.js
scripts/email.js
touch books/management_book_ideas.md
git add .
git status
Changes to be committed:
(use "git restore --staged <file>..." to un
stage)
new file: books/management_book_ideas.md
modified: videos/content_ideas.md
git reset
Execute the following command to remove the
change to books/management_book_ideas.md
from the staging area:
git log
Changes to be committed:
(use "git restore --staged <file>..." to un
stage)
modified: videos/content_ideas.md
Untracked files:
(use "git add <file>..." to include in what
will be committed)
books/management_book_ideas.md
mkdir website
mv videos/platform_ideas.md website
Untracked files:
(use "git add <file>..." to include in what
will be committed)
books/management_book_ideas.md
website/
videos/platform_ideas.md (tracked)
videos/content_ideas.md (tracked)
videos/platform_ideas.md (deleted)
videos/content_ideas.md (tracked)
website/platform_ideas.md (untracked)
mv website/platform_ideas.md videos/
Changes to be committed:
(use "git restore --staged <file>..." to un
stage)
renamed: videos/platform_ideas.md -> we
bsite/platform_ideas.md
Untracked files:
(use "git add <file>..." to include in what
will be committed)
books/management_book_ideas.md
rm articles/live_streaming_ideas.md
And then execute git status to see what Git’s
reaction is:
Untracked files:
(use "git add <file>..." to include in what
will be committed)
books/management_book_ideas.md
Changes to be committed:
(use "git restore --staged <file>..." to un
stage)
deleted: articles/live_streaming_ideas.
md
Untracked files:
(use "git add <file>..." to include in what
will be committed)
books/management_book_ideas.md
git rm articles/live_streaming_ideas.md
And, finally, commit that change with an
appropriate message:
Key points
The staging area lets you construct your
next commit in a logical, structure fashion.
Introducing .gitignore
Git’s answer to this is the .gitignore file, which
is a set of rules held in a file that tell Git to not
track files or sets of files. That seems like a very
simple solution, and it is. But the real power of
.gitignore is in its ability to pattern-match a
wide range of files so that you don’t have to
spell out every single file you want Git to ignore,
and you can even instruct Git to ignore the same
types of files across multiple projects. Taking
that a step further, you can have a global
.gitignore that applies to all of your repositories,
and then put project-specific .gitignore files
within directories or subdirectories under the
projects that need a particularly pedantic level
of control.
Getting started
Imagine that you have a tool in your arsenal
that “builds” your markdown into HTML in
preparation for deploying your stunning book,
tutorial and other ideas to a private website for
your team to comment on.
mkdir sitehtml
touch sitehtml/all-todos.html
Untracked files:
(use "git add <file>..." to include in what
will be committed)
sitehtml/
touch .gitignore
*.html
Save and exit. What you’ve done is to tell Git,
"For this project, ignore all files that match this
pattern." In this case, you’ve asked it to ignore
all files that have an .html extension.
Untracked files:
(use "git add <file>..." to include in what
will be committed)
.gitignore
*/*.html
touch index.html
Untracked files:
(use "git add <file>..." to include in what
will be committed)
.gitignore
index.html
mkdir htmlrefs
touch htmlrefs/utils.html
touch htmlrefs/.gitignore
!/*.html
Untracked files:
(use "git add <file>..." to include in what
will be committed)
.gitignore
htmlrefs/
index.html
git add .
touch ~/.gitignore_global
Head over to
https://github.com/github/gitignore and have a
look through the packages it offers. Sample files
that are appropriate for your OS can be found in
the Global subfolder of the repository.
1. Navigate to
https://github.com/github/gitignore/tree/m
aster/Global.
Key points
.gitignore lets you configure Git so that it
ignores specific files or files that match a
certain pattern.
commit ffcedc2397503831938894edffda5c5795c38
7ff
Author: Chris Belanger <chris@razeware.com>
Date: Tue Jan 22 20:26:30 2019 -0400
commit 84094274a447e76eb8f55def2c38b909ef94f
a42
Author: Chris Belanger <chris@razeware.com>
Date: Tue Jan 22 20:17:03 2019 -0400
commit 67fd0aa99b5afc18b7c6cc9b4300a07e9fc88
418
Author: Chris Belanger <chris@razeware.com>
Date: Tue Jan 22 19:47:23 2019 -0400
Limiting results
This is straightforward; simply execute the
following command to show the number of
commits you’d like to see, starting from the
most recent:
git log -3
commit 477e542bfa35942ddf069d85fbe3fb0923cfa
b47 (HEAD -> master)
Initial commit
But that’s still too much information. How could
you collapse this tree-like view to only see the
commit messages, but still see the branching
history? That’s right — by stacking the options
to git log.
git shortlog
crispy8888 (1):
Initial commit
.
.
.
I can see that I have 18 commits to this
repository — and then there’s this crispy8888
chap that created the initial repository. Well,
that was nice of him. There are likely other
changes from other users in there, including
yourself.
commit 43b4998d7bf0a6d7f779dd2c0fa4fe17aa3d2
453
Author: Chris Belanger <chris@razeware.com>
Date: Thu Jan 10 10:12:36 2019 -0400
Challenges
Speaking of brains, why don’t you exercise yours
and reinforce the skills you learned in this
chapter by taking on the four challenges of this
chapter?
[x]
You’ll need to search for the above string, and
you’ll need to use an option to not only show
the basic commit details, but also show the
contents of the changeset of the commit.
Key points
git log by itself shows a basic, vanilla view
of the ancestral commits of the current
HEAD.
What is a commit?
That question was asked and answered in a
shallow manner a few chapters ago, but it’s a
good time to revisit that question and explore
commits in more detail.
What is a branch?
The concept of a branch is massively simple in
Git: It’s simply a reference, or a label, to a
commit in your repository. That’s it. Really. And
because you can refer to a commit in Git simply
through its hash, you can see how creating
branches is a terribly cheap operation. There’s
no copying, no extra cloning, just Git saying
“OK, your new branch is a label to commit
477e542”. Boom, done.
Creating a branch
You created a branch before in the crash-course
chapter, but now you’re going to create a branch
and watch exactly what Git is doing.
ls .git/refs/heads/
This directory contains the files that point to all
of your branches. I get the following result of
two files in that directory:
master testBranch
cat .git/refs/heads/testBranch
git log -1
git branch
Without any arguments or options, git branch
simply shows you the list of local branches on
your repository. You should have the two
following branches listed:
* master
testBranch
* master
remotes/origin/HEAD -> origin/master
remotes/origin/clickbait
remotes/origin/master
Explaining origin
OK, what is this origin thing that you keep
seeing?
git remote -v
Key points
A commit in Git includes information about
the state of the files in your repository,
along with metadata such as the commit
time, the commit creator, and the commit’s
parent or parents.
The hash of your commit becomes the
unique ID, or key, to identify that particular
commit in your repository.
Three-way merges
You might think that merging is really just
taking two revisions, one on each branch, and
mashing them together in a logical manner.
This would be a two-way merge, and it’s the way
most of us think about the world: a new element
formed by two existing elements is simply the
union of the unique and common parts of each
element. However, a merge in Git actually uses
three revisions to perform what is known as a
three-way merge.
To see why this is, take a look at the two-way
merge scenario below. You have one simple text
file; you’re working on one copy of the file while
your friend is working on another, separate copy
of that same file.
Merging a branch
In this scenario, you’re going to look at the work
that someone else has made in the clickbait
branch of the ideas repository, and merge those
changes back into master.
commit e69a76a6febf996a44a5de4dda6bde8569ef0
2bc (HEAD -> clickbait, origin/clickbait)
Author: Chris Belanger <chris@razeware.com>
Date: Thu Jan 10 10:28:14 2019 -0400
commit 5096c545075411b09a6861a4c447f1af45393
3c3
Author: Chris Belanger <chris@razeware.com>
Date: Thu Jan 10 10:27:10 2019 -0400
cat articles/clickbait_ideas.md
You can see at the top of the graph that Git has
merged in your clickbait branch to master and
that HEAD has now moved up to the latest
revision, i.e., your merge commit.
cat articles/clickbait_ideas.md
Fast-forward merge
There’s another type of merge that happens in
Git, known as the fast-forward merge. To
illustrate this, think back to the example above,
where you and your friend were working on a
file. Your friend has gone away (probably hired
away by Google or Apple, lucky sod), and you’re
now working on that file by yourself.
Key points
Merging combines work done on one
branch with work done on another branch.
Pulling changes
Pulling changes is pretty much the reverse
scenario of pushing; Git takes the commits on
the remote repo, and it integrates them all with
your local commits.
ls .git
cat .git/FETCH_HEAD
git remote -v
crispy8888 https://github.com/crispy8888/id
eas.git (fetch)
crispy8888 https://github.com/crispy8888/id
eas.git (push)
origin https://www.github.com/belangerc/ide
as (fetch)
origin https://www.github.com/belangerc/ide
as (push)
Updating e69a76a..9ff4582
Fast-forward
articles/clickbait_ideas.md | 1 +
1 file changed, 1 insertion(+)
Key points
Git has two mechanisms for
synchronization: pushing and pulling.
Getting started
Many people will blindly tell you that the
easiest way to create a repository is to “Go to
GitHub, click ‘New Repository’, and then clone
it locally.” But, in most cases, you’ll have a small
project built up on disk before you ever think
about turning it into a full-fledged repository.
So this chapter will put you right into the
middle of your project development and walk
you through turning a simple project directory
into a full-fledged repository.
git init
contact: @crispytwit
No commits yet
Untracked files:
(use "git add <file>..." to include in what
will be committed)
LICENSE
README.md
css/
images/
index.html
git add .
Create mode
That create mode is something you’ve seen
before in the output from git commit, and have
probably wondered about. It’s of academic
interest only at this point; it really doesn’t
affect you much at this stage of your interaction
with repositories.
git remote -v
origin https://github.com/<your-username>/g
it-apprentice-web.git (fetch)
origin https://github.com/<your-username>/g
it-apprentice-web.git (push)
git branch
* master
Key points
Use git init to set up a Git repository.
Installing on Windows
To remain as platform-agnostic as possible,
you’ll install Git using one of the official
standalone installers. While you can use the
Chocolatey Package Manager for Windows, or
even download and install GitHub Desktop
(which installs Git on its own), you’ll install and
configure the plain-vanilla version of Git for
Windows.
https://github.com/git-for-
windows/git/releases/
https://github.com/git-for-
windows/git/releases/download/v2.27.0.win
dows.1/Git-2.27.0-64-bit.exe
Installing on macOS
There are a few ways to install Git on macOS.
There is a standalone installer for Git, but it’s
unfortunately quite out of date and isn’t
recommended anymore. Installing GitHub
Desktop will set up Git for you. However, the
two recommended methods for maximum
control are to either install with Xcode’s
command-line tools or use the Homebrew
package manager to install Git on your system.
xcode-select --install
git --version
git --version
Configuring credentials
There are a few things you can do once you’ve
installed Git to make your life a tiny bit easier;
they’re optional but highly recommended. One
of those things is to set up your GitHub
credentials in Git so they stick, saving you from
having to re-enter them frequently.
https://github.com/Microsoft/Git-
Credential-Manager-for-Windows
Conclusion
We hope this book has helped you get up to
speed with Git! You know everything you need
to know to effectively use Git on any sized
project and team.