git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
To: git@vger.kernel.org
Cc: "Junio C Hamano" <gitster@pobox.com>,
	"Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
Subject: [PATCH v6 27/32] prune: strategies for linked checkouts
Date: Wed,  9 Jul 2014 14:33:12 +0700	[thread overview]
Message-ID: <1404891197-18067-28-git-send-email-pclouds@gmail.com> (raw)
In-Reply-To: <1404891197-18067-1-git-send-email-pclouds@gmail.com>

(alias R=$GIT_COMMON_DIR/repos/<id>)

 - linked checkouts are supposed to keep its location in $R/gitdir up
   to date. The use case is auto fixup after a manual checkout move.

 - linked checkouts are supposed to update mtime of $R/gitdir. If
   $R/gitdir's mtime is older than a limit, and it points to nowhere,
   repos/<id> is to be pruned.

 - If $R/locked exists, repos/<id> is not supposed to be pruned. If
   $R/locked exists and $R/gitdir's mtime is older than a really long
   limit, warn about old unused repo.

 - "git checkout --to" is supposed to make a hard link named $R/link
   pointing to the .git file on supported file systems to help detect
   the user manually deleting the checkout. If $R/link exists and its
   link count is greated than 1, the repo is kept.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 Documentation/git-prune.txt            |  3 ++
 Documentation/gitrepository-layout.txt | 19 +++++++++
 builtin/checkout.c                     | 14 +++++++
 builtin/prune.c                        | 74 ++++++++++++++++++++++++++++++++++
 setup.c                                | 13 ++++++
 5 files changed, 123 insertions(+)

diff --git a/Documentation/git-prune.txt b/Documentation/git-prune.txt
index 7a493c8..50e39ec 100644
--- a/Documentation/git-prune.txt
+++ b/Documentation/git-prune.txt
@@ -48,6 +48,9 @@ OPTIONS
 --expire <time>::
 	Only expire loose objects older than <time>.
 
+--repos::
+	Prune directories in $GIT_DIR/repos.
+
 <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 543d874..bed4f1a 100644
--- a/Documentation/gitrepository-layout.txt
+++ b/Documentation/gitrepository-layout.txt
@@ -256,6 +256,25 @@ repos::
 	$GIT_COMMON_DIR is set and "$GIT_COMMON_DIR/repos" will be
 	used instead.
 
+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 069e803..98a2f5f 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -898,12 +898,22 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,
 	junk_git_dir = sb_repo.buf;
 	is_junk = 1;
 
