Skip to content

Commit 58a646a

Browse files
pks-tgitster
authored andcommitted
fetch: extract writing to FETCH_HEAD
When performing a fetch with the default `--write-fetch-head` option, we write all updated references to FETCH_HEAD while the updates are performed. Given that updates are not performed atomically, it means that we we write to FETCH_HEAD even if some or all of the reference updates fail. Given that we simply update FETCH_HEAD ad-hoc with each reference, the logic is completely contained in `store_update_refs` and thus quite hard to extend. This can already be seen by the way we skip writing to the FETCH_HEAD: instead of having a conditional which simply skips writing, we instead open "/dev/null" and needlessly write all updates there. We are about to extend git-fetch(1) to accept an `--atomic` flag which will make the fetch an all-or-nothing operation with regards to the reference updates. This will also require us to make the updates to FETCH_HEAD an all-or-nothing operation, but as explained doing so is not easy with the current layout. This commit thus refactors the wa we write to FETCH_HEAD and pulls out the logic to open, append to, commit and close the file. While this may seem rather over-the top at first, pulling out this logic will make it a lot easier to update the code in a subsequent commit. It also allows us to easily skip writing completely in case `--no-write-fetch-head` was passed. Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 72c4083 commit 58a646a

File tree

2 files changed

+80
-30
lines changed

2 files changed

+80
-30
lines changed

builtin/fetch.c

Lines changed: 79 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -897,6 +897,73 @@ static int iterate_ref_map(void *cb_data, struct object_id *oid)
897897
return 0;
898898
}
899899

900+
struct fetch_head {
901+
FILE *fp;
902+
};
903+
904+
static int open_fetch_head(struct fetch_head *fetch_head)
905+
{
906+
const char *filename = git_path_fetch_head(the_repository);
907+
908+
if (write_fetch_head) {
909+
fetch_head->fp = fopen(filename, "a");
910+
if (!fetch_head->fp)
911+
return error_errno(_("cannot open %s"), filename);
912+
} else {
913+
fetch_head->fp = NULL;
914+
}
915+
916+
return 0;
917+
}
918+
919+
static void append_fetch_head(struct fetch_head *fetch_head,
920+
const struct object_id *old_oid,
921+
enum fetch_head_status fetch_head_status,
922+
const char *note,
923+
const char *url, size_t url_len)
924+
{
925+
char old_oid_hex[GIT_MAX_HEXSZ + 1];
926+
const char *merge_status_marker;
927+
size_t i;
928+
929+
if (!fetch_head->fp)
930+
return;
931+
932+
switch (fetch_head_status) {
933+
case FETCH_HEAD_NOT_FOR_MERGE:
934+
merge_status_marker = "not-for-merge";
935+
break;
936+
case FETCH_HEAD_MERGE:
937+
merge_status_marker = "";
938+
break;
939+
default:
940+
/* do not write anything to FETCH_HEAD */
941+
return;
942+
}
943+
944+
fprintf(fetch_head->fp, "%s\t%s\t%s",
945+
oid_to_hex_r(old_oid_hex, old_oid), merge_status_marker, note);
946+
for (i = 0; i < url_len; ++i)
947+
if ('\n' == url[i])
948+
fputs("\\n", fetch_head->fp);
949+
else
950+
fputc(url[i], fetch_head->fp);
951+
fputc('\n', fetch_head->fp);
952+
}
953+
954+
static void commit_fetch_head(struct fetch_head *fetch_head)
955+
{
956+
/* Nothing to commit yet. */
957+
}
958+
959+
static void close_fetch_head(struct fetch_head *fetch_head)
960+
{
961+
if (!fetch_head->fp)
962+
return;
963+
964+
fclose(fetch_head->fp);
965+
}
966+
900967
static const char warn_show_forced_updates[] =
901968
N_("Fetch normally indicates which branches had a forced update,\n"
902969
"but that check has been disabled. To re-enable, use '--show-forced-updates'\n"
@@ -909,22 +976,19 @@ N_("It took %.2f seconds to check forced updates. You can use\n"
909976
static int store_updated_refs(const char *raw_url, const char *remote_name,
910977
int connectivity_checked, struct ref *ref_map)
911978
{
912-
FILE *fp;
979+
struct fetch_head fetch_head;
913980
struct commit *commit;
914981
int url_len, i, rc = 0;
915982
struct strbuf note = STRBUF_INIT;
916983
const char *what, *kind;
917984
struct ref *rm;
918985
char *url;
919-
const char *filename = (!write_fetch_head
920-
? "/dev/null"
921-
: git_path_fetch_head(the_repository));
922986
int want_status;
923987
int summary_width = transport_summary_width(ref_map);
924988

925-
fp = fopen(filename, "a");
926-
if (!fp)
927-
return error_errno(_("cannot open %s"), filename);
989+
rc = open_fetch_head(&fetch_head);
990+
if (rc)
991+
return -1;
928992

929993
if (raw_url)
930994
url = transport_anonymize_url(raw_url);
@@ -953,7 +1017,6 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
9531017
want_status++) {
9541018
for (rm = ref_map; rm; rm = rm->next) {
9551019
struct ref *ref = NULL;
956-
const char *merge_status_marker = "";
9571020

9581021
if (rm->status == REF_STATUS_REJECT_SHALLOW) {
9591022
if (want_status == FETCH_HEAD_MERGE)
@@ -1011,26 +1074,10 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
10111074
strbuf_addf(&note, "%s ", kind);
10121075
strbuf_addf(&note, "'%s' of ", what);
10131076
}
1014-
switch (rm->fetch_head_status) {
1015-
case FETCH_HEAD_NOT_FOR_MERGE:
1016-
merge_status_marker = "not-for-merge";
1017-
/* fall-through */
1018-
case FETCH_HEAD_MERGE:
1019-
fprintf(fp, "%s\t%s\t%s",
1020-
oid_to_hex(&rm->old_oid),
1021-
merge_status_marker,
1022-
note.buf);
1023-
for (i = 0; i < url_len; ++i)
1024-
if ('\n' == url[i])
1025-
fputs("\\n", fp);
1026-
else
1027-
fputc(url[i], fp);
1028-
fputc('\n', fp);
1029-
break;
1030-
default:
1031-
/* do not write anything to FETCH_HEAD */
1032-
break;
1033-
}
1077+
1078+
append_fetch_head(&fetch_head, &rm->old_oid,
1079+
rm->fetch_head_status,
1080+
note.buf, url, url_len);
10341081

10351082
strbuf_reset(&note);
10361083
if (ref) {
@@ -1060,6 +1107,9 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
10601107
}
10611108
}
10621109

1110+
if (!rc)
1111+
commit_fetch_head(&fetch_head);
1112+
10631113
if (rc & STORE_REF_ERROR_DF_CONFLICT)
10641114
error(_("some local refs could not be updated; try running\n"
10651115
" 'git remote prune %s' to remove any old, conflicting "
@@ -1077,7 +1127,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
10771127
abort:
10781128
strbuf_release(&note);
10791129
free(url);
1080-
fclose(fp);
1130+
close_fetch_head(&fetch_head);
10811131
return rc;
10821132
}
10831133

remote.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ struct ref {
134134
* should be 0, so that xcalloc'd structures get it
135135
* by default.
136136
*/
137-
enum {
137+
enum fetch_head_status {
138138
FETCH_HEAD_MERGE = -1,
139139
FETCH_HEAD_NOT_FOR_MERGE = 0,
140140
FETCH_HEAD_IGNORE = 1

0 commit comments

Comments
 (0)