Git is a Version Control System(VCS), aka Source Code Management(SCM).
- History of VCS Tools
- Github
- Distributed Version Control
- Architecture
- Git Workflow
- Git Configuration
- Tree-ish
- Commonly Used Commands
- Source Code Control System(SCCS):
- 1972
- closed-source
- only Unix support
- supported text files
- stored original version file and sets of changes
- one user could work on a file at a time (local repository)
- Revision Control System(RCS):
- 1982
- open-source
- cross-platform
- supported text files
- stored latest version file and sets of changes
- one user could work on a file at a time (local repository)
- Concurrent Versions System(RCS):
- 1986-1990
- open-source
- cross-platform
- supported text files
- stored all versions of the files
- multi-user repositories (remote repositories)
- Apache Subversion(SVN):
- 2000
- open-source
- cross-platform
- supported text and image files
- directory based tracking
- multi-user repositories (remote repositories)
- Bitkeeper SCM:
- 2000
- similar to SVN
- closed-source, proprietary
- distributed version control
- community version was free
- used for source code of Linux kernel from 2002 to 2005
- controversial to use proprietary SCM for open-source project
- April 2005: the community version was not free anymore
- Git:
- April 2005
- created by Linus Torvalds(also created Linux)
- replacement for Bitkeeper for Linux source code management
- distributed version control
- open-source and free software
- faster than other SCMs
- better safeguards against data corruption
- 2008
- hosts Git repositories
- purchased by Microsoft in 2018
- different users maintain their own repositories
- no single central or master repository
- changes are stored as change sets
- tracks changes and not versions unlike CVS and SVN
- change sets can be exchanged between repositories ("merge-in change sets" or "apply patches")
- no need to communicate with a central server
- faster than other traditional version control systems
- no network access required
- no single failure point
- encourages participation and forking of projects
- developers can work independently
- submit change sets for inclusion or rejection
- Git uses Three Tree Architecture as opposed to other VCSs that use traditional Two Tree Architecture
Two Tree Architecture - Traditional | Three Tree Architecture - Git |
---|---|
![]() |
![]() |
- Make changes
- Stage changes
- Commit changes
Sample Workflow - Involving three changes A, B, C
-
System Config File-
- Scope      :Â
git config --system
- Win Path :Â
Program Files\Git\etc\gitconfig
- Mac Path :Â
etc\gitconfig
- Scope      :Â
-
User Config File-
- Scope      :Â
git config --global
- Win Path :Â
Users\<user_name>\.gitconfig
- Mac Path :Â
$Home\.gitconfig
- Scope      :Â
-
Project Config File-
- Scope     :Â
git config
- Path        :Â
my_project\.git\config
- Scope     :Â
The following git objects qualify as a tree-ish:
- SHA-1 hash
- HEAD pointer reference
- Branch reference
- Tag reference
- Ancestry
Use ^ to refer to the ancestors of a commit
- parent -
git show HEAD^
orgit show HEAD~1
- grandparent -
git show HEAD^^
orgit show HEAD~2
- great-grandparent -
git show HEAD^^^
orgit show HEAD~3
git config --global <config_name> <value>
git config --global user.name "Ashutosh Ojha"
git config --global user.email "ashutosh.ojha2009@gmail.com"
git config --global core.editor "atom --wait"
git config --global color.ui true
Note:
- config can be used to set aliases for commands.
- Syntax: git config --global alias.<alias_name> <"command">
- Some popular aliases:
- git config --global alias.st "status"
- git config --global alias.co "checkout"
- git config --global alias.ci "commit"
- git config --global alias.br "branch"
- git config --global alias.dfs "diff --staged"
- git config --global alias.logg "log --graph --decorate --oneline --all"
// lists all config properties
git config --list
// shows a specific configuration property
git config <config_property>
// shows a user name
git config user.name
// git command
git help <command_name>
// os command
man git-<command_name>
// adds .git to directory
// converts a normal directory into a repository
git init
// gives status info like edited, staged, unstaged and untracked files
git status
git add <change_path>
// stages all changed files in the directory
git add .
// stages a specific change
git add changed_file.txt
// stages multiple files
git add changed_file_1.txt changed_file_2.txt
// stage interactively
// provides a command line based gui
// can stage specific files or even specific lines
git add --interactive
git add -i
Interactive Staging
// makes the staged changes permanent and tracks them
git commit
// commit with a message
git commit -m "<message>"
// commit with a message and additional descriptions
git commit -m "<message>" "<desc1>" "<desc2>"
// amend or modify the most recent commit
git commit --amend -m "<message>"
// directly commits all the changes(no manual staging needed)
// commits all changes(staged and unstaged) except untracked changes
git commit -a
git commit --all
// commits all changes with a message
git commit -am "<message>"
// lists all the commits
git log
// limits the number of commits to be shown
git log -n <limit>
// filter by date
git log --since=<date1> --until=<date2>
// filter by author name
git log --author="<author_name>"
// filter all the meta data of the commits using regex
git log --grep="<regex>"
// filter by a range
git log <commit_1_id>..<commit_2_id>
// filter by file
git log <file>
// formatting logs
// shows changes in form of change-sets or patches
git log -p
// formats log to give a statistical view
// shows the files changed and number of insertions(+) and deletions(-) in the files
git log --stat
// other formats: oneline, medium(default), full, fuller, email, raw
// gives shorter description of commits
git log --format=short
// show commits with one-line commit messages
// better than using --format=oneline
// ignores the descriptions
// shortens the commit id
git log --oneline
// provides a graphical view
// useful when multiple branches are present
git log --graph
// best combination of formatters
git log --graph --all --oneline --decorate
// shows all the changes(git diff) done in a particular commit
// need not type entire 40 character commit id, first few characters also work
git show <commit_id>
// shows difference between two versions of each file
// lists differences in all modified files
// shows the line by line difference between file versions
// compares working directory version against staging tree version of the files
git diff
// shows and highlights word by word difference
git diff --color-words
// shows difference in a specific modified file
git diff changed_file.txt
// shows difference in multiple modified files
git diff changed_file_1.txt changed_file_2.txt
// compares staging tree version against repository version of the files (staged/cached)
git diff --staged
git diff --cached
// view differences between commits
// HEAD can be used instead of the commit id to point to the latest commit
git diff <commit_1_id>..<commit_2_id>
// view differences between branches
git diff <branch_1>..<branch_2>
// hit 'q' to exit out of diff view
// stages deleted file if manually deleted
git rm <deleted_file>
// permanently deletes and stages a file
git rm <to_be_deleted_file>
-
Manually moving or renaming a file is identified as deleting old file and adding new file.
-
Upon staging it is recognized as rename operation.
-
moving a file using
git mv
command is identified as renaming and is automatically staged.// renaming file name git mv <old_file_name> <new_file_name> // moving a file git mv <old_file_name> <new_path/old_file_name>
// undo changes in working directory
// restores the repository version of a file from current branch
git checkout -- <file>
// retrieve old versions of files using commit id
git checkout <commit_id> -- <file>
// performs a dry run and informs which files would be deleted
git clean -n
// actually deletes the untracked files
git clean -f
// remove untracked files in an interactive way
git clean -i
// lists the directories and files under a tree-ish(commit, branch, etc)
// a file is labeled as blob(binary large object)
// a directory is labeled as tree
git ls-tree <tree-ish>
// list the content of a specific directory under a tree-ish
git ls-tree <tree-ish> <path/>
// lists all the branches in the repository
git branch
// lists all the branches that have their commits merged in current branch
git branch --merged
// lists all the branches whose commits are not merged in current branch
git branch --no-merged
// create a new branch
// allowed characters for branch name: [a-zA-Z0-9_]*
git branch <new_branch>
// swtich to a branch
git checkout <branch_name>
// create and switch in one go
git checkout -b <new_branch>
Note:-
- Cannot switch branch if changes in working directory conflict.
- Can switch branch if changes in working directory can be applied without conflict.
- Can switch branch if files are not being tracked.
- To switch branch with uncommited changes:
- Commit the changes to the current branch.
- Remove the changes, checkout the files again.
- Stash the changes.
- Install git-prompt.sh to configure command prompt to show the current branch.
// renames the old branch with a new branch name
// does not require the old branch to be checked out
git branch -m <old_branch> <new_branch>
// renaming the current checked out branch
git branch -m <new_branch>
// deletes the specified branch
// can specify more than one branch names to be deleted
// cannot delete the current checked out branch
// git objects deletion if branch is not merged to other branch
git branch -d <branch>
// force delete a not yet merged branch
git branch -D <branch>
// soft reset
// similar to git commit --amend
// moves HEAD pointer
// does not change staging index or working directory
git reset --soft <tree-ish>
// mixed reset
// default choice
// moves HEAD pointer
// changes staging index to match repository
// does not change working directory
git reset --mixed <tree-ish>
// hard reset
// moves HEAD pointer
// changes both staging index & working directory to match repository
git reset --hard <tree-ish>
// revert commit
// alternate safe option
// reverts the change done in the specified commit
git revert <commit_id>
Note:
- Both reset and revert serve the same purpose.
- However, there is key difference in how both commands achieve the objective.
- Reset discards and orphans the undesired commits.
- Revert makes a new commit after making the necessary changes.
- Hence, using revert is safer when multiple collaborators share the repository.
- Reset should be used in non-collaborative repositories.
// merge new branch into current branch
git merge <new_branch>
// in case of merge conflicts:
// abort the merge
git merge --abort
// after resolving conflicts
git add <conflicted_files>
git commit
// creates a new stash named stash_name
// moves the changes from working directory to the stash
// untracked changes are not stashed by default
git stash save <stash_name>
// to stash untracked files too
git stash save -u <stash_name>
git stash save --include-untracked <stash_name>
// view stashed changes
// lists all the stashes by name
git stash list
// show the changes in a particular stash
// lists all the modified files along with the changes as stats
git stash show <stash_id>
// to show the line-by-line changes
git stash show -p <stash_id>
// retrieve stash
// applies the changes of a stash to current branch's working directory
git stash apply <stash_id>
// applies and removes a specific stash
// the newest stash is popped if stash_id is not provided
git stash pop
git stash pop <stash_id>
// delete stash
// removes a specific stash
git stash drop <stash_id>
// clears the stash list completely
git stash clear
Note:
- Similar to merge, while applying a stash, conflicts might occur.
- The conflicts are handled similar to merge conflicts.
// lists all added remotes
git remote
// shows remotes used for fetch and push
git remote -v
// adds a remote repo url as origin
git remote add origin <url>
// removes the remote
git remote rm origin
// local branches need to be pushed individually
// locally checkout branch to be created on remote
// -u ensures the remote branch gets tracked
git push -u origin <branch>
Note:
git push
is also used to push changes to remote repo.- If the remote branch is being tracked, need not mention branch name.
// makes a copy of the remote repo locally
git clone <remote_repo_url>
// to give a custom name to the local repo
git clone <remote_repo_url> <repo_name>
Note:
- Use
git branch
to list all local branches.- Use
git branch -r
to list all remote branches.- Use
git branch -a
to list all branches(local + remote).
// syncs remote repo with local repo
// gets info like new commits, new branches
// does not merge any changes from remote to local repo
git fetch
// merges the fetched changes of remote to local branch
// should follow a git fetch command
git merge origin/master
// directly fetches and merges remote branch changes
// fetch + merge
git pull
// checkout a new branch from remote branch
// sets up the remote branch for tracking
// switches to the new branch
git branch -b <new_branch> <remote_branch>
git branch -b <new_branch> origin/branch
// remove a branch from the remote repo
// the branch can remain locally
// useful when a feature branch is complete and merged
// deletes both tracking and corresponding remote branches
git push origin :<branch>
// command support from v1.7.0+
git push --delete origin <branch>
// short notation support from v2.8.0+
git push -d origin <branch>
Note:
- If a collaborator deletes a remote branch, the tracking branch still remains on others' local system.
- Use
git remote prune origin
to clean the stale tracking branches from local system.git remote prune origin --dry-run
shows what branches would be pruned.- A
git fetch
does not clean the stale tracking branches automatically.- Shortcut: prune, then fetch -
git fetch --prune/-p
- Config to always prune before fetch:
git config --global fetch.prune true
// done when difference between local and remote branches
// discards the commits in remote branches, that are not present in local
git push -f
git push --force
Note:
- Reasons to Force Push:
- Local version is better than remote version.
- Remote version went wrong and needs repair.
- Versions have diverged and merging is undesirable.
- The other collaborators need to do a hard reset
git reset --hard origin/master
after a force push.
- Tags are named reference to a commit.
- Most often used to mark releases or special points in history.
- Two types of tags:
- lightweight tags
- annotated tags
// add a lightweight tag
git tag <tag> <commit_id>
git tag issue136 655da716e7
// annotated tag (most common)
// can add one line or multi-line message
git tag -a <tag> -m <message> <commit_id>
git tag -a v1.1 -m "version 1.0" dd5c49428a0
git tag -a -m "version 2.0" v2.0 94857bb4fc6
// alternate way to create annotated tags
git tag -am <message> <tag> <commit_id>
git tag -am "version 1.0" v1.1 dd5c49428a0
// list all tags
git tag --list
git tag -l
// filter and list tags
git tag -l "filter_string"
// list all tags beginning with "v2"
git tag -l "v2*"
// list tags with annotations
git tag -l -n
git tag -ln
// deletes a local tag
git tag --delete <tag>
git tag -d <tag>
-
Tags are local unless shared to a remote.
-
git push
does not transfer tags. -
Tags must be explicitly transferred.
-
git fetch
automatically retrieves shared tags.// push a tag to remote git push origin <tag> // push all tags to remote git push origin --tags // fetch only tags (with necessary commits) // rarely used git fetch --tags // use push to delete a remote tag // similar to deleting a remote branch git push origin :<tag> git push --delete origin <tag> git push -d origin <tag>
-
Tags are not branches.
-
Tags can be checked out, just like any commit.
// optimal way to checkout a tag // create a branch from the tag git checkout -b <new_branch> <tag>
Note:
- It's also possible to directly checkout a tag using
git checkout <tag>
.- This leads to a state known as Detached HEAD State:
- Checking out a commit puts the local repo in a detached HEAD state.
- It's like being on an unnamed branch.
- New commits will not belong to any branch.
- Detached commits will be garbage collected (~ 2 weeks).
- There are 3 ways to save the orphaned commits:
- Tag the commit (HEAD detached):
git tag temp
- Create a branch (HEAD detached):
git branch temp_branch
- Create a branch and reattach HEAD:
git checkout -b temp_branch
- To simply get out of the detached HEAD state, switch to any branch:
git checkout master
- Apply the changes from one or more existing commits.
- Each cherry-picked commit is recorded as a new commit on the current branch.
- Conceptually similar to copy-paste.
- New commits will have different SHAs.
// cherry-pick a specific commit
git cherry-pick <commit_id>
// cherry-pick a range of commits
git cherry-pick <start_commit_id>..<end_commit_id>
Note:
- Cannot cherry-pick a merge commit.
- By default, the same commit message wil be used.
- The original commit message can be modified using --edit or -e flag followed by the new commit message.
- Can result in conflicts which must be resolved.
-
Can abort cherry-picking in case of conflict.
-
Can resolve the conflicts and continue with cherry-picking.
// abort cherry-picking git cherry-pick --abort // continue after resolving conflicts git cherry-pick --continue
Note:
- Cherry-picking will automatically be paused in case of conflicts.
- After resolving the conflicts,
git add
is to be used to mark resolution.- Only after resolution is marked, use command
git cherry-pick --continue
- Share changes via files.
- Useful when changes are not ready for a public branch.
- Useful when collaborators do not share a remote.
- Often used in discussions, reviews or approval processes.
// create a diff patch file
git diff <commit_id_1> <commit_id_2> > <file_name.diff>
Note:
- Let there be three successive commits:
- commit-3 (latest / HEAD)
- commit-2
- commit-1 (oldest)
- If we want commit-3 and commit-2 to be in a diff patch, command will be:
git diff commit1 commit3 > output.diff
-
Apply changes in a diff patch file to the working directory.
-
Makes changes, but not commits.
-
No commit history transferred.
// apply a diff patch file git apply <path/file_name.diff>
Note:
- A patch cannot be applied to any working directory.
- The current state of the working directory is a deciding factor in the applicability of a patch.
-
Export each commit in Unix mailbox format.
-
Useful for email distribution of changes.
-
Is a regular diff file including commit messages.
-
One commit per file by default.
// export a single commit git format-patch -1 <commit_id> // export range of commits as formatted patch git format-patch <commit_id_1>..<commit_id_2> // scenario: export all commits on current branch which are not in master branch // by default considers blank as HEAD git format-patch master // put patch files into a directory // creates the directory if not present git format-patch master -o <directory_name> // output patches as a single file git format-patch <commit_id_start>..<commit_id_end> --stdout > <filename.patch>
-
Extract author, commit message, and changes from a mailbox message and apply them to the current branch.
-
Similar to cherry-picking: same changes, different SHAs.
-
Unlike diff patch, commit history is transferred.
// apply single patch // am: apply mailbox patch git am <filename.patch> // apply all patches in a directory git am <directory>/*.patch
- Take commits from a branch and replay them at the end of another branch.
- Useful to integrate recent commits without merging.
- Maintains a cleaner, more linear project history.
- Ensures topic branch commits apply cleanly.
Note:
- Rebase abandons existing, shared commits and creates new, similar commits instead.
- Collaborators would see the project history vanish.
- Getting all collaborators back in sync can be a nightmare.
- Merge to allow commits to stand out or to be clearly grouped.
- Merge to bring large topic branches back into master.
- Rebase to add minor commits in master to a topic branch.
- Rebase to move commits from one branch to another.
- Merge anytime the topic branch is already public and being used by others. (The Golden Rule of Rebasing)
Merge Rebase Adds a merge commit. No additional merge commit. Nondestructive. Destructive: SHA changes, commits are rewritten. Complete record of what happened and when. No longer a complete record of what happened and when. Easy to undo. Tricky to undo. Logs can become cluttered, non-linear. Logs are cleaner, more linear.
// rebase current branch to tip of master
git rebase master
// rebase speicifc branch to tip of master
git rebase master <branch_name>
Note:
- Two very useful commands while rebasing:
- Useful for visualizing branches:
git log --graph --all --decorate --oneline
- Return commit where specific branch diverges:
git merge-base master <branch_name>
- Rebasing creates new commits on existing code.
- May conflict with existing code.
- Git pauses rebase before each conflicting commit, waiting for it to be resolved before proceeding with other commits.
- Similar to resolving merge conflicts.
- Unlike merge, after resolving commits,
git rebase --continue
is used instead ofgit commit
. - Before using
git rebase --continue
, make sure the conflicts are also marked resolved usinggit add <conflicted_file>
. - In order to skip a conflicting commit, use
git rebase --skip
to move on to other commits. - Rebasing can also be stopped by using
git rebase --abort
.
-
Has three parameters:
- newbase
- upstream
- branch
-
The command specifies to gather commits from the branch upto the point where diverges from branch and rebase the commits on branch.
// syntax git rebase --onto <newbase> <upstream> <branch> git rebase --onto master ui ui_rework
-
Can undo simple rebases.
-
Rebase is destructive.
-
SHAs, commit messages, change sets can be altered.
-
Undoing complex rebases may lose data.
// undo, unless ORIG_HEAD has changed again git reset --hard ORIG_HEAD // undo by rebasing to former merge-base SHA git rebase --onto 9291f0c88 master new_feature
-
Chance to modify commits as they are being replayed.
-
Opens the git-rebase-todo file for editing.
-
Can reorder or skip commits.
-
Can edit commit contents.
// interactive rebase git rebase -i master new_feature
Note:
- Interactive rebase choices:
- pick, drop
- reword, edit
- squash, fixup
- exec
- Squash Commits:
- Fold two or more commits into one
- squash: combine change sets, concatenate messages
- fixup: combine change sets, discards commit message
- uses first author in the author series
-
Fetch from remote, then rebase instead of merging.
-
Keeps history cleaner by reducing merge commits.
-
Only use on local commits, that have not been shared to remote.
// pull rebase git pull --rebase git pull -r
-
Log is the primary interface to Git.
-
Log has many options like sorting, filtering and output formatting.
// log commits as patches (diffs) git log -p git log --patch // list edits to lines 100-150 in filename.txt git log -L 100,150:filename.txt
-
Browse annotated file
-
Determine who changed which lines in a file and why.
-
Useful for probing the history behind a file's content.
-
Useful for identifying which commit introduced a bug.
// annotate file with commit details git blame filename.txt // ignore whitespace git blame -w filename.txt // annotate lines 100-150 git blame -L 100,150 filename.txt git blame -L 100,+50 filename.txt // annotate file at specific commit git blame <commit_id> filename.txt git blame <commit_id> -- filename.txt
-
Find the commit that introduced a bug or regression.
-
Mark last good revision and first bad revision.
-
Resets code to the midpoint of revisions.
-
Mark as good or bad revision.
-
Repeat
-
Makes use of binary search logic for finding out the bug-introducing commit.
// begin bisecting git bisect start // mark last known bad revision git bisect bad <treeish> // mark last known good revision git bisect good <treeish> // stop and reset git bisect reset