+	/*
+	 * lock the incomplete repo so prune won't delete it, unlock
+	 * after the preparation is over.
+	 */
+	strbuf_addf(&sb, "%s/locked", sb_repo.buf);
+	write_file(sb.buf, 1, "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_file(sb.buf, 1, "%s\n", real_path(sb_git.buf));
 	write_file(sb_git.buf, 1, "gitdir: %s/repos/%s\n",
 		   real_path(get_git_common_dir()), name);
 	/*
@@ -912,6 +922,7 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,
 	 * value would do because this value will be ignored and
 	 * replaced at the next (real) checkout.
 	 */
+	strbuf_reset(&sb);
 	strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
 	write_file(sb.buf, 1, "%s\n", sha1_to_hex(new->commit->object.sha1));
 	strbuf_reset(&sb);
@@ -930,6 +941,9 @@ static int prepare_linked_checkout(const struct checkout_opts *opts,
 	ret = run_command(&cp);
 	if (!ret)
 		is_junk = 0;
+	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 144a3bd..6db6bcc 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -112,6 +112,70 @@ 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/gitdir", id), st)) {
+		st->st_mtime = expire;
+		return _("gitdir does not exist");
+	}
+	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)) {
+		struct stat st_link;
+		free(path);
+		/*
+		 * the repo is moved manually and has not been
+		 * accessed since?
+		 */
+		if (!stat(git_path("repos/%s/link", id), &st_link) &&
+		    st_link.st_nlink > 1)
+			return NULL;
+		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;
+	struct stat st;
+	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, &st)) != NULL &&
+		    st.st_mtime <= expire) {
+			struct strbuf sb = STRBUF_INIT;
+			if (show_only || verbose)
+				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
@@ -138,10 +202,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()
@@ -154,6 +220,14 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
 	init_revisions(&revs, prefix);
 
 	argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
+
+	if (prune_repos) {
+		if (argc)
+			die(_("--repos does not take extra arguments"));
+		prune_repos_dir();
+		return 0;
+	}
+
 	while (argc--) {
 		unsigned char sha1[20];
 		const char *name = *argv++;
diff --git a/setup.c b/setup.c
index 8f90bc3..da2d669 100644
--- a/setup.c
+++ b/setup.c
@@ -390,6 +390,17 @@ static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
 	return ret;
 }
 
+static void update_linked_gitdir(const char *gitfile, const char *gitdir)
+{
+	struct strbuf path = STRBUF_INIT;
+	struct stat st;
+
+	strbuf_addf(&path, "%s/gitfile", gitdir);
+	if (stat(path.buf, &st) || st.st_mtime + 24 * 3600 < time(NULL))
+		write_file(path.buf, 0, "%s\n", gitfile);
+	strbuf_release(&path);
+}
+
 /*
  * Try to read the location of the git directory from the .git file,
  * return path to git directory if found.
@@ -438,6 +449,8 @@ const char *read_gitfile(const char *path)
 
 	if (!is_git_directory(dir))
 		die("Not a git repository: %s", dir);
+
+	update_linked_gitdir(path, dir);
 	path = real_path(dir);
 
 	free(buf);
-- 
1.9.1.346.ga2b5940

  parent reply	other threads:[~2014-07-09  7:35 UTC|newest]

Thread overview: 83+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-07-09  7:32 [PATCH v6 00/32] Support multiple checkouts Nguyễn Thái Ngọc Duy
2014-07-09  7:32 ` [PATCH v6 01/32] path.c: make get_pathname() return strbuf instead of static buffer Nguyễn Thái Ngọc Duy
2014-07-09  7:32 ` [PATCH v6 02/32] path.c: make get_pathname() call sites return const char * Nguyễn Thái Ngọc Duy
2014-07-09  7:32 ` [PATCH v6 03/32] git_snpath(): retire and replace with strbuf_git_path() Nguyễn Thái Ngọc Duy
2014-07-09  7:32 ` [PATCH v6 04/32] path.c: rename vsnpath() to do_git_path() Nguyễn Thái Ngọc Duy
2014-07-09  7:32 ` [PATCH v6 05/32] path.c: group git_path(), git_pathdup() and strbuf_git_path() together Nguyễn Thái Ngọc Duy
2014-07-09  7:32 ` [PATCH v6 06/32] setup_git_env: use git_pathdup instead of xmalloc + sprintf Nguyễn Thái Ngọc Duy
2014-07-09  7:32 ` [PATCH v6 07/32] setup_git_env(): introduce git_path_from_env() helper Nguyễn Thái Ngọc Duy
2014-07-09  7:32 ` [PATCH v6 08/32] git_path(): be aware of file relocation in $GIT_DIR Nguyễn Thái Ngọc Duy
2014-07-09  7:32 ` [PATCH v6 09/32] *.sh: respect $GIT_INDEX_FILE Nguyễn Thái Ngọc Duy
2014-07-09  7:32 ` [PATCH v6 10/32] reflog: avoid constructing .lock path with git_path Nguyễn Thái Ngọc Duy
2014-07-09  7:32 ` [PATCH v6 11/32] fast-import: use git_path() for accessing .git dir instead of get_git_dir() Nguyễn Thái Ngọc Duy
2014-07-09  7:32 ` [PATCH v6 12/32] commit: use SEQ_DIR instead of hardcoding "sequencer" Nguyễn Thái Ngọc Duy
2014-07-09  7:32 ` [PATCH v6 13/32] $GIT_COMMON_DIR: a new environment variable Nguyễn Thái Ngọc Duy
2014-07-09  7:32 ` [PATCH v6 14/32] git-sh-setup.sh: use rev-parse --git-path to get $GIT_DIR/objects Nguyễn Thái Ngọc Duy
2014-07-09  7:33 ` [PATCH v6 15/32] *.sh: avoid hardcoding $GIT_DIR/hooks/ Nguyễn Thái Ngọc Duy
2014-07-09  7:33 ` [PATCH v6 16/32] git-stash: avoid hardcoding $GIT_DIR/logs/ Nguyễn Thái Ngọc Duy
2014-07-09  7:33 ` [PATCH v6 17/32] setup.c: convert is_git_directory() to use strbuf Nguyễn Thái Ngọc Duy
2014-07-09  7:33 ` [PATCH v6 18/32] setup.c: detect $GIT_COMMON_DIR in is_git_directory() Nguyễn Thái Ngọc Duy
2014-07-09  7:33 ` [PATCH v6 19/32] setup.c: convert check_repository_format_gently to use strbuf Nguyễn Thái Ngọc Duy
2014-07-09  7:33 ` [PATCH v6 20/32] setup.c: detect $GIT_COMMON_DIR check_repository_format_gently() Nguyễn Thái Ngọc Duy
2014-07-09  7:33 ` [PATCH v6 21/32] setup.c: support multi-checkout repo setup Nguyễn Thái Ngọc Duy
2014-07-09  7:33 ` [PATCH v6 22/32] wrapper.c: wrapper to open a file, fprintf then close Nguyễn Thái Ngọc Duy
2014-07-09  7:33 ` [PATCH v6 23/32] use new wrapper write_file() for simple file writing Nguyễn Thái Ngọc Duy
2014-07-09  7:33 ` [PATCH v6 24/32] checkout: support checking out into a new working directory Nguyễn Thái Ngọc Duy
2014-07-09  7:33 ` [PATCH v6 25/32] checkout: clean up half-prepared directories in --to mode Nguyễn Thái Ngọc Duy
2014-07-09  7:33 ` [PATCH v6 26/32] checkout: detach if the branch is already checked out elsewhere Nguyễn Thái Ngọc Duy
2014-07-12 12:21   ` Max Kirillov
2014-07-09  7:33 ` Nguyễn Thái Ngọc Duy [this message]
2014-07-09 11:24   ` [PATCH v6 27/32] prune: strategies for linked checkouts Eric Sunshine
2014-07-09  7:33 ` [PATCH v6 28/32] gc: style change -- no SP before closing bracket Nguyễn Thái Ngọc Duy
2014-07-09  9:47   ` Eric Sunshine
2014-07-14  4:40     ` Junio C Hamano
2014-07-09  7:33 ` [PATCH v6 29/32] gc: support prune --repos Nguyễn Thái Ngọc Duy
2014-07-09 10:05   ` Eric Sunshine
2014-07-09  7:33 ` [PATCH v6 30/32] count-objects: report unused files in $GIT_DIR/repos/ Nguyễn Thái Ngọc Duy
2014-07-09  7:33 ` [PATCH v6 31/32] git_path(): keep "info/sparse-checkout" per work-tree Nguyễn Thái Ngọc Duy
2014-07-09  7:33 ` [PATCH v6 32/32] checkout: don't require a work tree when checking out into a new one Nguyễn Thái Ngọc Duy
2014-07-11  7:13 ` [PATCH v6 00/32] Support multiple checkouts Dennis Kaarsemaker
2014-07-13  4:50 ` [PATCH v7 00/31] " Nguyễn Thái Ngọc Duy
2014-07-13  4:50   ` [PATCH v7 01/31] path.c: make get_pathname() return strbuf instead of static buffer Nguyễn Thái Ngọc Duy
2014-07-13  4:50   ` [PATCH v7 02/31] path.c: make get_pathname() call sites return const char * Nguyễn Thái Ngọc Duy
2014-07-13  4:50   ` [PATCH v7 03/31] git_snpath(): retire and replace with strbuf_git_path() Nguyễn Thái Ngọc Duy
2014-07-13  4:50   ` [PATCH v7 04/31] path.c: rename vsnpath() to do_git_path() Nguyễn Thái Ngọc Duy
2014-07-13  4:50   ` [PATCH v7 05/31] path.c: group git_path(), git_pathdup() and strbuf_git_path() together Nguyễn Thái Ngọc Duy
2014-07-13  4:50   ` [PATCH v7 06/31] git_path(): be aware of file relocation in $GIT_DIR Nguyễn Thái Ngọc Duy
2014-07-13  4:50   ` [PATCH v7 07/31] *.sh: respect $GIT_INDEX_FILE Nguyễn Thái Ngọc Duy
2014-07-13  4:50   ` [PATCH v7 08/31] reflog: avoid constructing .lock path with git_path Nguyễn Thái Ngọc Duy
2014-07-13  4:50   ` [PATCH v7 09/31] fast-import: use git_path() for accessing .git dir instead of get_git_dir() Nguyễn Thái Ngọc Duy
2014-07-13  4:50   ` [PATCH v7 10/31] commit: use SEQ_DIR instead of hardcoding "sequencer" Nguyễn Thái Ngọc Duy
2014-07-13  4:50   ` [PATCH v7 11/31] $GIT_COMMON_DIR: a new environment variable Nguyễn Thái Ngọc Duy
2014-07-23  5:21     ` Eric Sunshine
2014-07-13  4:50   ` [PATCH v7 12/31] git-sh-setup.sh: use rev-parse --git-path to get $GIT_DIR/objects Nguyễn Thái Ngọc Duy
2014-07-13  4:50   ` [PATCH v7 13/31] *.sh: avoid hardcoding $GIT_DIR/hooks/ Nguyễn Thái Ngọc Duy
2014-07-13  4:50   ` [PATCH v7 14/31] git-stash: avoid hardcoding $GIT_DIR/logs/ Nguyễn Thái Ngọc Duy
2014-07-13  4:50   ` [PATCH v7 15/31] setup.c: convert is_git_directory() to use strbuf Nguyễn Thái Ngọc Duy
2014-07-13  4:50   ` [PATCH v7 16/31] setup.c: detect $GIT_COMMON_DIR in is_git_directory() Nguyễn Thái Ngọc Duy
2014-07-13  4:50   ` [PATCH v7 17/31] setup.c: convert check_repository_format_gently to use strbuf Nguyễn Thái Ngọc Duy
2014-07-13  4:50   ` [PATCH v7 18/31] setup.c: detect $GIT_COMMON_DIR check_repository_format_gently() Nguyễn Thái Ngọc Duy
2014-07-13  4:50   ` [PATCH v7 19/31] setup.c: support multi-checkout repo setup Nguyễn Thái Ngọc Duy
2014-07-13  4:50   ` [PATCH v7 20/31] wrapper.c: wrapper to open a file, fprintf then close Nguyễn Thái Ngọc Duy
2014-07-13  4:50   ` [PATCH v7 21/31] use new wrapper write_file() for simple file writing Nguyễn Thái Ngọc Duy
2014-07-13  4:50   ` [PATCH v7 22/31] checkout: support checking out into a new working directory Nguyễn Thái Ngọc Duy
2014-07-17  4:19     ` Max Kirillov
2014-07-17  6:37       ` Junio C Hamano
2014-07-18  4:10     ` Eric Sunshine
2014-07-13  4:51   ` [PATCH v7 23/31] checkout: clean up half-prepared directories in --to mode Nguyễn Thái Ngọc Duy
2014-07-20 23:55     ` Eric Sunshine
2014-07-21  3:34       ` Eric Sunshine
2014-07-23  8:02       ` Duy Nguyen
2014-07-13  4:51   ` [PATCH v7 24/31] checkout: detach if the branch is already checked out elsewhere Nguyễn Thái Ngọc Duy
2014-07-13  4:51   ` [PATCH v7 25/31] prune: strategies for linked checkouts Nguyễn Thái Ngọc Duy
2014-07-18 18:17     ` Thomas Rast
2014-07-19 12:52       ` Duy Nguyen
2014-07-13  4:51   ` [PATCH v7 26/31] gc: style change -- no SP before closing bracket Nguyễn Thái Ngọc Duy
2014-07-13  4:51   ` [PATCH v7 27/31] gc: factor out gc.pruneexpire parsing code Nguyễn Thái Ngọc Duy
2014-07-13  4:51   ` [PATCH v7 28/31] gc: support prune --repos Nguyễn Thái Ngọc Duy
2014-07-13  4:51   ` [PATCH v7 29/31] count-objects: report unused files in $GIT_DIR/repos/ Nguyễn Thái Ngọc Duy
2014-07-13  4:51   ` [PATCH v7 30/31] git_path(): keep "info/sparse-checkout" per work-tree Nguyễn Thái Ngọc Duy
2014-07-13  4:51   ` [PATCH v7 31/31] checkout: don't require a work tree when checking out into a new one Nguyễn Thái Ngọc Duy
2014-07-14  4:45   ` [PATCH v7 00/31] Support multiple checkouts Junio C Hamano
2014-07-14 11:06     ` Duy Nguyen
2014-07-14 17:05       ` Junio C Hamano

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=1404891197-18067-28-git-send-email-pclouds@gmail.com \
    --to=pclouds@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.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).