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 <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
parent
72c4083ddf
commit
58a646a368
108
builtin/fetch.c
108
builtin/fetch.c
|
@ -897,6 +897,73 @@ static int iterate_ref_map(void *cb_data, struct object_id *oid)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct fetch_head {
|
||||||
|
FILE *fp;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int open_fetch_head(struct fetch_head *fetch_head)
|
||||||
|
{
|
||||||
|
const char *filename = git_path_fetch_head(the_repository);
|
||||||
|
|
||||||
|
if (write_fetch_head) {
|
||||||
|
fetch_head->fp = fopen(filename, "a");
|
||||||
|
if (!fetch_head->fp)
|
||||||
|
return error_errno(_("cannot open %s"), filename);
|
||||||
|
} else {
|
||||||
|
fetch_head->fp = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void append_fetch_head(struct fetch_head *fetch_head,
|
||||||
|
const struct object_id *old_oid,
|
||||||
|
enum fetch_head_status fetch_head_status,
|
||||||
|
const char *note,
|
||||||
|
const char *url, size_t url_len)
|
||||||
|
{
|
||||||
|
char old_oid_hex[GIT_MAX_HEXSZ + 1];
|
||||||
|
const char *merge_status_marker;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (!fetch_head->fp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (fetch_head_status) {
|
||||||
|
case FETCH_HEAD_NOT_FOR_MERGE:
|
||||||
|
merge_status_marker = "not-for-merge";
|
||||||
|
break;
|
||||||
|
case FETCH_HEAD_MERGE:
|
||||||
|
merge_status_marker = "";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* do not write anything to FETCH_HEAD */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(fetch_head->fp, "%s\t%s\t%s",
|
||||||
|
oid_to_hex_r(old_oid_hex, old_oid), merge_status_marker, note);
|
||||||
|
for (i = 0; i < url_len; ++i)
|
||||||
|
if ('\n' == url[i])
|
||||||
|
fputs("\\n", fetch_head->fp);
|
||||||
|
else
|
||||||
|
fputc(url[i], fetch_head->fp);
|
||||||
|
fputc('\n', fetch_head->fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void commit_fetch_head(struct fetch_head *fetch_head)
|
||||||
|
{
|
||||||
|
/* Nothing to commit yet. */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void close_fetch_head(struct fetch_head *fetch_head)
|
||||||
|
{
|
||||||
|
if (!fetch_head->fp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fclose(fetch_head->fp);
|
||||||
|
}
|
||||||
|
|
||||||
static const char warn_show_forced_updates[] =
|
static const char warn_show_forced_updates[] =
|
||||||
N_("Fetch normally indicates which branches had a forced update,\n"
|
N_("Fetch normally indicates which branches had a forced update,\n"
|
||||||
"but that check has been disabled. To re-enable, use '--show-forced-updates'\n"
|
"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"
|
||||||
static int store_updated_refs(const char *raw_url, const char *remote_name,
|
static int store_updated_refs(const char *raw_url, const char *remote_name,
|
||||||
int connectivity_checked, struct ref *ref_map)
|
int connectivity_checked, struct ref *ref_map)
|
||||||
{
|
{
|
||||||
FILE *fp;
|
struct fetch_head fetch_head;
|
||||||
struct commit *commit;
|
struct commit *commit;
|
||||||
int url_len, i, rc = 0;
|
int url_len, i, rc = 0;
|
||||||
struct strbuf note = STRBUF_INIT;
|
struct strbuf note = STRBUF_INIT;
|
||||||
const char *what, *kind;
|
const char *what, *kind;
|
||||||
struct ref *rm;
|
struct ref *rm;
|
||||||
char *url;
|
char *url;
|
||||||
const char *filename = (!write_fetch_head
|
|
||||||
? "/dev/null"
|
|
||||||
: git_path_fetch_head(the_repository));
|
|
||||||
int want_status;
|
int want_status;
|
||||||
int summary_width = transport_summary_width(ref_map);
|
int summary_width = transport_summary_width(ref_map);
|
||||||
|
|
||||||
fp = fopen(filename, "a");
|
rc = open_fetch_head(&fetch_head);
|
||||||
if (!fp)
|
if (rc)
|
||||||
return error_errno(_("cannot open %s"), filename);
|
return -1;
|
||||||
|
|
||||||
if (raw_url)
|
if (raw_url)
|
||||||
url = transport_anonymize_url(raw_url);
|
url = transport_anonymize_url(raw_url);
|
||||||
|
@ -953,7 +1017,6 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
|
||||||
want_status++) {
|
want_status++) {
|
||||||
for (rm = ref_map; rm; rm = rm->next) {
|
for (rm = ref_map; rm; rm = rm->next) {
|
||||||
struct ref *ref = NULL;
|
struct ref *ref = NULL;
|
||||||
const char *merge_status_marker = "";
|
|
||||||
|
|
||||||
if (rm->status == REF_STATUS_REJECT_SHALLOW) {
|
if (rm->status == REF_STATUS_REJECT_SHALLOW) {
|
||||||
if (want_status == FETCH_HEAD_MERGE)
|
if (want_status == FETCH_HEAD_MERGE)
|
||||||
|
@ -1011,26 +1074,10 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
|
||||||
strbuf_addf(¬e, "%s ", kind);
|
strbuf_addf(¬e, "%s ", kind);
|
||||||
strbuf_addf(¬e, "'%s' of ", what);
|
strbuf_addf(¬e, "'%s' of ", what);
|
||||||
}
|
}
|
||||||
switch (rm->fetch_head_status) {
|
|
||||||
case FETCH_HEAD_NOT_FOR_MERGE:
|
append_fetch_head(&fetch_head, &rm->old_oid,
|
||||||
merge_status_marker = "not-for-merge";
|
rm->fetch_head_status,
|
||||||
/* fall-through */
|
note.buf, url, url_len);
|
||||||
case FETCH_HEAD_MERGE:
|
|
||||||
fprintf(fp, "%s\t%s\t%s",
|
|
||||||
oid_to_hex(&rm->old_oid),
|
|
||||||
merge_status_marker,
|
|
||||||
note.buf);
|
|
||||||
for (i = 0; i < url_len; ++i)
|
|
||||||
if ('\n' == url[i])
|
|
||||||
fputs("\\n", fp);
|
|
||||||
else
|
|
||||||
fputc(url[i], fp);
|
|
||||||
fputc('\n', fp);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* do not write anything to FETCH_HEAD */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
strbuf_reset(¬e);
|
strbuf_reset(¬e);
|
||||||
if (ref) {
|
if (ref) {
|
||||||
|
@ -1060,6 +1107,9 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!rc)
|
||||||
|
commit_fetch_head(&fetch_head);
|
||||||
|
|
||||||
if (rc & STORE_REF_ERROR_DF_CONFLICT)
|
if (rc & STORE_REF_ERROR_DF_CONFLICT)
|
||||||
error(_("some local refs could not be updated; try running\n"
|
error(_("some local refs could not be updated; try running\n"
|
||||||
" 'git remote prune %s' to remove any old, conflicting "
|
" '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,
|
||||||
abort:
|
abort:
|
||||||
strbuf_release(¬e);
|
strbuf_release(¬e);
|
||||||
free(url);
|
free(url);
|
||||||
fclose(fp);
|
close_fetch_head(&fetch_head);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
2
remote.h
2
remote.h
|
@ -134,7 +134,7 @@ struct ref {
|
||||||
* should be 0, so that xcalloc'd structures get it
|
* should be 0, so that xcalloc'd structures get it
|
||||||
* by default.
|
* by default.
|
||||||
*/
|
*/
|
||||||
enum {
|
enum fetch_head_status {
|
||||||
FETCH_HEAD_MERGE = -1,
|
FETCH_HEAD_MERGE = -1,
|
||||||
FETCH_HEAD_NOT_FOR_MERGE = 0,
|
FETCH_HEAD_NOT_FOR_MERGE = 0,
|
||||||
FETCH_HEAD_IGNORE = 1
|
FETCH_HEAD_IGNORE = 1
|
||||||
|
|
Loading…
Reference in New Issue