Skip to content

Commit 813a3a2

Browse files
committed
Merge branch 'ew/update-server-info'
"git update-server-info" learned not to rewrite the file with the same contents. * ew/update-server-info: update-server-info: avoid needless overwrites
2 parents 8d32d25 + f4f476b commit 813a3a2

File tree

3 files changed

+158
-34
lines changed

3 files changed

+158
-34
lines changed

Documentation/git-update-server-info.txt

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ git-update-server-info - Update auxiliary info file to help dumb servers
99
SYNOPSIS
1010
--------
1111
[verse]
12-
'git update-server-info' [--force]
12+
'git update-server-info'
1313

1414
DESCRIPTION
1515
-----------
@@ -19,15 +19,6 @@ $GIT_OBJECT_DIRECTORY/info directories to help clients discover
1919
what references and packs the server has. This command
2020
generates such auxiliary files.
2121

22-
23-
OPTIONS
24-
-------
25-
26-
-f::
27-
--force::
28-
Update the info files from scratch.
29-
30-
3122
OUTPUT
3223
------
3324

server-info.c

Lines changed: 116 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,82 +6,174 @@
66
#include "tag.h"
77
#include "packfile.h"
88
#include "object-store.h"
9+
#include "strbuf.h"
10+
11+
struct update_info_ctx {
12+
FILE *cur_fp;
13+
FILE *old_fp; /* becomes NULL if it differs from cur_fp */
14+
struct strbuf cur_sb;
15+
struct strbuf old_sb;
16+
};
17+
18+
static void uic_mark_stale(struct update_info_ctx *uic)
19+
{
20+
fclose(uic->old_fp);
21+
uic->old_fp = NULL;
22+
}
23+
24+
static int uic_is_stale(const struct update_info_ctx *uic)
25+
{
26+
return uic->old_fp == NULL;
27+
}
28+
29+
static int uic_printf(struct update_info_ctx *uic, const char *fmt, ...)
30+
{
31+
va_list ap;
32+
int ret = -1;
33+
34+
va_start(ap, fmt);
35+
36+
if (uic_is_stale(uic)) {
37+
ret = vfprintf(uic->cur_fp, fmt, ap);
38+
} else {
39+
ssize_t r;
40+
struct strbuf *cur = &uic->cur_sb;
41+
struct strbuf *old = &uic->old_sb;
42+
43+
strbuf_reset(cur);
44+
strbuf_vinsertf(cur, 0, fmt, ap);
45+
46+
strbuf_reset(old);
47+
strbuf_grow(old, cur->len);
48+
r = fread(old->buf, 1, cur->len, uic->old_fp);
49+
if (r != cur->len || memcmp(old->buf, cur->buf, r))
50+
uic_mark_stale(uic);
51+
52+
if (fwrite(cur->buf, 1, cur->len, uic->cur_fp) == cur->len)
53+
ret = 0;
54+
}
55+
56+
va_end(ap);
57+
58+
return ret;
59+
}
960

1061
/*
1162
* Create the file "path" by writing to a temporary file and renaming
1263
* it into place. The contents of the file come from "generate", which
1364
* should return non-zero if it encounters an error.
1465
*/
15-
static int update_info_file(char *path, int (*generate)(FILE *))
66+
static int update_info_file(char *path,
67+
int (*generate)(struct update_info_ctx *),
68+
int force)
1669
{
1770
char *tmp = mkpathdup("%s_XXXXXX", path);
1871
int ret = -1;
1972
int fd = -1;
20-
FILE *fp = NULL, *to_close;
73+
FILE *to_close;
74+
struct update_info_ctx uic = {
75+
.cur_fp = NULL,
76+
.old_fp = NULL,
77+
.cur_sb = STRBUF_INIT,
78+
.old_sb = STRBUF_INIT
79+
};
2180

2281
safe_create_leading_directories(path);
2382
fd = git_mkstemp_mode(tmp, 0666);
2483
if (fd < 0)
2584
goto out;
26-
to_close = fp = fdopen(fd, "w");
27-
if (!fp)
85+
to_close = uic.cur_fp = fdopen(fd, "w");
86+
if (!uic.cur_fp)
2887
goto out;
2988
fd = -1;
30-
ret = generate(fp);
89+
90+
/* no problem on ENOENT and old_fp == NULL, it's stale, now */
91+
if (!force)
92+
uic.old_fp = fopen_or_warn(path, "r");
93+
94+
/*
95+
* uic_printf will compare incremental comparison aginst old_fp
96+
* and mark uic as stale if needed
97+
*/
98+
ret = generate(&uic);
3199
if (ret)
32100
goto out;
33-
fp = NULL;
101+
102+
/* new file may be shorter than the old one, check here */
103+
if (!uic_is_stale(&uic)) {
104+
struct stat st;
105+
long new_len = ftell(uic.cur_fp);
106+
int old_fd = fileno(uic.old_fp);
107+
108+
if (new_len < 0) {
109+
ret = -1;
110+
goto out;
111+
}
112+
if (fstat(old_fd, &st) || (st.st_size != (size_t)new_len))
113+
uic_mark_stale(&uic);
114+
}
115+
116+
uic.cur_fp = NULL;
34117
if (fclose(to_close))
35118
goto out;
36-
if (adjust_shared_perm(tmp) < 0)
37-
goto out;
38-
if (rename(tmp, path) < 0)
39-
goto out;
119+
120+
if (uic_is_stale(&uic)) {
121+
if (adjust_shared_perm(tmp) < 0)
122+
goto out;
123+
if (rename(tmp, path) < 0)
124+
goto out;
125+
} else {
126+
unlink(tmp);
127+
}
40128
ret = 0;
41129

42130
out:
43131
if (ret) {
44132
error_errno("unable to update %s", path);
45-
if (fp)
46-
fclose(fp);
133+
if (uic.cur_fp)
134+
fclose(uic.cur_fp);
47135
else if (fd >= 0)
48136
close(fd);
49137
unlink(tmp);
50138
}
51139
free(tmp);
140+
if (uic.old_fp)
141+
fclose(uic.old_fp);
142+
strbuf_release(&uic.old_sb);
143+
strbuf_release(&uic.cur_sb);
52144
return ret;
53145
}
54146

