@tertium/gith 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +18 -12
- package/README.md +7 -1
- package/bin/gith.js +46 -1
- package/package.json +2 -2
package/LICENSE
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
MIT License
|
|
2
2
|
|
|
3
3
|
Copyright (c) 2026 Vitalii Balabanov
|
|
4
4
|
|
|
5
|
-
Permission
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# gith
|
|
1
|
+
# @tertium/gith
|
|
2
2
|
|
|
3
3
|
```
|
|
4
4
|
o
|
|
@@ -101,6 +101,7 @@ gith stat --last-week -limit 50 # the same for last week, looking at no more
|
|
|
101
101
|
|
|
102
102
|
# Remote and health
|
|
103
103
|
gith sync # fetch all remotes and tags, prune stale refs
|
|
104
|
+
gith sync develop:develop # two-way sync of one branch with origin
|
|
104
105
|
gith open # open origin in the browser
|
|
105
106
|
gith doctor # check the repo is ready for the branch flow
|
|
106
107
|
gith changelog --dry-run # preview the CHANGELOG.md entry for package.json's version
|
|
@@ -177,6 +178,7 @@ Commands that choose between flags (for example `create -b`, `-r` and `-h`, or `
|
|
|
177
178
|
- **`open`** opens origin in the browser, turning an `ssh` or `https` remote into a web address. GitHub and GitLab style addresses work.
|
|
178
179
|
- **`doctor`** checks git, a repository, origin, its reachability, `main` and `develop` on origin, and a clean working tree. It exits 1 if a required check fails, and only warns about the tree.
|
|
179
180
|
- **`sync`** runs `git fetch --all --tags --prune`.
|
|
181
|
+
- **`sync <branch>:<branch>`** (for example `sync develop:develop`) syncs one branch with origin in both directions. If origin is ahead, it fetches the branch like `git fetch origin develop:develop`; if local is ahead, it pushes it; if it is the same, it says so. A checked-out branch is fast-forwarded with `merge --ff-only`. Diverged branches stop with an error, and nothing is forced. A branch missing on one side is created there. `sync develop` alone works too.
|
|
180
182
|
- **`tree`** lists the working trees of the repository (`git worktree list`): each path, its branch or `(detached)`, and its short HEAD. The current one is marked `*`, and bare, locked or prunable trees are noted. `-limit <n>` caps the rows; all are shown by default.
|
|
181
183
|
- **`list --tags`** shows every tag once, with yes/no columns for local and origin, so you can see which tags are unpushed or missing locally. Version tags are listed newest first, and only the first 5 are shown by default. `-limit <n>` (or `-l <n>`) changes that and adds a note when rows are left out.
|
|
182
184
|
- **`list --commits`** shows the newest commits on the current branch, one per line: short hash, date, author and subject. With `--graph` it draws the branch graph instead. It shows 5 by default; `-l <n>` (or `-limit <n>`) changes that and must be a positive whole number. `-limit` only applies to listings. `gith list` with no flag prints usage.
|
|
@@ -221,3 +223,7 @@ bun run typecheck # tsc --noEmit
|
|
|
221
223
|
## Publishing
|
|
222
224
|
|
|
223
225
|
The package ships one built file, `bin/gith.js`, which runs on Node. `npm pack --dry-run` lists what would be published. `prepublishOnly` runs lint, typecheck, the tests and the build before any publish. `.github/workflows/publish.yml` runs the same checks, then `npm publish --access public`, whenever a `v*` tag is pushed. It uses npm trusted publishing (OIDC), so no npm token is stored in the repo. Bump the version with `gith release` or `npm version`, which creates the tag.
|
|
226
|
+
|
|
227
|
+
## Support
|
|
228
|
+
|
|
229
|
+
If this project is useful to you, you can support its development on [Ko-fi](https://ko-fi.com/tertiumnon).
|
package/bin/gith.js
CHANGED
|
@@ -168,6 +168,14 @@ function capture(args) {
|
|
|
168
168
|
function hasOnOrigin(branch) {
|
|
169
169
|
return capture(`ls-remote --heads origin ${branch}`) !== "";
|
|
170
170
|
}
|
|
171
|
+
function isAncestor(ancestor, descendant) {
|
|
172
|
+
try {
|
|
173
|
+
captureArgs(["merge-base", "--is-ancestor", ancestor, descendant]);
|
|
174
|
+
return true;
|
|
175
|
+
} catch {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
171
179
|
function listTags() {
|
|
172
180
|
const local = capture("tag --list").split(`
|
|
173
181
|
`);
|
|
@@ -1294,6 +1302,39 @@ function sync(flags) {
|
|
|
1294
1302
|
noFlags2(flags, "sync");
|
|
1295
1303
|
gitArgs(["fetch", "--all", "--tags", "--prune"]);
|
|
1296
1304
|
}
|
|
1305
|
+
function syncBranch(args) {
|
|
1306
|
+
const [spec = ""] = args;
|
|
1307
|
+
const [name = "", other = name] = spec.split(":");
|
|
1308
|
+
if (args.length !== 1 || other !== name || name === "")
|
|
1309
|
+
throw new Error("usage: gith sync <branch>:<branch>, the same name on both sides.");
|
|
1310
|
+
assertBranchName(name);
|
|
1311
|
+
const local = hasLocal(name);
|
|
1312
|
+
const remote = hasOnOrigin(name);
|
|
1313
|
+
if (!local && !remote)
|
|
1314
|
+
throw new Error(`'${name}' does not exist, locally or on origin.`);
|
|
1315
|
+
if (!remote) {
|
|
1316
|
+
gitArgs(["push", "origin", `${name}:${name}`]);
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
gitArgs(["fetch", "origin", name]);
|
|
1320
|
+
if (!local) {
|
|
1321
|
+
gitArgs(["fetch", "origin", `${name}:${name}`]);
|
|
1322
|
+
return;
|
|
1323
|
+
}
|
|
1324
|
+
const here = capture(`rev-parse refs/heads/${name}`);
|
|
1325
|
+
const there = capture(`rev-parse refs/remotes/origin/${name}`);
|
|
1326
|
+
if (here === there) {
|
|
1327
|
+
console.log(`'${name}' is already in sync with origin.`);
|
|
1328
|
+
} else if (isAncestor(there, here)) {
|
|
1329
|
+
gitArgs(["push", "origin", `${name}:${name}`]);
|
|
1330
|
+
} else if (!isAncestor(here, there)) {
|
|
1331
|
+
throw new Error(`'${name}' has diverged from origin/${name}; merge or rebase one side first.`);
|
|
1332
|
+
} else if (currentBranch2() === name) {
|
|
1333
|
+
gitArgs(["merge", "--ff-only", `origin/${name}`]);
|
|
1334
|
+
} else {
|
|
1335
|
+
gitArgs(["fetch", "origin", `${name}:${name}`]);
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1297
1338
|
async function reset(flags) {
|
|
1298
1339
|
noFlags2(flags, "reset");
|
|
1299
1340
|
const changes = captureArgs(["status", "--short"]).split(`
|
|
@@ -2019,6 +2060,7 @@ Look at the repo
|
|
|
2019
2060
|
|
|
2020
2061
|
Remote and health
|
|
2021
2062
|
gith sync fetch all remotes and tags, prune stale refs
|
|
2063
|
+
gith sync develop:develop two-way sync of one branch with origin: fetch it when behind, push it when ahead, stop when diverged
|
|
2022
2064
|
gith open open origin in the browser
|
|
2023
2065
|
gith doctor check the repo is ready for the branch flow (exit 1 on failures)
|
|
2024
2066
|
gith changelog [--dry-run] add an entry for package.json's version to CHANGELOG.md (commits since the last tag)
|
|
@@ -2068,7 +2110,10 @@ try {
|
|
|
2068
2110
|
await remove(parseFlags(rest));
|
|
2069
2111
|
break;
|
|
2070
2112
|
case "sync":
|
|
2071
|
-
|
|
2113
|
+
if (rest[0] !== undefined && !rest[0].startsWith("-"))
|
|
2114
|
+
syncBranch(rest);
|
|
2115
|
+
else
|
|
2116
|
+
sync(parseFlags(rest));
|
|
2072
2117
|
break;
|
|
2073
2118
|
case "clean":
|
|
2074
2119
|
await clean(parseFlags(rest));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tertium/gith",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Git helper: branch flow with goal branches, release and hotfix containers, and everyday git commands.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"release:major": "node bin/gith.js release --major",
|
|
25
25
|
"version": "bun run build && node bin/gith.js changelog"
|
|
26
26
|
},
|
|
27
|
-
"license": "
|
|
27
|
+
"license": "MIT",
|
|
28
28
|
"author": "Vitalii Balabanov",
|
|
29
29
|
"repository": {
|
|
30
30
|
"type": "git",
|