From: "Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
To: git@vger.kernel.org
Cc: "Jonathan Niedier" <jrnieder@gmail.com>,
"Junio C Hamano" <gitster@pobox.com>,
"Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
Subject: [PATCH v2 17/21] prune: strategies for split repositories
Date: Sat, 14 Dec 2013 17:55:03 +0700 [thread overview]
Message-ID: <1387018507-21999-18-git-send-email-pclouds@gmail.com> (raw)
In-Reply-To: <1387018507-21999-1-git-send-email-pclouds@gmail.com>
alias REPO=$GIT_SUPER_DIR/repos/<id>
- split repos are supposed to update mtime of $REPO/gitdir
- split repos are supposed to keep its location in $REPO/gitdir up to
date
- "git checkout --to" is supposed to create $REPO/locked if the new
repo is on a different partition than the shared one.
- "git checkout --to" is supposed to make hardlink named $REPO/link
pointing to the .git file
The pruning rules are:
- if $REPO/locked exists, repos/<id> is not supposed to be pruned.
- if $REPO/locked exists and $REPO/gitdir's mtimer is older than a
really long limit, warn about old unused repo.
- if $REPO/link exists and its link count is 1, repos/<id> is to be
pruned.
- if $REPO/gitdir's mtime is older than a limit, and it points to
nowhere, repos/<id> is to be pruned. Warn about it some time before
the prune time.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
Documentation/git-prune.txt | 4 +++
Documentation/gitrepository-layout.txt | 19 ++++++++++
builtin/checkout.c | 36 ++++++++++++++++++-
builtin/prune.c | 65 ++++++++++++++++++++++++++++++++++
setup.c | 13 +++++++
5 files changed, 136 insertions(+), 1 deletion(-)
diff --git a/Documentation/git-prune.txt b/Documentation/git-prune.txt
index bf82410..55a9341 100644
--- a/Documentation/git-prune.txt
+++ b/Documentation/git-prune.txt
@@ -46,6 +46,10 @@ OPTIONS
--expire <time>::
Only expire loose objects older than <time>.
+--repos::
+ Prune directories in $GIT_DIR/repos other than `<time>` given
+ by `--expire` (or default 6 months)
+
<head>...::
In addition to objects
reachable from any of our references, keep objects
diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt
index 3c6149e..a8a0426 100644
--- a/Documentation/gitrepository-layout.txt
+++ b/Documentation/gitrepository-layout.txt
@@ -225,6 +225,25 @@ repos/<id>::
repository in question (e.g. HEAD or index). Such .git files
are created by `git checkout --to`.
+repos/<id>/gitdir::
+ A text file containing the absolute path back to the .git file
+ that points to here. This is used to check if the linked
+ repository has been manually removed and there is no need to
+ keep this directory any more. mtime of this file should be
+ updated every time the linked repository is accessed.
+
+repos/<id>/locked::
+ If this file exists, the linked repository may be on a
+ portable device and not available. It does not mean that the
+ linked repository is gone and `repos/<id>` could be
+ removed. The file's content contains a reason string on why
+ the repository is locked.
+
+repos/<id>/link::
+ If this file exists, it is a hard link to the linked .git
+ file. It is used to detect if the linked repository is
+ manually removed.
+
SEE ALSO
--------
linkgit:git-init[1],
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 6353557..aece20d 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -856,6 +856,17 @@ static void remove_junk_on_signal(int signo)
raise(signo);
}
+static dev_t get_device_or_die(const char *path)
+{
+ struct stat buf;
+ if (stat(path, &buf))
+ die_errno("failed to stat '%s'", path);
+ /* Ah Windows! Make different drives different "partitions" */
+ if (buf.st_dev == 0 && has_dos_drive_prefix("c:\\"))
+ buf.st_dev = toupper(real_path(path)[0]);
+ return buf.st_dev;
+}
+
static int checkout_new_worktree(const struct checkout_opts *opts,
struct branch_info *new)
{
@@ -865,7 +876,7 @@ static int checkout_new_worktree(const struct checkout_opts *opts,
struct stat st;
const char *name;
struct child_process cp;
- int counter = 0, len, ret;
+ int counter = 0, len, keep_locked = 0, ret;
if (!new->commit)
die(_("no branch specified"));
@@ -900,17 +911,35 @@ static int checkout_new_worktree(const struct checkout_opts *opts,
junk_git_dir = sb_repo.buf;
is_junk = 1;
+ strbuf_addf(&sb, "%s/locked", sb_repo.buf);
+ write_to_file(sb.buf, "initializing\n");
+
strbuf_addf(&sb_git, "%s/.git", path);
if (safe_create_leading_directories_const(sb_git.buf))
die_errno(_("could not create leading directories of '%s'"),
sb_git.buf);
junk_work_tree = path;
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "%s/gitdir", sb_repo.buf);
+ write_to_file(sb.buf, "%s\n", real_path(sb_git.buf));
write_to_file(sb_git.buf, "gitsuper: %s\ngitdir: %s\n",
real_path(get_git_dir()), name);
+ strbuf_reset(&sb);
strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
write_to_file(sb.buf, "%s\n", sha1_to_hex(new->commit->object.sha1));
+ if (get_device_or_die(path) != get_device_or_die(get_git_dir())) {
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "%s/locked", sb_repo.buf);
+ write_to_file(sb.buf, "located on a different file system\n");
+ keep_locked = 1;
+ } else {
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "%s/link", sb_repo.buf);
+ link(sb_git.buf, sb.buf); /* it's ok to fail */
+ }
+
if (!opts->quiet)
fprintf_ln(stderr, _("Enter %s (identifier %s)"), path, name);
@@ -928,6 +957,11 @@ static int checkout_new_worktree(const struct checkout_opts *opts,
ret = run_command(&cp);
if (!ret)
is_junk = 0;
+ if (!keep_locked) {
+ strbuf_reset(&sb);
+ strbuf_addf(&sb, "%s/locked", sb_repo.buf);
+ unlink_or_warn(sb.buf);
+ }
strbuf_release(&sb);
strbuf_release(&sb_repo);
strbuf_release(&sb_git);
diff --git a/builtin/prune.c b/builtin/prune.c
index 6366917..16eb99c 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -102,6 +102,62 @@ static void prune_object_dir(const char *path)
}
}
+static const char *prune_repo_dir(const char *id)
+{
+ struct stat st;
+ char *path;
+ int fd, len;
+ if (file_exists(git_path("repos/%s/locked", id)))
+ return NULL;
+ if (!stat(git_path("repos/%s/link", id), &st) && st.st_nlink == 1)
+ return _("hard link count is 1");
+ if (stat(git_path("repos/%s/gitdir", id), &st))
+ return "gitdir does not exist";
+ if (st.st_mtime > expire)
+ return NULL;
+ fd = open(git_path("repos/%s/gitdir", id), O_RDONLY);
+ len = st.st_size;
+ path = xmalloc(len + 1);
+ read_in_full(fd, path, len);
+ close(fd);
+ while (path[len - 1] == '\n' || path[len - 1] == '\r')
+ len--;
+ path[len] = '\0';
+ if (!file_exists(path)) {
+ free(path);
+ return "gitdir points to non-existing file";
+ }
+ free(path);
+ return NULL;
+}
+
+static void prune_repos_dir(void)
+{
+ const char *reason;
+ DIR *dir = opendir(git_path("repos"));
+ struct dirent *d;
+ int removed = 0;
+ if (!dir)
+ return;
+ while ((d = readdir(dir)) != NULL) {
+ if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+ continue;
+ if ((reason = prune_repo_dir(d->d_name)) != NULL) {
+ struct strbuf sb = STRBUF_INIT;
+ printf(_("Removing repos/%s: %s\n"), d->d_name, reason);
+ if (show_only)
+ continue;
+ strbuf_addstr(&sb, git_path("repos/%s", d->d_name));
+ remove_dir_recursively(&sb, 0);
+ strbuf_release(&sb);
+ removed = 1;
+ }
+ }
+ closedir(dir);
+ if (removed)
+ rmdir(git_path("repos"));
+}
+
/*
* Write errors (particularly out of space) can result in
* failed temporary packs (and more rarely indexes and other
@@ -128,10 +184,12 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
struct progress *progress = NULL;
+ int prune_repos = 0;
const struct option options[] = {
OPT__DRY_RUN(&show_only, N_("do not remove, show only")),
OPT__VERBOSE(&verbose, N_("report pruned objects")),
OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
+ OPT_BOOL(0, "repos", &prune_repos, N_("prune .git/repos/")),
OPT_EXPIRY_DATE(0, "expire", &expire,
N_("expire objects older than <time>")),
OPT_END()
@@ -156,6 +214,13 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
die("unrecognized argument: %s", name);
}
+ if (prune_repos) {
+ if (expire == ULONG_MAX)
+ expire = time(NULL) - 6 * 30 * 24 * 3600; /* 6 months */
+ prune_repos_dir();
+ return 0;
+ }
+
if (show_progress == -1)
show_progress = isatty(2);
if (show_progress)
diff --git a/setup.c b/setup.c
index ac7d90c..95a3f40 100644
--- a/setup.c
+++ b/setup.c
@@ -765,6 +765,17 @@ static const char *setup_git_directory_gently_1(int *nongit_ok,
}
}
+static int update_super_gitdir(char *path)
+{
+ FILE *fp = fopen(git_path("gitdir"), "w");
+ if (!fp)
+ return error(_("unable to update %s: %s"),
+ git_path("gitdir"), strerror(errno));
+ fprintf(stderr, "%s\n", path);
+ fclose(fp);
+ return 0;
+}
+
const char *setup_git_directory_gently(int *nongit_ok)
{
const char *prefix;
@@ -780,6 +791,8 @@ const char *setup_git_directory_gently(int *nongit_ok)
startup_info->have_repository = !nongit_ok || !*nongit_ok;
startup_info->prefix = prefix;
}
+ if (get_git_super_dir() && gitfile)
+ update_super_gitdir(gitfile);
free(gitfile);
return prefix;
}
--
1.8.5.1.77.g42c48fa
next prev parent reply other threads:[~2013-12-14 10:53 UTC|newest]
Thread overview: 49+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-12-11 14:15 [PATCH/POC 0/7] Support multiple worktrees Nguyễn Thái Ngọc Duy
2013-12-11 14:15 ` [PATCH/POC 1/7] Make git_path() beware of file relocation in $GIT_DIR Nguyễn Thái Ngọc Duy
2013-12-13 16:30 ` Junio C Hamano
2013-12-11 14:15 ` [PATCH/POC 2/7] Add new environment variable $GIT_SUPER_DIR Nguyễn Thái Ngọc Duy
2013-12-13 18:12 ` Junio C Hamano
2013-12-14 1:11 ` Duy Nguyen
2013-12-14 19:43 ` Junio C Hamano
2013-12-11 14:15 ` [PATCH/POC 3/7] setup.c: add split-repo support to .git files Nguyễn Thái Ngọc Duy
2013-12-13 18:30 ` Junio C Hamano
2013-12-13 20:43 ` Jonathan Nieder
2013-12-14 1:28 ` Duy Nguyen
2013-12-23 3:38 ` Duy Nguyen
2013-12-11 14:15 ` [PATCH/POC 4/7] setup.c: add split-repo support to is_git_directory() Nguyễn Thái Ngọc Duy
2013-12-11 14:15 ` [PATCH/POC 5/7] setup.c: reduce cleanup sites in setup_explicit_git_dir() Nguyễn Thái Ngọc Duy
2013-12-11 14:15 ` [PATCH/POC 6/7] setup.c: add split-repo support to setup_git_directory* Nguyễn Thái Ngọc Duy
2013-12-11 14:15 ` [PATCH/POC 7/7] init: add --split-repo with the same functionality as git-new-workdir Nguyễn Thái Ngọc Duy
2013-12-14 10:54 ` [PATCH v2 00/21] Support multiple worktrees Nguyễn Thái Ngọc Duy
2013-12-14 10:54 ` [PATCH v2 01/21] path.c: avoid PATH_MAX as buffer size from get_pathname() Nguyễn Thái Ngọc Duy
2013-12-15 8:35 ` Torsten Bögershausen
2013-12-15 9:02 ` Duy Nguyen
2013-12-15 9:33 ` Antoine Pelisse
2013-12-14 10:54 ` [PATCH v2 02/21] path.c: rename vsnpath() to git_vsnpath() Nguyễn Thái Ngọc Duy
2013-12-14 20:23 ` Ramsay Jones
2013-12-15 2:25 ` Duy Nguyen
2013-12-15 21:13 ` Ramsay Jones
2013-12-16 7:21 ` Duy Nguyen
2013-12-16 17:11 ` Jonathan Nieder
2013-12-16 20:16 ` Junio C Hamano
2013-12-16 22:59 ` Ramsay Jones
2013-12-14 10:54 ` [PATCH v2 03/21] path.c: move git_path() closer to similar functions git_pathdup() Nguyễn Thái Ngọc Duy
2013-12-14 10:54 ` [PATCH v2 04/21] Make git_path() aware of file relocation in $GIT_DIR Nguyễn Thái Ngọc Duy
2013-12-14 10:54 ` [PATCH v2 05/21] reflog: use avoid constructing .lock path with git_path Nguyễn Thái Ngọc Duy
2013-12-14 10:54 ` [PATCH v2 06/21] fast-import: use git_path() for accessing .git dir instead of get_git_dir() Nguyễn Thái Ngọc Duy
2013-12-14 10:54 ` [PATCH v2 07/21] Add new environment variable $GIT_SUPER_DIR Nguyễn Thái Ngọc Duy
2013-12-14 10:54 ` [PATCH v2 08/21] setup.c: refactor path manipulation out of read_gitfile() Nguyễn Thái Ngọc Duy
2013-12-14 10:54 ` [PATCH v2 09/21] setup.c: add split-repo support to .git files Nguyễn Thái Ngọc Duy
2013-12-14 10:54 ` [PATCH v2 10/21] setup.c: add split-repo support to is_git_directory() Nguyễn Thái Ngọc Duy
2013-12-14 10:54 ` [PATCH v2 11/21] setup.c: reduce cleanup sites in setup_explicit_git_dir() Nguyễn Thái Ngọc Duy
2013-12-14 10:54 ` [PATCH v2 12/21] environment.c: support super .git file specified by $GIT_DIR Nguyễn Thái Ngọc Duy
2013-12-14 10:54 ` [PATCH v2 13/21] setup: support $GIT_SUPER_DIR as well as super .git files Nguyễn Thái Ngọc Duy
2013-12-14 10:55 ` [PATCH v2 14/21] checkout: support checking out into a new working directory Nguyễn Thái Ngọc Duy
2013-12-14 10:55 ` [PATCH v2 15/21] checkout: clean up half-prepared directories in --to mode Nguyễn Thái Ngọc Duy
2013-12-14 10:55 ` [PATCH v2 16/21] setup.c: keep track of the .git file location if read Nguyễn Thái Ngọc Duy
2013-12-14 10:55 ` Nguyễn Thái Ngọc Duy [this message]
2013-12-14 10:55 ` [PATCH v2 18/21] refs: adjust reflog path for repos/<id>/HEAD Nguyễn Thái Ngọc Duy
2013-12-14 10:55 ` [PATCH v2 19/21] refs: detach split repos' HEAD when the linked ref is updated/deleted Nguyễn Thái Ngọc Duy
2013-12-14 10:55 ` [PATCH v2 20/21] refs.c: refactor do_head_ref(... to do_one_head_ref("HEAD", Nguyễn Thái Ngọc Duy
2013-12-14 10:55 ` [PATCH v2 21/21] revision: include repos/../HEAD in --all Nguyễn Thái Ngọc Duy
2013-12-15 2:29 ` [PATCH v2 00/21] Support multiple worktrees Duy Nguyen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1387018507-21999-18-git-send-email-pclouds@gmail.com \
--to=pclouds@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=jrnieder@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).