Files
oam/knowledge base/git.md
2022-04-21 17:40:05 +02:00

483 lines
14 KiB
Markdown

# 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
# FIXME: there has to be a better way to do this
git add . && git commit -m '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 git's configuration
git config --list
git config --list --show-scope
git config --list --show-origin
# render the current configuration
git config --list \
| awk -F '=' '{print $1}' | uniq \
| xargs -n 1 -I {} sh -c 'printf "{}=" && git config --get {}'
# 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 <https://git-scm.com/docs/git-config#git-config-branchltnamegtremote>.
## 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