Skip to content

Instantly share code, notes, and snippets.

@gadzhimari
Last active August 20, 2019 08:23
Show Gist options
  • Save gadzhimari/4670b8b01cfc5e15b35d0a524bfc9b1c to your computer and use it in GitHub Desktop.
Save gadzhimari/4670b8b01cfc5e15b35d0a524bfc9b1c to your computer and use it in GitHub Desktop.
List of resources and helpful info about commands

Basics

In git there is three essential entity: blob, tree object and commit.

Reset

To throw away local changes exits couple of similar commands
git reset --hard
git checkout -f <branch>

Note: Untracked files will be untouched. The <branch> is optional and if we specified the HEAD it's unnecessary, because by default it's reference to the currently checked out commit.

git clean -dn this will show you which files or directories are going to be removed without actually removing them

To remove all untracked files
git clean -dfx

Note: -f(force removal), -d(directories), -x(don't use the standard ignore rules from .gitignore and $GIT_DIR/info/exclude

This commands works in conjuction
git reset --hard && git clean -dfx

Note: Removes all modified and untracked files

To restore older state of specific branch use git reset --hard <commit>. If you want to cancel last resetting use commit hash from link. This hash stores in file .git/ORIG_HEAD. The full command git reset --hard ORIG_HEAD

Checkout to previous commit state, but save all files in working directory and index
git reset --soft @~2

To cancel last action
git reset --soft ORIG_HEAD

To save message from last commit use one of the following command
git commit -c ORIG_HEAD opens editor with prefilled commit message
git commit -C ORIG_HEAD skip the step of opening editor(like git commit -m)

git commit --amend combine two steps: git reset --soft and git commit -c ORIG_HEAD
git commit --amend --no-edit use the selected commit message without launching an editor

By default git uses git reset --mixed or short version git reset. In mixed mode, all files that was added to index removed from there and stay in working directory. The following table show difference between 3 type of mode. The yes means removed from index or working directory and no means the opposite.

--hard --mixed --soft --keep --merge
working directory yes yes no no no
index yes no no yes yes

In addition, the git reset --mixed <commit hash> <path to file> allows set the path to file to reset to state of commit where that file was.

Note: git reset --keep resets index entries and updates files in the working tree that are different between and HEAD. If a file that is different between and HEAD has local changes, reset is aborted.

Note: git reset --merge resets the index and updates the files in the working tree that are different between and HEAD, but keeps those which are different between the index and working tree (i.e. which have changes which have not been added). If a file that is different between and the index has unstaged changes, reset is aborted.

Note: git reset should generally be considered a 'local' undo method. The preferred method of undoing shared history is git revert. A revert is safer than a reset because it will not remove any commits from a shared history. A revert will retain the commits you want to undo and create a new commit that inverts the undesired commit. This method is safer for shared remote collaboration because a remote developer can then pull the branch and receive the new revert commit which undoes the undesired commit.

git revert HEAD create a new commit with the inverse of the last commit. This adds a new commit to the current branch.

Note: Diff between reset and checkout is git reset moves both the HEAD and branch refs to the specified commit, but git checkout moves only HEAD

Branch

Go back couple commits ago of branch
git branch -f master <hash or branch>
git checkout -B master <hash of branch>

Note: First command set ref to new commit without switching to specified branch. The second one do all the first and switch it.

git branch --merged show merged branches
git branch --no-merged show branches that not yet merged

Rename local branch name git branch -m new-name

If you on different branch
git branch -m old-name new-name

Restore files

To get previous version only specific file use the following commands
git checkout <hash> <path>

Note: Checking out an old file add file both to working directory and index and does not move the HEAD pointer. It remains on the same branch and same commit, avoiding a 'detached head' state.

Reset to HEAD state
git checkout HEAD <path> if the file was on index
git checkout <path> if the file was on working directory

Merge

git merge consists of 3 parts: base(common ancestor) + our changes + their changes

To find common ancestor between two branches run git merge-base <branch1> <branch2>. When we start merging git creates MERGE_HEAD file which contains the commit which we are merging into to current branch. For example, we are on master branch and git merge fix add last ref to last commit hash of fix branch into MERGE_HEAD file. After successful merging this file will be deleted.

In process of merging conflict we can choose thoose changes are choose. For this case git checkout --ours <file> take our changes from branch where we stay right now. git checkout --theirs <file> will take change from branch that merging. To restore state before choosing git checkout --merge <file>

To show 3 way merge diff(base + ours + theirs) changes git checkout --conflict=diff3 --merge <file>. After merging in index we have 3 simultaneously changes of same file. And it's mean that we can't just run git commit because we have an unmerged changes and to commit we first need add files to index.

git show :1:<file> show base version of file
git show :2:<file> changes in branch where we currently is
git show :3:<file> changes in branch that we merged

git reset --merge or git merge --abort undo the merging. The second one it's a alias of the first command.

To show diff between merged and one of two or n-th parent of merged commit, we can use ^.
git diff @^1 show the first parent of 3-way merge(current branch where we stay)
git diff @^2 show the second parent of 3-way merge(merging branch)

Carats(^) and tildes(~) are relative commit markers in Git. They both mean "parent" but in a different way.

Most commonly used, they are the same. HEAD^1 (or HEAD^ for short) is the same as HEAD. Always.

The difference comes when they stack. So HEAD^2 means "The second parent of HEAD". This only means anything if there's been a merge. In a merge, the main branch is parent #1; the merged in branch is parent 2. So, HEAD^2 will be the merged parent, whereas HEAD^1 will be the parent merged into.

Here diagram that show diff between ^ and ~

Sometimes merging not going well. Everything merged without conflicts, but we have semantic issue. For example, we've changed declaration of function and in other place where we call this function we've changed that call too. And your teammate create another branch, where he used older version of that function. When it's time, we merge both changes and in our files we have two version of calling function and git merging went well. If we want change older calling function to new, we need to add yet another commit. But we won't leave in our branch commits, that has conflicts. For that case, we have git merge --no-commit which do merging, but not run automatically commit. We do changes in our conflict files and add to index and after run git commit or git merge --continue(both commands do the same operation - run commit).

When we do fast-forward merging it's hard to determine which changes belongs to that branch. The history is straight and log doesn't help us. We can use explicit merge git merge --no-ff <branch> that disable fast-forward merge algorithm.

git merge --squash <branch> takes all commits from <branch> and squash them into one commit and merge it with current branch.

Note: The MERGE_HEAD file doesn't created. Because in squashing we have only one parent.

There are many merge strategies. By default Git uses recursive strategy.

  • recursive
    • ours
    • theirs
    • renormalize
    • ignore-all-spaces
    • no-renames
    • find-renames=n
    • subtree=path
  • octopus
  • ours
  • resolve
  • subtree

There is important distinction between strategy ours and option ours of recursive strategy. git merge -s ours <branch> ignores all changes made from other branches

git merge -Xours <branch> include all changes that will not cause a conflict and in conflict files take our version
git merge -Xtheirs <branch> include all changes that will not cause a conflict and in conflict files take their version

We can merge multiple branches at once using git merge feature fix. In this case will be applied octopus strategy

Cherry-pick

git cherry-pick <hash> copy single commit
git cherry-pick master..feature copy all commits that are in feature and not in master

In process of cherry picking we can have conflicts and we have 3 options to resolve them:

  1. git cherry-pick --continue can be used to continue after resolving conflicts in a failed cherry-pick or revert
  2. git cherry-pick --quit leave successfully applied commits and stop on failed cherry-pick
  3. git cherry-pick --abort cancel the operation and return to the pre-sequence state

git cherry-pick -n(or --no-commit) master..feature applies all commits, and only ads to working directory and index without committing

History

Show log and textual diff of commit
git show <hash>

Show the diff of two commits ago
git show HEAD~2 or short version of HEAD is @
git show @~2

Show the whole file in that specific commit state
git show HEAD~3:<path>

Show the file that currently added to the index git show :<path>

Show log and diff using the keyword that matched commit message
git show :/<keyword>

git log feature ^master or equivalent git log master..feature show all commits that are in feature and not in master

Find commit message what matched pattern git log --grep <word>. Find two alternatives git log --grep <word1> --grep <word2>. Find both of them git log --grep <word1> --grep <word2> --all-match

Note: The pattern is case sensitive.

Trace the evolution of the line range given by <start>,<end> (or the function name regex <funcname>) within the <file>
git log -L 10,20:src/controllers/Auth.js

Show what revision and author last modified each line of a file git blame <path>. To show only specific range git blame <path> -L 20,30

git log --first-parent view history of branch in isolation More about first-parent

Syncing fork

https://help.github.com/articles/syncing-a-fork/

Clone

To clone shallow copy of repo git clone --depth=1 <repo>

git clone git@github.com:whatever folder-name clone into specific folder

Difference between git clone --mirror and git clone --bare described here

Reflog

To show the log of references run git reflog. It's the same as git reflog show.

If something was added to repo by mistake and you want delete all information you need to do the following steps.

  1. Delete branch or commit.
  2. git gc runs a garbage collector that remove all unreachable refs.

Note: Deleting a branch or commit doesn't remove information from database if exists record about this commit in database(in other words, exists reference from reflog to this commit). Two options are responsible for this gc.reflogExpire="90 days ago" and gc.reflogExpireUnreachable="30 days ago". It's default time for reachable and unreachable(deleted) commits. In addition, if reflog info is clear, were exist another option gc.pruneExpire="2 weeks ago", that allow us to checkout desired commit. In summary just running git gc doesn't give what we want. If we force this behaviour do the 3rd and 4th step.

  1. git reflog expire --expire=now --all just delete all info from now from reflogs, but commit stay alive
  2. git gc --pruneExpire=now and done

Tags

If you want to mark some important release use tags. git tag <name> set reference to that commit and don't change in compare of other type of refs, such as branches, HEAD. There is two type of tags: lightweighted and annotated. The previous one is lightweight. To create annotated git tag -a v1.0.0 or like in commit command -m flag allow skip writing message in text editor git tag -a -m 'Fix performance on mobile' v1.0.0. The difference between them is the second one add additional info about tagger and tag message when we run git show v1.0.0 command.

Show tag by commit git describe <hash>. To archive all files from project and save on disk(flag -o) git archive -o ~/Desktop/v0.173.0-3-gaa32694e.zip <hash or tag>.

git tag -d <tagname> delete tag

By default, git push will not push tags. Tags have to be explicitly passed to git push origin <tagname>. To push multiple tags simultaneously pass the --tags option to git push command git push origin --tags. When another user clones or pulls a repo they will receive the new tags.

Diff

Diff between two commits git diff <hash1> <hash2>. This equivalent to git diff <hash1>..<hash2>
Diff between two branches git diff <branch1>..<branch2>

Difference between two dots operator and three described here

git diff diff between working directory and index
git diff HEAD show diff between working directory and repository(HEAD)
git diff --staged diff between index and repository(HEAD)

git diff and git diff HEAD do the same if we have changes in working directory only. If the file was added to index, then show diff between working directory and index

Show difference between branches in specific file git diff <branch1> <branch2> <file>

Show names of changed files between branches git diff --name-only <branch1> <branch2>

Show diff outside git repository, i.e. uncontrolled by Git use git diff --no-index <path1> <path2>

By default, diff driver not good for showing diff in text words. For that, exists flag --word-diff. Not everything highlights diff in words by default. To add driver or rules for driver, we should create .gitattribute file in project root.

git diff <hash> -- ':!package-lock.json' ignore noise files from git diff output

Stashing

By default, running git stash will stash:

  • changes that have been added to your index (staged changes)
  • changes made to files that are currently tracked by Git (unstaged changes)

But it will not stash:

  • new files in your working copy that have not yet been staged
  • files that have been ignored

Adding the -u option (or --include-untracked) tells git stash to also stash your untracked files:
git stash -u.

You can include changes to ignored files as well by passing the -a option (or --all) when running git stash.
git stash -a.

To provide a bit more context, it's good practice to annotate your stashes with a description, using:
git stash save "add style to our site"

You can choose which stash to re-apply by passing its identifier as the last argument, for example:
git stash pop stash@{2}

You can view a summary of a stash with git stash show: git stash show or pass the -p option (or --patch) to view the full diff of a stash git stash show -p

You can also choose to stash just a single file, a collection of files, or individual changes from within files. If you pass the -p option (or --patch) to git stash, it will iterate through each changed "hunk" in your working copy and ask whether you wish to stash it.

To delete stash: git stash drop stash@{1}
To delete all: git stash clear

Ignoring files

If you want to ignore a file that you've committed in the past, you'll need to delete the file from your repository and then add a .gitignore rule for it. Using the --cached option with git rm means that the file will be deleted from your repository, but will remain in your working directory as an ignored file.
git rm --cached <file>
You can omit the --cached option if you want to delete the file from both the repository and your local file system.

It is possible to force an ignored file to be committed to the repository using the -f (or --force) option with git add: git add -f <file>

If you already have unstaged changes you must run the following after editing your ignore-patterns: git update-index --assume-unchanged <file>

Debugging .gitignore files

If you have complicated .gitignore patterns, or patterns spread over multiple .gitignore files, it can be difficult to track down why a particular file is being ignored. You can use the git check-ignore command with the -v (or --verbose) option to determine which pattern is causing a particular file to be ignored
git check-ignore -v <file>

Remote

git checkout <remote_branch_name> fetch remote branch and switch to it
git diff origin/<remote_branch_name> show diff between origin/<remote_branch_name> and local
git branch --unset-upstream stop tracking remote branch

Utils

git ls-files show information about files in the index and the working tree
git cat-file -p <object> pretty-print the contents of based on its type

Resources

Pro Git
Atlassian Git
Git Magic
Ilya Kantor Youtube tuts

Workflow

Simple git branching model
Introduction to GitLab Flow
GitHub Flow

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment