gitpick 6.0.0 → 6.0.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/README.md +59 -14
- package/dist/index.mjs +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
[](https://www.npmjs.com/package/gitpick)
|
|
10
10
|
[](https://github.com/nrjdalal/gitpick)
|
|
11
11
|
|
|
12
|
-
📦 `Zero dependencies` / `Un/packed (~
|
|
12
|
+
📦 `Zero dependencies` / `Un/packed (~72/26kb)` / `Faster and more features` yet drop-in replacement for `degit`
|
|
13
13
|
|
|
14
14
|
> #### Just `copy-and-paste` any GitHub, GitLab, Bitbucket or Codeberg URL - no editing required (shorthands work too) - to clone individual files, folders, branches, commits, raw content or even entire repositories without the `.git` directory.
|
|
15
15
|
|
|
16
|
-
Unlike other tools that force you to tweak URLs or follow strict formats to clone files, folders, branches or commits GitPick works seamlessly with any URL.
|
|
16
|
+
Unlike other tools that force you to tweak URLs or follow strict formats to clone files, folders, branches or commits, GitPick works seamlessly with any URL.
|
|
17
17
|
|
|
18
18
|
**You can also try [Interactive Mode](#-interactive-mode)**. Browse any repo right in your terminal. See every file, pick what you want, skip what you don't. Just `gitpick owner/repo -i` and you're in. No more guessing paths.
|
|
19
19
|
|
|
@@ -30,6 +30,7 @@ Unlike other tools that force you to tweak URLs or follow strict formats to clon
|
|
|
30
30
|
- [Interactive Mode](#-interactive-mode)
|
|
31
31
|
- [Private Repos](#-private-repos)
|
|
32
32
|
- [Config File](#-config-file)
|
|
33
|
+
- [.gitpickignore](#-gitpickignore)
|
|
33
34
|
- [Install Globally](#-install-globally-optional)
|
|
34
35
|
- [Used By](#-used-by)
|
|
35
36
|
- [Related Projects](#-related-projects)
|
|
@@ -39,7 +40,7 @@ Unlike other tools that force you to tweak URLs or follow strict formats to clon
|
|
|
39
40
|
|
|
40
41
|
## 📖 Some Examples
|
|
41
42
|
|
|
42
|
-
|
|
43
|
+
See [Quick Usage](#-quick-usage) to learn more.
|
|
43
44
|
|
|
44
45
|
```sh
|
|
45
46
|
# interactive mode - browse and pick files/folders
|
|
@@ -87,19 +88,19 @@ npx gitpick owner/repo -i --dry-run
|
|
|
87
88
|
|
|
88
89
|
- 🔍 Clone individual files or folders from GitHub, GitLab, Bitbucket and Codeberg
|
|
89
90
|
- 🧠 Use shorthands `TanStack/router` or full URL's `https://github.com/TanStack/router`
|
|
90
|
-
- ⚙️ Auto-detects branches and target directory (if not specified) like `git clone`
|
|
91
91
|
- **🔥 Interactive mode** - browse and cherry-pick files/folders with `-i` | `--interactive`
|
|
92
|
+
- ⚙️ Auto-detects branches and target directory (if not specified) like `git clone`
|
|
92
93
|
- 🔐 Seamlessly works with both public and private repositories using a PAT
|
|
93
94
|
- 📦 Can easily clone all submodules with `-r` | `--recursive`
|
|
95
|
+
- 🚫 `.gitpickignore` at the root of the picked path excludes matching paths from the copy (gitignore-style patterns)
|
|
94
96
|
- 🧱 Initialize the cloned output as a fresh git repo with `--init`
|
|
95
|
-
- 💾
|
|
97
|
+
- 💾 Commit with `--commit "<msg>"` (or `--auto-commit`, which uses `chore: gitpick'ed`) - implies `--init`
|
|
98
|
+
- 🔁 Sync changes remotely with `--watch` using intervals (e.g., `15s`, `1m`, `1h`)
|
|
96
99
|
- 🔎 Preview what would be cloned with `--dry-run` before cloning
|
|
97
100
|
- 🌳 View cloned file structure as a colored tree with `--tree`
|
|
98
101
|
- 🗑️ Overwrite or replace existing files without a prompt using `-o` | `--overwrite`
|
|
99
|
-
- 🔁 Sync changes remotely with `--watch` using intervals (e.g., `15s`, `1m`, `1h`)
|
|
100
|
-
- 🔇 Silent mode with `--quiet` for CI pipelines, debug mode with `--verbose`
|
|
101
102
|
- 📋 Config file support (`.gitpick.json` / `.gitpick.jsonc`) for multi-path picks
|
|
102
|
-
-
|
|
103
|
+
- 🔇 Silent mode with `--quiet` for CI pipelines, debug mode with `--verbose`
|
|
103
104
|
|
|
104
105
|
---
|
|
105
106
|
|
|
@@ -126,8 +127,8 @@ npx gitpick <url/shorthand> -r # clone submodules
|
|
|
126
127
|
npx gitpick <url/shorthand> --dry-run # preview without cloning
|
|
127
128
|
npx gitpick <url/shorthand> -w 30s # sync every 30 seconds
|
|
128
129
|
npx gitpick <url/shorthand> --init # initialize as a new git repository
|
|
129
|
-
npx gitpick <url/shorthand> --auto-commit #
|
|
130
|
-
npx gitpick <url/shorthand> --commit "gitpick'ed" #
|
|
130
|
+
npx gitpick <url/shorthand> --auto-commit # Commit with message "chore: gitpick'ed"
|
|
131
|
+
npx gitpick <url/shorthand> --commit "gitpick'ed" # Commit with a message
|
|
131
132
|
npx gitpick https://<token>@github.com/owner/repo # private repository
|
|
132
133
|
npx gitpick https://gitlab.com/owner/repo # GitLab
|
|
133
134
|
npx gitpick https://bitbucket.org/owner/repo # Bitbucket
|
|
@@ -150,8 +151,8 @@ npx gitpick https://codeberg.org/owner/repo # Codeberg
|
|
|
150
151
|
-w, --watch [time] Watch the repository and sync every [time]
|
|
151
152
|
(e.g. 1h, 30m, 15s)
|
|
152
153
|
--init Initialize the cloned output as a new git repository
|
|
153
|
-
--auto-commit
|
|
154
|
-
--commit <msg>
|
|
154
|
+
--auto-commit Commit with message "chore: gitpick'ed" (implies --init)
|
|
155
|
+
--commit <msg> Commit with <msg> (implies --init)
|
|
155
156
|
--tree List copied files as a tree
|
|
156
157
|
--verbose Show detailed clone information
|
|
157
158
|
-h, --help display help for command
|
|
@@ -174,7 +175,19 @@ npx gitpick https://codeberg.org/owner/repo -i
|
|
|
174
175
|
|
|
175
176
|
<img width="900" alt="gitpick interactive mode: browse and pick files/folders" src="https://raw.githubusercontent.com/nrjdalal/demo-kit/main/gitpick/demo-interactive.gif" />
|
|
176
177
|
|
|
177
|
-
Navigate with arrow keys,
|
|
178
|
+
Navigate with arrow keys, `space` to select, `enter` to expand a folder or preview a file, `.` to select all, `c` to confirm, `q` to quit. File previews come with syntax highlighting for 38 languages. Works with GitHub, GitLab, Bitbucket, Codeberg, public and private repos.
|
|
179
|
+
|
|
180
|
+
### Pick from a local folder
|
|
181
|
+
|
|
182
|
+
Interactive mode also works on any folder already on your machine, so you can use GitPick as a local file cherry-picker:
|
|
183
|
+
|
|
184
|
+
```sh
|
|
185
|
+
npx gitpick -i # browse the current directory
|
|
186
|
+
npx gitpick -i my-app # browse cwd, copy the selection into ./my-app
|
|
187
|
+
npx gitpick ./src -i my-app # browse ./src, copy the selection into ./my-app
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Inside a git repo it respects your `.gitignore` (via `git ls-files`) and preserves symlinks when copying.
|
|
178
191
|
|
|
179
192
|
---
|
|
180
193
|
|
|
@@ -255,6 +268,36 @@ Each entry follows the same `<url> [target]` syntax as the CLI. All entries are
|
|
|
255
268
|
|
|
256
269
|
---
|
|
257
270
|
|
|
271
|
+
## 🚫 .gitpickignore
|
|
272
|
+
|
|
273
|
+
Add a `.gitpickignore` file at the root of the path you are picking to keep matching files out of the copy. It uses gitignore-style patterns, so the syntax is already familiar:
|
|
274
|
+
|
|
275
|
+
```gitignore
|
|
276
|
+
# .gitpickignore lives at the root of the picked folder or repo
|
|
277
|
+
|
|
278
|
+
# any .log file, at any depth
|
|
279
|
+
*.log
|
|
280
|
+
|
|
281
|
+
# a directory named node_modules, anywhere (trailing slash means directory only)
|
|
282
|
+
node_modules/
|
|
283
|
+
|
|
284
|
+
# only the dist at the picked root (a leading slash anchors the pattern)
|
|
285
|
+
/dist
|
|
286
|
+
|
|
287
|
+
# every png under docs, at any depth
|
|
288
|
+
docs/**/*.png
|
|
289
|
+
|
|
290
|
+
# re-include a file an earlier rule excluded (! negates, last match wins)
|
|
291
|
+
!docs/logo.png
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
- The file must sit at the **root of what you pick** - the folder for a `tree` URL, or the repo root for a full clone.
|
|
295
|
+
- `*` matches within a path segment, `**` spans directories, `?` matches a single character, and a trailing `/` limits a rule to directories.
|
|
296
|
+
- A leading or embedded `/` anchors a pattern to the picked root; otherwise it matches by basename at any depth.
|
|
297
|
+
- Full-line `#` comments and blank lines are ignored, and the `.gitpickignore` file itself is never copied.
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
258
301
|
## 📦 Install Globally (Optional)
|
|
259
302
|
|
|
260
303
|
```sh
|
|
@@ -262,6 +305,8 @@ npm install -g gitpick
|
|
|
262
305
|
gitpick <url/shorthand> [target] [options]
|
|
263
306
|
```
|
|
264
307
|
|
|
308
|
+
This installs two commands, `gitpick` and `degit`, so existing `degit owner/repo` workflows keep working unchanged.
|
|
309
|
+
|
|
265
310
|
---
|
|
266
311
|
|
|
267
312
|
## 🌍 Used By
|
|
@@ -299,4 +344,4 @@ Thank you for helping improve GitPick!
|
|
|
299
344
|
|
|
300
345
|
## 📄 License
|
|
301
346
|
|
|
302
|
-
MIT
|
|
347
|
+
MIT - [LICENSE](LICENSE)
|
package/dist/index.mjs
CHANGED
|
@@ -19,7 +19,7 @@ Skipping commit: nothing was cloned to commit.`));return}let u=i.commit||`chore:
|
|
|
19
19
|
`;let s=f.length>r?E(`${l+1}-${Math.min(l+r,f.length)}/${f.length}`)+E(` • `):``,d=E(`↑↓:navigate esc/q:back`);i+=` ${s}${d}\n`,u.write(i)}function S(e){let t=e.toString();if(t===``){g=!1,_=null,d.removeListener(`data`,S),p(),process.removeListener(`exit`,m),process.removeListener(`SIGINT`,h),a([]);return}if(t===`\x1B`||t===`q`||t===`Q`||t===`\r`){g=!1,_=null,d.removeListener(`data`,S),d.on(`data`,x),y();return}(t===`\x1B[A`||t===`k`)&&c>0&&c--,(t===`\x1B[B`||t===`j`)&&c<f.length-1&&c++,b()}g=!0,_=b,d.on(`data`,S),b()}function x(e){let t=qn(o),n=t.length+1,r=e.toString();if(r===``||r===`q`||r===`Q`){p(),process.removeListener(`exit`,m),process.removeListener(`SIGINT`,h),a([]);return}if(r===`c`||r===`C`){p(),process.removeListener(`exit`,m),process.removeListener(`SIGINT`,h),a(Xn(o));return}if((r===`\x1B[A`||r===`k`)&&c>0&&c--,(r===`\x1B[B`||r===`j`)&&c<n-1&&c++,c===0&&(r===` `||r===`\r`)||r===`.`){let e=o.every(e=>e.selected);for(let t of o)W(t,!e)}if(r===` `&&c>0){let e=t[c-1];if(e){let t=!e.node.selected;if(W(e.node,t),t&&e.node.type===`symlink`&&e.node.linkTarget){let t=G(o,Jn(e.node.path,e.node.linkTarget));t&&W(t,!0)}Yn(o)}}if(r===`\r`&&c>0){let e=t[c-1];if(e&&e.node.type===`tree`)e.node.expanded=!e.node.expanded;else if(e&&e.node.type===`symlink`&&e.node.linkTarget.endsWith(`/`)){let t=Jn(e.node.path,e.node.linkTarget),n=t.split(`/`);for(let e=1;e<=n.length;e++){let t=G(o,n.slice(0,e).join(`/`));t&&t.type===`tree`&&(t.expanded=!0)}let r=G(o,t);if(r){r.type===`tree`&&(r.expanded=!0);let e=qn(o).findIndex(e=>e.node===r);e>=0&&(c=e+1)}}else if(e&&i&&(e.node.type===`blob`||e.node.type===`symlink`)){b(e.node);return}}if((r===`\x1B[C`||r===`l`)&&c>0){let e=t[c-1];e&&e.node.type===`tree`&&(e.node.expanded=!0)}if((r===`\x1B[D`||r===`h`)&&c>0){let e=t[c-1];e&&e.node.type===`tree`&&(e.node.expanded=!1)}y()}d.on(`data`,x),y()})}function $n(e){if(typeof e==`number`||/^\d+$/.test(e))return typeof e==`number`?e:parseInt(e,10);let t=/(\d+)([hms])/g,n=0,r;for(;(r=t.exec(e))!==null;){let e=parseInt(r[1],10);switch(r[2]){case`h`:n+=e*36e5;break;case`m`:n+=e*6e4;break;case`s`:n+=e*1e3;break}}return n}const q=async e=>{let t=(await S(`git`,[`ls-remote`,e])).stdout,n=t.match(/(.+)\s+HEAD/)?.[1],r=t.match(RegExp(`${n}\\s+refs/heads/(.+)`))?.[1];if(!r)throw Error(`Could not determine default branch!`);return r},er=[{prefix:`git@github.com:`,host:`github.com`},{prefix:`https://github.com/`,host:`github.com`},{prefix:`https://raw.githubusercontent.com/`,host:`github.com`},{prefix:`git@gitlab.com:`,host:`gitlab.com`},{prefix:`https://gitlab.com/`,host:`gitlab.com`},{prefix:`git@bitbucket.org:`,host:`bitbucket.org`},{prefix:`https://bitbucket.org/`,host:`bitbucket.org`},{prefix:`git@codeberg.org:`,host:`codeberg.org`},{prefix:`https://codeberg.org/`,host:`codeberg.org`}];async function tr(e,{branch:t,target:n}){let r=e.match(/^https:\/\/([^@]+)@(github\.com|gitlab\.com|bitbucket\.org|codeberg\.org)/),i=``;if(r)i=r[1],e=e.replace(`${r[1]}@`,``);else{let t={"github.com":process.env.GITHUB_TOKEN||process.env.GH_TOKEN||``,"gitlab.com":process.env.GITLAB_TOKEN||``,"bitbucket.org":process.env.BITBUCKET_TOKEN||``,"codeberg.org":process.env.CODEBERG_TOKEN||``};for(let{prefix:n,host:r}of er)if(e.startsWith(n)){i=t[r];break}!i&&!e.startsWith(`https://`)&&!e.startsWith(`git@`)&&(i=t[`github.com`])}let a=`github.com`;for(let{prefix:t,host:n}of er)if(e.startsWith(t)){a=n,e=e.replace(t,``);break}let o=e.split(`/`),s=o[0],c=o[1]?.endsWith(`.git`)?o[1].slice(0,-4):o[1],l=`https://${i&&i+`@`}${a}/${s}/${c}`,u,d,f;if(a===`github.com`)o[2]===`refs`&&[`heads`,`tags`].includes(o[3])?(u=`raw`,d=t||o[4],f=o.slice(5).join(`/`)):o[2]===`refs`&&o[3]===`remotes`?(u=`raw`,d=t||`${o[4]}/${o[5]}`,f=o.slice(6).join(`/`)):o[2]===`blob`?(u=`blob`,d=t||o[3],f=o.slice(4).join(`/`)):o[2]===`tree`?(u=`tree`,d=t||o[3],f=o.slice(4).join(`/`)):o[2]===`commit`?(u=`repository`,d=t||o[3],f=``):(u=`repository`,d=t||await q(l),f=``);else if(a===`gitlab.com`)o[2]===`-`&&o[3]===`blob`?(u=`blob`,d=t||o[4],f=o.slice(5).join(`/`)):o[2]===`-`&&o[3]===`tree`?(u=`tree`,d=t||o[4],f=o.slice(5).join(`/`)):(u=`repository`,d=t||await q(l),f=``);else if(a===`bitbucket.org`)o[2]===`src`?(u=`tree`,d=t||o[3],f=o.slice(4).join(`/`)):(u=`repository`,d=t||await q(l),f=``);else if(a===`codeberg.org`)[`src`,`raw`,`media`].includes(o[2])&&[`branch`,`tag`,`commit`].includes(o[3])?(u=o[2]===`src`?`tree`:`blob`,d=t||o[4],f=o.slice(5).join(`/`)):(u=`repository`,d=t||await q(l),f=``);else{let e=a;throw Error(`Unsupported host: ${e}`)}let p=n||(u===`blob`?`.`:f.split(`/`).pop()||c),m=!t&&(u===`tree`||u===`blob`)?[d,...f?f.split(`/`):[]]:void 0;return{token:i,host:a,owner:s,repository:c,type:u,branch:d,path:f,target:p,refSegments:m}}const nr=n.join(t.homedir(),`.cache`,`gitpick`),rr=n.join(nr,`update-check.json`);function ir(){try{return JSON.parse(e.readFileSync(rr,`utf-8`))}catch{return null}}function ar(t){try{e.mkdirSync(nr,{recursive:!0}),e.writeFileSync(rr,JSON.stringify(t))}catch{}}function or(){return new Promise(e=>{let t=d.get(`https://registry.npmjs.org/gitpick/latest`,{headers:{Accept:`application/json`},timeout:3e3},t=>{if(t.statusCode!==200)return t.resume(),e(null);let n=``;t.on(`data`,e=>n+=e),t.on(`end`,()=>{try{e(JSON.parse(n).version||null)}catch{e(null)}})});t.on(`error`,()=>e(null)),t.on(`timeout`,()=>{t.destroy(),e(null)})})}function sr(e,t){let n=e.split(`.`).map(Number),r=t.split(`.`).map(Number);for(let e=0;e<3;e++){if((n[e]||0)>(r[e]||0))return!0;if((n[e]||0)<(r[e]||0))return!1}return!1}function J(e,t){if(t)return;let n=ir();n&&sr(n.latestVersion,e)&&console.log(E(`\n Update available: ${k(e)} → ${A(T(n.latestVersion))}\n Run ${A(`npm i -g gitpick`)} to update\n`))}function cr(){let e=ir();e&&Date.now()-e.lastCheck<864e5||setTimeout(async()=>{let e=await or();e&&ar({lastCheck:Date.now(),latestVersion:e})},0)}const Y=Symbol(`singleComment`),lr=Symbol(`multiComment`),X=(e,t,n)=>e.slice(t,n).replace(/[^ \t\r\n]/g,` `),ur=(e,t)=>{let n=t-1,r=0;for(;e[n]===`\\`;)--n,r+=1;return!!(r%2)};function dr(e){if(typeof e!=`string`)throw TypeError(`Expected argument \`jsonString\` to be a \`string\`, got \`${typeof e}\``);let t=!1,n=!1,r=0,i=``,a=``,o=-1;for(let s=0;s<e.length;s++){let c=e[s],l=e[s+1];if(!n&&c===`"`&&(ur(e,s)||(t=!t)),!t)if(!n&&c+l===`//`)i+=e.slice(r,s),r=s,n=Y,s++;else if(n===Y&&c+l===`\r
|
|
20
20
|
`){s++,n=!1,i+=X(e,r,s),r=s;continue}else if(n===Y&&c===`
|
|
21
21
|
`)n=!1,i+=X(e,r,s),r=s;else if(!n&&c+l===`/*`){i+=e.slice(r,s),r=s,n=lr,s++;continue}else if(n===lr&&c+l===`*/`){s++,n=!1,i+=X(e,r,s+1),r=s+1;continue}else n||(o===-1?c===`,`&&(a+=i+e.slice(r,s),i=``,r=s,o=s):c===`}`||c===`]`?(i+=e.slice(r,s),a+=X(i,0,1)+i.slice(1),i=``,r=s,o=-1):c!==` `&&c!==` `&&c!==`\r`&&c!==`
|
|
22
|
-
`&&(i+=e.slice(r,s),r=s,o=-1))}let s=n===Y?X(e,r):e.slice(r);return a+i+s}const fr=[`.gitpick.json`,`.gitpick.jsonc`],pr=async()=>{let t;for(let r of fr){let i=n.resolve(r);if(e.existsSync(i)){t=i;break}}if(!t)return!1;let r=await e.promises.readFile(t,`utf-8`),i=JSON.parse(dr(r));if(!Array.isArray(i)||!i.every(e=>typeof e==`string`))throw Error(`${n.basename(t)} must be an array of strings`);for(let e of i)await S(process.argv[0],[...process.argv.slice(1),...e.split(/\s+/),`-o`],{stdio:`inherit`});return!0};var mr=`gitpick`,Z=`6.0.
|
|
22
|
+
`&&(i+=e.slice(r,s),r=s,o=-1))}let s=n===Y?X(e,r):e.slice(r);return a+i+s}const fr=[`.gitpick.json`,`.gitpick.jsonc`],pr=async()=>{let t;for(let r of fr){let i=n.resolve(r);if(e.existsSync(i)){t=i;break}}if(!t)return!1;let r=await e.promises.readFile(t,`utf-8`),i=JSON.parse(dr(r));if(!Array.isArray(i)||!i.every(e=>typeof e==`string`))throw Error(`${n.basename(t)} must be an array of strings`);for(let e of i)await S(process.argv[0],[...process.argv.slice(1),...e.split(/\s+/),`-o`],{stdio:`inherit`});return!0};var mr=`gitpick`,Z=`6.0.2`;const hr=(e,t)=>`\x1b]8;;${t}\x07${e}\x1b]8;;\x07`,gr=`
|
|
23
23
|
With ${T(`${hr(`GitPick`,`https://github.com/nrjdalal/gitpick`)}`)} clone specific directories or files from GitHub, GitLab, Bitbucket and Codeberg!
|
|
24
24
|
|
|
25
25
|
$ gitpick ${k(`<url>`)} ${O(`[target]`)} ${A(`[options]`)}
|
|
@@ -43,8 +43,8 @@ ${T(`Options:`)}
|
|
|
43
43
|
${A(`-w, --watch [time]`)} Watch the repository and sync every [time]
|
|
44
44
|
(e.g. 1h, 30m, 15s)
|
|
45
45
|
${A(` --init`)} Initialize the cloned output as a new git repository
|
|
46
|
-
${A(` --auto-commit`)}
|
|
47
|
-
${A(` --commit <msg>`)}
|
|
46
|
+
${A(` --auto-commit`)} Commit with message "chore: gitpick'ed" (implies --init)
|
|
47
|
+
${A(` --commit <msg>`)} Commit with <msg> (implies --init)
|
|
48
48
|
${A(` --tree`)} List copied files as a tree
|
|
49
49
|
${A(` --verbose`)} Show detailed clone information
|
|
50
50
|
${A(`-h, --help`)} display help for command
|