Print this page
5292 findunref is both slow and broken

*** 51,84 **** unsigned int npath; unsigned int maxpaths; } pnset_t; /* ! * Data associated with the current Mercurial manifest. */ ! typedef struct hgdata { pnset_t *manifest; ! char hgpath[MAXPATHLEN]; char root[MAXPATHLEN]; unsigned int rootlen; boolean_t rootwarn; ! } hgdata_t; /* * Hooks used to check if a given unreferenced file is known to an SCM ! * (currently Mercurial and TeamWare). */ typedef int checkscm_func_t(const char *, const struct FTW *); typedef void chdirscm_func_t(const char *); typedef struct { const char *name; checkscm_func_t *checkfunc; chdirscm_func_t *chdirfunc; } scm_t; ! static checkscm_func_t check_tw, check_hg, check_git; static chdirscm_func_t chdir_hg, chdir_git; static int pnset_add(pnset_t *, const char *); static int pnset_check(const pnset_t *, const char *); static void pnset_empty(pnset_t *); static void pnset_free(pnset_t *); --- 51,84 ---- unsigned int npath; unsigned int maxpaths; } pnset_t; /* ! * Data associated with the current SCM manifest. */ ! typedef struct scmdata { pnset_t *manifest; ! char metapath[MAXPATHLEN]; char root[MAXPATHLEN]; unsigned int rootlen; boolean_t rootwarn; ! } scmdata_t; /* * Hooks used to check if a given unreferenced file is known to an SCM ! * (currently Git, Mercurial and TeamWare). */ typedef int checkscm_func_t(const char *, const struct FTW *); typedef void chdirscm_func_t(const char *); typedef struct { const char *name; checkscm_func_t *checkfunc; chdirscm_func_t *chdirfunc; } scm_t; ! static checkscm_func_t check_tw, check_scmdata; static chdirscm_func_t chdir_hg, chdir_git; static int pnset_add(pnset_t *, const char *); static int pnset_check(const pnset_t *, const char *); static void pnset_empty(pnset_t *); static void pnset_free(pnset_t *);
*** 88,106 **** static void die(const char *, ...); static const scm_t scms[] = { { "tw", check_tw, NULL }, { "teamware", check_tw, NULL }, ! { "hg", check_hg, chdir_hg }, ! { "mercurial", check_hg, chdir_hg }, ! { "git", check_git, chdir_git }, { NULL, NULL, NULL } }; static const scm_t *scm; ! static hgdata_t hgdata; ! static pnset_t *gitmanifest = NULL; static time_t tstamp; /* timestamp to compare files to */ static pnset_t *exsetp; /* pathname globs to ignore */ static const char *progname; int --- 88,105 ---- static void die(const char *, ...); static const scm_t scms[] = { { "tw", check_tw, NULL }, { "teamware", check_tw, NULL }, ! { "hg", check_scmdata, chdir_hg }, ! { "mercurial", check_scmdata, chdir_hg }, ! { "git", check_scmdata, chdir_git }, { NULL, NULL, NULL } }; static const scm_t *scm; ! static scmdata_t scmdata; static time_t tstamp; /* timestamp to compare files to */ static pnset_t *exsetp; /* pathname globs to ignore */ static const char *progname; int
*** 191,201 **** /* * Load and return a pnset for the manifest for the Mercurial repo at `hgroot'. */ static pnset_t * ! load_manifest(const char *hgroot) { FILE *fp = NULL; char *hgcmd = NULL; char *newline; pnset_t *pnsetp; --- 190,200 ---- /* * Load and return a pnset for the manifest for the Mercurial repo at `hgroot'. */ static pnset_t * ! hg_manifest(const char *hgroot) { FILE *fp = NULL; char *hgcmd = NULL; char *newline; pnset_t *pnsetp;
*** 229,362 **** free(hgcmd); pnset_free(pnsetp); return (NULL); } ! static void ! chdir_git(const char *path) { FILE *fp = NULL; char *gitcmd = NULL; char *newline; - char fn[MAXPATHLEN]; pnset_t *pnsetp; pnsetp = calloc(sizeof (pnset_t), 1); ! if ((pnsetp == NULL) || ! (asprintf(&gitcmd, "git ls-files %s", path) == -1)) goto fail; ! if ((fp = popen(gitcmd, "r")) == NULL) goto fail; ! while (fgets(fn, sizeof (fn), fp) != NULL) { ! if ((newline = strrchr(fn, '\n')) != NULL) *newline = '\0'; ! if (pnset_add(pnsetp, fn) == 0) goto fail; } (void) pclose(fp); free(gitcmd); ! gitmanifest = pnsetp; ! return; fail: ! warn("cannot load git manifest"); if (fp != NULL) (void) pclose(fp); - if (pnsetp != NULL) - free(pnsetp); - if (gitcmd != NULL) free(gitcmd); } /* * If necessary, change our active manifest to be appropriate for `path'. */ static void ! chdir_hg(const char *path) { ! char hgpath[MAXPATHLEN]; char basepath[MAXPATHLEN]; char *slash; ! (void) snprintf(hgpath, MAXPATHLEN, "%s/.hg", path); /* * Change our active manifest if any one of the following is true: * ! * 1. No manifest is loaded. Find the nearest hgroot to load from. * * 2. A manifest is loaded, but we've moved into a directory with ! * its own hgroot (e.g., usr/closed). Load from its hgroot. * * 3. A manifest is loaded, but no longer applies (e.g., the manifest * under usr/closed is loaded, but we've moved to usr/src). */ ! if (hgdata.manifest == NULL || ! strcmp(hgpath, hgdata.hgpath) != 0 && access(hgpath, X_OK) == 0 || ! strncmp(path, hgdata.root, hgdata.rootlen - 1) != 0) { ! pnset_free(hgdata.manifest); ! hgdata.manifest = NULL; (void) strlcpy(basepath, path, MAXPATHLEN); /* ! * Walk up the directory tree looking for .hg subdirectories. */ ! while (access(hgpath, X_OK) == -1) { slash = strrchr(basepath, '/'); if (slash == NULL) { ! if (!hgdata.rootwarn) { ! warn("no hg root for \"%s\"\n", path); ! hgdata.rootwarn = B_TRUE; } return; } *slash = '\0'; ! (void) snprintf(hgpath, MAXPATHLEN, "%s/.hg", basepath); } /* ! * We found a directory with an .hg subdirectory; record it ! * and load its manifest. */ ! (void) strlcpy(hgdata.hgpath, hgpath, MAXPATHLEN); ! (void) strlcpy(hgdata.root, basepath, MAXPATHLEN); ! hgdata.manifest = load_manifest(hgdata.root); /* ! * The logic in check_hg() depends on hgdata.root having a ! * single trailing slash, so only add it if it's missing. */ ! if (hgdata.root[strlen(hgdata.root) - 1] != '/') ! (void) strlcat(hgdata.root, "/", MAXPATHLEN); ! hgdata.rootlen = strlen(hgdata.root); } } /* ! * Check if a file is under Mercurial control by checking against the manifest. */ /* ARGSUSED */ static int ! check_hg(const char *path, const struct FTW *ftwp) { /* * The manifest paths are relative to the manifest root; skip past it. */ ! path += hgdata.rootlen; ! return (hgdata.manifest != NULL && pnset_check(hgdata.manifest, path)); ! } ! /* ARGSUSED */ ! static int ! check_git(const char *path, const struct FTW *ftwp) ! { ! path += 2; /* Skip "./" */ ! return (gitmanifest != NULL && pnset_check(gitmanifest, path)); } /* * Check if a file is under TeamWare control by checking for its corresponding * SCCS "s-dot" file. --- 228,376 ---- free(hgcmd); pnset_free(pnsetp); return (NULL); } ! /* ! * Load and return a pnset for the manifest for the Git repo at `gitroot'. ! */ ! static pnset_t * ! git_manifest(const char *gitroot) { FILE *fp = NULL; char *gitcmd = NULL; char *newline; pnset_t *pnsetp; + char path[MAXPATHLEN]; pnsetp = calloc(sizeof (pnset_t), 1); ! if (pnsetp == NULL || ! asprintf(&gitcmd, "git --git-dir=%s/.git ls-files", gitroot) == -1) goto fail; ! fp = popen(gitcmd, "r"); ! if (fp == NULL) goto fail; ! while (fgets(path, sizeof (path), fp) != NULL) { ! newline = strrchr(path, '\n'); ! if (newline != NULL) *newline = '\0'; ! if (pnset_add(pnsetp, path) == 0) goto fail; } (void) pclose(fp); free(gitcmd); ! return (pnsetp); fail: ! warn("cannot load git manifest at %s", gitroot); if (fp != NULL) (void) pclose(fp); free(gitcmd); + pnset_free(pnsetp); + return (NULL); } /* * If necessary, change our active manifest to be appropriate for `path'. */ static void ! chdir_scmdata(const char *path, const char *meta, ! pnset_t *(*manifest_func)(const char *path)) { ! char scmpath[MAXPATHLEN]; char basepath[MAXPATHLEN]; char *slash; ! (void) snprintf(scmpath, MAXPATHLEN, "%s/%s", path, meta); /* * Change our active manifest if any one of the following is true: * ! * 1. No manifest is loaded. Find the nearest SCM root to load from. * * 2. A manifest is loaded, but we've moved into a directory with ! * its own metadata directory (e.g., usr/closed). Load from its ! * root. * * 3. A manifest is loaded, but no longer applies (e.g., the manifest * under usr/closed is loaded, but we've moved to usr/src). */ ! if (scmdata.manifest == NULL || ! (strcmp(scmpath, scmdata.metapath) != 0 && ! access(scmpath, X_OK) == 0) || ! strncmp(path, scmdata.root, scmdata.rootlen - 1) != 0) { ! pnset_free(scmdata.manifest); ! scmdata.manifest = NULL; (void) strlcpy(basepath, path, MAXPATHLEN); /* ! * Walk up the directory tree looking for metadata ! * subdirectories. */ ! while (access(scmpath, X_OK) == -1) { slash = strrchr(basepath, '/'); if (slash == NULL) { ! if (!scmdata.rootwarn) { ! warn("no metadata directory " ! "for \"%s\"\n", path); ! scmdata.rootwarn = B_TRUE; } return; } *slash = '\0'; ! (void) snprintf(scmpath, MAXPATHLEN, "%s/%s", basepath, ! meta); } /* ! * We found a directory with an SCM metadata directory; record ! * it and load its manifest. */ ! (void) strlcpy(scmdata.metapath, scmpath, MAXPATHLEN); ! (void) strlcpy(scmdata.root, basepath, MAXPATHLEN); ! scmdata.manifest = manifest_func(scmdata.root); /* ! * The logic in check_scmdata() depends on scmdata.root having ! * a single trailing slash, so only add it if it's missing. */ ! if (scmdata.root[strlen(scmdata.root) - 1] != '/') ! (void) strlcat(scmdata.root, "/", MAXPATHLEN); ! scmdata.rootlen = strlen(scmdata.root); } } + static void + chdir_git(const char *path) + { + chdir_scmdata(path, ".git", git_manifest); + } + /* ! * If necessary, change our active manifest to be appropriate for `path'. */ + static void + chdir_hg(const char *path) + { + chdir_scmdata(path, ".hg", hg_manifest); + } + /* ARGSUSED */ static int ! check_scmdata(const char *path, const struct FTW *ftwp) { /* * The manifest paths are relative to the manifest root; skip past it. */ ! path += scmdata.rootlen; ! return (scmdata.manifest != NULL && pnset_check(scmdata.manifest, ! path)); } /* * Check if a file is under TeamWare control by checking for its corresponding * SCCS "s-dot" file.