Add documentation

This commit is contained in:
2021-11-24 17:22:49 +01:00
parent 0c6a4a72ef
commit 1cf5585e2c
8 changed files with 365 additions and 0 deletions

7
docs/src/SUMMARY.md Normal file
View File

@@ -0,0 +1,7 @@
# Summary
- [Overview](./overview.md)
- [Getting started](./getting_started.md)
- [Repository trees](./repos.md)
- [Git Worktrees](./worktrees.md)
- [FAQ](./faq.md)

10
docs/src/faq.md Normal file
View File

@@ -0,0 +1,10 @@
# FAQ
## Why is the nightly toolchain required?
Building GRM currently requires nightly features due to the usage of
[`std::path::Path::is_symlink()`](https://doc.rust-lang.org/std/fs/struct.FileType.html#method.is_symlink).
See the [tracking issue](https://github.com/rust-lang/rust/issues/85748).
`is_symlink()` is actually available in rustc 1.57, so it will be on stable in
the near future. This would mean that GRM can be built using the stable toolchain!

View File

@@ -0,0 +1,22 @@
# Quickstart
## Installation
Building GRM currently requires the nightly Rust toolchain. The easiest way
is using [`rustup`](https://rustup.rs/). Make sure that rustup is properly installed.
Make sure that the nightly toolchain is installed:
```
$ rustup toolchain install nightly
```
```bash
$ cargo +nightly install --git https://github.com/hakoerber/git-repo-manager.git --branch master
```
If you're brave, you can also run the development build:
```bash
$ cargo +nightly install --git https://github.com/hakoerber/git-repo-manager.git --branch develop
```

33
docs/src/overview.md Normal file
View File

@@ -0,0 +1,33 @@
# Overview
Welcome! This is the documentation for [Git Repo
Manager](https://github.com/hakoerber/git-repo-manager/) (GRM for short), a
tool that helps you manage git repositories.
GRM helps you manage git repositories in a declarative way. Configure your
repositories in a TOML file, GRM does the rest. Take a look at [the example
configuration](https://github.com/hakoerber/git-repo-manager/blob/master/example.config.toml)
to get a feel for the way you configure your repositories. See the [repository
tree chapter](./repos.md) for details.
GRM also provides some tooling to work with single git repositories using
`git-worktree`. See [the worktree chapter](./worktree.md) for more details.
## Why use GRM?
If you're working with a lot of git repositories, GRM can help you to manage them
in an easy way:
* You want to easily clone many repositories to a new machine.
* You want to change remotes for multiple repositories (e.g. because your GitLab
domain changed).
* You want to get an overview over all repositories you have, and check whether
you forgot to commit or push something.
If you want to work with [git worktrees](https://git-scm.com/docs/git-worktree)
in a streamlined, easy way, GRM provides you with an opinionated workflow. It's
especially helpful when the following describes you:
* You're juggling a lot of git branches, switching between them a lot.
* When switching branches, you'd like to just leave your work as-is, without
using the stash or temporary commits.

76
docs/src/repos.md Normal file
View File

@@ -0,0 +1,76 @@
# Managing tree of git repositories
When managing multiple git repositories with GRM, you'll generally have a
configuration file containing information about all the repos you have. GRM then
makes sure that you repositories match that config. If they don't exist yet, it
will clone them. It will also make sure that all remotes are configured properly.
Let's try it out:
## Get the example configuration
```bash
$ curl --proto '=https' --tlsv1.2 -sSfO https://raw.githubusercontent.com/hakoerber/git-repo-manager/master/example.config.toml
```
Then, you're ready to run the first sync. This will clone all configured repositories
and set up the remotes.
```bash
$ grm repo sync --config example.config.toml
[] Cloning into "/home/me/projects/git-repo-manager" from "https://code.hkoerber.de/hannes/git-repo-manager.git"
[] git-repo-manager: Repository successfully cloned
[] git-repo-manager: Setting up new remote "github" to "https://github.com/hakoerber/git-repo-manager.git"
[] git-repo-manager: OK
[] Cloning into "/home/me/projects/dotfiles" from "https://github.com/hakoerber/dotfiles.git"
[] dotfiles: Repository successfully cloned
[] dotfiles: OK
```
If you run it again, it will report no changes:
```
$ grm repo sync --config example.config.toml
[✔] git-repo-manager: OK
[✔] dotfiles: OK
```
### Generate your own configuration
Now, if you already have a few repositories, it would be quite laborious to write
a configuration from scratch. Luckily, GRM has a way to generate a configuration
from an existing file tree:
```bash
$ grm repo find ~/your/project/root > config.toml
```
This will detect all repositories and remotes and write them to `config.toml`.
### Show the state of your projects
```bash
$ grm repo status --config example.config.toml
╭──────────────────┬──────────┬────────┬───────────────────┬────────┬─────────╮
│ Repo ┆ Worktree ┆ Status ┆ Branches ┆ HEAD ┆ Remotes │
╞══════════════════╪══════════╪════════╪═══════════════════╪════════╪═════════╡
│ git-repo-manager ┆ ┆ ✔ ┆ branch: master ┆ master ┆ github │
│ ┆ ┆ ┆ <origin/master> ✔ ┆ ┆ origin │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤
│ dotfiles ┆ ┆ ✔ ┆ ┆ Empty ┆ origin │
╰──────────────────┴──────────┴────────┴───────────────────┴────────┴─────────╯
```
You can also use `status` without `--config` to check the repository you're currently
in:
```
$ cd ~/example-projects/dotfiles
$ grm repo status
╭──────────┬──────────┬────────┬──────────┬───────┬─────────╮
│ Repo ┆ Worktree ┆ Status ┆ Branches ┆ HEAD ┆ Remotes │
╞══════════╪══════════╪════════╪══════════╪═══════╪═════════╡
│ dotfiles ┆ ┆ ✔ ┆ ┆ Empty ┆ origin │
╰──────────┴──────────┴────────┴──────────┴───────┴─────────╯
```

207
docs/src/worktrees.md Normal file
View File

@@ -0,0 +1,207 @@
# Git Worktrees
## Why?
The default workflow when using git is having your repository in a single directory.
Then, you can check out a certain reference (usually a branch), which will update
the files in the directory to match the state of that reference. Most of the time,
this is exactly what you need and works perfectly. But especially when you're using
with branches a lot, you may notice that there is a lot of work required to make
everything run smootly.
Maybe you experienced the following: You're working on a feature branch. Then,
for some reason, you have to change branches (maybe to investigate some issue).
But you get the following:
```
error: Your local changes to the following files would be overwritten by checkout
```
Now you can create a temporary commit or stash your changes. In any case, you have
some mental overhead before you can work on something else. Especially with stashes,
you'll have to remember to do a `git stash pop` before resuming your work (I
cannot count the number of times where is "rediscovered" some code hidden in some
old stash I forgot about.
And even worse: If you're currently in the process of resolving merge conflicts or an
interactive rebase, there is just no way to "pause" this work to check out a
different branch.
Sometimes, it's crucial to have an unchanging state of your repository until some
long-running process finishes. I'm thinking of Ansible and Terraform runs. I'd
rather not change to a different branch while ansible or Terraform are running as
I have no idea how those tools would behave (and I'm not too eager to find out).
In any case, Git Worktrees are here for the rescue:
## What are git worktrees?
[Git Worktrees](https://git-scm.com/docs/git-worktree) allow you to have multiple
independent checkouts of your repository on different directories. You can have
multiple directories that correspond to different references in your repository.
Each worktree has it's independent working tree (duh) and index, so there is no
to run into conflicts. Changing to a different branch is just a `cd` away (if
the worktree is already set up).
## Worktrees in GRM
GRM exposes an opinionated way to use worktrees in your repositories. Opinionated,
because there is a single invariant that makes reasoning about your worktree
setup quite easy:
**The branch inside the worktree is always the same as the directory name of the worktree.**
In other words: If you're checking out branch `mybranch` into a new worktree, the
worktree directory will be named `mybranch`.
GRM can be used with both "normal" and worktree-enabled repositories. But note
that a single repository can be either the former or the latter. You'll have to
decide during the initial setup which way you want to go for that repository.
If you want to clone your repository in a worktree-enabled way, specify
`worktree_setup = true` for the repository in your `config.toml`:
```toml
[[trees.repos]]
name = "git-repo-manager"
worktree_setup = true
```
Now, when you run a `grm sync`, you'll notice that the directory of the repository
is empty! Well, not totally, there is a hidden directory called `.git-main-working-tree`.
This is where the repository actually "lives" (it's a bare checkout).
### Creating a new worktree
To actually work, you'll first have to create a new worktree checkout. All
worktree-related commands are available as subcommands of `grm worktree` (or
`grm wt` for short):
```
$ grm wt add mybranch
[✔] Worktree mybranch created
```
You'll see that there is now a directory called `mybranch` that contains a checkout
of your repository, using the branch `mybranch`
```bash
$ cd ./mybranch && git status
On branch mybranch
nothing to commit, working tree clean
```
You can work in this repository as usual. Make changes, commit them, revert them,
whatever you're up to :)
Just note that you *should* not change the branch inside the worktree
directory. There is nothing preventing you from doing so, but you will notice
that you'll run into problems when trying to remove a worktree (more on that
later). It may also lead to confusing behaviour, as there can be no two
worktrees that have the same branch checked out. So if you decide to use the
worktree setup, go all in, let `grm` manage your branches and bury `git branch`
(and `git checkout -b`).
You will notice that there is no tracking branch set up for the new branch. You
can of course set up one manually after creating the worktree, but there is an
easier way, using the `--track` flag during creation. Let's create another
worktree. Go back to the root of the repository, and run:
```bash
$ grm wt add mybranch2 --track origin/mybranch2
[] Worktree mybranch2 created
```
You'll see that this branch is now tracking `mybranch` on the `origin` remote:
```bash
$ cd ./mybranch2 && git status
On branch mybranch
Your branch is up to date with 'origin/mybranch2'.
nothing to commit, working tree clean
```
The behaviour of `--track` differs depending on the existence of the remote branch:
* If the remote branch already exists, `grm` uses it as the base of the new
local branch.
* If the remote branch does not exist (as in our example), `grm` will create a
new remote tracking branch, using the default branch (either `main` or `master`)
as the base
### Showing the status of your worktrees
There is a handy little command that will show your an overview over all worktrees
in a repository, including their status (i.e. changes files). Just run the following
in the root of your repository:
```
$ grm wt status
╭───────────┬────────┬──────────┬──────────────────╮
│ Worktree ┆ Status ┆ Branch ┆ Remote branch │
╞═══════════╪════════╪══════════╪══════════════════╡
│ mybranch ┆ ✔ ┆ mybranch ┆ │
│ mybranch2 ┆ ✔ ┆ mybranch ┆ origin/mybranch2 │
╰───────────┴────────┴──────────┴──────────────────╯
```
The "Status" column would show any uncommitted changes (new / modified / deleted
files) and the "Remote branch" would show differences to the remote branch (e.g.
if there are new pushes to the remote branch that are not yet incorporated into
your local branch).
### Deleting worktrees
If you're done with your worktrees, use `grm wt delete` to delete them. Let's
start with `mybranch2`:
```
$ grm wt delete mybranch2
[✔] Worktree mybranch2 deleted
```
Easy. On to `mybranch`:
```
$ grm wt delete mybranch
[!] Changes in worktree: No remote tracking branch for branch mybranch found. Refusing to delete
```
Hmmm. `grm` tells you:
"Hey, there is no remote branch that you could have pushed
your changes to. I'd rather not delete work that you cannot recover."
Note that `grm` is very cautious here. As your repository will not be deleted,
you could still recover the commits via [`git-reflog`](https://git-scm.com/docs/git-reflog).
But better safe then sorry! Note that you'd get a similar error message if your
worktree had any uncommitted files, for the same reason. Now you can either
commit & push your changes, or your tell `grm` that you know what you're doing:
```
$ grm wt delete mybranch --force
[✔] Worktree mybranch deleted
```
If you just want to delete all worktrees that do not contain any changes, you
can also use the following:
```
$ grm wt clean
```
### Manual access
GRM isn't doing any magic, it's just git under the hood. If you need to have access
to the underlying git repository, you can always do this:
```
$ git --git-dir ./.git-main-working-tree [...]
```
This should never be required (whenever you have to do this, you can consider
this a bug in GRM and open an [issue](https://github.com/hakoerber/git-repo-manager/issues/new),
but it may help in a pinch.