name: inverse layout: true class: center, middle, inverse
Licensed under CC BY 4.0. Code examples: OSI-approved MIT license.
layout: false
- Distributed version control
- Non-bare and bare repositories
- Cloning repositories
- Collaboration with others
- Fetching changes from remote repositories
- Pushing changes to remote repositories
- Git implements a distributed version control
- All these topologies are possible
- In contrast to other version control tools we do not contribute to a repository through a lightweight working copy but rather by cloning the entire repository
- Until now we have met only non-bare repositories
$ git init # creates a non-bare repository
- A non-bare repository contains
.git
and "the normal files" - We can checkout local branches
- We can and do work inside non-bare repositories
- We can create a bare repository
$ git init --bare # creates a bare repository
- A bare repository contains only the
.git
part - We never work inside a bare repository
- We can clone repositories
- We can clone on the same host
host1$ cd /path/to/repo
host1$ git init # creates non-bare
host1$ git clone /path/to/repo /path/to/clone # creates non-bare
host1$ git clone --bare /path/to/repo /path/to/clone-bare # creates bare
- Or you clone from another machine via ssh (or via other protocols)
host2$ git clone ssh://user@host1/path/to/repo [/path/to/clone] # non-bare
- A clone is a full-fledged repository
- We can clone again and again
- Typically we create a star-topology or tree-topologies or triangular topologies
- Think of
git clone
as ascp -r
"plus" - We will see what the "plus" means
- By cloning we clone all commits, all branches, entire history
- We need a mechanism to communicate changes between the repositories
- We will pull or fetch updates from remote repositories
- There is a difference between pull and fetch and we will see what the difference is
- We will push updates to remote repositories
- We collaborate with other people through clones by pulling/fetching and pushing changes
- Everybody typically works on own clones
- Sometimes one person works on several clones (typically on different machines)
- We will only push to bare repositories
- Think of Dropbox as a clone which automatically pulls/pushes from/to a bare clone sitting somewhere on Dropbox servers
- Imagine there is a remote repository on host1
- We are ready to clone the repository
host2$ git clone ssh://user@host1/path/to/repo [/path/to/clone]
- We clone the entire history, all branches, all commits
git clone
creates pointersorigin/master
andorigin/dev
origin
refers to where we cloned from- It is a shortcut for
ssh://user@host1/path/to/repo
origin/master
andorigin/dev
are read-only pointers- They only move during
git pull
orgit fetch
orgit push
- Only
git pull
orgit fetch
orgit push
require network - All other operations are local operations
- Remote repository receives a new commit
- Pointer is moved (pushed) by someone else (could be you)
- Nothing changes on the local repository until we pull/fetch these changes over the network
$ git fetch origin
git fetch origin
updatesorigin/master
andorigin/dev
$ git merge origin/master
- In a second step we merge
origin/master
(in this case fast-forward)
$ git fetch origin
$ git merge origin/master
- This is equivalent to
$ git pull origin master
git pull
consists of two operations: agit fetch
followed by agit merge
- Summary:
git pull origin master
fetchesmaster
fromorigin
and merges it - There is always a
git merge
"hidden" ingit pull
- Many people will simply
git pull
, very careful people firstgit fetch
and inspect the commits before merging them - With Git you typically merge several times a day without even noticing
- We can commit locally
- These commits are not visible to others until we
git push
- Observe that
master
,origin/master
, andmaster
on remote repository are 3 different pointers
$ git push origin master
- Only now the remote
master
as well asorigin/master
move
- We commit
d7
and in the meantime remote receives commitc7
from someone else - What happens if we
git pull origin master
?
$ git pull origin master
- First we fetched (
origin/master
moved), then we mergedorigin/master
tomaster
(master
moved) - Local master and remote master are two different branches
- If they diverge, Git will merge them during
git pull
$ git pull --rebase origin master
- You can avoid this using
--rebase
- This will replay your unpublished local master commits at the end of
origin/master
- Note how
d8
changed tod8*
$ git checkout -b dev origin/dev
- We create and switch to local branch
dev
trackingorigin/dev
- If there is no local branch
dev
and there is a remote branchorigin/dev
, then both are equivalent
$ git checkout -b dev origin/dev
$ git checkout dev
- As we commit to
dev
the pointer moves whileorigin/dev
does not
$ git push origin dev
- We have pushed
origin/dev
forward
- What if you have created a local branch and want to make it public?
- Simply push it upstream
$ git checkout -b cool-branch # create and switch to cool-branch
$ git commit # work and commit
$ git push -u origin cool-branch # push to origin and set as upstream
- We can also delete remote branches
$ git push origin --delete cool-branch
git clone
copies everything and sets some pointers to remember where the clone came from
$ git remote -v
origin user@somehost:somerepo (fetch)
origin user@somehost:somerepo (push)
- You communicate commits with
git fetch
/git pull
andgit push
- All other Git operations are offline: you can work on a plane while your coworker is on vacation in North Korea
origin
refers to where you cloned from (but you can relocate it)origin/foo
is a read-only pointer to branchfoo
on originorigin
pointers only move when yougit fetch
/git pull
orgit push
- Branches are just pointers to commits
master
andorigin/master
are two different branchesHEAD
points to where we currently are
- We do some work on the local
master
and create two commits - Time arrow goes from left to right (commit arrows point to their parents)
- What happens if we try to
git push
?
- In this case
origin/master
received no other commits - First we see a fast-forward merge (pointer
origin/master
simply moves) - This is followed by a successful push
- However, what if
origin/master
receives other commits in the meantime? git push
is rejected:
$ git push
To https://github.com/user/repo.git
! [rejected] master -> master (non-fast-forward)
error: failed to push some refs to 'https://github.com/user/repo.git'
To prevent you from losing history, non-fast-forward updates were rejected
Merge the remote changes (e.g. 'git pull') before pushing again. See the
'Note about fast-forwards' section of 'git push --help' for details.
- We now have two options to integrate the commits on
origin/master
- First option:
git pull
- A
git pull
always consists ofgit fetch
andgit merge
- In this case a fast-forward merge was not possible and a merge commit is automatically created
- Second option:
git pull --rebase
- This moves our local commits after the commits on
origin/master
- This operation creates new commits (marked asterisk)
- The new commits are rebased
- The source code after
git pull
andgit pull --rebase
is the same - The history is different (no merge commit for
git pull --rebase
) - It is possible to make
git pull --rebase
default withgit config
- Real example
- A developer committed big changes to local master
- But he did not pull or push for several weeks
- When he tried to push, he could not because origin/master was out of date
- When he tried to pull in order to update origin/master there were conflicts everywhere
- After this experience he hated Git
- Explanation
- Local master and remote master are two different branches
- Local feature branch and remote feature branch are two different branches
git pull
fetches and merges- If you never pull then the branches may diverge
git pull
often to stay in sync with upstream developmentgit push
whenever you want other people to know about your changes- If you never
git push
others will not see your changes - Nontrivial changes should not be done on master
- There is nothing special about the name
origin
origin
is just an alias- Working with multiple remotes is not scary
- We can call these aliases as we like
- We can add and remove remotes
$ git remote add upstream https://github.com/foo/foo.git
$ git remote rm upstream
$ git remote add group-repo https://example.com/exciting-project.git
$ git remote rm group-repo
- We synchronize remotes via the local clone
- To see all remotes
$ git remote -v
- Centralized layout
- Decentralized layout
- Fork/pull-request layout
- Tags are also just pointers to commits
- While branches are mutable, tags are (typically) immutable
- Tags can carry extra annotation
- Always tag your releases
$ git tag # list all tags
$ git tag -a v1.4 -m 'my version 1.4' # create annotated tag
$ git tag v1.4 # create lightweight tag
$ git push origin v1.5 # share tag to upstream (origin)
$ git push origin --tags # push all tags
$ ls -l .git/hooks/
- Hooks are scripts that are executed before/after certain events
- They can be used to enforce nearly any kind of policy for your project
- There are client-side and server-side hooks
pre-commit
: before commit message editor (example: make sure tests run)prepare-commit-msg
: before commit message editor (example: modify default messages)commit-msg
: after commit message editor (example: validate commit message pattern)post-commit
: after commit process (example: notification)pre-rebase
: before rebase anything (example: disallow rebasing published commits)post-rewrite
: run by commands that rewrite commitspost-checkout
: after successfulgit checkout
(example: generating documentation)post-merge
: after successful mergepre-push
: runs duringgit push
before any objects have been transferredpre-auto-gc
: invoked just before the garbage collection takes place
-
pre-receive
: before accepting any references -
update
: likepre-receive
but runs once per pushed branch -
post-receive
: after entire process is completed -
Typical use:
- Maintenance work
- Refreshing of documentation/website
- Sanity checks
- Code style checks
- Email notification
- Rebuilding software packages