Commit e807860fa9b5278932a0ba25f84b4035b0ebda84

Russell Belfer 2013-06-27T16:52:38

Add timestamp check to submodule status This is probably not the final form of this change, but this is a preliminary version of checking a timestamp to see if the cached working directory HEAD OID matches the current. Right now, this uses the timestamp on the index and is, like most of our timestamp checking, subject to having only second accuracy.

diff --git a/src/submodule.c b/src/submodule.c
index dcd58d0..e6ed7e3 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -9,9 +9,7 @@
 #include "git2/config.h"
 #include "git2/sys/config.h"
 #include "git2/types.h"
-#include "git2/repository.h"
 #include "git2/index.h"
-#include "git2/submodule.h"
 #include "buffer.h"
 #include "buf_text.h"
 #include "vector.h"
@@ -544,6 +542,15 @@ const git_oid *git_submodule_wd_id(git_submodule *submodule)
 {
 	assert(submodule);
 
+	/* if we know the submodule index timestamp and it has moved, then
+	 * let's reload the working directory information for the submodule
+	 */
+	if (submodule->wd_head_path != NULL &&
+		git_futils_filestamp_check(
+			&submodule->wd_stamp, submodule->wd_head_path))
+		submodule->flags &= ~GIT_SUBMODULE_STATUS__WD_OID_VALID;
+
+	/* load unless we think we have a valid oid */
 	if (!(submodule->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID)) {
 		git_repository *subrepo;
 
@@ -702,11 +709,36 @@ int git_submodule_open(
 
 	/* if we have opened the submodule successfully, let's grab the HEAD OID */
 	if (!error) {
+		git_buf buf = GIT_BUF_INIT;
+
+		/* For now, let's just the index timestamp...
+		 *
+		 * git_buf_joinpath(&buf, git_repository_path(*subrepo), GIT_HEAD_FILE);
+		 * if (!git_path_exists(buf.ptr)) {
+		 */
+			git_index *index;
+			if (!git_repository_index__weakptr(&index, *subrepo))
+				git_buf_sets(&buf, git_index_path(index));
+			else
+				git_buf_free(&buf);
+		/* } */
+
+		if (git_buf_len(&buf) > 0) {
+			git__free(submodule->wd_head_path);
+			submodule->wd_head_path = git_buf_detach(&buf);
+		}
+
 		if (!git_reference_name_to_id(
-				&submodule->wd_oid, *subrepo, GIT_HEAD_FILE))
+				&submodule->wd_oid, *subrepo, GIT_HEAD_FILE)) {
+
 			submodule->flags |= GIT_SUBMODULE_STATUS__WD_OID_VALID;
-		else
-			giterr_clear();
+
+			if (submodule->wd_head_path)
+				git_futils_filestamp_check(
+					&submodule->wd_stamp, submodule->wd_head_path);
+		}
+
+		giterr_clear();
 	}
 
 	return error;
diff --git a/src/submodule.h b/src/submodule.h
index ba8e251..88d4f97 100644
--- a/src/submodule.h
+++ b/src/submodule.h
@@ -7,6 +7,10 @@
 #ifndef INCLUDE_submodule_h__
 #define INCLUDE_submodule_h__
 
+#include "git2/submodule.h"
+#include "git2/repository.h"
+#include "fileops.h"
+
 /* Notes:
  *
  * Submodule information can be in four places: the index, the config files
@@ -44,43 +48,53 @@
  * an entry for every submodule found in the HEAD and index, and for every
  * submodule described in .gitmodules.  The fields are as follows:
  *
- * - `owner` is the git_repository containing this submodule
  * - `name` is the name of the submodule from .gitmodules.
  * - `path` is the path to the submodule from the repo root.  It is almost
  *    always the same as `name`.
  * - `url` is the url for the submodule.
- * - `tree_oid` is the SHA1 for the submodule path in the repo HEAD.
- * - `index_oid` is the SHA1 for the submodule recorded in the index.
- * - `workdir_oid` is the SHA1 for the HEAD of the checked out submodule.
  * - `update` is a git_submodule_update_t value - see gitmodules(5) update.
+ * - `update_default` is the update value from the config
  * - `ignore` is a git_submodule_ignore_t value - see gitmodules(5) ignore.
+ * - `ignore_default` is the ignore value from the config
  * - `fetch_recurse` is 0 or 1 - see gitmodules(5) fetchRecurseSubmodules.
- * - `refcount` tracks how many hashmap entries there are for this submodule.
- *   It only comes into play if the name and path of the submodule differ.
- * - `flags` is for internal use, tracking where this submodule has been
- *   found (head, index, config, workdir) and other misc info about it.
+ *
+ * - `owner` is the git_repository containing this submodule
+ * - `flags` after for internal use, tracking where this submodule has been
+ *   found (head, index, config, workdir) and known status info, etc.
+ * - `head_oid` is the SHA1 for the submodule path in the repo HEAD.
+ * - `index_oid` is the SHA1 for the submodule recorded in the index.
+ * - `wd_oid` is the SHA1 for the HEAD of the checked out submodule.
+ * - `wd_index_path` is the path to the index of the checked out submodule
+ * - `wd_last_index` is a timestamp of that submodule index so we can
+ *   quickly check if the `wd_oid` should be rechecked
+ * - `refcount` tracks how many hash table entries in the
+ *   git_submodule_cache there are for this submodule.  It only comes into
+ *   play if the name and path of the submodule differ.
  *
  * If the submodule has been added to .gitmodules but not yet git added,
- * then the `index_oid` will be valid and zero.  If the submodule has been
- * deleted, but the delete has not been committed yet, then the `index_oid`
- * will be set, but the `url` will be NULL.
+ * then the `index_oid` will be zero but still marked valid.  If the
+ * submodule has been deleted, but the delete has not been committed yet,
+ * then the `index_oid` will be set, but the `url` will be NULL.
  */
 struct git_submodule {
-	git_repository *owner;
+	/* information from config */
 	char *name;
 	char *path; /* important: may point to same string data as "name" */
 	char *url;
-	uint32_t flags;
-	git_oid head_oid;
-	git_oid index_oid;
-	git_oid wd_oid;
-	/* information from config */
 	git_submodule_update_t update;
 	git_submodule_update_t update_default;
 	git_submodule_ignore_t ignore;
 	git_submodule_ignore_t ignore_default;
 	int fetch_recurse;
+
 	/* internal information */
+	git_repository *owner;
+	uint32_t flags;
+	git_oid head_oid;
+	git_oid index_oid;
+	git_oid wd_oid;
+	char *wd_head_path;
+	git_futils_filestamp wd_stamp;
 	int refcount;
 };
 
diff --git a/tests-clar/diff/submodules.c b/tests-clar/diff/submodules.c
index 9b77897..c94fd57 100644
--- a/tests-clar/diff/submodules.c
+++ b/tests-clar/diff/submodules.c
@@ -333,29 +333,34 @@ void test_diff_submodules__invalid_cache(void)
 	check_diff_patches(diff, expected_unchanged);
 	git_diff_list_free(diff);
 
+	sleep(2);
+
 	/* commit changed index of submodule */
 	{
 		git_object *parent;
 		git_oid tree_id, commit_id;
 		git_tree *tree;
 		git_signature *sig;
+		git_reference *ref;
 
-		cl_git_pass(git_revparse_single(&parent, smrepo, "HEAD"));
+		cl_git_pass(git_revparse_ext(&parent, &ref, smrepo, "HEAD"));
 		cl_git_pass(git_index_write_tree(&tree_id, smindex));
 		cl_git_pass(git_index_write(smindex));
 		cl_git_pass(git_tree_lookup(&tree, smrepo, &tree_id));
 		cl_git_pass(git_signature_new(&sig, "Sm Test", "sm@tester.test", 1372350000, 480));
 		cl_git_pass(git_commit_create_v(
-			&commit_id, smrepo, "HEAD", sig, sig, NULL,
-			"Move it", tree, 1, parent));
+			&commit_id, smrepo, git_reference_name(ref), sig, sig,
+			NULL, "Move it", tree, 1, parent));
 		git_object_free(parent);
 		git_tree_free(tree);
+		git_reference_free(ref);
 		git_signature_free(sig);
 	}
 
-	/* THIS RELOAD SHOULD NOT BE REQUIRED */
+	/* THIS RELOAD SHOULD NOT BE REQUIRED
 	cl_git_pass(git_submodule_reload_all(g_repo));
 	cl_git_pass(git_submodule_lookup(&sm, g_repo, smpath));
+ */
 
 	git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_DIRTY);