{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "126cc10f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Xvc"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "068f1cff-2437-499d-9673-d88bc2a835c9",
   "metadata": {},
   "source": [
    "This is the Python port of [Xvc](https://github.com/iesahin/xvc). Xvc's goal is let users perform all ML operations regarding data, files, models and pipelines from the command line, and version all of these on top of Git. Xvc.py extends this goal by allowing all commands to be run from Python shells, code and Jupyter notebooks. "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1f8d3713-3641-4a8f-ab05-b0802a4323f9",
   "metadata": {},
   "source": [
    "!pip install xvc"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "e4efce5b",
   "metadata": {},
   "outputs": [
    {
     "ename": "ModuleNotFoundError",
     "evalue": "No module named 'xvc'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mModuleNotFoundError\u001b[0m                       Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[1], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mxvc\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m Xvc\n",
      "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'xvc'"
     ]
    }
   ],
   "source": [
    "from xvc import Xvc"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "512c3978-4fb3-472c-a5f9-183e8fc9c101",
   "metadata": {},
   "source": [
    "Let's create a directory tree have with the data files we want to track."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "75cfac9d",
   "metadata": {},
   "outputs": [],
   "source": [
    "!xvc-test-helper create-directory-tree --directories 3 --files 3 --root test-data --seed 20240101"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "f7f3449b-ff8f-49c7-9f2d-2647787d69fa",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[01;34m.\u001b[0m\n",
      "├── \u001b[00mReadme.ipynb\u001b[0m\n",
      "└── \u001b[01;34mtest-data\u001b[0m\n",
      "    ├── \u001b[01;34mdir-0001\u001b[0m\n",
      "    │   ├── \u001b[00mfile-0001.bin\u001b[0m\n",
      "    │   ├── \u001b[00mfile-0002.bin\u001b[0m\n",
      "    │   └── \u001b[00mfile-0003.bin\u001b[0m\n",
      "    ├── \u001b[01;34mdir-0002\u001b[0m\n",
      "    │   ├── \u001b[00mfile-0001.bin\u001b[0m\n",
      "    │   ├── \u001b[00mfile-0002.bin\u001b[0m\n",
      "    │   └── \u001b[00mfile-0003.bin\u001b[0m\n",
      "    └── \u001b[01;34mdir-0003\u001b[0m\n",
      "        ├── \u001b[00mfile-0001.bin\u001b[0m\n",
      "        ├── \u001b[00mfile-0002.bin\u001b[0m\n",
      "        └── \u001b[00mfile-0003.bin\u001b[0m\n",
      "\n",
      "5 directories, 10 files\n"
     ]
    }
   ],
   "source": [
    "!tree"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "546239e2-de27-4af1-859d-2bc5a4c56333",
   "metadata": {},
   "source": [
    "Now we can initialize Xvc repository. We first init Git in the directory. Xvc tracks binary files with their metadata (size, timestamp and digest) and this metadata is kept in text files. You can use Xvc without Git, but adding versioning to your data is usually a good choice.   Xvc manages Git operations on the text files it creates automatically by default, so it won't add any extra commands to the workflow."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8263b5a3-f602-4a55-a258-44cd3f99f33a",
   "metadata": {},
   "outputs": [],
   "source": [
    "!git init test-data/"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "415297f2-42cc-4189-bb4d-40e3f96503fe",
   "metadata": {},
   "source": [
    "Now we can init the Xvc repository"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4684fa48-6914-4594-b105-8a6d83a2fb30",
   "metadata": {},
   "source": [
    "First we create an Xvc instance. You can specify the workdir and this instance will work on that directory. You can use multiple such instances to manage multiple directories. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "0d844f5f-0026-4158-8ac0-1581434cbfcd",
   "metadata": {},
   "outputs": [],
   "source": [
    "xvc = Xvc(workdir=\"test-data\", debug=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eb430c65-7e92-49f3-8a0b-ea45ee4c50ef",
   "metadata": {},
   "source": [
    "Let's initialize Xvc in the directory. This will  create a `.xvc` directory, will put several initial files and also create a `.gitignore` entry for those elements that shouldn't be tracked by Git. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4895e57f-75c8-4dd3-ab5a-9a548fd83518",
   "metadata": {},
   "outputs": [],
   "source": [
    "xvc.init()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9b0f7115-4cbf-4b17-b799-520c9aba9428",
   "metadata": {},
   "outputs": [],
   "source": [
    "xvc.file().track(\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f9dd607d",
   "metadata": {},
   "source": [
    "## 🏃🏾 Quicktart\n",
    "\n",
    "Xvc seamlessly monitors your files and directories on top of Git. To commence, execute the following command within the repository:\n",
    "\n",
    "```console\n",
    "$ git init # if you're not already in a Git repository\n",
    "Initialized empty Git repository in [CWD]/.git/\n",
    "\n",
    "$ xvc init\n",
    "```\n",
    "\n",
    "This command initializes the `.xvc/` directory and adds a `.xvcignore` file for specifying paths you wish to conceal from Xvc.\n",
    "\n",
    "Include your data files and directories for tracking:\n",
    "\n",
    "```shell\n",
    "$ xvc file track my-data/ --as symlink\n",
    "```\n",
    "\n",
    "This command calculates content hashes for data (using BLAKE-3, by default) and logs them. The changes are committed to Git, and the files are copied to content-addressed directories within `.xvc/b3`. Additionally, read-only symbolic links to these directories are created. \n",
    "\n",
    "You can specify different [recheck (checkout) methods](https://docs.xvc.dev/ref/xvc-file-recheck/) for files and directories, depending on your use case.\n",
    "If you need to track model files that change frequently, you can set recheck method `--as copy` (the default).\n",
    "\n",
    "```shell\n",
    "$ xvc file track my-models/ --as copy\n",
    "```\n",
    "\n",
    "Configure a cloud storage to share the files you added.\n",
    "\n",
    "```shell\n",
    "$ xvc storage new s3 --name my-remote --region us-east-1 --bucket-name my-xvc-remote\n",
    "```\n",
    "\n",
    "You can send the files to this storage.\n",
    "\n",
    "```shell\n",
    "$ xvc file send --to my-remote\n",
    "```\n",
    "\n",
    "When you (or someone else) want to access these files later, you can clone the Git repository and get the files from the\n",
    "storage.\n",
    "\n",
    "```shell\n",
    "$ git clone https://example.com/my-machine-learning-project\n",
    "Cloning into 'my-machine-learning-project'...\n",
    "\n",
    "$ cd my-machine-learning-project\n",
    "$ xvc file bring my-data/ --from my-remote\n",
    "\n",
    "```\n",
    "\n",
    "This approach ensures convenient access to files from the shared storage when needed.\n",
    "\n",
    "You don't have to reconfigure the storage after cloning, but you need to have valid credentials as environment variables\n",
    "to access the storage.\n",
    "Xvc never stores any credentials.\n",
    "\n",
    "If you have commands that depend on data or code elements, you can configure a pipeline.\n",
    "\n",
    "For this example, we'll use [a Python script](https://github.com/iesahin/xvc/blob/main/workflow_tests/templates/README.in/generate_data.py) to generate a data set with random names with random IQ scores.\n",
    "\n",
    "The script uses the Faker library and this library must be available where you run the pipeline. To make it repeatable, we start the pipeline by adding a step that installs dependencies.\n",
    "\n",
    "```console\n",
    "$ xvc pipeline step new --step-name install-deps --command 'python3 -m pip install --quiet --user -r requirements.txt'\n",
    "```\n",
    "\n",
    "We'll make this this step to depend on `requirements.txt` file, so when the file changes it will make the step run. \n",
    "\n",
    "```console\n",
    "$ xvc pipeline step dependency --step-name install-deps --file requirements.txt\n",
    "```\n",
    "\n",
    "Xvc allows to create dependencies between pipeline steps. Dependent steps wait for dependencies to finish successfully. \n",
    "\n",
    "Now we create a step to run the script and make `install-deps` step a dependency of it. \n",
    "\n",
    "```console\n",
    "$ xvc pipeline step new --step-name generate-data --command 'python3 generate_data.py'\n",
    "$ xvc pipeline step dependency --step-name generate-data --step install-deps\n",
    "```\n",
    "\n",
    "After you define the pipeline, you can run it by:\n",
    "\n",
    "```console\n",
    "$ xvc pipeline run\n",
    "[DONE] install-deps (python3 -m pip install --quiet --user -r requirements.txt)\n",
    "[OUT] [generate-data] CSV file generated successfully.\n",
    " \n",
    "[DONE] generate-data (python3 generate_data.py)\n",
    "\n",
    "```\n",
    "\n",
    "Xvc allows many kinds of dependnecies, like [files](https://docs.xvc.dev/ref/xvc-pipeline-step-dependency#file-dependencies), \n",
    "[groups of files and directories defined by globs](https://docs.xvc.dev/ref/xvc-pipeline-step-dependency#glob-dependencies), \n",
    "[regular expression searches in files](https://docs.xvc.dev/ref/xvc-pipeline-step-dependency#regex-dependencies), \n",
    "[line ranges in files](https://docs.xvc.dev/ref/xvc-pipeline-step-dependency#line-dependencies), \n",
    "[hyper-parameters defined in YAML, JSON or TOML files](https://docs.xvc.dev/ref/xvc-pipeline-step-dependency#hyper-parameter-dependencies)\n",
    "[HTTP URLs](https://docs.xvc.dev/ref/xvc-pipeline-step-dependency#url-dependencies),\n",
    "[shell command outputs](https://docs.xvc.dev/ref/xvc-pipeline-step-dependency#generic-command-dependencies), \n",
    "and [other steps](https://docs.xvc.dev/ref/xvc-pipeline-step-dependency#step-dependencies). \n",
    "\n",
    "Suppose you're only interested in the IQ scores of those with _Dr._ in front of their names and how they differ from the rest in the dataset we created. Let's create a regex search dependency to the data file that will show all _doctors_ IQ scores.\n",
    "\n",
    "```console\n",
    "$ xvc pipeline step new --step-name dr-iq --command 'echo \"${XVC_REGEX_ADDED_ITEMS}\" >> dr-iq-scores.csv '\n",
    "$ xvc pipeline step dependency --step-name dr-iq --regex-items 'random_names_iq_scores.csv:/^Dr\\..*'\n",
    "```\n",
    "\n",
    "The first line specifies a command, when run writes `${XVC_REGEX_ADDED_ITEMS}` environment variable to `dr-iq-scores.csv` file. \n",
    "The second line specifies the dependency which will also populate the `$[XVC_REGEX_ADDED_ITEMS]` environment variable in the command. \n",
    "\n",
    "Some dependency types like [regex items], \n",
    "[line items] and [glob items] inject environment variables in the commands they are a dependency.\n",
    "For example, if you have two million files specified with a glob, but want to run a script only on the added files after the last run, you can use these environment variables. \n",
    "\n",
    "\n",
    "When you run the pipeline again, a file named `dr-iq-scores.csv` will be created. Note that, as `requirements.txt` didn't change `install-deps` step and its dependent `generate-data` steps didn't run.\n",
    "\n",
    "```console\n",
    "$ xvc pipeline run\n",
    "[DONE] dr-iq (echo \"${XVC_REGEX_ADDED_ITEMS}\" >> dr-iq-scores.csv )\n",
    "\n",
    "$ cat dr-iq-scores.csv\n",
    "Dr. Brian Shaffer,122\n",
    "Dr. Brittany Chang,82\n",
    "Dr. Mallory Payne MD,70\n",
    "Dr. Sherry Leonard,93\n",
    "Dr. Susan Swanson,81\n",
    "\n",
    "````\n",
    "\n",
    "We are using this feature to get lines starting with `Dr.` from the file and write them to another file. When the file changes, e.g. another record matching the dependency regex added to the `random_names_iq_scores.csv` file, it will also be added to `dr-iq-scores.csv` file.\n",
    "\n",
    "```console\n",
    "$ zsh -cl 'echo \"Dr. Albert Einstein,144\" >> random_names_iq_scores.csv'\n",
    "\n",
    "$ xvc pipeline run\n",
    "[DONE] dr-iq (echo \"${XVC_REGEX_ADDED_ITEMS}\" >> dr-iq-scores.csv )\n",
    "\n",
    "$ cat dr-iq-scores.csv\n",
    "Dr. Brian Shaffer,122\n",
    "Dr. Brittany Chang,82\n",
    "Dr. Mallory Payne MD,70\n",
    "Dr. Sherry Leonard,93\n",
    "Dr. Susan Swanson,81\n",
    "Dr. Albert Einstein,144\n",
    "\n",
    "```\n",
    "\n",
    "Now we want to add a another command that draws a fancy histogram from `dr-iq-scores.csv`. As this new step must wait `dr-iq-scores.csv` file to be ready, we'll define `dr-iq-scores.csv` as an _output_ of `dr-iq` step and set the file as a dependency to this new `visualize` step.\n",
    "\n",
    "```console\n",
    "$ xvc pipeline step output --step-name dr-iq --output-file dr-iq-scores.csv\n",
    "$ xvc pipeline step new --step-name visualize --command 'python3 visualize.py'\n",
    "$ xvc pipeline step dependency --step-name visualize --file dr-iq-scores.csv\n",
    "$ xvc pipeline run\n",
    "[ERROR] Step visualize finished UNSUCCESSFULLY with command python3 visualize.py\n",
    "\n",
    "```\n",
    "\n",
    "You can get the pipeline in Graphviz DOT format to convert to an image.\n",
    "\n",
    "```console\n",
    "$ zsh -cl 'xvc pipeline dag | dot -opipeline.png'\n",
    "\n",
    "```\n",
    "\n",
    "You can also export and import the pipeline to JSON to edit in your editor.\n",
    "\n",
    "```console\n",
    "$ xvc pipeline export --file my-pipeline.json\n",
    "\n",
    "$ cat my-pipeline.json\n",
    "{\n",
    "  \"name\": \"default\",\n",
    "  \"steps\": [\n",
    "    {\n",
    "      \"command\": \"python3 -m pip install --quiet --user -r requirements.txt\",\n",
    "      \"dependencies\": [\n",
    "        {\n",
    "          \"File\": {\n",
    "            \"content_digest\": {\n",
    "              \"algorithm\": \"Blake3\",\n",
    "              \"digest\": [\n",
    "                43,\n",
    "                86,\n",
    "                244,\n",
    "                111,\n",
    "                13,\n",
    "                243,\n",
    "                28,\n",
    "                110,\n",
    "                140,\n",
    "                213,\n",
    "                105,\n",
    "                20,\n",
    "                239,\n",
    "                62,\n",
    "                73,\n",
    "                75,\n",
    "                13,\n",
    "                146,\n",
    "                82,\n",
    "                17,\n",
    "                148,\n",
    "                152,\n",
    "                66,\n",
    "                86,\n",
    "                154,\n",
    "                230,\n",
    "                154,\n",
    "                246,\n",
    "                213,\n",
    "                214,\n",
    "                40,\n",
    "                119\n",
    "              ]\n",
    "            },\n",
    "            \"path\": \"requirements.txt\",\n",
    "            \"xvc_metadata\": {\n",
    "              \"file_type\": \"File\",\n",
    "              \"modified\": {\n",
    "                \"nanos_since_epoch\": [..],\n",
    "                \"secs_since_epoch\": [..]\n",
    "              },\n",
    "              \"size\": 14\n",
    "            }\n",
    "          }\n",
    "        }\n",
    "      ],\n",
    "      \"invalidate\": \"ByDependencies\",\n",
    "      \"name\": \"install-deps\",\n",
    "      \"outputs\": []\n",
    "    },\n",
    "    {\n",
    "      \"command\": \"python3 generate_data.py\",\n",
    "      \"dependencies\": [\n",
    "        {\n",
    "          \"Step\": {\n",
    "            \"name\": \"install-deps\"\n",
    "          }\n",
    "        }\n",
    "      ],\n",
    "      \"invalidate\": \"ByDependencies\",\n",
    "      \"name\": \"generate-data\",\n",
    "      \"outputs\": []\n",
    "    },\n",
    "    {\n",
    "      \"command\": \"echo /\"${XVC_REGEX_ADDED_ITEMS}/\" >> dr-iq-scores.csv \",\n",
    "      \"dependencies\": [\n",
    "        {\n",
    "          \"RegexItems\": {\n",
    "            \"lines\": [\n",
    "              \"Dr. Brian Shaffer,122\",\n",
    "              \"Dr. Susan Swanson,81\",\n",
    "              \"Dr. Brittany Chang,82\",\n",
    "              \"Dr. Mallory Payne MD,70\",\n",
    "              \"Dr. Sherry Leonard,93\",\n",
    "              \"Dr. Albert Einstein,144\"\n",
    "            ],\n",
    "            \"path\": \"random_names_iq_scores.csv\",\n",
    "            \"regex\": \"^Dr//..*\",\n",
    "            \"xvc_metadata\": {\n",
    "              \"file_type\": \"File\",\n",
    "              \"modified\": {\n",
    "                \"nanos_since_epoch\": [..],\n",
    "                \"secs_since_epoch\": [..]\n",
    "              },\n",
    "              \"size\": 19021\n",
    "            }\n",
    "          }\n",
    "        }\n",
    "      ],\n",
    "      \"invalidate\": \"ByDependencies\",\n",
    "      \"name\": \"dr-iq\",\n",
    "      \"outputs\": [\n",
    "        {\n",
    "          \"File\": {\n",
    "            \"path\": \"dr-iq-scores.csv\"\n",
    "          }\n",
    "        }\n",
    "      ]\n",
    "    },\n",
    "    {\n",
    "      \"command\": \"python3 visualize.py\",\n",
    "      \"dependencies\": [\n",
    "        {\n",
    "          \"File\": {\n",
    "            \"content_digest\": null,\n",
    "            \"path\": \"dr-iq-scores.csv\",\n",
    "            \"xvc_metadata\": null\n",
    "          }\n",
    "        }\n",
    "      ],\n",
    "      \"invalidate\": \"ByDependencies\",\n",
    "      \"name\": \"visualize\",\n",
    "      \"outputs\": []\n",
    "    }\n",
    "  ],\n",
    "  \"version\": 1,\n",
    "  \"workdir\": \"\"\n",
    "}\n",
    "```\n",
    "\n",
    "You can edit the file to change commands, add new dependencies, etc. and import it back to Xvc.\n",
    "\n",
    "```console\n",
    "$ xvc pipeline import --file my-pipeline.json --overwrite\n",
    "```\n",
    "\n",
    "Lastly, if you noticed that the commands are long to type, there is an `xvc aliases` command that prints a set of aliases for commands. You can source the output in your `.zshrc` or `.bashrc`, and use the following commands instead, e.g., `xvc pipelines run` becomes `pvc run`. \n",
    "\n",
    "\n",
    "```console\n",
    "$ xvc aliases\n",
    "\n",
    "alias xls='xvc file list'\n",
    "alias pvc='xvc pipeline'\n",
    "alias fvc='xvc file'\n",
    "alias xvcf='xvc file'\n",
    "alias xvcft='xvc file track'\n",
    "alias xvcfl='xvc file list'\n",
    "alias xvcfs='xvc file send'\n",
    "alias xvcfb='xvc file bring'\n",
    "alias xvcfh='xvc file hash'\n",
    "alias xvcfco='xvc file checkout'\n",
    "alias xvcfr='xvc file recheck'\n",
    "alias xvcp='xvc pipeline'\n",
    "alias xvcpr='xvc pipeline run'\n",
    "alias xvcps='xvc pipeline step'\n",
    "alias xvcpsn='xvc pipeline step new'\n",
    "alias xvcpsd='xvc pipeline step dependency'\n",
    "alias xvcpso='xvc pipeline step output'\n",
    "alias xvcpi='xvc pipeline import'\n",
    "alias xvcpe='xvc pipeline export'\n",
    "alias xvcpl='xvc pipeline list'\n",
    "alias xvcpn='xvc pipeline new'\n",
    "alias xvcpu='xvc pipeline update'\n",
    "alias xvcpd='xvc pipeline dag'\n",
    "alias xvcs='xvc storage'\n",
    "alias xvcsn='xvc storage new'\n",
    "alias xvcsl='xvc storage list'\n",
    "alias xvcsr='xvc storage remove'\n",
    "\n",
    "```\n",
    "\n",
    "Please create an issue or discussion for any other kinds of dependencies that you'd like to be included.\n",
    "\n",
    "I'm planning to add [data label and annotations tracking](https://github.com/iesahin/xvc/discussions/208)), [experiments tracking](https://github.com/iesahin/xvc/discussions/207)), [model tracking](https://github.com/iesahin/xvc/discussions/211)), encrypted cache, server to control all commands from a web interface, and more as my time permits.\n",
    "\n",
    "Please check [`docs.xvc.dev`](https://docs.xvc.dev) for documentation.\n",
    "\n",
    "## 🤟 Big Thanks\n",
    "\n",
    "xvc stands on the following (giant) crates:\n",
    "\n",
    "- [trycmd] is used to run all example commands in this file, [reference, and how-to documentation](https://docs.xvc.dev) at\n",
    "  every PR. It makes sure that the documentation is always up-to-date and shown commands work as described. We start\n",
    "  development by writing documentation and implementing them thanks to [trycmd].\n",
    "\n",
    "- [serde] allows all data structures to be stored in text files. Special thanks from [`xvc-ecs`] for serializing components in an ECS with a single line of code.\n",
    "\n",
    "- Xvc processes files in parallel with pipelines and parallel iterators thanks to [crossbeam] and [rayon].\n",
    "\n",
    "- Thanks to [strum], Xvc uses enums extensively and converts almost everything to typed values from strings.\n",
    "\n",
    "- Xvc has a deep CLI that has subcommands of subcommands (e.g. `xvc storage new s3`), and all these work with minimum bugs thanks to [clap].\n",
    "\n",
    "- Xvc uses [rust-s3] to connect to S3 and compatible storage services. It employs excellent [tokio] for fast async Rust. These cloud storage features can be turned off thanks to Rust conditional compilation.\n",
    "\n",
    "- Without implementations of [BLAKE3], BLAKE2, SHA-2 and SHA-3 from Rust [crypto] crate, Xvc couldn't detect file changes so fast.\n",
    "\n",
    "- Many thanks to small and well built crates, [reflink], [relative-path], [path-absolutize], [glob] for file system and glob handling.\n",
    "\n",
    "- Thanks to [sad_machine] for providing a State Machine implementation that I used in `xvc pipeline run`. A DAG composed of State Machines made running pipeline steps in parallel with a clean separation of process states.\n",
    "\n",
    "- Thanks to [thiserror] and [anyhow] for making error handling a breeze. These two crates make me feel I'm doing something good for the humanity when handling errors.\n",
    "\n",
    "- Xvc is split into many crates and owes this organization to [cargo workspaces].\n",
    "\n",
    "[crossbeam]: https://docs.rs/crossbeam/\n",
    "[cargo workspaces]: https://crates.io/crates/cargo-workspaces\n",
    "[rayon]: https://docs.rs/rayon/\n",
    "[strum]: https://docs.rs/strum/\n",
    "[clap]: https://docs.rs/clap/\n",
    "[serde]: https://serde.rs\n",
    "[blake3]: https://docs.rs/blake3/\n",
    "[crypto]: https://docs.rs/rust-crypto/\n",
    "[reflink]: https://docs.rs/reflink/\n",
    "[relative-path]: https://docs.rs/relative-path/\n",
    "[path-absolutize]: https://docs.rs/path-absolutize/\n",
    "[glob]: https://docs.rs/glob/\n",
    "[wax]: https://docs.rs/wax/\n",
    "[trycmd]: https://docs.rs/trycmd/\n",
    "[sad_machine]: https://docs.rs/sad_machine/\n",
    "[thiserror]: https://docs.rs/thiserror/\n",
    "[anyhow]: https://docs.rs/anyhow/\n",
    "[rust-s3]: https://docs.rs/rust-s3/\n",
    "[`xvc-ecs`]: https://docs.rs/xvc-ecs/\n",
    "[tokio]: https://tokio.rs\n",
    "\n",
    "And, biggest thanks to Rust designers, developers and contributors. Although I can't see myself expert to appreciate it all, it's a fabulous language and environment to work with.\n",
    "\n",
    "## 🚁 Support\n",
    "\n",
    "- You can use [Discussions](https://github.com/iesahin/xvc/discussions) to ask questions. I'll answer as much as possible. Thank you.\n",
    "- I don't follow any other sites regularly. You can also reach me at [emre@xvc.dev](mailto:emre@xvc.dev)\n",
    "\n",
    "## 👐 Contributing\n",
    "\n",
    "- Star this repo. I feel very happy for every star and send my best wishes to you. That's a certain win to spend your two seconds for me. Thanks.\n",
    "- Use xvc. Tell me how it works for you, read the [documentation](https://docs.xvc.dev), [report bugs](https://github.com/iesahin/xvc/issues), [discuss features](https://github.com/iesahin/xvc/discussions).\n",
    "- Please note that, I don't accept large code PRs. Please open an issue to discuss your idea and write/modify a\n",
    "  reference page before sending a PR. I'm happy to discuss and help you to implement your idea. Also, it may require a copyright transfer to me, as there may be cases which I provide the code in other licenses. \n",
    "\n",
    "## 📜 License\n",
    "\n",
    "Xvc is licensed under the [GNU GPL 3.0 License](https://github.com/iesahin/xvc/blob/main/LICENSE). If you want to use the code in your project with other licenses, please contact me.\n",
    "\n",
    "## 🌦️ Future and Maintenance\n",
    "\n",
    "I'm using Xvc daily and I'm happy with it. Tracking all my files with Git via arbitrary servers and cloud providers is\n",
    "something I always need. I'm happy to improve and maintain it as long as I use it.\n",
    "\n",
    "Given that I'm working on this for the last two years for pure technical bliss, you can expect me to work on it more. \n",
    "\n",
    "## ⚠️ Disclaimer\n",
    "\n",
    "This software is fresh and ambitious. Although I use it and test it close to real-world conditions, it didn't go under\n",
    "the test of time. **Xvc can eat your files and spit them into the eternal void!** Please take backups.\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
