From 1cf5585e2c24ddd6f5e292d4ae6e63f252cc7f40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20K=C3=B6rber?= Date: Wed, 24 Nov 2021 17:22:49 +0100 Subject: [PATCH] Add documentation --- docs/.gitignore | 1 + docs/book.toml | 9 ++ docs/src/SUMMARY.md | 7 ++ docs/src/faq.md | 10 ++ docs/src/getting_started.md | 22 ++++ docs/src/overview.md | 33 ++++++ docs/src/repos.md | 76 +++++++++++++ docs/src/worktrees.md | 207 ++++++++++++++++++++++++++++++++++++ 8 files changed, 365 insertions(+) create mode 100644 docs/.gitignore create mode 100644 docs/book.toml create mode 100644 docs/src/SUMMARY.md create mode 100644 docs/src/faq.md create mode 100644 docs/src/getting_started.md create mode 100644 docs/src/overview.md create mode 100644 docs/src/repos.md create mode 100644 docs/src/worktrees.md diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..7585238 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1 @@ +book diff --git a/docs/book.toml b/docs/book.toml new file mode 100644 index 0000000..b77b6dd --- /dev/null +++ b/docs/book.toml @@ -0,0 +1,9 @@ +[book] +authors = ["Hannes Körber"] +language = "en" +multilingual = false +src = "src" +title = "Git Repo Manager" + +[output.html] +mathjax-support = true diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md new file mode 100644 index 0000000..5dc3e45 --- /dev/null +++ b/docs/src/SUMMARY.md @@ -0,0 +1,7 @@ +# Summary + +- [Overview](./overview.md) +- [Getting started](./getting_started.md) +- [Repository trees](./repos.md) +- [Git Worktrees](./worktrees.md) +- [FAQ](./faq.md) diff --git a/docs/src/faq.md b/docs/src/faq.md new file mode 100644 index 0000000..fde7266 --- /dev/null +++ b/docs/src/faq.md @@ -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! diff --git a/docs/src/getting_started.md b/docs/src/getting_started.md new file mode 100644 index 0000000..c630fbd --- /dev/null +++ b/docs/src/getting_started.md @@ -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 +``` diff --git a/docs/src/overview.md b/docs/src/overview.md new file mode 100644 index 0000000..7583d19 --- /dev/null +++ b/docs/src/overview.md @@ -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. diff --git a/docs/src/repos.md b/docs/src/repos.md new file mode 100644 index 0000000..840e448 --- /dev/null +++ b/docs/src/repos.md @@ -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 │ +├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤ +│ 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 │ +╰──────────┴──────────┴────────┴──────────┴───────┴─────────╯ +``` + diff --git a/docs/src/worktrees.md b/docs/src/worktrees.md new file mode 100644 index 0000000..a99b55a --- /dev/null +++ b/docs/src/worktrees.md @@ -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. +