name: slide_title class: slide_title, center, middle layout: true
{{content}}
name: slide_section class: slide_section, center, middle layout: true
{{content}}
template: slide_title layout: false
template: slide_section
layout: false
- System that tracks one or more files over time
- Lets you check the files state on the past
- In theory any file can be tracked
- However, some features are only possible in text files
- Tracking very large files can slow things down
- Git is not the first source version control, there are many
- However, developers are more happy with Git than anything else
- Local Version Control Systems (LCVS)
- Local repository
- Doesn't allow collaborative work
- Not pratical if you want to work on multiple devices
- Centralized Version Control Systems (CVCS)
- Repository is hosted in one or more servers
- Clients download one version of the repository, work over it, then send it to the server
- All operations are online (network overhead, can't work offline)
- Not really realiable
- What if the server is down?
- What if there's an accident with physical damage in the servers?
- Example: SVN, Perforce
- Distributed Version Control Systems (DVCS)
- Repository hosted in one or more servers (like CVCS)
- Clients have a full repository copy
- Most operations are local
- Fast
- No internet connection is needed
- Examples: Git, Mercurial
- May take several disk time and require several time for downloading and uploading new versions if:
- Very long history
- Very large binary files
template: slide_section
layout: false
- Windows: https://git-scm.com/download/win
- Mac: https://git-scm.com/download/mac
- Linux: https://git-scm.com/download/linux
- Changes on the repository are identified by the author with a name and email
- Global configuration
$ git config --global user.name "example"
$ git config --global user.email "[email protected]"
- Repository configuration (overrides global configurations)
$ git config user.name "example"
$ git config user.email "[email protected]"
template: slide_section
layout: false
- Create a new empty repository with
init
command.- Can be applied in empty or non-empty directories
- Git won't initialize the reposity in either case
- No files are automatically tracked
- A new hidden directory .git is created
- Repository configurations
- Data
# create an empty directory
$ mkdir workshop-git
# go inside that directory
$ cd workshop-git/
# initialize empty repository
$ git init
- You edit and create files in your working directory
- Next step is to stage
- The add command creates a new snapshot of the current file(s) content
- Whatever snapshots you create, that are what will be added to the repository when you commit
- Staging is an intermidiate phase between what you do in the working directory and what will be recorded in the repository/database
# add specific file
$ git add <filename>
# add everything (new | modified | deleted)
$ git add .
# add everything (new | modified | deleted)
$ git add -A
# add already-tracked files (modified | deleted)
$ git add -u
- The commit act takes the new snapshots and stores them "permantely" in the database
- Each commit has an author and message
- The author depends on your global and local configurations (name + email)
$ git commit -m "message"
- You can stage and commit with the following shortcut
- Only known files (modified or deleted) are taken into account
$ git commit -a -m "message"
$ git commit -am "message"
template: slide_section
layout: false
- The
status
command shows a summary of:- modified files
- staged files (a snapshot for file was created)
- new files, untracked
- The same file can be modified and staged. Why?
$ git status
- The
diff
command show changes between:- two commits
- commits and the working directory
- ...
# compare the working directory with staging area
# what would be staged if you run `git add`
$ git diff
# Compare the changes which are staged relative to a given <commit>
# If the <commit> is not given, it compares with the last commit
# It shows what changes will be recorded if you run `git commit`
$ git diff --cached [<commit>]
$ git diff <commit> <commit>
# The changes between two commits
# Commits can be identified with hashes (sha1) or via symbolic name HEAD (explained further)
# If you run `git diff <A> <B>`, see it as: if I am at A and I walk to B, what has changed?
# Running `git diff <B> <A>` will show a symmetric output
$ git diff <commit>
# Compares the modifications in the working directory relatively to a given commit
- Logs let you see the commit history over time
- By default, it shows, for each commit:
- an id
- the author (name and email)
- date & time
- the message
$ git log
- If you wish a more condensed output (the id, first line of the message)
$ git log --oneline
- You can see a short brief of the changes introduced by each commit
# Which files were modified, and the ammount of insertions and deletions
# IMPORTANT: Changing a line is removing a line and inserting a new one
$ git log --stat
- If you want, you can see the actual changes introduced on each commit
$ git log -p
# show `N` commits
$ git log -n N
$ git log -N # shortcut
# dates
$ git log --since="1 week ago"
$ git log --since="17/03/2019 20:00"
$ git log --until="today"
$ git log <since>..<until>
# author (regular expression)
$ git log --author="fabio"
# files
$ git log -- hello.c
- You can also see the changes of a particular commit
$ git show <commit> # hash or symbolic names
template: slide_section
template: slide_section
layout: false
template: slide_section
layout: false
- A branch let's you diverge the development of your project
- Branches tipically have a common node (commit)
- You can switch between branches and merge them
- You have the main development branch
- You fix a bug in a separate branch. Then you want to merge in the main branch
.flex[
- A branch is just a pointer to a commit
- For example,
master
is pointing for the last commit ofmaster
branch- Ok, you have multiple branches. How does Git know at which branch you are?
- Special symbolic pointer: HEAD
- It points to the local branch you are currently on
- Use
branch
command
$ git branch <branch_name>
.flex[
.div[ What happens internally?
- A new branch (pointer) is created
- Its pointing to the same commit as
HEAD
- However,
HEAD
is not updated, it doesn't point to the new branch ]
In order to switch to a new branch:
$ git checkout <branch>
.flex[ .div[ What happens internally?
- Symbolic name
HEAD
can be used to refer branches, and therefore commits HEAD
itself is pointing to the current checked out branch- You can use two aditional operators:
^
and~
.flex[ .flex-child[
-
HEAD~1
orHEAD~
: commit's first parent -
HEAD~2
: commit's first parent's first parent -
...
-
HEAD^1
orHEAD^
: commit's first parent -
HEAD^2
: commit's second parent
If you want to create a branch pointing to a specific commit
$ git branch <branch name> <commit SHA1>
$ git branch <branch name> HEAD~n
In order to create a new branch and immediately checkout to that branch
$ git checkout –b <branch name>
$ git checkout –b <branch name> <commit sha1>
$ git checkout –b <branch name> HEAD~n
-
At some point you will want to merge branches
-
Telling Git to merge two branches is easy
-
However, it's important to understand how Git does it, and what problems may occur
-
Scenario: You have a branch
fix-10
, which diverged frommaster
-
You want merge the changes from
fix-10
tomaster
- Checkout the branch that we will merge into (in this case,
master
) - Run the command
$ git merge <source branch>
(fix-10
in our example) - Cross fingers
- There are three possible scenarios when you attempt to merge branches
- Fast-Forward
- Non-Fast Forward
- Conflict
- Git does fast-forward when one commit is directly reachable from another one
- This is as simple as moving pointers, no further actions are needed
- We want to merge
new-branch
intomaster
- We checkout
master
$ git merge new-branch
- If the two branches being merged diverged at some point, Git can't simply move pointers
- Git finds the common commit to both branches
- Will see the changes performed on both branches
- It sees that one or more files were modified, but at different chuncks
- Then, Git can handle this automatically, it uses changes from both branches
- Similar to the non-fast forward scenarion
- However, the same file was modified on both branches on the same chuncks
- Git doesn't know how to handle this, so it reports a conflict
- The conflict is resolved manually by the user
- Upon conflict, git will report which files have conflicts
- It inserts delimeters on those files around chuncks with conflicts
>>>>>>> master
...
=======
...
<<<<<<< new-branch
- You edit the file manually
- When done, stage files with conflicts and commit
- To delete a local branch:
$ git branch -d <branch>
- If the branch to be deleted is not totally merged with other branches (data loss), Git aborts the operation. To force, use
-D
$ git branch -D <branch>
template: slide_section
layout: false
- So far, you have worked locally
- If you intend to collaborate with others, you need to host the repository on a server
- Several questions arise:
- How does git know where it should send data?
- Where should it pull data from? How can I get changes done by others?
- How to manage remote branches?
- You can view which remote servers are configured in the repository:
$ git remote
$ git remove -v # more detailed (show urls for Read and Write)
- For a repository created locally, the output is most likely empty
- If you clone the repository from GitHub, you will see at least one entry.
- Most likely named origin - the default name given to the server you cloned the repository from
$ git clone [email protected]:ieeeupsb/workshop-git.git
$ cd workshop-git/
$ git remote -v
origin [email protected]:ieeeupsb/workshop-git.git (fetch)
origin [email protected]:ieeeupsb/workshop-git.git (push)
- You can have several remote configurations
- One configuration for each collaborator, for example
bakkdoor https://github.com/bakkdoor/grit (fetch)
bakkdoor https://github.com/bakkdoor/grit (push)
cho45 https://github.com/cho45/grit (fetch)
cho45 https://github.com/cho45/grit (push)
defunkt https://github.com/defunkt/grit (fetch)
defunkt https://github.com/defunkt/grit (push)
origin [email protected]:mojombo/grit.git (fetch)
origin [email protected]:mojombo/grit.git (push)
- You can add new remote configurations:
$ git remote add <remote name> <url>
- Remove them:
$ git remote remove <remote name>
- Rename them:
$ git rename <old> <new>
- Remotes are just a friendly reference to the repository URL
- Instead of saying "Hey Git, send my commits to [email protected]:ieeeupsb/workshop-git.git"
- ... we say "Git, send my commits to <remote>"
- At this point, you know Git can be configured to communicate with remote repositories
- Git also has remote references
- Pointers to branches, tags, ..., on the remote repository
- Remote-tracking branches are references to the state of remote branches (local references you can't move)
- Everytime you do any network connection, Git updates the references
- Remote-tracking branches take the form <remote>/<branch>
- E.g. origin/master
- In summary we have:
- Local branches
- Remote(-tracking) branches
- A local branch can have a relationship with a remote branch
# clone a repository from a given URL (git supports several protocols)
# automatically it creates a directory with the repository name
# you can customize if you specify `<directory>`
$ git clone <repository url> [<directory>]
$ git clone [email protected]:ieeeupsb/workshop-git.git example
- Clones the repository in a new directory
- Creates remote-tracking branches for each branch in the cloned repository
- Creates and checks out an initial branch (typically
master
) - You now have a local branch
master
tracking the remoteorigin/master
- You work locally on a branch. How to share it with the world?
- Git doesn't automatically synchronize your local branches to remote branches
- You have to explicitly push your changes
$ git push <remote> <branch>
# You can have a local branch named `hello`, and the remote be `unit-test`
$ git push <remote> <local branch name>:<remote branch name>
- Fetch downloads latest changes from the repository
- Commits, files, ...
- Lets you see what everybody else has been working on
- It has no effect on your local content
- Safe way to review commits before integrating them with your local repository
# Fetch all branches from the repository pointed by `remote`
$ git fetch <remote>
# Fetch a specific branch the repository `remote`
$ git fetch <remote> <branch>
# Fetch all data from all registered `remotes`
$ git fetch --all
- It fetches and merges the downloaded changes into your local content
$ git fetch
$ git merge
- It only fetches and merges the local checked out branch (referenced by
HEAD
)
- A merge is performed, can lead to merge conflicts
# Equivalent to `$ git fetch origin HEAD`
# Followed by `$ git merge HEAD`
$ git pull
# Equivalent to above, but instead of remote `origin` use `<remote>`
$ git pull <remote>
# Fetches the remote branch <remote>/<branch>...
# ... and merges to current branch `HEAD`
$ git pull <remote> <branch>
-
A local branch can be configured to track a remote branch
-
This simplifies the previous operations
-
No longer have to specify the
remote
andbranch
fields -
Lets say you tell Git "The local branch
a
tracks the remote branchorigin/b
" -
Now assume you are the at branch
a
-
Operations such
pull
,push
andfetch
will get/send data toorigin/b
automatically
- At any time, you can change what remote branch a local branch is tracking
$ git branch --set-upstream-to=<remote>/<branch> [<local branch>]
$ git branch -u <remote>/<branch> [<local branch>]
- Tip: Use
git branch -vv
to check relationship of local branches with remote ones
- For example, for the remote
origin
, remote branchdev
and local branchdevelopment
:
$ git branch -u origin/dev development
# If you are at the development branch, you can ommit the local branch name
$ git branch -u origin/dev
- This configuration is possible if and only if the remote branch already exists
- You may need to use
$ git fetch --all
to update remote references
- Imagine you have a local branch
issue-12
with some work - How to create a remote branch
issue-12
so that other collaborators can access your work?
$ git push -u <remote>/<branch>
- You can't directly work on remote branches
- You need to have a local branch which is tracking that remote branch
Method 1
- Create the local branch with
git branch
- Ensure you have the reference for the remote branch with
git branch -r
- Settup the upstream of the new branch with
$ git branch -u <remote>/<branch>
- Run
git pull
to merge remote branch with your local branch
Method 2
- Use the following commands
- They create a local branch which is already tracking the desired remote branch
- Examples assume you have local references for the remote branch
$ git checkout -b <branch> <remote>/<branch>
# shortcut for above (local branch with same name as the remote one)
$ git checkout --track <remote>/<branch>
# shortcut for the shortcut
# works if:
# a) the branch doesn't exist locally
# b) matches a name of a single remote's branch
$ git checkout <branch>
-
Scenario: you have a local branch
A
tracking the remote branchremote-A
-
You delete your local branch
A
-
The remote branch is not affected
-
If you really want to delete the remote branch, use:
$ git push <remote> --delete <branch>