# git Distributed version control from Finland. # Branches Branches are a way to keep some code separate from other code. Usually you should adopt a branching model, here is a good example: https://nvie.com/posts/a-successful-git-branching-model/ It isn't a good idea to commit directly to your main branch. You should instead have separate branches for different actions: * main branch * eternal branch * reflects production-ready state * **develop** branch – where work is done * eternal branch * reflects latest (unstable) changes * **feature** branches * branch off from `develop` * *name*: relevant feature name (except reserved names) * work on new features to be integrated soon(TM) * usually kept local, not pushed to origin * feature is done -> merge back into `develop` * remove after feature finished * **release** branches * branch off from `develop` * *name*: `release-*` * prep for new stable release * assign new version number * add new bug fixes here! * merge into `main` -> marks official stable release * tag version for easy reference with `git tag -a X.x` * release is done -> merge back into ``develop`` * remove after release done * **hotfix** branches * branch off from `main` * *name*: ``hotfix-*`` * emergency fixes to live production software * hotfix is done -> merge back into `main` * merge into current release branch, otherwise into `develop` * remove after hotfix done # Merging If you want to merge the `fix-foo` branch into the `main` branch: `git checkout main && git merge fix-foo` If you don't have diverging history, this will usually result in a fast-forward. ## Keep history When merging branches using the previous model, you should use ``--no-ff`` as it keeps the supporting branches history. To make this the default behaviour, use ``git config --global merge.ff no`` along with ``git config --global pull.ff only`` to keep git from creating a merge commit when pulling. To override this, use ``git merge --ff`` # Commits Write good, standardized commits. **Commit title:** ``[optional scope]: `` **Types:** fix, feat, chore, docs, refactor, test **Scopes:** the specific component/area you're working on See https://www.conventionalcommits.org/en/v1.0.0/ for more info. ## Checkout old versions You want to revert a file to a previous commit's state? `git checkout 0a1b2c file.txt` ## Amend commits Change the last commit message with `git commit --amend`. Change its contents as well by running the regular commands and amend as regular. ## Search commits Search through commits with ``git log `` Some useful options: * ``git log --since=2.weeks`` -> commits from the last 2 weeks * ``git log -S string`` -> commits that added/removed a reference to string; git's "pickaxe" option * You can use this to find what/where ## View changes in commit editor By default, git shows you which files were modified while writing a commit message in your configured editor. It may be helpful to also see what changed, or the patch set. You can configure git to do this by default with ``git config --global commit.verbose true`` ## Verified commits If you want to use verified commits, you should have a GPG key added to git and your git server account. To add a GPG key to git, first get your key ID with ``gpg --list-secret-keys --keyid-format=long``, then use ``git config --global user.signingkey IDHERE`` Enable signing for commits and tags with ``git config --global commit.gpgsign true`` and ``git config --global tag.gpgsign true`` respectively. To not manually type the GPG key's password every time you commit, you can integrate GPG with a compatible DBus Secret Service API provider, such as KeePassXC. See https://wiki.archlinux.org/title/GnuPG#pinentry and https://c3pb.de/blog/keepassxc-secrets-service.html for more details. # Reset You can undo your changes with ``git reset HEAD~`` ``git reset`` does three things depending on its paremeters: 1. Move the branch HEAD points to (``--soft``) * Undo your last commit 2. Make the index look like HEAD (default) * Undo your last commit and unstage everything 3. Make the working directory look like the index (``--hard``) * **Warning:** You can lose work forever: git overwrites any changes with the last commit's version. # Remotes Git remotes # Rebasing ``git rebase branch`` modifies your git history so that two branches are merged into one without a merge commit (the history is now linear, as if there was only one branch) ## Split up commits You can split up commits that are too large by rebasing. Run ``git rebase -i HEAD~2`` to rebase the last two commits, and change the appropiate's commit command to ``edit``. Uncommit your latest changes (mixed reset) with ``git reset HEAD^`` and start an interactive commit session with ``git commit -p``. This allows you to edit the patch manually, selecting what you need for specific, more self-contained commits. Git's interactive commit editor has a lot of options (press ``?`` to summarize them) but normally you need to split the commit with ``s``, accept with ``y`` and quit with ``q``. See https://git-scm.com/book/en/v2/Git-Branching-Rebasing and https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History for info on rebasing. See https://git-rebase.io/ for a guide with examples on rebasing. ## Squash multiple commits If you want to squash multiple commits into one, rebase interactively the last n commits with ``git rebase -i HEAD~n`` and squash the commits with `s`. # Aliases Git aliases help you run frequent commands faster. To set an alias, use ``git config --global alias. '``' Current custom aliases: | Alias | Git command | Note | |------------|-------------------------------------|-------------------------------------------------| | graph | log --pretty=format:"%h %s" --graph | Prints a graph of the commit history | | last | log -1 HEAD | Shows last commit | # Searching through code You can use ``git grep`` to search through git trees really fast. You can also search in a tag by adding the tag name at the end. See https://git-scm.com/book/en/v2/Git-Tools-Searching for more info. # Offline git A solution would be to use something like a USB stick, make a bare repo on it with `git init --bare` and add the new repo as a remote for the local repo to be shared with `git remote add usbstick /mount/usb/repohere`. Push to it with `git push usbstick main` from your local repo. See James Gibbard's site for more info: https://www.gibbard.me/using_git_offline/ # Learn git Learn git by reading the git book: https://git-scm.com/book/en/v2 Additional material: - BetterExplained's Visual Guide to Version Control: https://betterexplained.com/articles/a-visual-guide-to-version-control/ - Distributed version control illustration: https://betterexplained.com/articles/intro-to-version-control-illustrated/ - Aha! Moments git: https://betterexplained.com/articles/aha-moments-when-learning-git/ # Best practice https://www.conventionalcommits.org/en/v1.0.0/ -> write good commits https://semver.org/ -> version your stuff good https://man.sr.ht/lists.sr.ht/etiquette.md -> sr.ht mailing list etiquette