- Git
- Practice more: Learn Git Branching
- Common mistakes: Oh Shit Git!!
- Man Tutorial:
man gittutorial
- Branches are pointers to specific commit
- Instead of mentioning whole hash of a commit, you can specify only few characters.
- If there conflicts in any command, you can use
--continue
after fixing them to continue the operation.- For example if cherry-picking caused some conflicts and you fixed those conflicts, run
git cherry-pick --continue
to continue cherry-picking
- For example if cherry-picking caused some conflicts and you fixed those conflicts, run
- Operations that modify commit history (e.g Interactive rebasing) should generally be avoided on commits that have already been pushed to a remote repository.
merging
andrebasing
are two ways to combine work from different branches- (you are in master branch and want to combine work of feature_x work into it, feature_x branch won't be effected at all in both operations)
git merge feature_x
will create a new merged commit in master branch with the work of both the branches into it.git rebase feature_x
will rebase the master branch
- (you are in master branch and want to combine work of feature_x work into it, feature_x branch won't be effected at all in both operations)
HEAD
symbolic name for currently checked out commitDetaching head
means attaching head to a commit rather than a branch- Even if the head and branch point to same commit, you can have detached head
@
alone is a short to HEAD<refname>@{<date>}
e.g master@{yesterday} or master@{2 months 1 week 3 hours ago}
- Branch early, and branch often
- Branches are pointers to specific commit.
- Very light weight (no space is consumed)
git branch name
create a new branchgit checkout name
switch to name branchgit checkout -b name
create and switch to name branchgit switch name
similar togit checkout
but still experimentalgit branch -f main dest_ref
(forcefully) reassigns main branch to some other commit. (dest_ref can be another branch, commit hash or relative ref like HEAD~4)
- Merging creates a special commit that has changes of two or more branches and has two or more parents
- If you go up the commit history and encounter all commits then that commit contains all work of repository
git merge otherBranch
will combine the commit/work of otherBranch into curBranch- A new commit will be created with the work of both curBranch and otherBranch
- curBranch (which is a commit pointer) will point to this commit that just happened
- otherBranch (pointer) wont be effected at all
- Rebasing essentially takes a set of commits, "copies" them, and plops them down somewhere else
- MORE TECHNICALLY: Rebasing involves moving the branch pointer to a new base commit and replaying the commits on top of it, creating new commits.
git rebase <new_base_ref> <ref>
will rebase the ref on new_base_ref
- In example below, feature_branch and master branch had common ancestor F. After a while both branches progressed. Lets say you want to incorporate changes of master into feature_branch.
- You run
git rebase master
which tells vim to make base commit of feature_branch as master (i.e H). Git copies all commits of feature_branch that are not in master and then replays them on top of the latest commit in master
- You run
Before the rebase:
A---B---C---D feature_branch (before rebase) / ---E---F---G---H master
After the rebase:
A'--B'--C'--D' feature_branch (after rebase) / ---E---F---G---H master
git reset
andgit revert
are used to reverse changesgit reset HEAD^
will remove the current commit as if it never happened- It may cause issues with remote repositories as it modifies the commit history
git revert HEAD^
will add a new commit such that it exactly reverses the current commit- In other words, current state repo will be exactly the same as of HEAD^ but the commit at will not be removed
git cherry-pick <commit1> <commit2> ...
apply the selected commits to current branch- New commits will be created with the changes from the cherry-picked commits.
-
git rebase -i ref
open interactive rebasing commits- ref can be a commit hash of reference such as
main^
- ref can be a commit hash of reference such as
-
- Leaves the commit as it is
-
- Repackage multiple commits into single commit (basically rewrites commit history)
-
- Modify commit messages
-
- Reorder commits
- Simple move the lines up and below
- Tags (aka anchors) act as (permanent) milestones. Contrary to tags, branches are always changing and temporary
git tag v1.0 <ref>
will create a tag at ref. If ref not given then HEAD will be usedgit describe <ref>
will give you output of the form<tag>_<num_commits>_g<hash>
wheretag
is the closest ancestor tag from which your ref or commit with hashref
isnum_commits
away.
Relative refs
convenient way of avoiding hashes by moving relative to branch or head^
move up by one commit. (git checkout master^^^ moves you 3 parents up from master)<ref>^0
refers to the ref itself<ref>^1
or<ref>^
moves refers to the first parent of ref<ref>^n
refers to the nth parent of ref
~<num>
move up by num commits. (git checkout master~3 moves you 3 parents up from master)- It follows only the first parent
- Some commits posses multiple parents (like the one created during merging).
- Here is an example. Both commit nodes B and C are parents of commit node A. Parent commits are ordered left-to-right. Thus B is first parent and C is 2nd parent of A
G H I J \ / \ / D E F \ | / \ \ | / | \|/ | B C \ / \ / A A = = A^0 B = A^ = A^1 = A~1 C = = A^2 D = A^^ = A^1^1 = A~2 E = B^2 = A^^2 F = B^3 = A^^3 G = A^^^ = A^1^1^1 = A~3 H = D^2 = B^^2 = A^^^2 = A~2^2 I = F^ = B^3^ = A^^3^ J = F^2 = B^3^2 = A^^3^2
- Every gitignore can effect only files at or below its level
/dir/file
/ refers to the dir of gitignore/dir/*.txt
matches all text files recursively in dir directory*.txt
matches all text files recursively in directory gitignore
!
-> negates pattern#
-> comment
git clone
creates local copy of remote repository- Remote branches are special branches that reflect the state of remote repository. You cannot commit on them directly as then they won't reflect remote changes. You cannot assign them to some different commit either i.e
git branch -f origin/main <commit_hash>
will fail (or create a new local branch which will be diff from remote branch)- They are special in the sense that when you check them out, git puts you in detached head state
- This is done so that you don't directly make any public changes
<remote_name>/<branch_name>
is the required naming convention for remote branches
- By default when you clone repo,
origin
name is given the remote
-
CASE: If the remote repository has changed since your last pull and you attempt to push your commits using git push, it will fail. You will have to rebase the work you have done based on the latest state of remote repository.
-
Lets say you are on main branch and you have done some commits on it. While you were doing those commits on your local branch, your colleagues also pushed some commits to the main branch of remote repository. In this case, you should perform the following steps:
git fetch
to update your local version of remote repositorygit rebase origin/main
to make your local main based on the remote version of main- You can of-course use
git merge origin/main
but i won't suggest it
- You can of-course use
git push
to finally upload your changes
- CASE: You created a new local branch and want to push it.
git push -u origin feature
will create a neworigin/feature
branch in your local repository as well as a newfeature
branch in your actual remote repository with all the changes from your feature branch- Note: Even if you use only
git push origin feature
and you don't have a remote feature branch, git will behave same as above
- Note: Even if you use only
- CASE: You want to contribute to an open-source project on github
- Fork the repository and clone it
- Create new branch and add features or bug fixes
- Once done, add the original projects url to your remotes.
upstream
is usually used as the name - Fetch the latest changes from original repo and rebase your work based on it
- Push the changes your forked repo
- Go to github.com and create a pull-request
- Performs two steps:
- Downloads commits that our local representation of remote does not have
- Updates out remote branches (i.e for example which commit
origin/main
points to)
- It essentially synchronizes our local copy of remote repo with actual remote repo
- Performs two steps:
- Run git fetch
- Merge whichever branch you downloaded with local corresponding branch
- Publish your changes to remote
- If the remote has changed, (see Diverged History), you can run
git fetch; git rebase origin/main main; git push
or shorthandgit pull --rebase; git push
.- You can also do
git fetch; git merge origin/main main; git push
or shorthandgit pull; git push
- You can also do
git push <remote> <place>
- e.g git push origin feature where feature is the branch from which Git will grab commits and apply them to the remote branch of remote named origin which feature is set to track
- It does not matter where you execute this command
- If feature branch does not track any remote branch then Git will automatically create and push feature branch to remote
-
Remote tracking is a property of branches.
- If a local branch tracks a remote branch then
git merge
andgit push
have implied target as remote branch. In other words, git merge automatically merges current branch with its remote branch and git push automatically sets destination as remote branch.
- If a local branch tracks a remote branch then
-
You can make a branch to track remote branch via following two methods
git checkout -b my_branch origin/main
will create a new branch my_branch and set it to track origin/maingit branch -u origin/main my_branch
will set my_branch to track origin/main. If you have already checked out my_branch, you can omit the branch name.