@@ -172,7 +172,10 @@ const main = async () => {
172172 url . startsWith ( "../" ) ||
173173 url . startsWith ( "/" ) ||
174174 url . startsWith ( "~/" ) ||
175- ( options . interactive && ! url . includes ( "/" ) && ! url . includes ( "." ) && ! url . startsWith ( "http" ) )
175+ ( options . interactive &&
176+ ! url . includes ( "/" ) &&
177+ ! url . startsWith ( "http" ) &&
178+ ! url . startsWith ( "git@" ) )
176179
177180 if ( isLocalPath && options . interactive ) {
178181 // If url doesn't look like a real path, it's the target (e.g. `gitpick -i hello`)
@@ -199,11 +202,37 @@ const main = async () => {
199202
200203 const targetDir = target ? path . resolve ( target ) : null
201204
205+ const SKIP_DIRS = new Set ( [
206+ ".git" ,
207+ "node_modules" ,
208+ ".next" ,
209+ ".nuxt" ,
210+ ".output" ,
211+ ".vercel" ,
212+ ".turbo" ,
213+ "dist" ,
214+ "build" ,
215+ "out" ,
216+ ".cache" ,
217+ ".parcel-cache" ,
218+ "coverage" ,
219+ "target" ,
220+ "vendor" ,
221+ "__pycache__" ,
222+ ".venv" ,
223+ "venv" ,
224+ ] )
225+
202226 const entries : TreeEntry [ ] = [ ]
203227 async function walkLocal ( dir : string , rel : string ) {
204- const items = await fs . promises . readdir ( dir , { withFileTypes : true } )
228+ let items
229+ try {
230+ items = await fs . promises . readdir ( dir , { withFileTypes : true } )
231+ } catch {
232+ return // skip unreadable directories
233+ }
205234 for ( const item of items ) {
206- if ( item . name === ".git" || item . name === "node_modules" ) continue
235+ if ( SKIP_DIRS . has ( item . name ) ) continue
207236 const itemRel = rel ? `${ rel } /${ item . name } ` : item . name
208237 const itemPath = path . join ( dir , item . name )
209238 if ( item . isSymbolicLink ( ) ) {
@@ -221,8 +250,12 @@ const main = async () => {
221250 entries . push ( { path : itemRel , type : "tree" } )
222251 await walkLocal ( itemPath , itemRel )
223252 } else {
224- const stat = await fs . promises . stat ( itemPath )
225- entries . push ( { path : itemRel , type : "blob" , size : stat . size } )
253+ try {
254+ const stat = await fs . promises . stat ( itemPath )
255+ entries . push ( { path : itemRel , type : "blob" , size : stat . size } )
256+ } catch {
257+ // skip unreadable files
258+ }
226259 }
227260 }
228261 }
@@ -263,9 +296,13 @@ const main = async () => {
263296 process . exit ( 0 )
264297 }
265298
266- if ( path . resolve ( resolvedSource ) === path . resolve ( targetDir ) ) {
299+ const resolvedTarget = path . resolve ( targetDir )
300+ if ( resolvedSource === resolvedTarget ) {
267301 throw new Error ( "Source and target directories are the same" )
268302 }
303+ if ( resolvedTarget . startsWith ( resolvedSource + path . sep ) ) {
304+ throw new Error ( "Target directory is inside the source directory" )
305+ }
269306
270307 console . log (
271308 `\n${ green ( "✔" ) } Picking ${ selected . length } selected path${ selected . length !== 1 ? "s" : "" } ...` ,
0 commit comments