+++
title = "Mastering Git"
description = "Overview of Git's main functionality"
date = 2025-05-03
tags = [
    "tech",
    "guide",
    "git"
]
+++

## Staging

Stage all files `git add -A` and unstage `git restore .`

## Commit

To immediately commit all changed files `git commit -am "Commit message"`

Change last commit `git commit --amend`. You can quickly rewrite a commit without editor `git commit --amend --no-edit`

Restore file from specific commit `git restore -s abcd123 file.txt`

## Diff

Show the state of a file in a specific commit `git show abcd123 file.c`

Show the difference between two commits `git diff abcd123..efgh123`

## Log

Show last five commits `git log -5`

Show history of changes in patch format `git log -p`

Show branch tree of commits `git log --pretty=oneline --graph`

## Stashing

You can hide your raw work with the `git stash` command, `git stash list` to show the stashes and `git stash apply` to apply the last stash, or `git stash apply stash@{2}` to apply stash by its index.

## Search

Search for a keyword in whole project `git grep -n myClass`

Display files with count of matching `git grep --count myClass`

Display commits which contains keyword `git log -S ZLIB_BUF_MAX --oneline`

By the way, you can specify the path to the directory in which you want to search as the last argument.

Show how the function git_deflate_bound() in the file zlib.c evolved over time `git log -L :git_deflate_bound:zlib.c`

## Branching

**git pull = git fetch + git merge**

**git switch -c = git branch + git switch**

Process of integration feature branch:

```sh
git switch main
git pull origin main
git merge feature-branch-name
git push origin main
```

## Tags

Tags usually calls as **v1.0.0** where numbers signify **major**.**minor**.**patch**

**major** — Breaking changes 💥
<br>
**minor** — New features ✨
<br>
**patch** — Bug fixes 🔧

There are types of pre-release versions:

**alpha** — raw version of the build
<br>
**beta** — testing version of the build
<br>
**rc** — release candidate or nearly finished release

`v1.0.0-alpha < v1.0.0-beta < v1.0.0-rc < v1.0.0`

You can create an annotated tag like this `git tag -a v1.0.0 -m "First release"` or lightweight tag `git tag v1.0.0` without descriptions. To push on server `git push origin --tags`. To delete `git tag -d v1.0.0` or delete on server `git push origin --delete v1.0.0`. You can switch to tag by `git checkout v1.0.0`, if you make changes and commit them in tag's "detached HEAD" state, your new commit won’t belong to any branch and will be unreachable. Then you need to create a new branch `git switch -c branch-name v1.0.0` which will contain your commit.

You can generate a build number with this command `git describe main` if you have annotated tags; if you use lightweight tags, you should add `--tags` flag.

## Archives

Git allows you to create archives for your releases:

```git archive main | gzip > `git describe main`.tar.gz```

```git archive main --format=zip > `git describe main`.zip```

## Submodules

You can add a submodule by this command `git submodule add <url>`. To update a submodule, you must execute this `git submodule update --remote <submodule_name>`. To clone a project with submodules `git clone --recurse-submodules <url>` or in an already cloned repo run this `git submodule update --init --recursive`.

## History rewriting

Change the base commit to solve merge problems when pulling `git pull --rebase`

When you fetch a feature branch from remote repo with few commits, you can merge it from a specific commit, skipping all commits above `git cherry-pick abcd123`

Interactive mode `git rebase -i` is a powerful tool to rewrite your Git history; for example, squash several commits in one, split one commit to several, change the commit order, or delete some commits. You can check out Drew DeVault's [guide](https://git-rebase.io) or read chapter "Rewriting History" in the book "Pro Git".

## Debugging

Git provides two commands for debugging your project: `git blame` and `git bisect`.

The first command shows you line by line who last changed each line of a file `git blame -L 70,80 Makefile`. If you are refactoring a file into multiple files, you can see where sections of the code originally came from by passing the `-C` option.

And the second one carries out binary search in interactive mode. When you start it by `git bisect start` you should enter `git bisect good` if the current commit is good or `git bisect bad` if not. If you answered positively, it means that all commits below are also good and do not contain bugs, and Git will switch to the middle commit between the good commit and the last commit for effective search. The search continues until you find the bug, then run `git bisect reset` to reset HEAD to where you were before you started.
You can fully automate this process by passing a script that runs tests and returns 0 in case of success or 1 in error.