55147
static int add_info_ref(const char *path, const struct object_id *oid,
56148
int flag, void *cb_data)
57149
{
58-
FILE *fp = cb_data;
150+
struct update_info_ctx *uic = cb_data;
59151
struct object *o = parse_object(the_repository, oid);
60152
if (!o)
61153
return -1;
62154

63-
if (fprintf(fp, "%s %s\n", oid_to_hex(oid), path) < 0)
155+
if (uic_printf(uic, "%s %s\n", oid_to_hex(oid), path) < 0)
64156
return -1;
65157

66158
if (o->type == OBJ_TAG) {
67159
o = deref_tag(the_repository, o, path, 0);
68160
if (o)
69-
if (fprintf(fp, "%s %s^{}\n",
161+
if (uic_printf(uic, "%s %s^{}\n",
70162
oid_to_hex(&o->oid), path) < 0)
71163
return -1;
72164
}
73165
return 0;
74166
}
75167

76-
static int generate_info_refs(FILE *fp)
168+
static int generate_info_refs(struct update_info_ctx *uic)
77169
{
78-
return for_each_ref(add_info_ref, fp);
170+
return for_each_ref(add_info_ref, uic);
79171
}
80172

81-
static int update_info_refs(void)
173+
static int update_info_refs(int force)
82174
{
83175
char *path = git_pathdup("info/refs");
84-
int ret = update_info_file(path, generate_info_refs);
176+
int ret = update_info_file(path, generate_info_refs, force);
85177
free(path);
86178
return ret;
87179
}
@@ -236,14 +328,14 @@ static void free_pack_info(void)
236328
free(info);
237329
}
238330

239-
static int write_pack_info_file(FILE *fp)
331+
static int write_pack_info_file(struct update_info_ctx *uic)
240332
{
241333
int i;
242334
for (i = 0; i < num_pack; i++) {
243-
if (fprintf(fp, "P %s\n", pack_basename(info[i]->p)) < 0)
335+
if (uic_printf(uic, "P %s\n", pack_basename(info[i]->p)) < 0)
244336
return -1;
245337
}
246-
if (fputc('\n', fp) == EOF)
338+
if (uic_printf(uic, "\n") < 0)
247339
return -1;
248340
return 0;
249341
}
@@ -254,7 +346,7 @@ static int update_info_packs(int force)
254346
int ret;
255347

256348
init_pack_info(infofile, force);
257-
ret = update_info_file(infofile, write_pack_info_file);
349+
ret = update_info_file(infofile, write_pack_info_file, force);
258350
free_pack_info();
259351
free(infofile);
260352
return ret;
@@ -269,7 +361,7 @@ int update_server_info(int force)
269361
*/
270362
int errs = 0;
271363

272-
errs = errs | update_info_refs();
364+
errs = errs | update_info_refs(force);
273365
errs = errs | update_info_packs(force);
274366

275367
/* remove leftover rev-cache file if there is any */

t/t5200-update-server-info.sh

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/bin/sh
2+
3+
test_description='Test git update-server-info'
4+
5+
. ./test-lib.sh
6+
7+
test_expect_success 'setup' 'test_commit file'
8+
9+
test_expect_success 'create info/refs' '
10+
git update-server-info &&
11+
test_path_is_file .git/info/refs
12+
'
13+
14+
test_expect_success 'modify and store mtime' '
15+
test-tool chmtime =0 .git/info/refs &&
16+
test-tool chmtime --get .git/info/refs >a
17+
'
18+
19+
test_expect_success 'info/refs is not needlessly overwritten' '
20+
git update-server-info &&
21+
test-tool chmtime --get .git/info/refs >b &&
22+
test_cmp a b
23+
'
24+
25+
test_expect_success 'info/refs can be forced to update' '
26+
git update-server-info -f &&
27+
test-tool chmtime --get .git/info/refs >b &&
28+
! test_cmp a b
29+
'
30+
31+
test_expect_success 'info/refs updates when changes are made' '
32+
test-tool chmtime =0 .git/info/refs &&
33+
test-tool chmtime --get .git/info/refs >b &&
34+
test_cmp a b &&
35+
git update-ref refs/heads/foo HEAD &&
36+
git update-server-info &&
37+
test-tool chmtime --get .git/info/refs >b &&
38+
! test_cmp a b
39+
'
40+
41+
test_done

0 commit comments

Comments
 (0)