/* ** Copyright (c) 2007 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file contains code used to check-out versions of the project ** from the local repository. */ #include "config.h" #include "add.h" #include #include /* ** Set to true if files whose names begin with "." should be ** included when processing a recursive "add" command. */ static int includeDotFiles = 0; /* ** This routine returns the names of files in a working checkout that ** are created by Fossil itself, and hence should not be added, deleted, ** or merge, and should be omitted from "clean" and "extra" lists. ** ** Return the N-th name. The first name has N==0. When all names have ** been used, return 0. */ const char *fossil_reserved_name(int N){ /* Possible names of the local per-checkout database file and ** its associated journals */ static const char *azName[] = { "_FOSSIL_", "_FOSSIL_-journal", "_FOSSIL_-wal", "_FOSSIL_-shm", ".fos", ".fos-journal", ".fos-wal", ".fos-shm", }; /* Names of auxiliary files generated by SQLite when the "manifest" ** properity is enabled */ static const char *azManifest[] = { "manifest", "manifest.uuid", }; if( N>=0 && N=count(azName) && N0 ) blob_append(&x, ",", 1); blob_appendf(&x, "'%s'", z); } zAll = blob_str(&x); } return zAll; } /* ** Add a single file */ static void add_one_file(const char *zName, int vid, Blob *pOmit){ Blob pathname; const char *zPath; int i; const char *zReserved; file_tree_name(zName, &pathname, 1); zPath = blob_str(&pathname); for(i=0; (zReserved = fossil_reserved_name(i))!=0; i++){ if( fossil_strcmp(zPath, zReserved)==0 ) break; } if( zReserved || (pOmit && blob_compare(&pathname, pOmit)==0) ){ fossil_warning("cannot add %s", zPath); }else{ if( !file_is_simple_pathname(zPath) ){ fossil_fatal("filename contains illegal characters: %s", zPath); } #if defined(_WIN32) if( db_exists("SELECT 1 FROM vfile" " WHERE pathname=%Q COLLATE nocase", zPath) ){ db_multi_exec("UPDATE vfile SET deleted=0" " WHERE pathname=%Q COLLATE nocase", zPath); } #else if( db_exists("SELECT 1 FROM vfile WHERE pathname=%Q", zPath) ){ db_multi_exec("UPDATE vfile SET deleted=0 WHERE pathname=%Q", zPath); } #endif else{ db_multi_exec( "INSERT INTO vfile(vid,deleted,rid,mrid,pathname)" "VALUES(%d,0,0,0,%Q)", vid, zPath); } printf("ADDED %s\n", zPath); } blob_reset(&pathname); } /* ** All content of the zDir directory to the SFILE table. */ void add_directory_content(const char *zDir){ DIR *d; int origSize; struct dirent *pEntry; Blob path; blob_zero(&path); blob_append(&path, zDir, -1); origSize = blob_size(&path); d = opendir(zDir); if( d ){ while( (pEntry=readdir(d))!=0 ){ char *zPath; if( pEntry->d_name[0]=='.' ){ if( !includeDotFiles ) continue; if( pEntry->d_name[1]==0 ) continue; if( pEntry->d_name[1]=='.' && pEntry->d_name[2]==0 ) continue; } blob_appendf(&path, "/%s", pEntry->d_name); zPath = blob_str(&path); if( file_isdir(zPath)==1 ){ add_directory_content(zPath); }else if( file_isfile(zPath) ){ db_multi_exec("INSERT INTO sfile VALUES(%Q)", zPath); } blob_resize(&path, origSize); } } closedir(d); blob_reset(&path); } /* ** Add all content of a directory. */ void add_directory(const char *zDir, int vid, Blob *pOmit){ Stmt q; add_directory_content(zDir); db_prepare(&q, "SELECT x FROM sfile ORDER BY x"); while( db_step(&q)==SQLITE_ROW ){ const char *zName = db_column_text(&q, 0); add_one_file(zName, vid, pOmit); } db_finalize(&q); db_multi_exec("DELETE FROM sfile"); } /* ** COMMAND: add ** ** Usage: %fossil add FILE... ** ** Make arrangements to add one or more files to the current checkout ** at the next commit. ** ** When adding files recursively, filenames that begin with "." are ** excluded by default. To include such files, add the "--dotfiles" ** option to the command-line. */ void add_cmd(void){ int i; int vid; Blob repo; includeDotFiles = find_option("dotfiles",0,0)!=0; db_must_be_within_tree(); vid = db_lget_int("checkout",0); if( vid==0 ){ fossil_panic("no checkout to add to"); } db_begin_transaction(); if( !file_tree_name(g.zRepositoryName, &repo, 0) ){ blob_zero(&repo); } db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY)"); #if defined(_WIN32) db_multi_exec( "CREATE INDEX IF NOT EXISTS vfile_pathname " " ON vfile(pathname COLLATE nocase)" ); #endif for(i=2; id_name[0]=='.'){ if( !includeDotFiles ) continue; if( pEntry->d_name[1]==0 ) continue; if( pEntry->d_name[1]=='.' && pEntry->d_name[2]==0 ) continue; } blob_appendf(&path, "/%s", pEntry->d_name); zPath = blob_str(&path); if( file_isdir(zPath)==1 ){ del_directory_content(zPath); }else if( file_isfile(zPath) ){ char *zFilePath; Blob pathname; file_tree_name(zPath, &pathname, 1); zFilePath = blob_str(&pathname); if( !db_exists( "SELECT 1 FROM vfile WHERE pathname=%Q AND NOT deleted", zFilePath) ){ printf("SKIPPED %s\n", zPath); }else{ db_multi_exec("UPDATE vfile SET deleted=1 WHERE pathname=%Q", zPath); printf("DELETED %s\n", zPath); } blob_reset(&pathname); } blob_resize(&path, origSize); } } closedir(d); blob_reset(&path); } /* ** COMMAND: rm ** COMMAND: delete ** ** Usage: %fossil rm FILE... ** or: %fossil delete FILE... ** ** Remove one or more files from the tree. ** ** This command does not remove the files from disk. It just marks the ** files as no longer being part of the project. In other words, future ** changes to the named files will not be versioned. */ void delete_cmd(void){ int i; int vid; db_must_be_within_tree(); vid = db_lget_int("checkout", 0); if( vid==0 ){ fossil_panic("no checkout to remove from"); } db_begin_transaction(); for(i=2; i