コード例 #1
0
ファイル: update_.c プロジェクト: sambassett/Fossil-Repo
/*
** Make sure empty directories are created
*/
void ensure_empty_dirs_created(void){
  /* Make empty directories? */
  char *zEmptyDirs = db_get("empty-dirs", 0);
  if( zEmptyDirs!=0 ){
    char *bc;
    Blob dirName;
    Blob dirsList;

    blob_zero(&dirsList);
    blob_init(&dirsList, zEmptyDirs, strlen(zEmptyDirs));
    /* Replace commas by spaces */
    bc = blob_str(&dirsList);
    while( (*bc)!='\0' ){
      if( (*bc)==',' ) { *bc = ' '; }
      ++bc;
    }
    /* Make directories */
    blob_zero(&dirName);
    while( blob_token(&dirsList, &dirName) ){
      const char *zDir = blob_str(&dirName);
      /* Make full pathname of the directory */
      Blob path;
      const char *zPath;

      blob_zero(&path);
      blob_appendf(&path, "%s/%s", g.zLocalRoot, zDir);
      zPath = blob_str(&path);
      /* Handle various cases of existence of the directory */
      switch( file_wd_isdir(zPath) ){
        case 0: { /* doesn't exist */
          if( file_mkdir(zPath, 0)!=0 ) {
            fossil_warning("couldn't create directory %s as "
                           "required by empty-dirs setting", zDir);
          }
          break;
        }
        case 1: { /* exists, and is a directory */
          /* do nothing - required directory exists already */
          break;
        }
        case 2: { /* exists, but isn't a directory */
          fossil_warning("file %s found, but a directory is required "
                         "by empty-dirs setting", zDir);
        }
      }
      blob_reset(&path);
    }
  }
}
コード例 #2
0
ファイル: update.c プロジェクト: digsrc/fossil
/*
** Create empty directories specified by the empty-dirs setting.
*/
void ensure_empty_dirs_created(void) {
    char *zEmptyDirs = db_get("empty-dirs", 0);
    if( zEmptyDirs!=0 ) {
        int i;
        Blob dirName;
        Blob dirsList;

        zEmptyDirs = fossil_strdup(zEmptyDirs);
        for(i=0; zEmptyDirs[i]; i++) {
            if( zEmptyDirs[i]==',' ) zEmptyDirs[i] = ' ';
        }
        blob_init(&dirsList, zEmptyDirs, -1);
        while( blob_token(&dirsList, &dirName) ) {
            char *zDir = blob_str(&dirName);
            char *zPath = mprintf("%s/%s", g.zLocalRoot, zDir);
            switch( file_wd_isdir(zPath) ) {
            case 0: { /* doesn't exist */
                fossil_free(zPath);
                zPath = mprintf("%s/%s/x", g.zLocalRoot, zDir);
                if( file_mkfolder(zPath, 0, 1)!=0 ) {
                    fossil_warning("couldn't create directory %s as "
                                   "required by empty-dirs setting", zDir);
                }
                break;
            }
            case 1: { /* exists, and is a directory */
                /* do nothing - required directory exists already */
                break;
            }
            case 2: { /* exists, but isn't a directory */
                fossil_warning("file %s found, but a directory is required "
                               "by empty-dirs setting", zDir);
            }
            }
            fossil_free(zPath);
            blob_reset(&dirName);
        }
        blob_reset(&dirsList);
        fossil_free(zEmptyDirs);
    }
}
コード例 #3
0
ファイル: vfile.c プロジェクト: ilchenkoanna/VCS
/*
** Check to see if the directory named in zPath is the top of a checkout.
** In other words, check to see if directory pPath contains a file named
** "_vcs_" or ".fslckout".  Return true or false.
*/
int vfile_top_of_checkout(const char *zPath){
  char *zFile;
  int fileFound = 0;

  zFile = mprintf("%s/_vcs_", zPath);
  fileFound = file_size(zFile)>=1024;
  vcs_free(zFile);
  if( !fileFound ){
    zFile = mprintf("%s/.fslckout", zPath);
    fileFound = file_size(zFile)>=1024;
    vcs_free(zFile);
  }

/*
** Load into table SFILE the name of every ordinary file in
** the directory pPath.   Omit the first nPrefix characters of
** of pPath when inserting into the SFILE table.
**
** Subdirectories are scanned recursively.
** Omit files named in VFILE.
**
** Files whose names begin with "." are omitted unless allFlag is true.
**
** Any files or directories that match the glob pattern pIgnore are 
** excluded from the scan.  Name matching occurs after the first
** nPrefix characters are elided from the filename.
*/
void vfile_scan(Blob *pPath, int nPrefix, int allFlag, Glob *pIgnore){
  DIR *d;
  int origSize;
  const char *zDir;
  struct dirent *pEntry;
  int skipAll = 0;
  static Stmt ins;
  static int depth = 0;
  char *zMbcs;

  origSize = blob_size(pPath);
  if( pIgnore ){
    blob_appendf(pPath, "/");
    if( glob_match(pIgnore, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1;
    blob_resize(pPath, origSize);
  }
  if( skipAll ) return;

  if( depth==0 ){
    db_prepare(&ins,
       "INSERT OR IGNORE INTO sfile(x) SELECT :file"
       "  WHERE NOT EXISTS(SELECT 1 FROM vfile WHERE pathname=:file)"
    );
  }
  depth++;

  zDir = blob_str(pPath);
  zMbcs = vcs_utf8_to_mbcs(zDir);
  d = opendir(zMbcs);
  if( d ){
    while( (pEntry=readdir(d))!=0 ){
      char *zPath;
      char *zUtf8;
      if( pEntry->d_name[0]=='.' ){
        if( !allFlag ) continue;
        if( pEntry->d_name[1]==0 ) continue;
        if( pEntry->d_name[1]=='.' && pEntry->d_name[2]==0 ) continue;
      }
      zUtf8 = vcs_mbcs_to_utf8(pEntry->d_name);
      blob_appendf(pPath, "/%s", zUtf8);
      vcs_mbcs_free(zUtf8);
      zPath = blob_str(pPath);
      if( glob_match(pIgnore, &zPath[nPrefix+1]) ){
        /* do nothing */
      }else if( file_wd_isdir(zPath)==1 ){
        if( !vfile_top_of_checkout(zPath) ){
          vfile_scan(pPath, nPrefix, allFlag, pIgnore);
        }
      }else if( file_wd_isfile_or_link(zPath) ){
        db_bind_text(&ins, ":file", &zPath[nPrefix+1]);
        db_step(&ins);
        db_reset(&ins);
      }
      blob_resize(pPath, origSize);
    }
    closedir(d);
  }
  vcs_mbcs_free(zMbcs);

  depth--;
  if( depth==0 ){
    db_finalize(&ins);
  }
}
コード例 #4
0
ファイル: vfile.c プロジェクト: ilchenkoanna/VCS
/*
** Write all files from vid to the disk.  Or if vid==0 and id!=0
** write just the specific file where VFILE.ID=id.
*/
void vfile_to_disk(
  int vid,               /* vid to write to disk */
  int id,                /* Write this one file, if not zero */
  int verbose,           /* Output progress information */
  int promptFlag         /* Prompt user to confirm overwrites */
){
  Stmt q;
  Blob content;
  int nRepos = strlen(g.zLocalRoot);

  if( vid>0 && id==0 ){
    db_prepare(&q, "SELECT id, %Q || pathname, mrid, isexe, islink"
                   "  FROM vfile"
                   " WHERE vid=%d AND mrid>0",
                   g.zLocalRoot, vid);
  }else{
    assert( vid==0 && id>0 );
    db_prepare(&q, "SELECT id, %Q || pathname, mrid, isexe, islink"
                   "  FROM vfile"
                   " WHERE id=%d AND mrid>0",
                   g.zLocalRoot, id);
  }
  while( db_step(&q)==SQLITE_ROW ){
    int id, rid, isExe, isLink;
    const char *zName;

    id = db_column_int(&q, 0);
    zName = db_column_text(&q, 1);
    rid = db_column_int(&q, 2);
    isExe = db_column_int(&q, 3);
    isLink = db_column_int(&q, 4);
    content_get(rid, &content);
    if( file_is_the_same(&content, zName) ){
      blob_reset(&content);
      if( file_wd_setexe(zName, isExe) ){
        db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
                      file_wd_mtime(zName), id);
      }
      continue;
    }
    if( promptFlag && file_wd_size(zName)>=0 ){
      Blob ans;
      char *zMsg;
      char cReply;
      zMsg = mprintf("overwrite %s (a=always/y/N)? ", zName);
      prompt_user(zMsg, &ans);
      free(zMsg);
      cReply = blob_str(&ans)[0];
      blob_reset(&ans);
      if( cReply=='a' || cReply=='A' ){
        promptFlag = 0;
        cReply = 'y';
      }
      if( cReply=='n' || cReply=='N' ){
        blob_reset(&content);
        continue;
      }
    }
    if( verbose ) vcs_print("%s\n", &zName[nRepos]);
    if( file_wd_isdir(zName) == 1 ){
      /*TODO(dchest): remove directories? */
      vcs_fatal("%s is directory, cannot overwrite\n", zName);
    }    
    if( file_wd_size(zName)>=0 && (isLink || file_wd_islink(zName)) ){
      file_delete(zName);
    }
    if( isLink ){
      symlink_create(blob_str(&content), zName);
    }else{
      blob_write_to_file(&content, zName);
    }
    file_wd_setexe(zName, isExe);
    blob_reset(&content);
    db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
                  file_wd_mtime(zName), id);
  }
  db_finalize(&q);
}
コード例 #5
0
/*
** COMMAND: mv
** COMMAND: rename*
**
** Usage: %fossil mv|rename OLDNAME NEWNAME
**    or: %fossil mv|rename OLDNAME... DIR
**
** Move or rename one or more files or directories within the repository tree.
** You can either rename a file or directory or move it to another subdirectory.
**
** This command does NOT rename or move the files on disk.  This command merely
** records the fact that filenames have changed so that appropriate notations
** can be made at the next commit/checkin.
**
** See also: changes, status
*/
void mv_cmd(void){
  int i;
  int vid;
  char *zDest;
  Blob dest;
  Stmt q;

  db_must_be_within_tree();
  vid = db_lget_int("checkout", 0);
  if( vid==0 ){
    fossil_panic("no checkout rename files in");
  }
  if( g.argc<4 ){
    usage("OLDNAME NEWNAME");
  }
  zDest = g.argv[g.argc-1];
  db_begin_transaction();
  file_tree_name(zDest, &dest, 1);
  db_multi_exec(
    "UPDATE vfile SET origname=pathname WHERE origname IS NULL;"
  );
  db_multi_exec(
    "CREATE TEMP TABLE mv(f TEXT UNIQUE ON CONFLICT IGNORE, t TEXT);"
  );
  if( file_wd_isdir(zDest)!=1 ){
    Blob orig;
    if( g.argc!=4 ){
      usage("OLDNAME NEWNAME");
    }
    file_tree_name(g.argv[2], &orig, 1);
    db_multi_exec(
      "INSERT INTO mv VALUES(%B,%B)", &orig, &dest
    );
  }else{
    if( blob_eq(&dest, ".") ){
      blob_reset(&dest);
    }else{
      blob_append(&dest, "/", 1);
    }
    for(i=2; i<g.argc-1; i++){
      Blob orig;
      char *zOrig;
      int nOrig;
      file_tree_name(g.argv[i], &orig, 1);
      zOrig = blob_str(&orig);
      nOrig = blob_size(&orig);
      db_prepare(&q,
         "SELECT pathname FROM vfile"
         " WHERE vid=%d"
         "   AND (pathname='%q' OR (pathname>'%q/' AND pathname<'%q0'))"
         " ORDER BY 1",
         vid, zOrig, zOrig, zOrig
      );
      while( db_step(&q)==SQLITE_ROW ){
        const char *zPath = db_column_text(&q, 0);
        int nPath = db_column_bytes(&q, 0);
        const char *zTail;
        if( nPath==nOrig ){
          zTail = file_tail(zPath);
        }else{
          zTail = &zPath[nOrig+1];
        }
        db_multi_exec(
          "INSERT INTO mv VALUES('%q','%q%q')",
          zPath, blob_str(&dest), zTail
        );
      }
      db_finalize(&q);
    }
  }
  db_prepare(&q, "SELECT f, t FROM mv ORDER BY f");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zFrom = db_column_text(&q, 0);
    const char *zTo = db_column_text(&q, 1);
    mv_one_file(vid, zFrom, zTo);
  }
  db_finalize(&q);
  db_end_transaction(0);
}
コード例 #6
0
/*
** COMMAND: add
**
** Usage: %fossil add ?OPTIONS? FILE1 ?FILE2 ...?
**
** Make arrangements to add one or more files or directories to the
** current checkout at the next commit.
**
** When adding files or directories recursively, filenames that begin
** with "." are excluded by default.  To include such files, add
** the "--dotfiles" option to the command-line.
**
** The --ignore option is a comma-separate list of glob patterns for files
** to be excluded.  Example:  '*.o,*.obj,*.exe'  If the --ignore option
** does not appear on the command line then the "ignore-glob" setting is
** used.
**
** The --case-sensitive option determines whether or not filenames should
** be treated case sensitive or not. If the option is not given, the default
** depends on the global setting, or the operating system default, if not set.
**
** Options:
**
**    --case-sensitive <BOOL> override case-sensitive setting
**    --dotfiles              include files beginning with a dot (".")   
**    --ignore <CSG>          ignore files matching patterns from the 
**                            comma separated list of glob patterns.
** 
** See also: addremove, rm
*/
void add_cmd(void){
  int i;                     /* Loop counter */
  int vid;                   /* Currently checked out version */
  int nRoot;                 /* Full path characters in g.zLocalRoot */
  const char *zIgnoreFlag;   /* The --ignore option or ignore-glob setting */
  Glob *pIgnore;             /* Ignore everything matching this glob pattern */
  int caseSensitive;         /* True if filenames are case sensitive */
  unsigned scanFlags = 0;    /* Flags passed to vfile_scan() */

  zIgnoreFlag = find_option("ignore",0,1);
  if( find_option("dotfiles",0,0)!=0 ) scanFlags |= SCAN_ALL;
  capture_case_sensitive_option();
  db_must_be_within_tree();
  caseSensitive = filenames_are_case_sensitive();
  if( zIgnoreFlag==0 ){
    zIgnoreFlag = db_get("ignore-glob", 0);
  }
  vid = db_lget_int("checkout",0);
  if( vid==0 ){
    fossil_panic("no checkout to add to");
  }
  db_begin_transaction();
  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
  pIgnore = glob_create(zIgnoreFlag);
  nRoot = strlen(g.zLocalRoot);
  
  /* Load the names of all files that are to be added into sfile temp table */
  for(i=2; i<g.argc; i++){
    char *zName;
    int isDir;
    Blob fullName;

    file_canonical_name(g.argv[i], &fullName, 0);
    zName = blob_str(&fullName);
    isDir = file_wd_isdir(zName);
    if( isDir==1 ){
      vfile_scan(&fullName, nRoot-1, scanFlags, pIgnore);
    }else if( isDir==0 ){
      fossil_warning("not found: %s", zName);
    }else if( file_access(zName, R_OK) ){
      fossil_fatal("cannot open %s", zName);
    }else{
      char *zTreeName = &zName[nRoot];
      db_multi_exec(
         "INSERT OR IGNORE INTO sfile(x) VALUES(%Q)",
         zTreeName
      );
    }
    blob_reset(&fullName);
  }
  glob_free(pIgnore);

  add_files_in_sfile(vid, caseSensitive);
  db_end_transaction(0);
}
コード例 #7
0
ファイル: update.c プロジェクト: digsrc/fossil
/*
** COMMAND: update
**
** Usage: %fossil update ?OPTIONS? ?VERSION? ?FILES...?
**
** Change the version of the current checkout to VERSION.  Any
** uncommitted changes are retained and applied to the new checkout.
**
** The VERSION argument can be a specific version or tag or branch
** name.  If the VERSION argument is omitted, then the leaf of the
** subtree that begins at the current version is used, if there is
** only a single leaf.  VERSION can also be "current" to select the
** leaf of the current version or "latest" to select the most recent
** check-in.
**
** If one or more FILES are listed after the VERSION then only the
** named files are candidates to be updated, and any updates to them
** will be treated as edits to the current version. Using a directory
** name for one of the FILES arguments is the same as using every
** subdirectory and file beneath that directory.
**
** If FILES is omitted, all files in the current checkout are subject
** to being updated and the version of the current checkout is changed
** to VERSION. Any uncommitted changes are retained and applied to the
** new checkout.
**
** The -n or --dry-run option causes this command to do a "dry run".
** It prints out what would have happened but does not actually make
** any changes to the current checkout or the repository.
**
** The -v or --verbose option prints status information about
** unchanged files in addition to those file that actually do change.
**
** Options:
**   --case-sensitive <BOOL> override case-sensitive setting
**   --debug          print debug information on stdout
**   --latest         acceptable in place of VERSION, update to latest version
**   --force-missing  force update if missing content after sync
**   -n|--dry-run     If given, display instead of run actions
**   -v|--verbose     print status information about all files
**   -W|--width <num> Width of lines (default is to auto-detect). Must be >20
**                    or 0 (= no limit, resulting in a single line per entry).
**
** See also: revert
*/
void update_cmd(void) {
    int vid;              /* Current version */
    int tid=0;            /* Target version - version we are changing to */
    Stmt q;
    int latestFlag;       /* --latest.  Pick the latest version if true */
    int dryRunFlag;       /* -n or --dry-run.  Do a dry run */
    int verboseFlag;      /* -v or --verbose.  Output extra information */
    int forceMissingFlag; /* --force-missing.  Continue if missing content */
    int debugFlag;        /* --debug option */
    int setmtimeFlag;     /* --setmtime.  Set mtimes on files */
    int nChng;            /* Number of file renames */
    int *aChng;           /* Array of file renames */
    int i;                /* Loop counter */
    int nConflict = 0;    /* Number of merge conflicts */
    int nOverwrite = 0;   /* Number of unmanaged files overwritten */
    int nUpdate = 0;      /* Number of changes of any kind */
    int width;            /* Width of printed comment lines */
    Stmt mtimeXfer;       /* Statement to transfer mtimes */
    const char *zWidth;   /* Width option string value */

    if( !internalUpdate ) {
        undo_capture_command_line();
        url_proxy_options();
    }
    zWidth = find_option("width","W",1);
    if( zWidth ) {
        width = atoi(zWidth);
        if( (width!=0) && (width<=20) ) {
            fossil_fatal("-W|--width value must be >20 or 0");
        }
    } else {
        width = -1;
    }
    latestFlag = find_option("latest",0, 0)!=0;
    dryRunFlag = find_option("dry-run","n",0)!=0;
    if( !dryRunFlag ) {
        dryRunFlag = find_option("nochange",0,0)!=0; /* deprecated */
    }
    verboseFlag = find_option("verbose","v",0)!=0;
    forceMissingFlag = find_option("force-missing",0,0)!=0;
    debugFlag = find_option("debug",0,0)!=0;
    setmtimeFlag = find_option("setmtime",0,0)!=0;

    /* We should be done with options.. */
    verify_all_options();

    db_must_be_within_tree();
    vid = db_lget_int("checkout", 0);
    user_select();
    if( !dryRunFlag && !internalUpdate ) {
        if( autosync_loop(SYNC_PULL + SYNC_VERBOSE*verboseFlag,
                          db_get_int("autosync-tries", 1)) ) {
            fossil_fatal("Cannot proceed with update");
        }
    }

    /* Create any empty directories now, as well as after the update,
    ** so changes in settings are reflected now */
    if( !dryRunFlag ) ensure_empty_dirs_created();

    if( internalUpdate ) {
        tid = internalUpdate;
    } else if( g.argc>=3 ) {
        if( fossil_strcmp(g.argv[2], "current")==0 ) {
            /* If VERSION is "current", then use the same algorithm to find the
            ** target as if VERSION were omitted. */
        } else if( fossil_strcmp(g.argv[2], "latest")==0 ) {
            /* If VERSION is "latest", then use the same algorithm to find the
            ** target as if VERSION were omitted and the --latest flag is present.
            */
            latestFlag = 1;
        } else {
            tid = name_to_typed_rid(g.argv[2],"ci");
            if( tid==0 || !is_a_version(tid) ) {
                fossil_fatal("no such check-in: %s", g.argv[2]);
            }
        }
    }

    /* If no VERSION is specified on the command-line, then look for a
    ** descendent of the current version.  If there are multiple descendants,
    ** look for one from the same branch as the current version.  If there
    ** are still multiple descendants, show them all and refuse to update
    ** until the user selects one.
    */
    if( tid==0 ) {
        int closeCode = 1;
        compute_leaves(vid, closeCode);
        if( !db_exists("SELECT 1 FROM leaves") ) {
            closeCode = 0;
            compute_leaves(vid, closeCode);
        }
        if( !latestFlag && db_int(0, "SELECT count(*) FROM leaves")>1 ) {
            db_multi_exec(
                "DELETE FROM leaves WHERE rid NOT IN"
                "   (SELECT leaves.rid FROM leaves, tagxref"
                "     WHERE leaves.rid=tagxref.rid AND tagxref.tagid=%d"
                "       AND tagxref.value==(SELECT value FROM tagxref"
                " WHERE tagid=%d AND rid=%d))",
                TAG_BRANCH, TAG_BRANCH, vid
            );
            if( db_int(0, "SELECT count(*) FROM leaves")>1 ) {
                compute_leaves(vid, closeCode);
                db_prepare(&q,
                           "%s "
                           "   AND event.objid IN leaves"
                           " ORDER BY event.mtime DESC",
                           timeline_query_for_tty()
                          );
                print_timeline(&q, -100, width, 0);
                db_finalize(&q);
                fossil_fatal("Multiple descendants");
            }
        }
        tid = db_int(0, "SELECT rid FROM leaves, event"
                     " WHERE event.objid=leaves.rid"
                     " ORDER BY event.mtime DESC");
        if( tid==0 ) tid = vid;
    }

    if( tid==0 ) {
        return;
    }

    db_begin_transaction();
    vfile_check_signature(vid, CKSIG_ENOTFILE);
    if( !dryRunFlag && !internalUpdate ) undo_begin();
    if( load_vfile_from_rid(tid) && !forceMissingFlag ) {
        fossil_fatal("missing content, unable to update");
    };

    /*
    ** The record.fn field is used to match files against each other.  The
    ** FV table contains one row for each each unique filename in
    ** in the current checkout, the pivot, and the version being merged.
    */
    db_multi_exec(
        "DROP TABLE IF EXISTS fv;"
        "CREATE TEMP TABLE fv("
        "  fn TEXT %s PRIMARY KEY,"   /* The filename relative to root */
        "  idv INTEGER,"              /* VFILE entry for current version */
        "  idt INTEGER,"              /* VFILE entry for target version */
        "  chnged BOOLEAN,"           /* True if current version has been edited */
        "  islinkv BOOLEAN,"          /* True if current file is a link */
        "  islinkt BOOLEAN,"          /* True if target file is a link */
        "  ridv INTEGER,"             /* Record ID for current version */
        "  ridt INTEGER,"             /* Record ID for target */
        "  isexe BOOLEAN,"            /* Does target have execute permission? */
        "  deleted BOOLEAN DEFAULT 0,"/* File marked by "rm" to become unmanaged */
        "  fnt TEXT %s"               /* Filename of same file on target version */
        ");",
        filename_collation(), filename_collation()
    );

    /* Add files found in the current version
    */
    db_multi_exec(
        "INSERT OR IGNORE INTO fv(fn,fnt,idv,idt,ridv,ridt,isexe,chnged,deleted)"
        " SELECT pathname, pathname, id, 0, rid, 0, isexe, chnged, deleted"
        "   FROM vfile WHERE vid=%d",
        vid
    );

    /* Compute file name changes on V->T.  Record name changes in files that
    ** have changed locally.
    */
    if( vid ) {
        find_filename_changes(vid, tid, 1, &nChng, &aChng, debugFlag ? "V->T": 0);
        if( nChng ) {
            for(i=0; i<nChng; i++) {
                db_multi_exec(
                    "UPDATE fv"
                    "   SET fnt=(SELECT name FROM filename WHERE fnid=%d)"
                    " WHERE fn=(SELECT name FROM filename WHERE fnid=%d) AND chnged",
                    aChng[i*2+1], aChng[i*2]
                );
            }
            fossil_free(aChng);
        }
    }

    /* Add files found in the target version T but missing from the current
    ** version V.
    */
    db_multi_exec(
        "INSERT OR IGNORE INTO fv(fn,fnt,idv,idt,ridv,ridt,isexe,chnged)"
        " SELECT pathname, pathname, 0, 0, 0, 0, isexe, 0 FROM vfile"
        "  WHERE vid=%d"
        "    AND pathname %s NOT IN (SELECT fnt FROM fv)",
        tid, filename_collation()
    );

    /*
    ** Compute the file version ids for T
    */
    db_multi_exec(
        "UPDATE fv SET"
        " idt=coalesce((SELECT id FROM vfile WHERE vid=%d AND fnt=pathname),0),"
        " ridt=coalesce((SELECT rid FROM vfile WHERE vid=%d AND fnt=pathname),0)",
        tid, tid
    );

    /*
    ** Add islink information
    */
    db_multi_exec(
        "UPDATE fv SET"
        " islinkv=coalesce((SELECT islink FROM vfile"
        " WHERE vid=%d AND fnt=pathname),0),"
        " islinkt=coalesce((SELECT islink FROM vfile"
        " WHERE vid=%d AND fnt=pathname),0)",
        vid, tid
    );


    if( debugFlag ) {
        db_prepare(&q,
                   "SELECT rowid, fn, fnt, chnged, ridv, ridt, isexe,"
                   "       islinkv, islinkt FROM fv"
                  );
        while( db_step(&q)==SQLITE_ROW ) {
            fossil_print("%3d: ridv=%-4d ridt=%-4d chnged=%d isexe=%d"
                         " islinkv=%d  islinkt=%d\n",
                         db_column_int(&q, 0),
                         db_column_int(&q, 4),
                         db_column_int(&q, 5),
                         db_column_int(&q, 3),
                         db_column_int(&q, 6),
                         db_column_int(&q, 7),
                         db_column_int(&q, 8));
            fossil_print("     fnv = [%s]\n", db_column_text(&q, 1));
            fossil_print("     fnt = [%s]\n", db_column_text(&q, 2));
        }
        db_finalize(&q);
    }

    /* If FILES appear on the command-line, remove from the "fv" table
    ** every entry that is not named on the command-line or which is not
    ** in a directory named on the command-line.
    */
    if( g.argc>=4 ) {
        Blob sql;              /* SQL statement to purge unwanted entries */
        Blob treename;         /* Normalized filename */
        int i;                 /* Loop counter */
        const char *zSep;      /* Term separator */

        blob_zero(&sql);
        blob_append(&sql, "DELETE FROM fv WHERE ", -1);
        zSep = "";
        for(i=3; i<g.argc; i++) {
            file_tree_name(g.argv[i], &treename, 0, 1);
            if( file_wd_isdir(g.argv[i])==1 ) {
                if( blob_size(&treename) != 1 || blob_str(&treename)[0] != '.' ) {
                    blob_append_sql(&sql, "%sfn NOT GLOB '%q/*' ",
                                    zSep /*safe-for-%s*/, blob_str(&treename));
                } else {
                    blob_reset(&sql);
                    break;
                }
            } else {
                blob_append_sql(&sql, "%sfn<>%Q ",
                                zSep /*safe-for-%s*/, blob_str(&treename));
            }
            zSep = "AND ";
            blob_reset(&treename);
        }
        db_multi_exec("%s", blob_sql_text(&sql));
        blob_reset(&sql);
    }

    /*
    ** Alter the content of the checkout so that it conforms with the
    ** target
    */
    db_prepare(&q,
               "SELECT fn, idv, ridv, idt, ridt, chnged, fnt,"
               "       isexe, islinkv, islinkt, deleted FROM fv ORDER BY 1"
              );
    db_prepare(&mtimeXfer,
               "UPDATE vfile SET mtime=(SELECT mtime FROM vfile WHERE id=:idv)"
               " WHERE id=:idt"
              );
    assert( g.zLocalRoot!=0 );
    assert( strlen(g.zLocalRoot)>0 );
    assert( g.zLocalRoot[strlen(g.zLocalRoot)-1]=='/' );
    while( db_step(&q)==SQLITE_ROW ) {
        const char *zName = db_column_text(&q, 0);  /* The filename from root */
        int idv = db_column_int(&q, 1);             /* VFILE entry for current */
        int ridv = db_column_int(&q, 2);            /* RecordID for current */
        int idt = db_column_int(&q, 3);             /* VFILE entry for target */
        int ridt = db_column_int(&q, 4);            /* RecordID for target */
        int chnged = db_column_int(&q, 5);          /* Current is edited */
        const char *zNewName = db_column_text(&q,6);/* New filename */
        int isexe = db_column_int(&q, 7);           /* EXE perm for new file */
        int islinkv = db_column_int(&q, 8);         /* Is current file is a link */
        int islinkt = db_column_int(&q, 9);         /* Is target file is a link */
        int deleted = db_column_int(&q, 10);        /* Marked for deletion */
        char *zFullPath;                            /* Full pathname of the file */
        char *zFullNewPath;                         /* Full pathname of dest */
        char nameChng;                              /* True if the name changed */

        zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
        zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName);
        nameChng = fossil_strcmp(zName, zNewName);
        nUpdate++;
        if( deleted ) {
            db_multi_exec("UPDATE vfile SET deleted=1 WHERE id=%d", idt);
        }
        if( idv>0 && ridv==0 && idt>0 && ridt>0 ) {
            /* Conflict.  This file has been added to the current checkout
            ** but also exists in the target checkout.  Use the current version.
            */
            fossil_print("CONFLICT %s\n", zName);
            nConflict++;
        } else if( idt>0 && idv==0 ) {
            /* File added in the target. */
            if( file_wd_isfile_or_link(zFullPath) ) {
                fossil_print("ADD %s - overwrites an unmanaged file\n", zName);
                nOverwrite++;
            } else {
                fossil_print("ADD %s\n", zName);
            }
            if( !dryRunFlag && !internalUpdate ) undo_save(zName);
            if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0);
        } else if( idt>0 && idv>0 && ridt!=ridv && (chnged==0 || deleted) ) {
            /* The file is unedited.  Change it to the target version */
            if( deleted ) {
                fossil_print("UPDATE %s - change to unmanaged file\n", zName);
            } else {
                fossil_print("UPDATE %s\n", zName);
            }
            if( !dryRunFlag && !internalUpdate ) undo_save(zName);
            if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0);
        } else if( idt>0 && idv>0 && !deleted && file_wd_size(zFullPath)<0 ) {
            /* The file missing from the local check-out. Restore it to the
            ** version that appears in the target. */
            fossil_print("UPDATE %s\n", zName);
            if( !dryRunFlag && !internalUpdate ) undo_save(zName);
            if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0);
        } else if( idt==0 && idv>0 ) {
            if( ridv==0 ) {
                /* Added in current checkout.  Continue to hold the file as
                ** as an addition */
                db_multi_exec("UPDATE vfile SET vid=%d WHERE id=%d", tid, idv);
            } else if( chnged ) {
                /* Edited locally but deleted from the target.  Do not track the
                ** file but keep the edited version around. */
                fossil_print("CONFLICT %s - edited locally but deleted by update\n",
                             zName);
                nConflict++;
            } else {
                fossil_print("REMOVE %s\n", zName);
                if( !dryRunFlag && !internalUpdate ) undo_save(zName);
                if( !dryRunFlag ) file_delete(zFullPath);
            }
        } else if( idt>0 && idv>0 && ridt!=ridv && chnged ) {
            /* Merge the changes in the current tree into the target version */
            Blob r, t, v;
            int rc;
            if( nameChng ) {
                fossil_print("MERGE %s -> %s\n", zName, zNewName);
            } else {
                fossil_print("MERGE %s\n", zName);
            }
            if( islinkv || islinkt /* || file_wd_islink(zFullPath) */ ) {
                fossil_print("***** Cannot merge symlink %s\n", zNewName);
                nConflict++;
            } else {
                unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0;
                if( !dryRunFlag && !internalUpdate ) undo_save(zName);
                content_get(ridt, &t);
                content_get(ridv, &v);
                rc = merge_3way(&v, zFullPath, &t, &r, mergeFlags);
                if( rc>=0 ) {
                    if( !dryRunFlag ) {
                        blob_write_to_file(&r, zFullNewPath);
                        file_wd_setexe(zFullNewPath, isexe);
                    }
                    if( rc>0 ) {
                        fossil_print("***** %d merge conflicts in %s\n", rc, zNewName);
                        nConflict++;
                    }
                } else {
                    if( !dryRunFlag ) {
                        blob_write_to_file(&t, zFullNewPath);
                        file_wd_setexe(zFullNewPath, isexe);
                    }
                    fossil_print("***** Cannot merge binary file %s\n", zNewName);
                    nConflict++;
                }
            }
            if( nameChng && !dryRunFlag ) file_delete(zFullPath);
            blob_reset(&v);
            blob_reset(&t);
            blob_reset(&r);
        } else {
            nUpdate--;
            if( chnged ) {
                if( verboseFlag ) fossil_print("EDITED %s\n", zName);
            } else {
                db_bind_int(&mtimeXfer, ":idv", idv);
                db_bind_int(&mtimeXfer, ":idt", idt);
                db_step(&mtimeXfer);
                db_reset(&mtimeXfer);
                if( verboseFlag ) fossil_print("UNCHANGED %s\n", zName);
            }
        }
        free(zFullPath);
        free(zFullNewPath);
    }
    db_finalize(&q);
    db_finalize(&mtimeXfer);
    fossil_print("%.79c\n",'-');
    if( nUpdate==0 ) {
        show_common_info(tid, "checkout:", 1, 0);
        fossil_print("%-13s None. Already up-to-date\n", "changes:");
    } else {
        show_common_info(tid, "updated-to:", 1, 0);
        fossil_print("%-13s %d file%s modified.\n", "changes:",
                     nUpdate, nUpdate>1 ? "s" : "");
    }

    /* Report on conflicts
    */
    if( !dryRunFlag ) {
        Stmt q;
        int nMerge = 0;
        db_prepare(&q, "SELECT uuid, id FROM vmerge JOIN blob ON merge=rid"
                   " WHERE id<=0");
        while( db_step(&q)==SQLITE_ROW ) {
            const char *zLabel = "merge";
            switch( db_column_int(&q, 1) ) {
            case -1:
                zLabel = "cherrypick merge";
                break;
            case -2:
                zLabel = "backout merge";
                break;
            }
            fossil_warning("uncommitted %s against %S.",
                           zLabel, db_column_text(&q, 0));
            nMerge++;
        }
        db_finalize(&q);
        leaf_ambiguity_warning(tid, tid);

        if( nConflict ) {
            if( internalUpdate ) {
                internalConflictCnt = nConflict;
                nConflict = 0;
            } else {
                fossil_warning("WARNING: %d merge conflicts", nConflict);
            }
        }
        if( nOverwrite ) {
            fossil_warning("WARNING: %d unmanaged files were overwritten",
                           nOverwrite);
        }
        if( nMerge ) {
            fossil_warning("WARNING: %d uncommitted prior merges", nMerge);
        }
    }

    /*
    ** Clean up the mid and pid VFILE entries.  Then commit the changes.
    */
    if( dryRunFlag ) {
        db_end_transaction(1);  /* With --dry-run, rollback changes */
    } else {
        ensure_empty_dirs_created();
        if( g.argc<=3 ) {
            /* All files updated.  Shift the current checkout to the target. */
            db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid);
            checkout_set_all_exe(tid);
            manifest_to_disk(tid);
            db_lset_int("checkout", tid);
        } else {
            /* A subset of files have been checked out.  Keep the current
            ** checkout unchanged. */
            db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
        }
        if( !internalUpdate ) undo_finish();
        if( setmtimeFlag ) vfile_check_signature(tid, CKSIG_SETMTIME);
        db_end_transaction(0);
    }
}
コード例 #8
0
ファイル: vfile_.c プロジェクト: LitleWaffle/sampleDirectory
/*
** Scans the specified base directory for any directories within it, while
** keeping a count of how many files they each contains, either directly or
** indirectly.
**
** Subdirectories are scanned recursively.
** Omit files named in VFILE.
**
** Directories whose names begin with "." are omitted unless the SCAN_ALL
** flag is set.
**
** Any directories that match the glob patterns pIgnore* are excluded from
** the scan.  Name matching occurs after the first nPrefix characters are
** elided from the filename.
**
** Returns the total number of files found.
*/
int vfile_dir_scan(
  Blob *pPath,           /* Base directory to be scanned */
  int nPrefix,           /* Number of bytes in base directory name */
  unsigned scanFlags,    /* Zero or more SCAN_xxx flags */
  Glob *pIgnore1,        /* Do not add directories that match this GLOB */
  Glob *pIgnore2,        /* Omit directories matching this GLOB too */
  Glob *pIgnore3         /* Omit directories matching this GLOB too */
){
  int result = 0;
  DIR *d;
  int origSize;
  struct dirent *pEntry;
  int skipAll = 0;
  static Stmt ins;
  static Stmt upd;
  static int depth = 0;
  void *zNative;

  origSize = blob_size(pPath);
  if( pIgnore1 || pIgnore2 || pIgnore3 ){
    blob_appendf(pPath, "/");
    if( glob_match(pIgnore1, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1;
    if( glob_match(pIgnore2, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1;
    if( glob_match(pIgnore3, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1;
    blob_resize(pPath, origSize);
  }
  if( skipAll ) return result;

  if( depth==0 ){
    db_multi_exec("DROP TABLE IF EXISTS dscan_temp;"
                  "CREATE TEMP TABLE dscan_temp("
                  "  x TEXT PRIMARY KEY %s, y INTEGER)",
                  filename_collation());
    db_prepare(&ins,
       "INSERT OR IGNORE INTO dscan_temp(x, y) SELECT :file, :count"
       "  WHERE NOT EXISTS(SELECT 1 FROM vfile WHERE"
       " pathname GLOB :file || '/*' %s)", filename_collation()
    );
    db_prepare(&upd,
       "UPDATE OR IGNORE dscan_temp SET y = coalesce(y, 0) + 1"
       "  WHERE x=:file %s",
       filename_collation()
    );
  }
  depth++;

  zNative = fossil_utf8_to_filename(blob_str(pPath));
  d = opendir(zNative);
  if( d ){
    while( (pEntry=readdir(d))!=0 ){
      char *zOrigPath;
      char *zPath;
      char *zUtf8;
      if( pEntry->d_name[0]=='.' ){
        if( (scanFlags & SCAN_ALL)==0 ) continue;
        if( pEntry->d_name[1]==0 ) continue;
        if( pEntry->d_name[1]=='.' && pEntry->d_name[2]==0 ) continue;
      }
      zOrigPath = mprintf("%s", blob_str(pPath));
      zUtf8 = fossil_filename_to_utf8(pEntry->d_name);
      blob_appendf(pPath, "/%s", zUtf8);
      zPath = blob_str(pPath);
      if( glob_match(pIgnore1, &zPath[nPrefix+1]) ||
          glob_match(pIgnore2, &zPath[nPrefix+1]) ||
          glob_match(pIgnore3, &zPath[nPrefix+1]) ){
        /* do nothing */
      }else if( file_wd_isdir(zPath)==1 ){
        if( (scanFlags & SCAN_NESTED) || !vfile_top_of_checkout(zPath) ){
          char *zSavePath = mprintf("%s", zPath);
          int count = vfile_dir_scan(pPath, nPrefix, scanFlags, pIgnore1,
                                     pIgnore2, pIgnore3);
          db_bind_text(&ins, ":file", &zSavePath[nPrefix+1]);
          db_bind_int(&ins, ":count", count);
          db_step(&ins);
          db_reset(&ins);
          fossil_free(zSavePath);
          result += count; /* found X normal files? */
        }
      }else if( file_wd_isfile_or_link(zPath) ){
        db_bind_text(&upd, ":file", zOrigPath);
        db_step(&upd);
        db_reset(&upd);
        result++; /* found 1 normal file */
      }
      fossil_filename_free(zUtf8);
      blob_resize(pPath, origSize);
      fossil_free(zOrigPath);
    }
    closedir(d);
  }
  fossil_filename_free(zNative);

  depth--;
  if( depth==0 ){
    db_finalize(&upd);
    db_finalize(&ins);
  }
  return result;
}
コード例 #9
0
ファイル: vfile_.c プロジェクト: LitleWaffle/sampleDirectory
/*
** Load into table SFILE the name of every ordinary file in
** the directory pPath.   Omit the first nPrefix characters of
** of pPath when inserting into the SFILE table.
**
** Subdirectories are scanned recursively.
** Omit files named in VFILE.
**
** Files whose names begin with "." are omitted unless the SCAN_ALL
** flag is set.
**
** Any files or directories that match the glob patterns pIgnore*
** are excluded from the scan.  Name matching occurs after the
** first nPrefix characters are elided from the filename.
*/
void vfile_scan(
  Blob *pPath,           /* Directory to be scanned */
  int nPrefix,           /* Number of bytes in directory name */
  unsigned scanFlags,    /* Zero or more SCAN_xxx flags */
  Glob *pIgnore1,        /* Do not add files that match this GLOB */
  Glob *pIgnore2         /* Omit files matching this GLOB too */
){
  DIR *d;
  int origSize;
  struct dirent *pEntry;
  int skipAll = 0;
  static Stmt ins;
  static int depth = 0;
  void *zNative;

  origSize = blob_size(pPath);
  if( pIgnore1 || pIgnore2 ){
    blob_appendf(pPath, "/");
    if( glob_match(pIgnore1, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1;
    if( glob_match(pIgnore2, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1;
    blob_resize(pPath, origSize);
  }
  if( skipAll ) return;

  if( depth==0 ){
    db_prepare(&ins,
       "INSERT OR IGNORE INTO sfile(x) SELECT :file"
       "  WHERE NOT EXISTS(SELECT 1 FROM vfile WHERE"
       " pathname=:file %s)", filename_collation()
    );
  }
  depth++;

  zNative = fossil_utf8_to_filename(blob_str(pPath));
  d = opendir(zNative);
  if( d ){
    while( (pEntry=readdir(d))!=0 ){
      char *zPath;
      char *zUtf8;
      if( pEntry->d_name[0]=='.' ){
        if( (scanFlags & SCAN_ALL)==0 ) continue;
        if( pEntry->d_name[1]==0 ) continue;
        if( pEntry->d_name[1]=='.' && pEntry->d_name[2]==0 ) continue;
      }
      zUtf8 = fossil_filename_to_utf8(pEntry->d_name);
      blob_appendf(pPath, "/%s", zUtf8);
      zPath = blob_str(pPath);
      if( glob_match(pIgnore1, &zPath[nPrefix+1]) ||
          glob_match(pIgnore2, &zPath[nPrefix+1]) ){
        /* do nothing */
      }else if( file_wd_isdir(zPath)==1 ){
        if( !vfile_top_of_checkout(zPath) ){
          vfile_scan(pPath, nPrefix, scanFlags, pIgnore1, pIgnore2);
        }
      }else if( file_wd_isfile_or_link(zPath) ){
        if( (scanFlags & SCAN_TEMP)==0 || is_temporary_file(zUtf8) ){
          db_bind_text(&ins, ":file", &zPath[nPrefix+1]);
          db_step(&ins);
          db_reset(&ins);
        }
      }
      fossil_filename_free(zUtf8);
      blob_resize(pPath, origSize);
    }
    closedir(d);
  }
  fossil_filename_free(zNative);

  depth--;
  if( depth==0 ){
    db_finalize(&ins);
  }
}