This file contains a list of git commands that I’ve found handy in situations I’ve faced over time. These are commands that I have used (successfully). I’m not claiming that this is the only way or even the right way to handle a particular situation that you may face. YMMV.
Along with commands, there are also some general git patterns that I have noted down. I hope this file is of help to you, or at least teaches you something about git that you didn’t know beforehand
The commands are ordered by type rather than difficulty, so you are going to find intermediate and slightly advanced commands intermingled with the easy ones. I mean for this document to be used as a cheat-sheet, not a tutorial, but if you are confused about something shoot me a mail
git init
My recommendation is to always create an empty commit after your initialize the repo.
git commit -m "Empty initial commit" --allow-empty
This trick is useful when initializing the git repository, since the initial commit has special properties which stop it from being used in rebase operations.
git add .
git commit -m
git remote add origin [email protected]:vedang/reponame.git
git checkout -b <branchname> <commit>
git checkout -d <branchname>
git checkout -m <branchname> <newbranchname>
git merge <branchname>
git push <remotename> <localbranchname>:<remotebranchname>
git push <remotename> :<branchname>
git symbolic-ref HEAD refs/heads/newbranch
rm .git/index
git clean -fdx
There is a much simpler way to do this in modern Git (above v1.7.2). This is as follows:
git checkout --orphan newbranch
git rm -rf .
git branch -r --merged origin/branchname | rg origin/master
git branch -r --merged origin/master | rg origin/branchname
** Create a separate repository from a range of commits restricted to a sub-directory
:PROPERTIES:
:CREATED: [2022-03-21 Mon 13:20]
:ID: 6baa94fc-2e01-4fe1-b6d0-6f43a5aa0183
:END:
git filter-branch --subdirectory-filter trunk HEAD
git remote add neworigin /path/to/trunk
git push neworigin master
Basically, what this snippet is doing is the following:
- Create a new repository from a sub-directory named trunk.
- Automatically remove commits that did not affect the sub-directory
This can also be done using git-subtree on git 1.7.11+ Refer to Subtree (1.7.11+)
git checkout masters_original_HEAD
git checkout -b tmp
git merge -s ours master
git checkout master
git merge tmp
git reset --soft HEAD^
The final step removes the merge-commit and reverts the local repo
back to state before the filter-branch
.
git checkout <branch_name>
git branch --set-upstream-to=origin/<branch_name>
$ git merge -s recursive -Xours <branchname>
$ git merge -s recursive -Xtheirs <branchname>
$ git checkout develop $ git merge -s ours master $ git checkout master $ git merge develop
[~/testing (develop *+|MERGING u+7)] $ git status
#
#
#
#
#
$ git checkout –ours README.txt $ git add README.txt $ git commit
$ git remote add -f projA /path/to/projA $ git merge -s ours –no-commit projA/master $ git read-tree –prefix=subdirA/ -u projA/master $ git commit -m “merging projA into subdirA”
$ git rebase -p <branchname>
$ git merge-base <branch1> <branch2>
#
$ git revert M -m 2
$ git tag -u <key-id> <tagname> <commitid>
$ git push <remotename> <tagname>
$ git push <remotename> <branchname> –tags
$ git tag -s <tagname> <commitid>
$ git fetch –tags origin
$ git tag -l
$ git tag –list –sort=version:refname
$ git tag -a
$ git describe –match v*
$ git describe –tags `git rev-list –tags –max-count=1`
$ git show v2.5:fs/locks.c
$ git rev-parse name
$ git tag -d 12345
$ git push origin :refs/tags/12345
$ git tag –contains <SHA>
$ git diff –name-only rev1 rev2
$ git show –pretty=”format:” –name-only <commitid>
$ git log –pretty=oneline
$ git log –author=foo
$ git log –pretty=format:’%C(bold red)%h%Creset -%C(bold yellow)%d%Creset %s %C(bold green)(%cr)%Creset %C(bold blue)[%an]%Creset’ –graph
$ git log –author=$USER –format=”- %B” –since=-7days –reverse
$ git diff –name-status master..branch
$ git log -p <filename>
$ git shortlog master..branch
$ git log –left-right –graph –cherry-pick –oneline branch1…branch2
$ git log –branches –not –remotes
$ git log –pretty=format:’%C(bold red)%h%Creset -%C(bold yellow)%d%Creset %s %C(bold green)(%cr)%Creset %C(bold blue)[%an]%Creset’ –graph
$ git log –branches –not –remotes –simplify-by-decoration –decorate –oneline
$ git log @{u}..
$ git diff origin/master..HEAD
$ git diff rev1:filename rev2:filename
git diff [commit-id-before] [commit-id-after] > my.patch
A better approach is to Create a ready-to-send patch with the last 3 commits
git format-patch -3
Note: In Magit, creating patches is tied to the W
key.
git format-patch master --stdout > diff-with-master.patch
The important thing to remember is to keep the email entirely in plain-text.
git format-patch -k -s --stdout R1..R2 | git am -3 -k
Here, the flags mean the following:
-k
: keep subject-s
: signoff-3
: use diff3 in case of conflict
There is also an easier way of doing this:
git cherry-pick R1..R2
git apply --stat diff-with-master.patch
git apply --check diff-with-master.patch
git am -3 --signoff < diff-with-master.patch
- Push all the changes to your public repository, preferably in a well-named branch.
- Run the following command to generate a well-formatted message:
git request-pull master https://github.com/vedang/pdf-tools/ feature/render-improvements
- Here,
master
is the upstream branch that you want to generate a PR against. https://github.com/vedang/pdf-tools/
is your public repositoryfeature/render-improvements
is your branch (which contains the changes you want to submit)
- Here,
- Generate the pull request as shown above
git request-pull master https://github.com/vedang/clj_fdb/ dev
- Copy the changes as mentioned in the output above
- Create a changelogs file.
git submodule add path_to_git_repo local_dir &&
git submodule init &&
git submodule update;
path_to_git_repo
: eg: [email protected]:vedang/csaoidlocal-dir
: eg: lib/csaoid
- Git submodule update keeps the submodule in headless state. When you
want to bring the submodule up-to-date, remember to checkout to a
branch first.
- NOTE: This is not true with the latest versions of git. These versions will checkout the default branch in the submodule for you
- Updating all submodules while pulling upstream changes:
- Get all the changes
git pull --recurse-submodules #requires git 1.7.3+
- Checkout the proper SHA-1
git submodule update --recursive
- Get all the changes
git clone --recursive <path-to-remote-repo>
- Delete the relevant section from the
.gitmodules
file. - Delete the relevant section from
.git/config
. - Run:
git rm --cached path_to_submodule # (no trailing slash)
- Commit and delete the now untracked submodule files.
- The primary difference between submodules and subtrees is in the
intent behind their usage.
- If you are adding your own child repo to a parent repo, and you plan to actively develop the child repo from inside the parent repo, use submodules.
- If you are adding an external child repo to a parent repo, and you never plan to contribute changes back to the child repo from inside the parent repo, use subtrees.
- Submodules : a pointer to a specific commit in another repository
- This means its trivial to push changes back, but you have to be careful about pulling changes. The submodule is an entirely different repo.
- Subtrees: a full copy of a repository pulled into a parent
repository.
- This means it’s trivial to pull changes, but you have to be careful about pushing changes. The subtree is a copy of a different repo, we don’t know anything about that repo in the parent. We only benefit from having the content.
git subtree add --prefix=path/in/curr/repo --squash \
git://github.com/yourname/your-repo.git master
git subtree pull --prefix=path/in/curr/repo --squash \
git://github.com/yourname/your-repo.git master
git subtree split --prefix=subdir/ --annotate='(split)' -b split_dir_latest
git push [email protected]:yourname/your-repo.git split_dir_latest:master
Git subtree guarantees that previously seen commits with retain the same SHAs which makes this operation relatively straightforward.
git subtree split --prefix=subdir/ --annotate='(split)' -b split_branch
git push [email protected]:yourname/your-repo.git split_branch:master
feature/s
[v@hm ~/src/m (feature/s $ u+1)] $ git worktree add -b s-tests ../mtesting feature/s Enter ../mtesting (identifier mtesting) Branch s-tests set up to track local branch feature/s by rebasing. Switched to a new branch 's-tests' [v@hm ~/src/m (feature/s $ u+1)] $ cd ../mtesting
Voila! I have a new directory with a new branch that is linked to my original repository!
[v@hm ~/src/mtesting (s-tests $ u=)] $ make testclj # Now I can start my tests here and go back to my work in the original # repository without worrying about breaking anything! [v@hm ~/src/mtesting (s-tests $ u=)] $ cd ../m [v@hm ~/src/m (feature/s $ u+1)] $
git worktree list
- Create the following folder structure to get started. For example, here we are developing the
automerge-clj
project.mkdir -p [work-dir]/automerge-clj/v1/ cd !$ clojure -Tnew lib :name me.vedang/automerge-clj cd automerge-clj git init
- In the step above, the
clojure new
command creates anotherautomerge-clj
directory inside ourv1
directory. This is going to be the master repo that will drive our prototyping. - Create an Initial commit of the bare-minimum skeleton. This gives us a good starting point to come back to.
- Now we make as many commits in
automerge-clj/v1/automerge-clj
as we want. - Once we feel the need to start a new prototype
cd [work-dir]/automerge-clj/v1/automerge-clj git worktree add ../../v2 cd ../../v2 # Here, git will have automatically created a new branch for us, called v2.
- We need to create an orphaned branch at the starting point of the repo in order to start from scratch again. You can also do a
git reset --hard
with the rev-list command to start from a previous commit.- To start from scratch, discarding even the initial commit:
git checkout --orphan auto-v2 git rm -rf .
- To reset the current branch to a previous commit
git reset --hard $(git rev-list HEAD | tail -n 1)
- To start from scratch, discarding even the initial commit:
- Caveat: note that it’s
v1/automerge-clj/deps.edn
(for example) vs justv2/deps.edn
. This is a gotcha but I don’t bother working around it. I find I get used to it almost immediately. - When done with prototypes, or when you want to clean up space, you can just
# from the "main" v1/project directory git worktree list git worktree remove path
- Brief explanation of what I’ve done and how I’ve tested it.
- Brief note on why I’ve done it this way.
- (Sometimes) list of pending stuff that I need to do as next steps / to complete the task / to add to the functionality.
git add -p
git cherry-pick <commit-id>
git commit --amend -C HEAD
# -d : delete untracked directories
# -x : delete ignored files.
# -f : force. This command is disabled otherwise.
git clean -dxf <path>
git checkout <branch_name> <filename>
$ git bisect start $ git bisect good v2.6.18 $ git bisect bad master
$ git bisect good # or $ git bisect bad
$ git bisect reset
$ git bisect run my_script
$ git fsck –lost-found –no-reflog
git reflog
$ gitk v1.5.0.. <filenames/directorynames>
$ gitk –since=”2 weeks ago” – <filename>
$ gitk –max-count=100 –all – <filename>
$ gitk –all $( git fsck –no-reflog | awk ’dangling commit {print $3}’ )
$ git reset –soft HEAD@{1}
$ git commit -C HEAD@{1}
$ git reset –soft HEAD^1
$ git stash
$ git pull origin <branchname>
$ git stash pop
$ git log -g # Walks reflog and shows you a history of your actions
$ git reflog show –no-abbrev <branch name>
$ git checkout -m <file>
git grep regex
git grep search_regex -- 'file_regex'
git for-each-ref --contains SHA1
$ git apply –whitespace=fix
$ git filter-branch –commit-filter ’ if [ “$GIT_AUTHOR_EMAIL” = “original_email_address” ]; then GIT_AUTHOR_NAME=”FirstName LastName”; GIT_AUTHOR_EMAIL=”new_email_address”; git commit-tree “$@”; else git commit-tree “$@”; fi’ HEAD
$ git commit –amend –reset-author
$ git daemon –reuseaddr –verbose –base-path=. –export-all ./.git
$ git config –global alias.serve ‘!git daemon –reuseaddr –verbose –base-path=. –export-all ./.git’
$ git clone git://192.168.254.135/ project
debug2: resolving “github.com” port 22 debug2: ssh_connect_direct debug1: Connecting to github.com [64:ff9b::dea:d226] port 22. debug1: connect to address 64:ff9b::dea:d226 port 22: Connection timed out debug1: Connecting to github.com [13.234.176.102] port 22. debug1: Connection established.$ git config –global user.name “FirstName LastName”
$ git config –global user.email “[email protected]”
$ git config –global color.ui “auto”
$ git config –global rerere.enabled 1
$ git config –global branch.autosetuprebase always
$ git config branch.*branch-name*.rebase true
$ git config –global branch.autosetupmerge always
$ git config –global merge.summary true
$ git config –global user.signingkey <keyid>
$ git config –list