CLI tool
yadm-dev/yadm avatar
yadm-dev/yadm

Two pinned versions, four status badges, and a quick tour that leaves the details to yadm.io

Yet Another Dotfiles Manager

6,480 stars203 forksPythonGPL-3.0

At a glance

What is it?
yadm-dev/yadm manages dotfiles with Git underneath, and its README is a set of pointers rather than a manual: five feature links, a six-command tour, and a badge row that mixes live version lookups with a hardcoded one. The Makefile and pyproject.toml are where the real constraints sit.
Who is it for?
yadm suits people who already want Git as the substrate for their dotfiles, and the README is honest about that division of labour: the tour shows six commands, everything else lives on yadm.io. Before you rely on it, check the version question yourself, because the badge row pins OBS at v3.5.0 while the tag badge reads live and the repository publishes no GitHub releases, and the license link points at master although the default branch is develop.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Activity is slowing. The repository last received commits 7 months ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • yadm-dev/yadm6,480 ★ · +51
Star history
Repository2026-09-232026-11-14
yadm-dev/yadm6.4k6.5k

51 Stars in the last 52 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

The quick tour is six commands, and each feature bullet links away

The whole working example fits in one indented block: `yadm init` initializes a new repository, `yadm clone <url>` takes an existing one, then `yadm add <important file>` and `yadm commit` record a change. Nothing in that sequence uses a Git flag directly, and the feature list explains why: yadm is based on Git with the full range of Git features. What the block does not do is pick a file to manage, choose an encryption backend, or say where anything is stored. Every feature bullet is a link instead, five of them, pointing at separate pages for alternate files, templates, encryption, bootstrap and hooks, with the sentence that complete features, usage, examples and installation instructions are on yadm.io. So the README is an index: the tour proves the tool works, the bullets tell you which five subjects to read about.

The OBS badge pins v3.5.0 while the release badge asks GitHub for a tag

Read the badge row left to right and two different mechanisms sit side by side. The homebrew, Arch and latest-version badges all fetch their number at render time: `img.shields.io/homebrew/v/yadm.svg` and the Arch one both name a package path, and the release badge queries the repository tag. The OBS badge is a static image with the version typed into the URL itself, `img.shields.io/badge/OBS-v3.5.0-blue`, so it renders the same string until somebody edits the README. Its link goes to an openSUSE download page for a user project, and that project name is the third naming scheme in the row alongside Homebrew and Arch. Meanwhile the release badge points at the GitHub releases page, which is worth knowing when reading version claims elsewhere: this repository has no GitHub releases, so whatever that badge resolves to is coming from tags.

The default branch is develop, and the license badge still points at master

The default branch is develop, and the four status badge images for master, develop, gh-pages and dev-pages all query the same `test.yml` workflow with the branch as a parameter. The master and develop badges link to the Tests workflow, while the gh-pages and dev-pages badges link to a workflow named test site. The date badges follow the same split, with last-commit images for master, develop and gh-pages. Into that arrangement the license badge links to a file on master, `blob/master/LICENSE`, and the project's development and website work happens elsewhere. Anyone scripting against raw file URLs should notice that the branch in the README's own links and the branch the repository develops on are not the same one.

make with no target prints usage through a pager

The default target is not the test suite. `all` depends on nothing and its recipe re-invokes make on the usage target, piping the output through less:

make
.PHONY: all
all:
	@$(MAKE) usage | less

So a bare `make` in a fresh checkout shows help text and nothing else, and the help text is a long run of echo lines assembled by hand rather than parsed from anything. The same recipe declares `usage` phony alongside it, which keeps the pager output from being mistaken for an up-to-date file named usage. Read together with the target list that follows, this is a project whose contributor entry point is documentation first: `make test` for the suite, `make testhost` for a container to poke at, `make scripthost` for a reproducible bug report, `make testenv` for a linting environment. The variable at the top of the file, `PYTESTS = $(wildcard test/test_*.py)`, suggests the suite is collected by filename pattern from the test directory.

The container image is a dated tag, and version= wires any ref in as a volume

Two variables decide what the container targets run against: `OCI = docker` names the tool, and `IMAGE = docker.io/yadm/testbed:2024-11-11` pins the image to a tag carrying a date rather than a floating latest. The usage text explains what the two host targets do with it. `make testhost` creates an ephemeral container for ad hoc testing and uses the working copy unless a version is given; a version can be any commit, branch or tag, and whichever ref you name is linked into the container as a local volume. `make scripthost` works the same way but for demonstrating a bug, and after you exit its shell it writes the commands you used into a file named script.txt, intended as the starting point for a repeatable test. Both targets are therefore reproducible by ref, at the cost of an image tag that has to be bumped deliberately when the testbed changes.

