@@ -6,37 +6,82 @@
#include "tag.h"
#include "packfile.h"
#include "object-store.h"
+#include "strbuf.h"
+
+static int files_differ(FILE *fp, const char *path)
+{
+ struct stat st;
+ git_hash_ctx c;
+ struct object_id oid_old, oid_new;
+ struct strbuf tmp = STRBUF_INIT;
+ long new_len = ftell(fp);
+
+ if (new_len < 0 || stat(path, &st) < 0)
+ return 1;
+ if (!S_ISREG(st.st_mode))
+ return 1;
+ if ((off_t)new_len != st.st_size)
+ return 1;
+
+ rewind(fp);
+ if (strbuf_fread(&tmp, (size_t)new_len, fp) != (size_t)new_len) {
+ strbuf_release(&tmp);
+ return 1;
+ }
+ the_hash_algo->init_fn(&c);
+ the_hash_algo->update_fn(&c, tmp.buf, tmp.len);
+ the_hash_algo->final_fn(oid_new.hash, &c);
+ strbuf_release(&tmp);
+
+ if (strbuf_read_file(&tmp, path, (size_t)st.st_size) < 0) {
+ strbuf_release(&tmp);
+ return 1;
+ }
+ the_hash_algo->init_fn(&c);
+ the_hash_algo->update_fn(&c, tmp.buf, tmp.len);
+ the_hash_algo->final_fn(oid_old.hash, &c);
+ strbuf_release(&tmp);
+
+ return hashcmp(oid_old.hash, oid_new.hash);
+}
/*
* Create the file "path" by writing to a temporary file and renaming
* it into place. The contents of the file come from "generate", which
* should return non-zero if it encounters an error.
*/
-static int update_info_file(char *path, int (*generate)(FILE *))
+static int update_info_file(char *path, int (*generate)(FILE *), int force)
{
char *tmp = mkpathdup("%s_XXXXXX", path);
int ret = -1;
int fd = -1;
FILE *fp = NULL, *to_close;
+ int do_update;
safe_create_leading_directories(path);
fd = git_mkstemp_mode(tmp, 0666);
if (fd < 0)
goto out;
- to_close = fp = fdopen(fd, "w");
+ to_close = fp = fdopen(fd, "w+");
if (!fp)
goto out;
fd = -1;
ret = generate(fp);
if (ret)
goto out;
+
+ do_update = force || files_differ(fp, path);
fp = NULL;
if (fclose(to_close))
goto out;
- if (adjust_shared_perm(tmp) < 0)
- goto out;
- if (rename(tmp, path) < 0)
- goto out;
+ if (do_update) {
+ if (adjust_shared_perm(tmp) < 0)
+ goto out;
+ if (rename(tmp, path) < 0)
+ goto out;
+ } else {
+ unlink(tmp);
+ }
ret = 0;
out:
@@ -78,10 +123,10 @@ static int generate_info_refs(FILE *fp)
return for_each_ref(add_info_ref, fp);
}
-static int update_info_refs(void)
+static int update_info_refs(int force)
{
char *path = git_pathdup("info/refs");
- int ret = update_info_file(path, generate_info_refs);
+ int ret = update_info_file(path, generate_info_refs, force);
free(path);
return ret;
}
@@ -254,7 +299,7 @@ static int update_info_packs(int force)
int ret;
init_pack_info(infofile, force);
- ret = update_info_file(infofile, write_pack_info_file);
+ ret = update_info_file(infofile, write_pack_info_file, force);
free_pack_info();
free(infofile);
return ret;
@@ -269,7 +314,7 @@ int update_server_info(int force)
*/
int errs = 0;
- errs = errs | update_info_refs();
+ errs = errs | update_info_refs(force);
errs = errs | update_info_packs(force);
/* remove leftover rev-cache file if there is any */
new file mode 100755
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+test_description='Test git update-server-info'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' 'test_commit file'
+
+test_expect_success 'create info/refs' '
+ git update-server-info &&
+ test_path_is_file .git/info/refs
+'
+
+test_expect_success 'modify and store mtime' '
+ test-tool chmtime =0 .git/info/refs &&
+ test-tool chmtime --get .git/info/refs >a
+'
+
+test_expect_success 'info/refs is not needlessly overwritten' '
+ git update-server-info &&
+ test-tool chmtime --get .git/info/refs >b &&
+ test_cmp a b
+'
+
+test_expect_success 'info/refs can be forced to update' '
+ git update-server-info -f &&
+ test-tool chmtime --get .git/info/refs >b &&
+ ! test_cmp a b
+'
+
+test_expect_success 'info/refs updates when changes are made' '
+ test-tool chmtime =0 .git/info/refs &&
+ test-tool chmtime --get .git/info/refs >b &&
+ test_cmp a b &&
+ git update-ref refs/heads/foo HEAD &&
+ git update-server-info &&
+ test-tool chmtime --get .git/info/refs >b &&
+ ! test_cmp a b
+'
+
+test_done