git
Git is a distributed version control system (DVCS) that stores a complete copy of a repository on your local machine. To track files, it stores snapshots of each file in a compressed database. Git tracks each of these files using a SHA-1 hash checksum–each time you commit a new or modified file to the database, git creates a checksum. If you do not change a file during a version, git copies the unchanged file into the new version.
States and directories
Git uses 3 stages:
- committed. Stored in your local db
- modified. Changed, but not committed to the local db
- staged. Marked a modified file to go into the next commit snapshot
Git uses 3 main sections of the git project:
- .git directory, the object and metadata database of the project. This is the cloned repo
- working directory, a checkout of one version of the .git directory. git checks files out of the .git database and places them in a directory in your filesystem so you can work on them
- staging area (also called the “index”), stores info about what is going into the next commit
Configuration
git configuration values are stored in the .gitconfig
file, located in one of the following places:
/etc/gitconfig
: system-wide git configurations~/.gitconfig
: user git configurations. Set these values with the--global
optionproject/.git/config
: local project repository configuration
Return git configuration information with the following command:
$ git config --list
Getting started
- Distributed Version Control System (DVCS): clients fully mirror the repo, including its full history.
- Any client can restore a corrupted server
git
- Thinks about data as a “stream of snapshots” (snapshots == commit) of a mini filesystem
- During each commit, git takes a snapshot of what the files look like and stores a reference to the snapshot
- If file hasn’t changed, git doesn’t store the file again. It links to the previous identical file that it already stored.
- Everything is local bc you have the entire project on your workstation
- Integrity - everything is stored as a checksum, so it always knows when information changes
- SHA-1 hash uses 40 char string
- Adds data - generally only adds data, hard to do anything that is not undoable
Three states
- modified
- Changed file but not committed to the db yet
- staged
- marked a modified file in its current version to go into the next commit snapshot
- committed
- data is stored in local database
Three sections of git project
- working tree
- single checkout of one version of the project. Its files are pulled out of the compressed db in the git directory and placed on disk for you to work with.
- staging area (index)
- A file in the git directory that stores info about what goes into the next commit.
- git directory (.git)
- Where git stores the metadata and object db for the project. This is what’s copied when you clone a repo.
General workflow:
- Modify files in working tree
- Add changes to staging area
- Commit, which stores a snapshot of staging area and stores in git directory
Setup
Install
sudo apt install git-all
git config
Use git config
to set config variables for your git install. Stored here in reverse order of precedence:
/etc/gitconfig
: Applys to every user on system.- Add
--system
option togit config
- Add
~/.gitconfig
or~/.config/git/config
: User options.- Add
--global
option to apply changes to all repos for your user account
- Add
/<git-project>/.git/config
: Specific to the repo
# get help
$ git help <verb>
$ git <verb> --help
$ man git-<verb>
# open git config man page in browser
$ git help config
# view all settings
$ git config --list
diff.astextplain.textconv=astextplain
filter.lfs.clean=git-lfs clean -- %f
filter.lfs.smudge=git-lfs smudge -- %f
...
# list all settings and where they are stored
$ git config --list --show-origin
file:C:/Program Files/Git/etc/gitconfig diff.astextplain.textconv=astextplain
file:C:/Program Files/Git/etc/gitconfig filter.lfs.clean=git-lfs clean -- %f
# set username and address
$ git config --global user.name "John Doe"
$ git config --global user.email johndoe@example.com
# set username and address for specific project
$ git config user.name "John Doe"
$ git config user.email johndoe@example.com
# change default editor
$ git config --global core.editor emacs
# set default branch name
$ git config --global init.defaultBranch main
# check specific value
$ git config user.name
<username>
git basics
Getting git repo
# start repo, creates .git directory
$ git init
# clone repo
# creates dir, inits /.git, pulls down all data, checks out working copy of latest version
# all files tracked and unmodified
$ git clone https://path/to/repo
Recording changes
Files have two states:
- tracked
- In the last snapshot, or newly staged files. Files that git knows about.
- untracked
- git sees a file not in last snapshot or in staging area.
Untracked Unmodified Modified Staged
| | | |
| Add the file -------------------------------------------> |
| | Edit file --> | |
| | | |
| | | Stage file -----> |
| <--Remove file | | |
| | <-------------------------------- Commit |
- Clean working directory
- No tracked files are modified
# get file status
$ git status
On branch master
No commits yet
Untracked files: # lists untracked files
(use "git add <file>..." to include in what will be committed)
README
hello.go
nothing added to commit but untracked files present (use "git add" to track)
# add this content (files, dirs) to start tracking
# if directory, adds all files in dir recursively
$ git add README hello.go
# added files are now tracked and staged to be committed
$ git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: README
new file: hello.go
# short, less verbose status
# contains 2 columns: staging area status working tree status
$ git status -s
M README # modified, but not staged
MM Rakefile # modified, staged, modified again
A lib/git.rb # added to staging
M lib/simplegit.rb # modified and staged
?? LICENSE.txt # untracked
.gitignore
Basic rules:
# ignore all .a files
*.a
# but do track lib.a, even though you're ignoring .a files above
!lib.a
# only ignore the TODO file in the current directory, not subdir/TODO
/TODO
# ignore all files in any directory named build
build/
# ignore doc/notes.txt, but not doc/server/arch.txt
doc/*.txt
# ignore all .pdf files in the doc/ directory and any of its subdirectories
doc/**/*.pdf
git diff
Shows exactly what you changed, uses pager to show long files:
git diff
: Changes that are unstaged, or changes in the working directory.git diff --staged
: Changes on staged filesgit diff --cached
: Same asgit diff --staged
# check status
$ git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: README
new file: hello.go
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: README
# compare working dir with staging area
$ git diff
warning: LF will be replaced by CRLF in README.
The file will have its original line endings in your working directory
diff --git a/README b/README
index e8798b5..e471777 100644
--- a/README
+++ b/README
@@ -165,4 +165,41 @@ Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: README
new file: hello.go
...
# see what is going in next commit
$ git diff --staged
diff --git a/README b/README
new file mode 100644
index 0000000..e8798b5
--- /dev/null
+++ b/README
@@ -0,0 +1,168 @@
+## Getting started
...
git commit
Commits files in the staging area:
# open default editor to enter commit msg
$ git commit
# show diff in default editor
$ git commit -v
# inline commit message
$ git commit -m 'first commit'
[master (root-commit) 0016653] first commit # branch committed to, SHA-1 checksum
2 files changed, 261 insertions(+) # files changed, stats about changes
create mode 100644 README
create mode 100644 hello.go
git commit -a
This command skips the staging area (skips git add <file | dir>
). The -a
option automatically stages every file that is already tracked before the commit:
# view unstaged files
$ gs
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: README
no changes added to commit (use "git add" and/or "git commit -a")
# skip staging area and provide commit message
$ git commit -am 'second commit'
[master 454f4d3] second commit
1 file changed, 27 insertions(+)
git rm
Removes file from staging and working directory:
##########################
# delete a tracked file
$ rm PROJECTS.md
# verify its deleted
$ git status
On branch master
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
deleted: PROJECTS.md
no changes added to commit (use "git add" and/or "git commit -a")
# remove file from working dir
$ git rm PROJECTS.md
rm 'PROJECTS.md'
# file is ready to delete
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: PROJECTS.md
# commit changes, file is no longer tracked
$ git commit -m 'deleted PROJECTS.md'
[master 3108c75] deleted PROJECTS.md
1 file changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 PROJECTS.md
##########################
# rm from staging area
$ git rm --cached PROJECTS.md
rm 'PROJECTS.md'
# file is no longer in staging area
$ git status
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
PROJECTS.md
nothing added to commit but untracked files present (use "git add" to track)
# rm all files in /log with .log extension
$ git rm log/\*.log
git mv
Rename a file in git. Otherwise, it deletes the file w/the old name, then tracks the new file name:
# change from *.txt -> *.md
$ git mv move.txt move.md
# verify change
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
renamed: move.txt -> move.md
# commit
$ git commit -m 'rename'
[master 98bf571] rename;
1 file changed, 0 insertions(+), 0 deletions(-)
rename move.txt => move.md (100%)
# filename is changed
$ ls
hello.go move.md PROJECTS.md README.md
Commit history
git log
Lists commits in reverse chronological order:
# view difference in each commit - patch command (-p or --patch)
git log -p
commit ca82a6dff817ec66f44342007202690a93763949 (HEAD -> master, origin/master, origin/HEAD)
Author: Scott Chacon <schacon@gmail.com>
Date: Mon Mar 17 21:52:11 2008 -0700
changed the verison number
diff --git a/Rakefile b/Rakefile
index a874b73..8f94139 100644
--- a/Rakefile
+++ b/Rakefile
@@ -5,7 +5,7 @@ require 'rake/gempackagetask'
spec = Gem::Specification.new do |s|
s.platform = Gem::Platform::RUBY
s.name = "simplegit"
- s.version = "0.1.0"
+ s.version = "0.1.1"
s.author = "Scott Chacon"
s.email = "schacon@gmail.com"
s.summary = "A simple gem for using Git in Ruby code."
# view patch for -N past commits, here just 1
git log -p -1
commit ca82a6dff817ec66f44342007202690a93763949 (HEAD -> master, origin/master, origin/HEAD)
Author: Scott Chacon <schacon@gmail.com>
Date: Mon Mar 17 21:52:11 2008 -0700
changed the verison number
...
# view abbreviated stats
$ git log --stat
...
commit a11bef06a3f659402fe7563abf99ad00de2209e6
Author: Scott Chacon <schacon@gmail.com>
Date: Sat Mar 15 10:31:28 2008 -0700
first commit
README | 6 ++++++
Rakefile | 23 +++++++++++++++++++++++
lib/simplegit.rb | 25 +++++++++++++++++++++++++
3 files changed, 54 insertions(+)
# pretty w log on oneline
$ git log --pretty=oneline
ca82a6dff817ec66f44342007202690a93763949 (HEAD -> master, origin/master, origin/HEAD) changed the verison number
085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 removed unnecessary test code
a11bef06a3f659402fe7563abf99ad00de2209e6 first commit
# short
$ git log --pretty=short
commit ca82a6dff817ec66f44342007202690a93763949 (HEAD -> master, origin/master, origin/HEAD)
Author: Scott Chacon <schacon@gmail.com>
changed the verison number
# full
$ git log --pretty=full
commit ca82a6dff817ec66f44342007202690a93763949 (HEAD -> master, origin/master, origin/HEAD)
Author: Scott Chacon <schacon@gmail.com>
Commit: Scott Chacon <schacon@gmail.com>
changed the verison number
# fuller
$ git log --pretty=fuller
commit ca82a6dff817ec66f44342007202690a93763949 (HEAD -> master, origin/master, origin/HEAD)
Author: Scott Chacon <schacon@gmail.com>
AuthorDate: Mon Mar 17 21:52:11 2008 -0700
Commit: Scott Chacon <schacon@gmail.com>
CommitDate: Fri Apr 17 21:56:31 2009 -0700
changed the verison number
# format
$ git log --pretty=format:"%h - %an, %ar : %s"
ca82a6d - Scott Chacon, 16 years ago : changed the verison number
085bb3b - Scott Chacon, 16 years ago : removed unnecessary test code
a11bef0 - Scott Chacon, 16 years ago : first commit
# graph to see branch and merge history
$ git log --pretty=format:"%h %s" --graph
* ca82a6d changed the verison number
* 085bb3b removed unnecessary test code
* a11bef0 first commit
# git log since the specified date
$ git log --since=2.weeks
# since YYYY-MM-DD format
$ git log --since="2008-01-15"
commit ca82a6dff817ec66f44342007202690a93763949 (HEAD -> master, origin/master, origin/HEAD)
Author: Scott Chacon <schacon@gmail.com>
Date: Mon Mar 17 21:52:11 2008 -0700
changed the verison number
...
# by author
$ git log --author="Scott Chacon"
commit ca82a6dff817ec66f44342007202690a93763949 (HEAD -> master, origin/master, origin/HEAD)
Author: Scott Chacon <schacon@gmail.com>
Date: Mon Mar 17 21:52:11 2008 -0700
...
# pickaxe option - takes a string
$ git log -S function_name
# by path or file
$ git log -- path/to/file
# exclude merges
$ git log --no-merges
Formatting options
Undoing things
amend
Replaces previous commit with new commit, including a new hash. Best when it is a local commit:
# original commit
$ git log --oneline
07c2519 (HEAD -> master) amend setup
...
$ git commit -am 'updated amend commit' --amend
[master 9a22606] updated amend commit
Date: Thu Apr 11 15:11:43 2024 -0400
2 files changed, 132 insertions(+), 2 deletions(-)
rename m.txt => move.txt (100%)
# view new commit message
$ git log --oneline
9a22606 (HEAD -> master) updated amend commit
bf731a2 renamed
...
# add forgotten file to previous commit
$ gs
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: move.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
forgot.file
$ git commit -am 'forgot a file'
$ git add forgot.file
$ git commit -am 'update previous commit' --amend
git restore
Unstages a file but keeps modifications:
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage) # tells you how
modified: README.md
new file: unstage.md
# If you already committed a file and then made changes, you can
# revert back to the what it looked like in the last snapshot:
git restore <file>
Remotes
Versions of your project that are hosted somewhere else (on the same host, on the network, etc).
git remote
# add a remote
git remote add origin https://github.com/path/to/repo.git
# maybe this is more correct?
git remote add origin git@github.com:<username>/<repo-name>.git
# view remote servers
$ git remote
origin # default name that git gives to cloned server
# verbose view
$ git remote -v
origin https://github.com/schacon/ticgit (fetch)
origin https://github.com/schacon/ticgit (push)
# inspect the remote
$ git remote show origin
warning: redirecting to https://path/to/hugo-vertica-doc.git/
* remote origin
Fetch URL: http://path/to/hugo-vertica-doc.git
Push URL: http://path/to/hugo-vertica-doc.git
HEAD branch: master
Remote branches:
bugfix/K8s-arch-image-overlap_rjs tracked
...
refs/remotes/origin/VER-86181-K8s-demote-operatorhub_rjs stale (use 'git remote prune' to remove)
...
Local branches configured for 'git pull':
bugfix/SCSS-partials_rjs merges with remote bugfix/SCSS-partials_rjs
...
master merges with remote master
...
Local refs configured for 'git push':
bugfix/SCSS-partials_rjs pushes to bugfix/SCSS-partials_rjs (up to date)
...
release/24.1.0-hotfix pushes to release/24.1.0-hotfix (local out of date)
# rename remote
$ git remote rename pb paul
Renaming remote references: 100% (2/2), done.
$ git remote
origin
paul
# remove remote
$ git remote
origin
paul
$ git remote remove paul
$ git remote
origin
git fetch
- Pulls all data from remote project that was pushed to the server since you cloned or the last
fetch
. - Does not merge data into existing branches.
$ git fetch <remote>
git pull
When a branch is tracking a remote branch, fetches and merges data from remote branch into local branch.
$ git pull
git push
Pushes the project upstream if the following are true:
- You have write access
- No one has pushed to the branch since you last fetched the data. You have to merge their work into yours
$ git push <remote> <branch>
$ git push origin master
Tagging
Mark a specific point in a repo’s history as signficant, such as a release point (v1.0). git has lightweight
and annotated
tags:
# list all tags
$ git tag
$ git tag -l
$ git tag --list
Lightweight tags
Like a branch that doesn’t change, a pointer to a specific commit. It is a commit checksum that is stored in a file:
# create lightweight tag (no flags)
$ git tag v1.4-lw
# view tags
$ git tag
v1.4
v1.4-lw
# git show <tag> just shows the commit
$ git show v1.4-lw
commit 3ecedf1e2e6554581fc298926090a10b1e89bfcd (HEAD -> master, tag: v1.4-lw, tag: v1.4)
Author: rseymour <rseymour@opentext.com>
Date: Fri Apr 12 08:56:35 2024 -0400
add to commit
...
Annotated tags
Stored as a full object in the git database:
- Checksummed
- tagger name, email, and date
- tagging message
- signed and verified with GNU Privacy Guard (GPG)
# create annotated tag
$ git tag -a v1.4 -m 'my version 1.4'
# view tag
$ git show v1.4
tag v1.4
Tagger: rseymour <rseymour@opentext.com>
Date: Mon Apr 15 09:30:00 2024 -0400
my version 1.4
commit 3ecedf1e2e6554581fc298926090a10b1e89bfcd (HEAD -> master, tag: v1.4)
Author: rseymour <rseymour@opentext.com>
Date: Fri Apr 12 08:56:35 2024 -0400
add to commit
...
Tagging later
Add a tag to an older commit:
# get checksum
$ git tag -a v1.2 ff612a9 -m 'tag older commit'
# view tags
$ git tag
v1.2
v1.4
v1.4-lw
# view commit
$ git show v1.2
tag v1.2
Tagger: rseymour <rseymour@opentext.com>
Date: Mon Apr 15 09:36:55 2024 -0400
tag older commit
commit ff612a95b393dc8b8f36ada8394d16b265c31611 (tag: v1.2)
Author: rseymour <rseymour@opentext.com>
Date: Thu Apr 11 09:09:41 2024 -0400
rm rmfile
diff --git a/rmfile b/rmfile
deleted file mode 100644
index e69de29..0000000
Push tags to remote
You have to explicitly push tags:
# push single tag
$ git push origin <tagname>
# push all tags (lightweight and annotated)
$ git push origin --tags
# push only annotated tags
git push <remote> --follow-tags
Deleting tags
$ git tag -d <tagname>
$ git tag
v1.2
v1.4
v1.4-lw
# delete tag
$ git tag -d v1.4-lw
Deleted tag 'v1.4-lw' (was 3ecedf1)
# verify
$ git tag
v1.2
v1.4
# delete tag in remote
$ git push origin --delete <tagname>
Checkout tags
You can checkout versions of files that the tag points to. This puts your repo in ‘detached HEAD’ state:
$ git checkout v1.0.2.1
Note: switching to 'v1.0.2.1'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at 871eb64 Bumped version to 1.0.2.1
This means that if you create a commit, the tag stays the same, but the new commit won’t belong to a branch and will be unreachable, except by the exact commit hash. If you’re fixing a bug on an older version, you probably need to create a branch.
git aliases
You can add aliases to often-used git commands. Similar to bash aliases:
git config --global alias.<alias> <command>
# $ git config --global alias.co checkout
$ git config --global alias.br branch
$ git config --global alias.ci commit
$ git config --global alias.st status
# alias for last commit
$ git config --global alias.last 'log -1 HEAD'
# alias
$ git last
commit 871eb64b61414d9633b8056ddb3d66e77609f8a6 (HEAD, tag: v1.0.2.1)
Author: Jeff Welling <jeff.welling@gmail.com>
Date: Sun Apr 3 16:50:10 2011 -0400
Bumped version to 1.0.2.1
# original
$ git log -1 HEAD
commit 871eb64b61414d9633b8056ddb3d66e77609f8a6 (HEAD, tag: v1.0.2.1)
Author: Jeff Welling <jeff.welling@gmail.com>
Date: Sun Apr 3 16:50:10 2011 -0400
Bumped version to 1.0.2.1