The usage text for the linting environment stops after three words

Every target's help text is a sequence of echo lines inside the Makefile. The entry for `make testenv` says it creates a python virtual environment with the same dependencies used by yadm's testbed environment, and that creating and activating it might be useful if your editor does real time linting of python files.

Lint limits sit high, one message class is off, and pytest caches in /tmp

pyproject.toml holds the whole quality configuration, and the numbers are set well above the defaults a Python project inherits. Pylint's design limits allow 14 arguments, 10 positional arguments, 28 locals, 8 attributes and 65 statements in a function; the format limit is 120 characters per line. The only message class disabled is redefined-outer-name, which is the standard concession for test modules, and the similarity checker is told to ignore imports and to report at 8 similar lines. Black and isort both use a line length of 120, with isort on the black profile, so the three tools agree on width. The pytest section sets its cache directory to /tmp, adds `-ra` to the default arguments, and registers a marker named deprecated, annotated with a comment explaining that it marks tests for deprecated features and can be deselected with `-m "not deprecated"`. A deprecated-feature marker implies there is a deprecation path being tracked in the test suite, though the config does not say what is deprecated.

One executable ships beside a man page, an RPM spec and a completion directory

The top level is mostly packaging rather than code: a single executable named yadm, its documentation as yadm.1 and yadm.md, an RPM spec file in yadm.spec, directories for completion, contrib and test, and a top-level file named bootstrap. The man page exists in two forms, a compiled-looking page and a markdown original, which fits a project that also maintains a site, since the site content lives on its own gh-pages branch and the badge row tracks that branch separately. yadm.spec covers the RPM side of distribution while Homebrew, Arch and OBS have their own package pages in the badge row, so the repository itself only carries one packaging recipe. Alongside those sit CHANGES and CONTRIBUTORS at the top level, and lint configuration split between pyproject.toml and a .flake8 file, with the pytest, pylint, black and isort settings all living in the former.

Editorial conclusion

yadm suits people who already want Git as the substrate for their dotfiles, and the README is honest about that division of labour: the tour shows six commands, everything else lives on yadm.io. Before you rely on it, check the version question yourself, because the badge row pins OBS at v3.5.0 while the tag badge reads live and the repository publishes no GitHub releases, and the license link points at master although the default branch is develop. For contributors, the practical notes are the dated container image, the four status badges spread over two workflows, and the lint ceiling in pyproject.toml, which is high enough that most style problems will not be caught for you. Anyone planning to add encrypted files should read the encryption page first, since the tour shows the encrypt file but not which backend fills it.

Frequently asked questions

What does yadm init create, and what comes after it?

It initializes a new repository, which is the Git store yadm manages dotfiles in, and the tour follows it with yadm add for a file and yadm commit. Adding files is a separate step: nothing is tracked until you name it.

How does yadm keep Linux and macOS versions of a file apart?

With alternate files whose names carry a suffix, as in `yadm add path/file.cfg##os.Linux` and `yadm add path/file.cfg##os.Darwin` in the tour. The feature list also names templated files as the other mechanism, and both subjects have their own pages on yadm.io.

Which encryption backends can yadm use for private data?

Four are named in the feature list: GnuPG, OpenSSL, transcrypt and git-crypt. The tour shows only `echo '.ssh/id_rsa' > ~/.config/yadm/encrypt` followed by yadm encrypt, and does not say which of them fills that file.

Which version of yadm do the README badges report?

Homebrew, Arch and the latest-version badge read their numbers live, while the OBS badge hardcodes v3.5.0 in the image URL, and the repository publishes no GitHub releases for the release badge to resolve against.

Do yadm's contributor targets need Docker, and what do they do?

The Makefile sets OCI to docker and pins the image to a dated testbed tag. make testhost creates an ephemeral container using the working copy or any commit, branch or tag you name as a local volume, and make scripthost writes the commands you typed into script.txt after you exit its shell.

Where are the lint settings for yadm's Python code configured?

In pyproject.toml: pylint with a 120 character line limit and raised design ceilings, black at the same 120 characters, isort on the black profile, and pytest with its cache in /tmp plus a marker named deprecated for tests of deprecated features.

Official sources

  1. Issues
  2. License: GPL-3.0
  3. Project website
  4. README
  5. yadm-dev/yadm on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/yadm-dev-yadm.svg)](https://hysenlabs.com/projects/yadm-dev-yadm)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/yadm-dev-yadm.svg)](https://hysenlabs.com/projects/yadm-dev-yadm)

Was this useful?