{"id":20401361,"url":"https://github.com/nieomylnieja/gitsync","last_synced_at":"2025-04-12T14:09:11.506Z","repository":{"id":250885399,"uuid":"826708937","full_name":"nieomylnieja/gitsync","owner":"nieomylnieja","description":"Git manager which keeps specified files in sync across multiple repositories","archived":false,"fork":false,"pushed_at":"2025-03-14T14:25:02.000Z","size":64,"stargazers_count":3,"open_issues_count":6,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-26T08:47:35.135Z","etag":null,"topics":["cli","git","github","go","repository-management","synchronization"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nieomylnieja.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-07-10T08:18:52.000Z","updated_at":"2025-01-27T08:46:17.000Z","dependencies_parsed_at":"2024-07-30T16:46:32.614Z","dependency_job_id":"c04038e6-7589-4e29-b124-3cc10dafec92","html_url":"https://github.com/nieomylnieja/gitsync","commit_stats":null,"previous_names":["nieomylnieja/gitsync"],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nieomylnieja%2Fgitsync","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nieomylnieja%2Fgitsync/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nieomylnieja%2Fgitsync/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nieomylnieja%2Fgitsync/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nieomylnieja","download_url":"https://codeload.github.com/nieomylnieja/gitsync/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248578870,"owners_count":21127713,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","git","github","go","repository-management","synchronization"],"created_at":"2024-11-15T04:49:08.429Z","updated_at":"2025-04-12T14:09:11.464Z","avatar_url":"https://github.com/nieomylnieja.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# gitsync\n\n`gitsync` helps manage multiple git repositories which share the same files\n(or parts of them) and helps keep them up-to-date and in sync.\n\nThis project was born out of the need to manage multiple repositories which\nshared the same development files, like linter configs, CI/CD scripts, etc.\n\nWhy not use submodules?\n\n- They won't be sufficient if there are slight differences between root and\n  synchronized files and If the tool does not support merging multiple\n  configs.\n- Some tools depend on their config files being in specific places,\n  like in the root of the repository.\n  While this can be solved by linking, the solution is not platform-agnostic.\n\nWhat I ended up doing was:\n\n- Define a _root_ repository, which would serve as a staple for other\n  repositories to follow.\n- Update linter configs, CI/CD scripts, GH workflows, etc. at the _root_\n  first and only then propagate these changes manually to all the other\n  _synchronized_ repositories.\n\nHowever, with a growing number of repositories I had to govern, it started\nto become more and more painful and time consuming to do that by hand.\n\nEnter `gitsync` 😉\n\n## Install\n\nUse pre-built binaries from\nthe [latest release](https://github.com/nieomylnieja/gitsync/releases/latest)\nor install\nwith Go:\n\n```shell\ngo install github.com/nieomylnieja/gitsync/cmd/gitsync@latest\n```\n\nIt can also be built directly from this repository:\n\n```shell\ngit clone https://github.com/nieomylnieja/gitsync.git\ncd gitsync\nmake build\n./bin/gitsync ./go.mod\n```\n\n### Requirements\n\nThe following programs must be installed and available in the `$PATH`:\n\n- `git`\n- `diff` (GNU version)\n- `gh` (GitHub CLI)\n\n## Usage\n\n`gitsync` ships with two commands:\n\n1. `sync` - interactively creates a patch and applies it to the synchronized\n   repositories' files.\n2. `diff` - shows the differences between the root and synchronized files in\n   unified format.\n\n```shell\ngitsync -c config.json [diff|sync]\n```\n\nIf the `-c` (config file path) flag is not provided,\n`gitsync` will look for a `gitsync.json` file in either\n`$XDG_CONFIG_HOME/gitsync/config.json` or\n`$HOME/.config/gitsync/config.json`.\n\n### Sync\n\n`sync` performs the following steps:\n\n1. Clones the repository (if not yet cloned) into `storePath`\n   (see [config](#config-file)).\n2. Fetches the latest changes from the root repository.\n3. Interactively creates a patch between the root and synchronized files.\n    - The user can choose to skip hunks.\n    - The user can choose to permanently ignore hunks, by:\n        - Adding `regex` rules to the `ignore` field in the config file.\n        - Choosing `i` option in the prompt, which will add `hunk` rules to the\n          `ignore` field in the config file.\n        - Manually adding `hunk` rules to the `ignore` field in the config file.\n4. Applies the patch to the synchronized repository.\n5. Commits the changes to the index.\n6. Pushes the changes to the remote repository.\n7. Creates a pull request (currently only GitHub is supported).\n\n### Diff\n\n`diff` runs the same as `sync`, but instead of applying the patch it simply\nprints it.\n\n### Config file\n\nThe config file is a JSON file which describes the synchronization process.\n\n```json5\n{\n  // Optional. Default: $HOME/.local/share/gitsync or $XDG_DATA_HOME/gitsync.\n  // Path to the directory where the repositories will be cloned and stored.\n  \"storePath\": \"~/.config/gitsync\",\n  // Required. Configuration of the root repository.\n  // Follows the same format as syncRepositories[].\n  \"root\": {\n    \"name\": \"template\",\n    \"url\": \"https://github.com/nieomylnieja/go-repo-template.git\"\n  },\n  // Optional.\n  \"ignore\": [\n    // If neither 'repositoryName' nor 'fileName' is provided,\n    // the rule will apply globally.\n    // If both are provided, the rule will apply only to the specific repository file.\n    {\n      // Optional. Name of the repository to which the ignore rule applies.\n      \"repositoryName\": \"go-libyear\",\n      // Optional. Name of the file to which the ignore rule applies.\n      \"fileName\": \"golangci linter config\",\n      // Optional. List of regular expressions used to ignore matching hunks.\n      // Note: This regular expression is passed to 'diff -I \u003cregex\u003e' and thus follows\n      // BRE (basic regular expression) rules, you may need to escape some characters, like '+'.\n      // Ref: https://www.gnu.org/software/grep/manual/html_node/Basic-vs-Extended.html.\n      \"regex\": [\"^\\\\s\\\\+local-prefixes:\"]\n    },\n    {\n      // Optional. Hunks to be ignored are represented with lines header and changes list.\n      // Either enter it manually or use the 'i' option in the sync command prompt.\n      //\n      // Ref: https://www.gnu.org/software/diffutils/manual/html_node/Detailed-Unified.html.\n      \"hunks\": [{\n        // Optional. If lines are not provided the changes will be matched anywhere within the file.\n        \"lines\": \"@@ -3,0 +4,2 @@\",\n        // Required.\n        \"changes\": [\n          \"+  skip-dirs:\",\n          \"+    - scripts\"\n        ]\n      }]\n    }\n  ],\n  // Required. At least one repository must be provided.\n  \"syncRepositories\": [\n    {\n      // Required. Name of the repository, must be unique.\n      \"name\": \"go-libyear\",\n      // Required. URL used to clone the repository.\n      \"url\": \"https://github.com/nieomylnieja/go-libyear.git\",\n      // Optional. Default: \"origin/main\".\n      \"ref\": \"dev-branch\"\n    },\n    {\n      \"name\": \"sword-to-obsidian\",\n      \"url\": \"https://github.com/nieomylnieja/sword-to-obsidian.git\"\n    }\n  ],\n  // Required. At least one file must be provided.\n  \"syncFiles\": [\n    {\n      // Required. Descriptive name of the file.\n      \"name\": \"golangci linter config\",\n      // Required. Relative path to the file in both root and synchronized repositories.\n      \"path\": \".golangci.yml\"\n    }\n  ]\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnieomylnieja%2Fgitsync","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnieomylnieja%2Fgitsync","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnieomylnieja%2Fgitsync/lists"}