fetch-pack: prepare updated shallow file before fetching the pack
index-pack --strict looks up and follows parent commits. If shallow information is not ready by the time index-pack is run, index-pack may be led to non-existent objects. Make fetch-pack save shallow file to disk before invoking index-pack. git learns new global option --shallow-file to pass on the alternate shallow file path. Undocumented (and not even support --shallow-file= syntax) because it's unlikely to be used again elsewhere. Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Nguyễn Thái Ngọc Duy committed
May 26, 2013 at 08:16 UTC
6035d6aad8ca11954c0d7821f6f3e7c047039c8f
5 files changed
+93
-38
commit.h
+2
@@ -176,6 +176,8 @@ extern int for_each_commit_graft(each_commit_graft_fn, void *);
176
extern int is_repository_shallow(void);
177
extern struct commit_list *get_shallow_commits(struct object_array *heads,
178
int depth, int shallow_flag, int not_shallow_flag);
179
+extern void check_shallow_file_for_update(void);
180
+extern void set_alternate_shallow_file(const char *path);
181
182
int is_descendant_of(struct commit *, struct commit_list *);
183
int in_merge_bases(struct commit *, struct commit *);
fetch-pack.c
+37
-36
@@ -20,6 +20,8 @@ static int no_done;
20
static int fetch_fsck_objects = -1;
21
static int transfer_fsck_objects = -1;
22
static int agent_supported;
23
+static struct lock_file shallow_lock;
24
+static const char *alternate_shallow_file;
25
26
#define COMPLETE (1U << 0)
27
#define COMMON (1U << 1)
@@ -683,7 +685,7 @@ static int get_pack(struct fetch_pack_args *args,
685
int xd[2], char **pack_lockfile)
686
{
687
struct async demux;
686
- const char *argv[20];
688
+ const char *argv[22];
689
char keep_arg[256];
690
char hdr_arg[256];
691
const char **av;
@@ -724,6 +726,11 @@ static int get_pack(struct fetch_pack_args *args,
726
do_keep = 1;
727
}
728
729
+ if (alternate_shallow_file) {
730
+ *av++ = "--shallow-file";
731
+ *av++ = alternate_shallow_file;
732
+ }
733
+
734
if (do_keep) {
735
if (pack_lockfile)
736
cmd.out = -1;
@@ -779,6 +786,27 @@ static int cmp_ref_by_name(const void *a_, const void *b_)
786
return strcmp(a->name, b->name);
787
}
788
789
+static void setup_alternate_shallow(void)
790
+{
791
+ struct strbuf sb = STRBUF_INIT;
792
+ int fd;
793
+
794
+ check_shallow_file_for_update();
795
+ fd = hold_lock_file_for_update(&shallow_lock, git_path("shallow"),
796
+ LOCK_DIE_ON_ERROR);
797
+ if (write_shallow_commits(&sb, 0)) {
798
+ if (write_in_full(fd, sb.buf, sb.len) != sb.len)
799
+ die_errno("failed to write to %s", shallow_lock.filename);
800
+ alternate_shallow_file = shallow_lock.filename;
801
+ } else
802
+ /*
803
+ * is_repository_shallow() sees empty string as "no
804
+ * shallow file".
805
+ */
806
+ alternate_shallow_file = "";
807
+ strbuf_release(&sb);
808
+}
809
+
810
static struct ref *do_fetch_pack(struct fetch_pack_args *args,
811
int fd[2],
812
const struct ref *orig_ref,
@@ -858,6 +886,8 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
886
887
if (args->stateless_rpc)
888
packet_flush(fd[1]);
889
+ if (args->depth > 0)
890
+ setup_alternate_shallow();
891
if (get_pack(args, fd, pack_lockfile))
892
die("git fetch-pack: fetch failed.");
893
@@ -936,15 +966,9 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
966
struct ref **sought, int nr_sought,
967
char **pack_lockfile)
968
{
939
- struct stat st;
969
struct ref *ref_cpy;
970
971
fetch_pack_setup();
943
- if (args->depth > 0) {
944
- if (stat(git_path("shallow"), &st))
945
- st.st_mtime = 0;
946
- }
947
-
972
if (nr_sought)
973
nr_sought = remove_duplicates_in_refs(sought, nr_sought);
974
@@ -954,35 +978,12 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
978
}
979
ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought, pack_lockfile);
980
957
- if (args->depth > 0) {
958
- static struct lock_file lock;
959
- struct cache_time mtime;
960
- struct strbuf sb = STRBUF_INIT;
961
- char *shallow = git_path("shallow");
962
- int fd;
963
-
964
- mtime.sec = st.st_mtime;
965
- mtime.nsec = ST_MTIME_NSEC(st);
966
- if (stat(shallow, &st)) {
967
- if (mtime.sec)
968
- die("shallow file was removed during fetch");
969
- } else if (st.st_mtime != mtime.sec
970
-#ifdef USE_NSEC
971
- || ST_MTIME_NSEC(st) != mtime.nsec
972
-#endif
973
- )
974
- die("shallow file was changed during fetch");
975
-
976
- fd = hold_lock_file_for_update(&lock, shallow,
977
- LOCK_DIE_ON_ERROR);
978
- if (!write_shallow_commits(&sb, 0)
979
- || write_in_full(fd, sb.buf, sb.len) != sb.len) {
980
- unlink_or_warn(shallow);
981
- rollback_lock_file(&lock);
982
- } else {
983
- commit_lock_file(&lock);
984
- }
985
- strbuf_release(&sb);
981
+ if (alternate_shallow_file) {
982
+ if (*alternate_shallow_file == '\0') { /* --unshallow */
983
+ unlink_or_warn(git_path("shallow"));
984
+ rollback_lock_file(&shallow_lock);
985
+ } else
986
+ commit_lock_file(&shallow_lock);
987
}
988
989
reprepare_packed_git();
git.c
+7
@@ -4,6 +4,7 @@
4
#include "help.h"
5
#include "quote.h"
6
#include "run-command.h"
7
+#include "commit.h"
8
9
const char git_usage_string[] =
10
"git [--version] [--help] [-c name=value]\n"
@@ -146,6 +147,12 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
147
setenv(GIT_LITERAL_PATHSPECS_ENVIRONMENT, "0", 1);
148
if (envchanged)
149
*envchanged = 1;
150
+ } else if (!strcmp(cmd, "--shallow-file")) {
151
+ (*argv)++;
152
+ (*argc)--;
153
+ set_alternate_shallow_file((*argv)[0]);
154
+ if (envchanged)
155
+ *envchanged = 1;
156
} else {
157
fprintf(stderr, "Unknown option: %s\n", cmd);
158
usage(git_usage_string);
shallow.c
+40
-2
@@ -3,6 +3,16 @@
3
#include "tag.h"
4
5
static int is_shallow = -1;
6
+static struct stat shallow_stat;
7
+static char *alternate_shallow_file;
8
+
9
+void set_alternate_shallow_file(const char *path)
10
+{
11
+ if (is_shallow != -1)
12
+ die("BUG: is_repository_shallow must not be called before set_alternate_shallow_file");
13
+ free(alternate_shallow_file);
14
+ alternate_shallow_file = path ? xstrdup(path) : NULL;
15
+}
16
17
int register_shallow(const unsigned char *sha1)
18
{
@@ -21,12 +31,21 @@ int is_repository_shallow(void)
31
{
32
FILE *fp;
33
char buf[1024];
34
+ const char *path = alternate_shallow_file;
35
36
if (is_shallow >= 0)
37
return is_shallow;
38
28
- fp = fopen(git_path("shallow"), "r");
29
- if (!fp) {
39
+ if (!path)
40
+ path = git_path("shallow");
41
+ /*
42
+ * fetch-pack sets '--shallow-file ""' as an indicator that no
43
+ * shallow file should be used. We could just open it and it
44
+ * will likely fail. But let's do an explicit check instead.
45
+ */
46
+ if (!*path ||
47
+ stat(path, &shallow_stat) ||
48
+ (fp = fopen(path, "r")) == NULL) {
49
is_shallow = 0;
50
return is_shallow;
51
}
@@ -108,3 +127,22 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
127
128
return result;
129
}
130
+
131
+void check_shallow_file_for_update(void)
132
+{
133
+ struct stat st;
134
+
135
+ if (!is_shallow)
136
+ return;
137
+ else if (is_shallow == -1)
138
+ die("BUG: shallow must be initialized by now");
139
+
140
+ if (stat(git_path("shallow"), &st))
141
+ die("shallow file was removed during fetch");
142
+ else if (st.st_mtime != shallow_stat.st_mtime
143
+#ifdef USE_NSEC
144
+ || ST_MTIME_NSEC(st) != ST_MTIME_NSEC(shallow_stat)
145
+#endif
146
+ )
147
+ die("shallow file was changed during fetch");
148
+}
t/t5500-fetch-pack.sh
+7
@@ -135,6 +135,13 @@ test_expect_success 'clone shallow depth 1' '
135
test "`git --git-dir=shallow0/.git rev-list --count HEAD`" = 1
136
'
137
138
+test_expect_success 'clone shallow depth 1 with fsck' '
139
+ git config --global fetch.fsckobjects true &&
140
+ git clone --no-single-branch --depth 1 "file://$(pwd)/." shallow0fsck &&
141
+ test "`git --git-dir=shallow0fsck/.git rev-list --count HEAD`" = 1 &&
142
+ git config --global --unset fetch.fsckobjects
143
+'
144
+
145
test_expect_success 'clone shallow' '
146
git clone --no-single-branch --depth 2 "file://$(pwd)/." shallow
147
'