diff --git a/knowledge base/git.md b/knowledge base/git.md new file mode 100644 index 0000000..0e6918f --- /dev/null +++ b/knowledge base/git.md @@ -0,0 +1,474 @@ +# Git-related useful commands + +## TL;DR + +```shell +# create a new empty repository or reinitialize an existing one +git init +git init --bare path/to/repo.git +git init --initial-branch main + +# get the current status of changes +git status +git status --verbose + +# check differences +git diff +git diff --staged + +# add the current changes +git add . +git add path/to/file + +# interactive review of chunks of changes +git add --patch + +# clone with submodules in a specific folder +git clone --recurse-submodules git@github.com:user/repo.git repos/repo + +# checkout a remote branch +git checkout -b local_branch origin/remote_branch + +# go back to the previous branch +git checkout - + +# create a commit with no changes nor message +git commit --allow-empty --allow-empty-message + +# add a new remote and push to it +git remote add gitlab git@gitlab.com:user/my-awesome-repo.git +git push gitlab + +# create a patch +git add . && git commit -m 'commit message' && git format-patch HEAD~1 && git reset HEAD~1 +git diff > file.patch + +# apply a patch +git apply file.patch + +# change last commit's author +git config user.name "user name" +git config user.email user.email@mail.com +git commit --amend --reset-author + +# sign all commits from now on +git config --global user.signingkey 'KEY_ID_IN_SHORT_FORMAT' +git config --local commit.gpgsign true + +# working with windows fellas +git config core.autocrlf "input" # unix +git config core.autocrlf "true" # windows + +# show the current configuration +git config --list +git config --list --show-origin + +# get the top-level directory of the current repository +git rev-parse --show-toplevel + +# get the current branch +git branch --show-current # git > v2.22 +git rev-parse --abbrev-ref HEAD + +# list tags +git tag + +# create annotated tags +# stored as full objects in git's database +git tag --annotate v0.1.0 +git tag -as v1.2.0-r0 -m "signed annotated tag for v1.2.0 release 0" +git tag -a 1.1.9 9fceb02 # specific to a commit + +# create lightweight tags +# stored as a pointer to a specific commit +git tag v0.1.1-rc0 + +# push tags +git push origin v1.5 +git push --follow-tags # all annotated tags only +git push --tags # all tags + +# delete tags +git tag -d v1.4-lw # local only +git push origin --delete v1.4-lw # remote only + +# create an alias +git config --local alias.co checkout +git config --global alias.unstage 'reset HEAD --' + +# remove merged branches +git fetch -p && awk '/origin/&&/gone/{print $1}' <(git branch -vv) | xargs git branch -d + +# get a more specific diff +git diff --word-diff +git diff --word-diff=color +git diff --word-diff=porcelain +``` + +## Debug + +When everything else fails, use this: + +```shell +export GIT_TRACE=1 +``` + +## Common configuration + +```shell +# required +git config --local user.email 'me@me.info' +git config --local user.name 'Me' + +# working with windows fellas +# 'input' on unix, 'true' on windows, 'false' only if you know what you are doing +git config --local core.autocrlf 'input' + +# sign commits +git config --local user.signingkey 'KEY_ID_IN_SHORT_FORMAT' # gpg --list-keys --keyid-format short +git config --local commit.gpgsign true # sign all commits +git commit --message "whatever" --gpg-sign # or -S + +# pull submodules by default +git config --global submodule.recurse true +``` + +## Checkout a remote branch + +AKA create a local branch tracking a remote branch + +```shell +git checkout -b "${LOCAL_BRANCH}" "${REMOTE}/${REMOTE_BRANCH}" +``` + +```shell +$ git checkout -b local_branch origin/remote_branch +Branch 'local_branch' set up to track remote branch 'remote_branch' from 'origin'. +Switched to a new branch 'local_branch' +``` + +## Delete all branches already merged on master + +Already present in `oh-my-zsh` as the `gbda` alias + +Command source [here][prune local branches that do not exist on remote anymore] + +```shell +git fetch -p && awk '/origin/&&/gone/{print $1}' <(git branch -vv) | xargs git branch -d +git branch --no-color --merged | command grep -vE "^(\*|\s*(master|develop|dev)\s*$)" | command xargs -n 1 git branch -d +``` + +```shell +for repo in $(find . -type d -name .git | awk -F '/.git' '{print $1}'); do cd ${repo}; echo "--- ${PWD##*/} ---"; gbda; cd - > /dev/null; done +``` + +## Sync up all repos in the current directory + +```shell +for repo in $(find . -type d -name .git | awk -F '/.git' '{print $1}'); do cd ${repo}; echo "--- ${PWD##*/} ---"; git pull; cd - > /dev/null; done +``` + +## Merge master into a feature branch + +```shell +git stash pull +git checkout master +git pull +git checkout feature +git pull +git merge --no-ff master +git stash pop +``` + +```shell +git checkout feature +git pull origin master +``` + +## Rebase a branch on top of another + +`rebase` takes the commits of a branch and appends them to the commits of a different branch. +The commits to rebase are previously saved into a temporary area and then reapplied to the new branch, one by one, in order. + +```shell +git rebase origin/${upstream} ${branch} +``` + +Rebase the current branch onto **upstream** branch `master` + +```shell +git pull --rebase=interactive origin master +``` + +## LFS + +1. install the LFS extension for git + + ```shell + # Ubuntu + apt install git-lfs + ``` + +1. enable the extension in the repository + + ```shell + $ cd "${REPOSITORY}" + [repository-root]$ git install lfs + ``` + +1. configure file tracking + + ```shell + [repository-root]$ git lfs track "*.exe" + [repository-root]$ git lfs track "enormous_file.*" + ``` + +- add the `.gitattributes` file to the traced files + + ```shell + [repository-root]$ git add .gitattributes + [repository-root]$ git commit -m "lfs configured" + ``` + +## Submodules + +See [Git Submodules: Adding, Using, Removing, Updating]. + +- add a submodule to an existing repository: + + ```shell + git submodule add https://github.com/ohmyzsh/ohmyzsh lib/ohmyzsh + ``` + +- clone a repository with submodules: + + ```shell + git clone --recursive keybase://public/bananas/dotfiles + git clone --recurse-submodules ohmyzsh keybase://public/bananas/dotfiles + ``` + +- update an existing repository with submodules: + + ```shell + git pull --recurse-submodules + ``` + +To delete a submodule the procedure is more complicated: + +1. de-init the submodule: + + ```shell + git submodule deinit lib/ohmyzsh + ``` + + this wil also remove the entry from `$REPO_ROOT/.git/config` + +1. remove the submodule from the index: + + ```shell + git rm -rf lib/ohmyzsh + ``` + + this wil also remove the entry from `$REPO_ROOT/.gitmodules` + +1. commit the changes + +## Crypt + +FIXME + +## Visualize the repo's history + +```shell +git log --graph --full-history --all --color --decorate --oneline +``` + +## Remove a file from a commit + +See [remove files from git commit]. + +## Remove a file from the repository + +1. **unstage the file** using `git reset` specify the HEAD as source + + ```shell + git reset HEAD superSecretFile + ``` + +1. **remove it from the index** using `git rm` with the `--cached` option + + ```shell + git rm --cached superSecretFile + ``` + +1. check the file is no longer in the index + + ```shell + $ git ls-files | grep superSecretFile + $ + ``` + +1. add it to `.gitignore` or remove it from the disk +1. amend the most recent commit from your repository + + ```shell + git commit --amend + ``` + +## Remotes management + +```shell +# add a remote +git remote add gitlab git@gitlab.com:user/my-awesome-repo.git + +# add other push urls to an existing remote +git remote set-url --push --add origin https://exampleuser@example.com/path/to/repo1 + +# change a remote +git remote set-url origin git@github.com:user/new-repo-name.git +``` + +### Push to multiple git remotes with the one command + +To always push to `repo1`, `repo2`, and `repo3`, but always pull only from `repo1`, set up the remote 'origin' as follows: + +```shell +git remote add origin https://exampleuser@example.com/path/to/repo1 +git remote set-url --push --add origin https://exampleuser@example.com/path/to/repo1 +git remote set-url --push --add origin https://exampleuser@example.com/path/to/repo2 +git remote set-url --push --add origin https://exampleuser@example.com/path/to/repo3 +``` + +```plaintext +[remote "origin"] + url = https://exampleuser@example.com/path/to/repo1 + pushurl = https://exampleuser@example.com/path/to/repo1 + pushurl = https://exampleuser@example.com/path/to/repo2 + pushurl = https://exampleuser@example.com/path/to/repo3 + fetch = +refs/heads/*:refs/remotes/origin/* +``` + +To only pull from `repo1` but push to `repo1` and `repo2` for a specific branch `specialBranch`: + +```plaintext +[remote "origin"] + url = ssh://git@aaa.xxx.com:7999/yyy/repo1.git + fetch = +refs/heads/*:refs/remotes/origin/* + ... +[remote "specialRemote"] + url = ssh://git@aaa.xxx.com:7999/yyy/repo1.git + pushurl = ssh://git@aaa.xxx.com:7999/yyy/repo1.git + pushurl = ssh://git@aaa.xxx.com:7999/yyy/repo2.git + fetch = +refs/heads/*:refs/remotes/origin/* + ... +[branch "specialBranch"] + remote = origin + pushRemote = specialRemote + ... +``` + +See . + +## Delete a branch + +```shell +# locally +git branch --delete feat-branch +git branch -D feat-branch + +# remote +git push origin :feat-branch +git push origin --delete feat-branch + +# both +git branch --delete --remotes feat-branch +``` + +## Sync the branch list + +```shell +git fetch --prune +``` + +## Sync the tags list + +```shell +git fetch --prune-tags +``` + +## Troubleshooting + +### GPG cannot sign a commit + +> ```shell +> error: gpg failed to sign the data +> fatal: failed to write commit object +> ``` + +If gnupg2 and gpg-agent 2.x are used, be sure to set the environment variable GPG_TTY, specially zsh users with Powerlevel10k with Instant Prompt enabled. + +```shell +export GPG_TTY=$(tty) +``` + +## Further readings + +- [Get the repository's root directory] +- [How do I check out a remote Git branch] on [StackOverflow] +- [How to manage your secrets with git-crypt] +- Question about [how to rebase a local branch with remote master] +- Question about how to [merge master into a feature branch] +- Question about how to [prune local branches that do not exist on remote anymore] +- Question about how to [rebase remote branches] +- Quick guide about [git rebase][rebase quick guide] +- Quick guide about how to [remove files from git commit] +- The official [LFS website] +- [How to get the current branch name in Git?] +- [Git Submodules: Adding, Using, Removing, Updating] +- [How to add and update git submodules] +- [Is there a way to make git pull automatically update submodules?] +- [How to change a git remote] +- Git [docs] +- [Why can't I delete a branch in a remote GitLab repository?] +- [How to Delete a Git Branch Both Locally and Remotely] +- [gpg failed to sign the data fatal: failed to write commit object] +- [Able to push to all git remotes with the one command?] +- [Create a git patch from the uncommitted changes in the current working directory] +- [Is there a way to gpg sign all previous commits?] +- [Tagging] +- [10 Git tips we can't live without] +- [Coloring white space in git-diff's output] +- [Multiple git configuration] +- [How to improve git's diff highlighting?] + +[docs]: https://git-scm.com/docs/git +[gitignore]: https://git-scm.com/docs/gitignore +[tagging]: https://git-scm.com/book/en/v2/Git-Basics-Tagging + +[stackoverflow]: https://stackoverflow.com + +[10 git tips we can't live without]: https://opensource.com/article/22/4/git-tips +[able to push to all git remotes with the one command?]: https://stackoverflow.com/questions/5785549/able-to-push-to-all-git-remotes-with-the-one-command +[coloring white space in git-diff's output]: https://stackoverflow.com/questions/5257553/coloring-white-space-in-git-diffs-output#5259137 +[create a git patch from the uncommitted changes in the current working directory]: https://stackoverflow.com/questions/5159185/create-a-git-patch-from-the-uncommitted-changes-in-the-current-working-directory +[get the repository's root directory]: https://stackoverflow.com/questions/957928/is-there-a-way-to-get-the-git-root-directory-in-one-command/#957978 +[git submodules: adding, using, removing, updating]: https://chrisjean.com/git-submodules-adding-using-removing-and-updating/ +[gpg failed to sign the data fatal: failed to write commit object]: https://stackoverflow.com/questions/39494631/gpg-failed-to-sign-the-data-fatal-failed-to-write-commit-object-git-2-10-0 +[how do i check out a remote git branch]: https://stackoverflow.com/questions/1783405/how-do-i-check-out-a-remote-git-branch/#1787014 +[how to add and update git submodules]: https://devconnected.com/how-to-add-and-update-git-submodules/ +[how to change a git remote]: https://careerkarma.com/blog/git-change-remote/ +[how to delete a git branch both locally and remotely]: https://www.freecodecamp.org/news/how-to-delete-a-git-branch-both-locally-and-remotely/ +[how to get the current branch name in git?]: https://stackoverflow.com/questions/6245570/how-to-get-the-current-branch-name-in-git#6245587 +[how to improve git's diff highlighting?]: https://stackoverflow.com/questions/49278577/how-to-improve-gits-diff-highlighting#49281425 +[how to manage your secrets with git-crypt]: https://dev.to/heroku/how-to-manage-your-secrets-with-git-crypt-56ih +[how to rebase a local branch with remote master]: https://stackoverflow.com/questions/7929369/how-to-rebase-local-branch-with-remote-master/#18442755 +[is there a way to gpg sign all previous commits?]: https://stackoverflow.com/questions/41882919/is-there-a-way-to-gpg-sign-all-previous-commits +[is there a way to make git pull automatically update submodules?]: https://stackoverflow.com/questions/4611512/is-there-a-way-to-make-git-pull-automatically-update-submodules#49427199 +[lfs website]: https://git-lfs.github.com/ +[merge master into a feature branch]: https://stackoverflow.com/questions/16955980/git-merge-master-into-feature-branch +[multiple git configuration]: https://riptutorial.com/git/example/1423/multiple-git-configurations +[prune local tracking branches that do not exist on remote anymore]: https://stackoverflow.com/questions/13064613/how-to-prune-local-tracking-branches-that-do-not-exist-on-remote-anymore#17029936 +[rebase quick guide]: https://medium.com/@gabriellamedas/git-rebase-and-git-rebase-onto-a6a3f83f9cce +[rebase remote branches]: https://stackoverflow.com/questions/6199889/rebasing-remote-branches-in-git/#6204804 +[remove files from git commit]: https://devconnected.com/how-to-remove-files-from-git-commit/ +[why can't i delete a branch in a remote gitlab repository?]: https://stackoverflow.com/questions/44657989/why-cant-i-delete-a-branch-in-a-remote-gitlab-repository#44658277