- creates a new Git repo
- convert an existing project or initialize a new empty repo
- creates a
.git
subdirectory in project root .git
contains metadata for the repo
command | meaning |
---|---|
git init |
transform current directory into Git repo |
git init <directory> |
create empty Git repo in specified directory |
git init --bare <directory> |
initialize empty Git repo, omit the working directory (for shared repos) |
Note: repos initialized with --bare
conventionally end in .git
(e.g. my-project.git
)
--bare
flag creates a repo without a working directory (makes it impossible to edit files and commit changes in that repo)- central repos should always be created as bare repositories (pushing branches to a non-bare repo has the potential to overwrite changes)
- think of
--bare
as marking a repository as a storage facility, not a development environment
git clone
used to create local copies of a project, so git init
is usually used to create a central repo
$ ssh <user>@<host> # 1. `ssh` into the server that will contain your central repo
$ cd path/above/repo # 2. navigate to location to store the project
$ git init --bare my-project.git # 3. use the `--bare` flag to create a central storage repo
# 4. other developers would then `git clone
# my-project.git` to create a local copy on their
# own machine
- copies an existing Git repo
- automatically creates a remote connection called
origin
pointing back to the original repo
command | meaning |
---|---|
git clone <repo> |
clone the repo onto local machine |
git clone <repo> <directory> |
clone <repo> into the <directory> on local machine |
git clone
is most common way to obtain a dev copy of a central repo- you
push
orpull
commit from one repo to another
$ git clone ssh://[email protected]/path/to/my-project.git # 1. init new local git repo within `my-project`
$ cd my-project # 2. `cd` into the project
# 3. start editing/working on project
- Lets you configure your Git install (or an individual repo)
- define user info, preferences, behavior of repo
command | meaning |
---|---|
git config user.name <name> |
define author name for all commits in current repo |
git config --global user.name <name> |
define author name to be used for all commits by the current user |
git config --global user.email <name> |
define author email for all commits by current user |
git config --global alias.<alias-name> <git-command> |
create an shortcut for a Git command |
git config --system core.editor <editor> |
define text editor used by commands like git commit for all users on current machine |
git config --global --edit |
open the global config file in a text editor |
all config options stored in three plaintext files:
location | description |
---|---|
<repo>/.git/config |
repository-specific config |
~/.gitconfig |
user-specific config (--global ) |
~$(prefix)/etc/gitconfig |
system-wide config |
- local settings override user settings, which override system-wide settings
config file example:
[user]
name = John Smith
email = [email protected]
[alias]
st = status
co = checkout
br = branch
up = rebase
ci = commit
[core]
editor = vim
Note: above aliases are SVN-like!
- adds a change in the working directory to the staging area
- tells Git you want to include updates to a particular file in the next commit
- changes are not recorded until you run
git commit
- you'll use
git status
to view the state of the working directory and staging area
command | meaning |
---|---|
git add <file> |
stage all changes in <file> for next commit |
git add <directory> |
stage all changes in <directory> for next commit |
git add -p |
begin interactive staging session that lets you |
choose portions of a file to add to next commit |
| | - will present you with a chunk of changes + prompt you for a command |
| | - use y
to stage the chunk |
| | - use n
to ignore the chunk |
| | - use s
to split it into smaller chunks |
| | - use e
to manually edit the chunk |
| | - use q
to exit |
- fundamental Git workflow:
git add
+git commit
- developing a project -> revolves around edit/stage/commit pattern
- edit your files
- when you're ready to save a copy of the current state, stage changes with
git add
- once you're happy with staged snapshot, commit it to the project history with
git commit
git add
only needs to be called every time you alter a file
- Big idea: buffer between the working directory and project history
- allows ability to group related changes into highly focused snapshots before actually committing to project history
- allows you to edit unrelated files, then retroactively split edits up into seperate logical commits by adding related changes to the stage and committing each of them piece-by-piece
- gives the ability to avoid committing all changes you've made since last commit
- atomic commits important = easier to track down bugs and revert changes with minimal impact on the rest of project
$ git add . # 1a.
$ git commit # 1b. create an initial commit of the current directory
$ git add hello.py # 2a. stage new file and its changes
$ git commit # 2b. commit changes
- new files can be added by passing their path to
git add
- above commands also can be used to record changes to existing files (Git doesn't differentiate between staging changes in new files vs. changes in files that already have been added to repo)
- commits staged snapshot to the project history of the local repository
- committed snapshots = "safe" versions of a project
command | meaning |
---|---|
git commit |
commit the staged snapshot, will launch an editor to compose commit message |
git commit -m "message" |
commit the staged snapshot with as the commit message |
git commit -a |
commit a snapshot of all changes of tracked files (files added with git add ) |
- NOTE: for
git commit
, enter the commit message and save and close to create the commit
- snapshots always commit to local repo
- staging allows for accumulation of commits in local repo before interacting with the central repo
- makes it easier to: split up a feature into atomic commits, keep related commits grouped together, clean up local history before publishing to the central repo
- lets developers work in an isolated environment, deferring integration until their at a convenient break point
- Git's version control model based on snapshots
- Git records the entire contents of each file in every commit
- record entire file: makes Git faster = particular version of a file doesn't need to be "assembled" from its diffs
the following assumes you edited some content in hello.py
and are ready to commit:
git add hello.py
git commit
- stage the file with
git add
- commit the staged snapshot using
git commit
- enter the commit message in the text editor set by
git config
, save and close the editor to make the commit
- summarize the entire commit with 1st line of commit message in less than 50 chars
- leave a blank line
- then a detail explanation of what's been changed
- Note: developers like to use present tense in commit messages
- this makes them read like actions on the repo, makes many of the history-rewriting operations more intuitive
displays the current state of working directory and staging area
- lets you see:
- which changes are staged
- which changes not staged
- which files aren't being tracked by Git
- doesn't show you committed project history (use
git log
)
git status
- lists which files are staged, unstated, and untracked
git status
shos you results of usinggit add
andgit commit
- status messages contain instructions on how to stage/upstage files
- two categories of untracked files"
- files that have just been added
- compiled binaries
- Git allows you to completely ignore files by placing paths in a
.gitignore
file - files that you'd like to ignore are included on a separate line
*
can be used as a wild-card
- good practice to check the state of the repo before committing changes
git log
displays committed snapshots- lets you list the project history, filter it, and search for specific changes
- log output can be customized (filtering commits, custom display formatting)
command | meaning |
---|---|
git log |
display entire commit history, default formatting (Space = advance, q = exit) |
git log -n <limit> |
limit number of commits displayed by a <limit> integer |
git log --oneline |
condense each commit to a single line |
git log --stat |
include which files were altered + relative # of lines added/deleted |
git log -p |
display patch (full diff) for each commit (most detailed view) |
git log --author="<pattern>" |
search for commits by author, "<pattern>" arg can be string or regrex |
git log --grep="<pattern>" |
search for commits by commit message that matches string or regex "<pattern>" |
git log <since>..<until> |
show only commits between <since> and <until> (can use commit ID, branch name, HEAD or other revision reference |
git log <file> |
only display commits including <file> |
git log --graph --decorate --oneline |
* --graph draws a text graph of commits on the left side of commit messages |
* --decorate adds names of branches or tags of commits that are shown |
|
* --oneline shows the commit info on a single line making it easier to browse |
git log
= basic tool used to explore a repo's history (i.e. starting point for finding commits)- use
git log
to find a specific version of a project or figure out what changes will be introduced by merging in a feature branch HEAD
refers to the current commit~
is useful for making relative references to the parent of a commit (e.g.3157e~1
refers to the commit directly before3157e
andHEAD~3
is the great-grandparent of the current commit)- ID methods allow you to perform actions based on specific commits
- several options can be combined in a single command:
command | meaning |
---|---|
git log --author="John Smith" -p hello.py |
display a full diff of all changes John Smith made to the file hello.py |
git log --oneline master..some-feature |
- display a brief overview of all commits in some-feature + not in master |
- .. syntax useful for comparing branches: |
load saved snapshots into development machine
allows you do perform 3 distinct operations:
- check out a file (see an old version of a particular file)
- check out a commit (makes entire working directory match a commit, see an old state of your project)
- check out a branch
command | meaning |
---|---|
git checkout master |
return to master branch ("current" state of the project) |
git checkout <commit> <file> |
* view previous version of a file |
* (turns in the working directory into | |
an exact copy of the one from <commit> and adds it to the staging area) |
|
git checkout <commit> |
update all files in working direcotry to |
match specified commit (puts you in detached HEAD state) |
-
purpose of version control = store 'safe' copies of a project -> avoid worrying about irreparably breaking code
-
checking out an old commit is read-only operation ("current" state of project remains untouched in
master
)A -
"detached
HEAD
" state- normally,
HEAD
points tomaster
or other local branch - when you
git checkout <previous commit>
,HEAD
no longer points to a branch, but to a commit
- normally,
-
use git checkout
git checkout <commit> <file>
to revert back to an old version of an individual file ()- checking out out an old file using
git checkout <commit> <file>
does affect current state of your repo - you can re-commit the old version in a new snapshot as you would any other file
- checking out out an old file using
This example assumes you're developing a crazy experiment, but not sure if you want to keep it or not:
$ git log --oneline # 1. find the ID of the revision you want to see
b7119f2 Continue doing crazy things
872fa7e Try something crazy
a1e8fb5 Make some changes to hello.py
435b61d Create hello.py
9773e52 Initial import # 2. Project history
$ git checkout a1e8fb5 # 3. use `git checkout` to view "make some import
# changes to hello.py." commit:
# - makes working dir match exact state of `a1e8fb5` commit
# - you make changes without losing current state of project
# - nothing you do here will be saved in your repo
$ git checkout master # 4. Get back to "current" state of project to continue developing
Note: above assumes that you're developing on the default master branch
- once on the master branch you can use
git revert
orgit reset
to undo undesired changes
If only interested in a single file, use git checkout
to fetch an old version of it
e.g. only want to see hello.py from the old commit:
git checkout a1e8fb5 hello.py
the above will affect current state of project: the old file revision will show up as a "Change to be committed", giving you the opportunity to revert back to the previous version of the file
If you don't want to keep the old version, check out the most recent version with:
git checkout HEAD hello.py
see above section
-
designed to safely remove public commits
-
git revert
undoes a committed snapshot by figuring out how to undo changes and creates a new commit with the resulting content (instead of removing the commit) -
prevents git from losing history
command | meaning |
---|---|
git revert <commit> |
generate a new commit that undoes all the changes |
introduced in <commit> and apply it to the current branch |
Reverting should be used when you want to remove an entire commit from your project history
Useful if you're tracking down a bug and find it was introduced by a single commit
git revert
undoes changes belonging to a single commit by adding a new commit
that undos those changes
(git revert
does not "revert" back to the previous state of a project by
removing all subsequent commits)
to return to a previous state of a project by removing all commits since a
certain commit, use reset
git revert
has 2 advantages over git reset
:
-
git revert
doesn't change project history (making it a "safe" operation for commits already publish to shared repo) by adding a new commit -
git revert
is able to target individual commits at an arbitrary point in history,git reset
can only work backwards from current commit (withgit reset
, to undo an old commit with git reset, you would have to remove all commits that occured after the target commit, remove it, then recommit all the subsequent commits)
# Edit some tracked files
# Commit a snapshot
git commit -m "Make some changes that will be undone"
# revert the commit we just created
git revert HEAD
# (NOTE: `HEAD` refers to the current commit)
git reset
designed to undo local changes
git reset
is a permanent undo:
its the "dangerous" method of undoing changes (vs. git revert
) - commonly
used to undo uncommitted but staged changes
undoing with git reset
= commits no longer referred by any ref or in reflog,
no way to retrieve original copy
can be used to remove committed snapshots, usually used to undo changes in the staging area/working directory
should only be used to undo local changes - never reset snapshots shared with other developers
command | meaning |
---|---|
git reset <file> |
unstage file without overwriting changes in working dir |
git reset |
unstage all files (to match most recent commit) without overwriting changes (allows you to re-build the staged snapshot from scratch) |
git reset --hard |
reset the staging area and working dir to most recent commit (overwrites all changes in working dir) (deletes all uncommitted changes) |
git reset <commit> |
move current branch tip backward to <commit> , reset staging area to match, leave working dir alone (all changes since <commit> remain in working dir, letting you recommit project history using cleaner, more atomic snapshots) |
git reset --hard <commit> |
move current branch tip backward to <commit> , reset both staging area and working dir (deletes all uncommitted changes + commits after <commit> ) |
without --hard
, use git reset
to clean up a repo by unstaging
changes/uncommited a series of snapshots and re-building from scratch
Use --hard
if an experiment has gone horribly wrong and you need a clean slate
Never use git reset <commit>
if any snapshots after <commit>
have been pushed to a public repo
after publishing a commit, assume other developers are reliant upon it
the merge commit required to synchronize repos after resetting a public commit is confusing
use git revert
to fix a public commit
git reset
frequently encountered while preparing the staged snapshot
This example assumes you have two files called hello.py
and main.py
already
added to the repo:
# Edit both hello.py and main.py
# Stage everything in the current directory
git add .
# Realize that the changes in hello.py and main.py
# should be committed in different snapshots
# Unstage main.py
git reset main.py
# Commit only hello.py
git commit -m "Make some changes to hello.py"
# Commit main.py in a separate snapshot
git add main.py
git commit -m "Edit main.py"
###Mary adds a follow-up commit