7 #include "sha1-array.h"
10 #include "commit-slab.h"
12 static int is_shallow
= -1;
13 static struct stat shallow_stat
;
14 static char *alternate_shallow_file
;
16 void set_alternate_shallow_file(const char *path
)
19 die("BUG: is_repository_shallow must not be called before set_alternate_shallow_file");
20 free(alternate_shallow_file
);
21 alternate_shallow_file
= path
? xstrdup(path
) : NULL
;
24 int register_shallow(const unsigned char *sha1
)
26 struct commit_graft
*graft
=
27 xmalloc(sizeof(struct commit_graft
));
28 struct commit
*commit
= lookup_commit(sha1
);
30 hashcpy(graft
->sha1
, sha1
);
31 graft
->nr_parent
= -1;
32 if (commit
&& commit
->object
.parsed
)
33 commit
->parents
= NULL
;
34 return register_commit_graft(graft
, 0);
37 int is_repository_shallow(void)
41 const char *path
= alternate_shallow_file
;
47 path
= git_path("shallow");
49 * fetch-pack sets '--shallow-file ""' as an indicator that no
50 * shallow file should be used. We could just open it and it
51 * will likely fail. But let's do an explicit check instead.
54 stat(path
, &shallow_stat
) ||
55 (fp
= fopen(path
, "r")) == NULL
) {
61 while (fgets(buf
, sizeof(buf
), fp
)) {
62 unsigned char sha1
[20];
63 if (get_sha1_hex(buf
, sha1
))
64 die("bad shallow line: %s", buf
);
65 register_shallow(sha1
);
71 struct commit_list
*get_shallow_commits(struct object_array
*heads
, int depth
,
72 int shallow_flag
, int not_shallow_flag
)
74 int i
= 0, cur_depth
= 0;
75 struct commit_list
*result
= NULL
;
76 struct object_array stack
= OBJECT_ARRAY_INIT
;
77 struct commit
*commit
= NULL
;
79 while (commit
|| i
< heads
->nr
|| stack
.nr
) {
80 struct commit_list
*p
;
83 commit
= (struct commit
*)
84 deref_tag(heads
->objects
[i
++].item
, NULL
, 0);
85 if (!commit
|| commit
->object
.type
!= OBJ_COMMIT
) {
90 commit
->util
= xmalloc(sizeof(int));
91 *(int *)commit
->util
= 0;
94 commit
= (struct commit
*)
95 stack
.objects
[--stack
.nr
].item
;
96 cur_depth
= *(int *)commit
->util
;
99 if (parse_commit(commit
))
100 die("invalid commit");
102 if (cur_depth
>= depth
) {
103 commit_list_insert(commit
, &result
);
104 commit
->object
.flags
|= shallow_flag
;
108 commit
->object
.flags
|= not_shallow_flag
;
109 for (p
= commit
->parents
, commit
= NULL
; p
; p
= p
->next
) {
110 if (!p
->item
->util
) {
111 int *pointer
= xmalloc(sizeof(int));
112 p
->item
->util
= pointer
;
113 *pointer
= cur_depth
;
115 int *pointer
= p
->item
->util
;
116 if (cur_depth
>= *pointer
)
118 *pointer
= cur_depth
;
121 add_object_array(&p
->item
->object
,
125 cur_depth
= *(int *)commit
->util
;
133 void check_shallow_file_for_update(void)
139 else if (is_shallow
== -1)
140 die("BUG: shallow must be initialized by now");
142 if (stat(git_path("shallow"), &st
))
143 die("shallow file was removed during fetch");
144 else if (st
.st_mtime
!= shallow_stat
.st_mtime
146 || ST_MTIME_NSEC(st
) != ST_MTIME_NSEC(shallow_stat
)
149 die("shallow file was changed during fetch");
152 struct write_shallow_data
{
154 int use_pack_protocol
;
158 static int write_one_shallow(const struct commit_graft
*graft
, void *cb_data
)
160 struct write_shallow_data
*data
= cb_data
;
161 const char *hex
= sha1_to_hex(graft
->sha1
);
162 if (graft
->nr_parent
!= -1)
165 if (data
->use_pack_protocol
)
166 packet_buf_write(data
->out
, "shallow %s", hex
);
168 strbuf_addstr(data
->out
, hex
);
169 strbuf_addch(data
->out
, '\n');
174 int write_shallow_commits(struct strbuf
*out
, int use_pack_protocol
,
175 const struct sha1_array
*extra
)
177 struct write_shallow_data data
;
180 data
.use_pack_protocol
= use_pack_protocol
;
182 for_each_commit_graft(write_one_shallow
, &data
);
185 for (i
= 0; i
< extra
->nr
; i
++) {
186 strbuf_addstr(out
, sha1_to_hex(extra
->sha1
[i
]));
187 strbuf_addch(out
, '\n');
193 char *setup_temporary_shallow(const struct sha1_array
*extra
)
195 struct strbuf sb
= STRBUF_INIT
;
198 if (write_shallow_commits(&sb
, 0, extra
)) {
199 struct strbuf path
= STRBUF_INIT
;
200 strbuf_addstr(&path
, git_path("shallow_XXXXXX"));
201 fd
= xmkstemp(path
.buf
);
202 if (write_in_full(fd
, sb
.buf
, sb
.len
) != sb
.len
)
203 die_errno("failed to write to %s",
207 return strbuf_detach(&path
, NULL
);
210 * is_repository_shallow() sees empty string as "no shallow
216 void setup_alternate_shallow(struct lock_file
*shallow_lock
,
217 const char **alternate_shallow_file
,
218 const struct sha1_array
*extra
)
220 struct strbuf sb
= STRBUF_INIT
;
223 check_shallow_file_for_update();
224 fd
= hold_lock_file_for_update(shallow_lock
, git_path("shallow"),
226 if (write_shallow_commits(&sb
, 0, extra
)) {
227 if (write_in_full(fd
, sb
.buf
, sb
.len
) != sb
.len
)
228 die_errno("failed to write to %s",
229 shallow_lock
->filename
);
230 *alternate_shallow_file
= shallow_lock
->filename
;
233 * is_repository_shallow() sees empty string as "no
236 *alternate_shallow_file
= "";
240 static int advertise_shallow_grafts_cb(const struct commit_graft
*graft
, void *cb
)
243 if (graft
->nr_parent
== -1)
244 packet_write(fd
, "shallow %s\n", sha1_to_hex(graft
->sha1
));
248 void advertise_shallow_grafts(int fd
)
250 if (!is_repository_shallow())
252 for_each_commit_graft(advertise_shallow_grafts_cb
, &fd
);
255 #define TRACE_KEY "GIT_TRACE_SHALLOW"
258 * Step 1, split sender shallow commits into "ours" and "theirs"
259 * Step 2, clean "ours" based on .git/shallow
261 void prepare_shallow_info(struct shallow_info
*info
, struct sha1_array
*sa
)
264 trace_printf_key(TRACE_KEY
, "shallow: prepare_shallow_info\n");
265 memset(info
, 0, sizeof(*info
));
269 info
->ours
= xmalloc(sizeof(*info
->ours
) * sa
->nr
);
270 info
->theirs
= xmalloc(sizeof(*info
->theirs
) * sa
->nr
);
271 for (i
= 0; i
< sa
->nr
; i
++) {
272 if (has_sha1_file(sa
->sha1
[i
])) {
273 struct commit_graft
*graft
;
274 graft
= lookup_commit_graft(sa
->sha1
[i
]);
275 if (graft
&& graft
->nr_parent
< 0)
277 info
->ours
[info
->nr_ours
++] = i
;
279 info
->theirs
[info
->nr_theirs
++] = i
;
283 void clear_shallow_info(struct shallow_info
*info
)
289 /* Step 4, remove non-existent ones in "theirs" after getting the pack */
291 void remove_nonexistent_theirs_shallow(struct shallow_info
*info
)
293 unsigned char (*sha1
)[20] = info
->shallow
->sha1
;
295 trace_printf_key(TRACE_KEY
, "shallow: remove_nonexistent_theirs_shallow\n");
296 for (i
= dst
= 0; i
< info
->nr_theirs
; i
++) {
298 info
->theirs
[dst
] = info
->theirs
[i
];
299 if (has_sha1_file(sha1
[info
->theirs
[i
]]))
302 info
->nr_theirs
= dst
;
305 /* Step 5, remove non-existent ones in "ours" in the pack */
306 void remove_nonexistent_ours_in_pack(struct shallow_info
*info
,
307 struct packed_git
*p
)
309 unsigned char (*sha1
)[20] = info
->shallow
->sha1
;
311 trace_printf_key(TRACE_KEY
, "shallow: remove_nonexistent_ours_in_pack\n");
312 for (i
= dst
= 0; i
< info
->nr_ours
; i
++) {
314 info
->ours
[dst
] = info
->ours
[i
];
315 if (find_pack_entry_one(sha1
[info
->ours
[i
]], p
))