```
$ git bisect start HEAD v1.0.0
$ git bisect run test-error.sh
```

## Configuration

If you use HTTP authentication, you can save your credentials in order to avoid entering a password `git config --global credential.helper cache`

### Aliases

Aliases is useful shortcuts, like in `.bashrc`, here's an instance of a handy ones:

```sh
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
```

## GUI

Git has graphical tools based on the **tk** library; `git gui` for managment and `gitk` for browsing repository.

## Contributing

Git is decentralized VCS (Version Control System) by default, like Email, and there are a few methods for co-development:

1. Sending patches via Email
2. Main developer, clone and merge the feature branch from your own git repository
3. Sending a git bundle as you wish

> As a general rule, your commit messages should start with a single line that's no more than about 50 characters and that describes the changeset concisely, followed by a blank line, followed by a more detailed explanation. The Git project requires that the more detailed explanation include your motivation for the change and contrast its implementation with previous behavior — this is a good guideline to follow. Write your commit message in the imperative: "Fix bug" and not "Fixed bug" or "Fixes bug." Here is a template you can follow, which we've lightly adapted from one [originally written by Tim Pope](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).

### Patches via Email

Email setup [guide](https://git-send-email.io) for Git.

Get patch: `git show -p HEAD > patch.diff`

Get pretty patch for contributing: `git format-patch -M origin/main`

Apply patch: `git apply patch.diff`

Apply pretty patch: `git am 0001-your-patch-name.patch`

### Remote repository

In this case, you clone the project, create a feature branch in which you're creating your feature, and then you push this feature branch to your own git repository. You inform the main developer about your feature branch, and he should add it to remotes or just pull your branch. If everything is okay, he can merge it into the main branch.

### Git bundle

In this case, you create a single binary file of the repository with all your changes, for example, in the feature branch, and send this file via email or as you wish.

Suppose, your repository looks like this:

```
$ git log --oneline
71b84da Last commit - second repo
c99cf5b Fourth commit - second repo
7011d3d Third commit - second repo
9a466c5 Second commit
b1ec324 First commit
```

and you want to send only your last three commits, then you need to define a range of commits in this command: `git bundle create <filename>.bundle main ^9a466c5`

You can send this file to the developer. For first, he has to verify your bundle `git bundle verify <filename>.bundle` and after this it can be applied `git pull <filename>.bundle`

## Plumbing and Porcelain

Show commit count `git rev-list HEAD --count`

## Paradigms

Lately, developers have been returning to [Trunk Based Development](https://trunkbaseddevelopment.com). It acts as an antagonist against branch hell. The emphasis here is on CI, which runs tests and ensures that the code being contributed is error-free, so developers with a high development culture can afford to send commits directly to the main branch. Larger projects, use short-term feature branches, in which developers work throughout the day and at the end of which they merge their changes. However, some old projects such as a [Git](https://git.kernel.org/pub/scm/git/git.git) still using a four-branch model: **main**, **next** for merging stable feature branches, **seen** for merging raw feature branches, and **maint** for maintenance backport.

## Git on server

Usually used Nginx and Fcgiwrap for **http** connections and **cgit** as a frontend. In the case of **ssh** you almost don't need to set up anything, just the home directory of Git user with repositories. To handle `git://` connections used **git-daemon** `git daemon --reuseaddr --base-path=/srv/git/ --export-all --enable=receive-pack --informative-errors --verbose /srv/git/`.

Here are useful links if you decide to raise your own git server:

* https://landchad.net/git
* https://landchad.net/cgit
* https://wiki.archlinux.org/title/Cgit

## Hooks

It's a standard way to organize **CI/CD** by handwriting  scripts, which live in the `.git/hooks` directory. There are client-side and server-side hooks. Sadly, but the hooks are not cloned, so you need to hold it in a separate place or repo. Server side hooks are located in the bare repository on the server and can function to build and deploy your **Hugo** website every time you pull your changes, for example.

## Summary

It's just a quick overview that includes only general functionality, and I strongly recommend you to read the book [**Git Pro**](https://git-scm.com/book).
