A practical introduction to git
Anna Kennedy
Overview
- Version control
- Git concepts
- Using git locally
- Using git with a (Gitlab) remote
- Undoing mistakes
- Branches
- Caring for your commit history
Version control
- Version control means that we keep a history of all the changes made in a set of documents.
- Git is one example of a version control system
- Pre-version control:
- CVS / SVN:
- Centralised storage with revision history
- Git:
SVN
Centralised storage with revision history
- client-server model
- central server houses master repo
- developer laptop is the client
- check-out -> modify -> check-in
- Advantages / disadvantages
- + simple system
- - difficult to work offline
- - difficult to work collaboratively
- - advanced functionality quite hard to get right
Git
Distributed version control
- every copy of the project is a full repository
- Advantages / disadvantages
- - steep learning curve
- + save changes locally when working offline
- + branching
- + merging
- + every copy is a full backup
- + very fast
Projects in git
- Any directory can be brought under git's control
- both new and existing projects
- git init initialises a directory with a .git subdir
- This directory is now a local git repository
- The .git folder contains metadata about the project
- To remove a project from git, simply delete the .git folder
Exercise: Initialise a new project in git
- Make a new directory and move into it
- Initialise the directory as a git repository
$ mkdir myproject
$ cd myproject
$ git init
Initialized empty Git repository in /home/myname/projects/myproject
$ ls -la
drwxr-xr-x 4096 Apr 5 09:54 .
drwxr-xr-x 4096 Apr 4 12:40 ..
drwxr-xr-x 4096 Apr 5 08:13 .git
Configure global git variables
- Git requires that version history is associated with users
- Thus we need to set name and email as global variables
- Trying to use git without this results in the following annoying message:
Your name and email address were configured automatically based
on your username and hostname. Please check that they are accurate.
You can suppress this message by setting them explicitly:
$ git config --global user.name "Your Name"
$ git config --global user.email you@example.com
Exercise: Check / set your name and email settings
- Use git config -l to see if you've already set your name and email address
- If you need to set them, use
$ git config --global user.name "Your Name"
$ git config --global user.email you@example.com
Checking the current status
- Running git status at any point shows the current state of the directory
- It also often gives hints about how to proceed or undo steps
$ git init
Initialized empty Git repository in /home/myname/projects/myproject
$ git status
On branch master
Initial commit
nothing to commit (create/copy files and use "git add" to track)
Getting help
- Access the man pages on the command-line like:
$ git help
The most commonly used git commands are:
add Add file contents to the index
branch List, create, or delete branches
checkout Checkout a branch or paths to the working tree
$ git help add
NAME
git-add - Add file contents to the index
SYNOPSIS
git add [-n] [-v] [-f] [-i] [-p] [--] [...]
Adding files to the project
- git add filename to add files to staging
- git add . to add everything
Exercise: Add files to the project
- Make a file inside the directory "helloworld.txt"
- Add this file to git
$ vim helloworld.txt
$ git status
On branch master
Untracked files:
(use "git add file..." to include in what will be committed)
helloworld.txt
$ git add helloworld.txt
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD file..." to unstage)
new file: helloworld.txt
Committing the changes to the local repo
- After a file is added to staging, we then have to commit the changes
- git commit and then enter commit message on the next screen
- git commit -m "commit message" to do both steps at once
Exercise: Commit the changes to your local repo
- Commit this file to your local repository
$ git commit -m "First commit"
$ git status
On branch master
nothing to commit, working directory clean
Basic git rhythm
- Make changes
- Stage the changes with git add
- Commit the changes with git commit
View commit history
- git log shows a history of commits
- git log -p shows what has changed with each commit
- git log -2 only shows the last two commits
$ git log -p
commit 320a8f00a4a0de7c9b3f39851d3bc164d0769697
Author: myname myname@ak-rl.example.com
Date: Mon Apr 4 12:52:23 2016 +0200
First commit
diff --git a/file1.txt b/file1.txt
new file mode 100644
index 0000000..907b308
--- /dev/null
+++ b/file1.txt
@@ -0,0 +1 @@
+Hello everyone!
A little more about commits
- Commits are basically snapshots
- Each commit is identified by a unique hash
- The most recent commit is called HEAD
- The two before that are called HEAD~ and HEAD~2
- We can use both the reference number and the HEAD notation to refer to previous commits
$ git log
commit 320a8f00a4a0de7c9b3f39851d3bc164d0769697
Author: myname myname@ak-rl.example.com
Date: Mon Apr 4 12:52:23 2016 +0200
First commit
Amend the commit message
- If you accidentally did a commit with a mistake in the commit message
- git commit --amend lets you change it
Exercise: Edit your most recent commit
- Edit your most recent commit message
$ git commit --amend
Allow user login
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
Exercise: Make and view a more interesting commit history
- So far we literally only have one commit - let's make a bit more to work with
- Make two more files, and commit them
- Edit one file, and commit that
- Make two files and only commit one of them
- Now commit the other one
- Use git log and git status to follow the history as you go
Comparing files
- git diff compares current files with the latest committed version
- After you've made a change to a file, but before it's committed
- Use 'git diff' to see what the difference between the files is
$ echo "Pleased to meet you all." >> helloworld.txt
$ git diff
diff --git a/file1.txt b/file1.txt
index 404c772..2d48565 100644
--- a/file1.txt
+++ b/file1.txt
@@ -1,2 +1,3 @@
Hello everyone!
+Pleased to meet you all.
Exercise: Compare files
- Make a change to a file
- Use 'git diff' to see what the difference between the file now and the last committed version
$ git diff
diff --git a/file1.txt b/file1.txt
index 404c772..2d48565 100644
--- a/file1.txt
+++ b/file1.txt
@@ -1,2 +1,3 @@
Hello everyone!
+Pleased to meet you all.
Stashing your work
- We like to try and keep our commits meaningful (more on this later)
- but if you're in the middle of working on something and you need to pause or switch branches
- you can save your work-in-progress state with git stash
- Can stash multiple events
- git stash list to view them
- git stash apply to pick up your most recent stash
Exercise: Stashing your work
- Make some changes to a file
- Stash your changes
- View the stash list
- Verify the directory is at the state of the latest commit
- Apply the stash to regain your work in progress
$ git stash
Saved working directory and index state WIP on master: e99a3ac Allow user login
HEAD is now at e99a3ac Latest hegnar configs
$ git stash list
stash@{0}: WIP on master: e99a3ac Allow user login
Using a remote repository
- So far we only used our local repository
- If we're interested in collaboration or backups, we need to look at communicating with a remote repository
- For this course, our remote is housed in Gitlab
- Otherwise the remote could be
- plain git
- gitolite
- Gitorious
- Github
Gitlab
- Gitlab provides a web-gui frontend
- Lots of useful collaborative and exploratory features
Creating a remote in Gitlab
- Pretty straightforwards to create a new remote in Gitlab
Exercise: Create a new remote repository
- Within the Gitlab UI, make a new project
- Name your project, leave it as private
- Notice that it tells you how to proceed!
- Within your local repository, tell git where the remote is:
$ git remote add origin git@gitlab.example.com:myname/myprojectname.git
# Set a new remote
Pushing changes from the local to the remote repo
- So far all our work is only in our local repository
- Use git push to send our commits to the remote repository
- The first time we do this we need to set the upstream branch
- origin is git's default name for the remote repo
$ git push -u origin master
Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (5/5), 721 bytes | 0 bytes/s, done.
Total 5 (delta 2), reused 0 (delta 0)
To git@gitlab.example.com:myname/myproject.git
e99a3ac..f91ed8c master -> master
Exercise: Push changes from the local to the remote repo
- Use git push to send your commits to the remote repository
- The first time we do this we need to set the upstream branch with -u
- You should see your files in Gitlab now
$ git push -u origin master
Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (5/5), 721 bytes | 0 bytes/s, done.
Total 5 (delta 2), reused 0 (delta 0)
To git@gitlab.example.com:myname/myproject.git
e99a3ac..f91ed8c master -> master
Retrieving changes from the remote
- If changes have been made on the remote, we need to update our local repository to get those changes
- Use git pull to retrieve any changes
Exercise: pulling remote changes
- Edit a file in Gitlab
- Pulled the changes your local repository
- What do git status and git log say?
$ git pull
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From gitlab.example.com:myname/myproject
f91ed8c..3f522c5 master -> origin/master
Updating f91ed8c..3f522c5
Fast-forward
file1.txt | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
Pull behind the scenes
- So far we've done git pull to get the changes from the remote repository
- git pull really means git fetch then git merge
- We'll talk more about merging soon
- Be aware that sometimes doing a fetch and then a merge instead of pull can give you more insight into what git is doing
Viewing remotes on the command line
- git remote shows and can amend the remote repository
- This information is also available in .git/config
- origin is just git's default name for the remote
$ git remote -v
origin git@gitlab.example.com:anna/testrepository.git (fetch)
origin git@gitlab.example.com:anna/testrepository.git (push)
Basic git rhythm with remote
- Get updates with git pull
- Make changes
- Stage the changes with git add
- Commit the changes with git commit
- Push the changes to the remote with git push
Basic git rhythm with remote
Working collaboratively
- Git is an amazing tool for working collaboratively
- However then we need to start worrying about changes we didn't make
- If you're the sole committer to a repository, things are usually straightforwards
- However in a team, chances are that people will make conflicting changes at times
- Let's take a pragmatic look at two common issues
Troubleshooting 1: the push is rejected
- If you try to git push your changes and see something like:
$ git push
To git@gitlab.example.com:myname/myproject.git
! [rejected] master -> master (fetch first)
error: failed to push some refs to 'git@gitlab.example.com:myname/testrepository.git'
hint: Updates were rejected because the remote contains work that you do not have locally.
- Then you're trying to push before you've updated your local repo with remote changes.
- Fix this by doing a git pull first
$ git pull
$ git push
Troubleshooting 2: we got a merge conflict
- If we try to git pull (git fetch + git merge) and see something like:
Auto-merging file1.txt
CONFLICT (content): Merge conflict in file1.txt
Automatic merge failed; fix conflicts and then commit the result.
- Then we have a merge conflict we need to resolve
Let's see what the problem is:
$ git status
Unmerged paths:
(use "git add ..." to mark resolution)
both modified: file1.txt
$ git diff
diff --cc file1.txt
index 528038e,f324c51..0000000
--- a/file1.txt
+++ b/file1.txt
@@@ -1,4 -1,4 +1,8 @@@
++<<<<<<< HEAD
+hello
++=======
+ goodbye
++>>>>>>> 3911012567a3a5a0432bea090b82b58d74d5aa9d
- Resolution: decide what the file should contain
- Git writes markers in the file around conflicting sections
- Edit the file (including deleting the markers)
- Add, commit, push.
$ cat file1.txt
<<<<<<< HEAD
hello
=======
goodbye
>>>>>>> 3911012567a3a5a0432bea090b82b58d74d5aa9d
$ nano file1.txt
$ git add file1.txt
$ git commit -m "Fixing conflict"
$ git push
Working collaboratively
- With that in mind, let's move on to using a common repository
- For the purposes of this course, we've set up an example repo in Gitlab for us all to clone
- Then we can experiment with making and resolving conflicts
Cloning an existing repository
- If we want to work on an existing repository, we can either edit the files in Gitlab, or clone the repo locally.
- Editing in the UI tends to only be practical for very small, isolated changes.
- To clone a repo, find the correct URL in Gitlab, displayed ssh/http on the project page.
- SSH needs a key to have been set up, HTTPS needs a username and password.
$ git clone git@gitlab.example.com:myname/myproject.git
or
$ git clone https://gitlab.example.com/myname/myproject.git
Exercise: Clone a remote
- Clone the example repo to your local directory
- Make a new file, add and commit the changes locally
- If you're working in a group, remember to do git pull to get any changes made by other people
- Push the changes back to the remote
- Did you get any conflicts?
- Verify the new file is visible in Gitlab
$ git clone git@gitlab.example.com:myname/myproject.git
$ git add ...
$ git commit ...
$ git pull
$ git push
Exercise: resolving issues
- Make a file in the Github UI and commit it
- Without doing a pull, edit the same file locally
- Add and commit the file
- What happens when you try to push it back to the remote?
- Look at git diff and git status
- Do a git pull to get the remote changes
- This should cause a merge conflict
- Edit the file to resolve the conflict
- Now try adding, committing, and pushing again
Undoing mistakes
- We're human, and as such we make mistakes
- If you use git regularly, you will screw it all up from time to time
- Let's look at how to un-screw it up so it's not so terrifying next time
Git reset to undo local changes
- git reset file1.txt un-stages a file (undoes git add)
- git reset --soft HEAD~ undoes a git commit back to staging
- git reset --hard HEAD~ undoes a git commit entirely
- use the commit number to revert to a specific commit
- use HEAD~2 to undo the last two commits
$ git log
commit ecb76ad2f5b753c10f5f24094ea27f29f8c2d004
Second commit
commit 320a8f00a4a0de7c9b3f39851d3bc164d0769697
First commit
$ git reset --hard HEAD~
or
$ git reset --hard 320a8f00a4a0de7c9b3f39851d3bc164d0769697
$ git log
commit 320a8f00a4a0de7c9b3f39851d3bc164d0769697
First commit
Exercise: Experiment with git reset
- Create some new files
- Add the changes and then use git reset to un-add them
- Add and commit some changes then use git reset to undo
- How do the --soft and --hard flags change the behaviour?
- Can you reset the whole project back to a specific commit?
$ git reset file2.txt
$ git reset --soft HEAD~
$ git reset --hard HEAD~2
$ git reset --hard 320a8f00a4a0de7c9b3f39851d3bc164d0769697
Git revert
Undo a commit but retain history
$ git log
commit 28972f7572e9465cc6a0994ce5fcc31ba2d1afbb
"Adding deploy button"
$ git revert HEAD~
Revert "Adding deploy button"
This reverts commit 28972f7572e9465cc6a0994ce5fcc31ba2d1afbb.
$ git log
commit 78413bb6fec87d10f24ec5c62592795befdb66f7
Reverts "Adding deploy button"
commit 28972f7572e9465cc6a0994ce5fcc31ba2d1afbb
"Adding deploy button"
Exercise: using git revert
- Make a change in a file
- Add, commit, and push it
- Now use git revert to roll back that change
- Keep an eye on git log
- What does the history look like when you've finished?
- How is this different to git reset?
$ git revert HEAD~
Revert "Adding deploy button"
This reverts commit 28972f7572e9465cc6a0994ce5fcc31ba2d1afbb.
Branches
- One of git's most powerful features
- Allow simultaneous work in different environments
- Develop features
- Only merge in new code when you're ready
Git workflow: master branch only
master is git's default main branch
Git workflow: master branch and a hotfix branch
Git workflow: development and release branches
Branch commands
- git branch displays the checked-out branches
- git branch branchname creates a new branch
- git checkout branchname switches to the new branch
- git checkout -b combines the above commands to create a new branch and switch to it
$ git branch
* master
$ git branch featurebranch
$ git branch
* master
featurebranch
$ git checkout featurebranch
$ git branch
* featurebranch
master
Exercise: working with branches
Let's make a new feature branch of the existing repository that you cloned.
$ git checkout -b featurebranch
Switched to a new branch 'featurebranch'
$ git branch
* featurebranch
master
- Create some more files and edit existing files
- Add and commit your changes
- The first time you push, you'll need to set up the remote for this branch like:
$ git push --set-upstream origin featurebranch
Merging branches
- You've developed a whole new feature in the feature branch, and now it's time to merge it back into the master branch
- We can merge branches on the command line
- or use merge request in Gitlab (this is called 'pull request' in Github)
- Merge requests are more appropriate for merging to production branches as they encourage code review and thoughtful deploys
Exercise: merging branches in Gitlab
- In Gitlab, create a new merge request to merge your feature branch into the master branch
- Explore the request and check the changes you made
- Accept the merge request
- See the changes now appear in the main branch in Gitlab
- Locally switch back to the master branch with git checkout and do a git pull to get up-to-date
$ git checkout master
Switched to branch 'master'
Your branch is behind 'origin/master' by 2 commits, and can be fast-forwarded.
(use "git pull" to update your local branch)
$ git pull
Updating 7a59581..4d4adb3
Fast-forward
file2.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 file2.txt
Exercise: merging branches on the command line
- On the command line, ensure you're on the feature branch
- Make some changes in the feature branch, add, commit, and push them
- Switch back to the master branch, merge in the feature branch, and push back to the remote
$ git checkout master
$ git merge master featurebranch
$ git push
- Note that we merge into the branch we're standing in
Investigative tools
- Sometimes we need to work out exactly what's going on in a repository
- git log to see a history of commits
- git reflog to see a history of both commits and amends
- git blame filename to see who authored every line in a file
- git log --graph to see the relationship of branches
$ git log --graph
* commit f91ed8cabfd053cde63f168087885bfdd0358419
| Updated readme
|
* commit b8775a17e6fa9c9b6a9dd9f7b323a280a4db8faf
|\ Merge: 9544040 e99a3ac
| | Merge branch 'master' of gitlab.example.com:myname/myproject
| |
| * commit e99a3ac35a86c9bce4c8993ed8b6f1b7c8bf2a39
| | Allow user login
| |
* | commit 9544040efc0537d2f5a10975c0b779c3ccad0142
|/ Add remote users
|
Exercise: Investigative tools
- Try out the following tools:
- git log to see a history of commits
- git reflog to see a history of both commits and amends
- git log --graph to see the relationship of branches
- git blame filename to see who authored every line in a file
Working with multiple stable branches
- Alongside the main master branch, there can also be other stable branches
- A common workflow is to develop features in a feature branch and then merge up through the environments
Working with multiple stable branches
Feature vs stable branches
- Feature branches
- can be considered private branches
- a place to experiment
- therefore often messy commit history
- Stable branches
- can be considered public branches
- need to think a bit more carefully about commit history
- Further reading: sandofsky.com/blog/git-workflow.html
Caring for your
commit history
Why do we need to worry about our commit history?
- A well-maintained commit history will allow you to:
- track when features were introduced
- discover when bugs were introduced
- provides some amount of documentation
- A good commit history tells a story
Commit message guidelines
- Use one line to describe what the commit does
- Examples of good commit messages:
- Allow users to sign in remotely
- Fixed bug #123
- Added postcode field to address table
- Examples of bad commit messages:
- Commit4
- Typo
- dfkasdjefskjh
Commit history choices: Rebasing
- You've made a feature branch, and done some work on it
- but while you've been working on it, your colleagues have made some changes to master
- if you do a git merge all the master commits will go on top of your feature commits
- So instead we do a git rebase to apply the commits underneath your feature commits
- Let's demonstrate the difference with a diagram
Commit history choices:
Merging with fast-forwarding
- If no changes have occured on the master branch while feature development was underway
- git will try to merge branches with fast-forward
- All commits from the feature branch are visible in the master branch
- This can be useful, or can be clutter in the version history
- We can use --no-ff to submit a single commit for the merge
- Let's visualise the difference
Editing the commit history
- In order to tell a good story with your commit history, you will probably need to edit your commit history at times
- git commit --amend lets you amend your most recent commit
- git rebase --interactive for more complex history rewrites
- git push --force branchname to push your new historys
- NB NEVER rewrite your commit history after you've 'gone public' with the branch (ie, pushed to a shared repository).
Interactive rebase
- git rebase --interactive will drop you into a text editor where we can specify all the things we want to change
- Commands:
- pick = use commit
- reword = use commit, but edit the commit message
- edit = use commit, but stop for amending
- squash = use commit, but meld into previous commit
- fixup = like "squash", but discard this commit's log message
- exec = run command (the rest of the line) using shell
Interactive rebase
$ git rebase -i HEAD~3
pick 6c169da Fixed bug #234
pick 78413bb Typo in user prefs
pick 2bc4f9b Allow users to update their preferences
# Rebase 28972f7..2bc4f9b onto :w28972f7
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
# Note that empty commits are commented out
Rewrite a commit message
$ git rebase -i HEAD~3
reword 6c169da Fixed bug #234
pick 78413bb Typo in user prefs
pick 2bc4f9b Allow users to update their preferences
Fixed bug #234: do not allow remote access to db
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
$ git log
commit 6c169da
Fixed bug #234: do not allow remote access to db
Squashing commits together
$ git rebase -i HEAD~3
pick 6c169da Fixed bug #234: do not allow remote access to db
squash 78413bb Typo in user prefs
pick 2bc4f9b Allow users to update their preferences
Rebasing
# This is a combination of 2 commits.
# The first commit's message is:
Fixed bug #234: do not allow remote access to db
# This is the 2nd commit message:
Typo in user prefs
$ git log
commit 9107a08
Fixed bug #234: do not allow remote access to db
Typo in user prefs
commit 2bc4f9b
Allow users to update their preferences
Final Exercise: Rewrite history
- Try out git rebase --interactive
- Rewrite a historic commit message
- Squash some commits together
- Edit a previous commit
- Don't panic!
Summary
- Version control
- Git concepts
- Using git locally
- Using git with a remote
- Undoing mistakes
- Branches
- Caring for your commit history