зеркало из https://github.com/microsoft/git.git
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>
This commit is contained in:
Родитель
72c4083ddf
Коммит
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;
|
||||
}
|
||||
|
||||
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[] =
|
||||
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"
|
||||
|
@ -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,
|
||||
int connectivity_checked, struct ref *ref_map)
|
||||
{
|
||||
FILE *fp;
|
||||
struct fetch_head fetch_head;
|
||||
struct commit *commit;
|
||||
int url_len, i, rc = 0;
|
||||
struct strbuf note = STRBUF_INIT;
|
||||
const char *what, *kind;
|
||||
struct ref *rm;
|
||||
char *url;
|
||||
const char *filename = (!write_fetch_head
|
||||
? "/dev/null"
|
||||
: git_path_fetch_head(the_repository));
|
||||
int want_status;
|
||||
int summary_width = transport_summary_width(ref_map);
|
||||
|
||||
fp = fopen(filename, "a");
|
||||
if (!fp)
|
||||
return error_errno(_("cannot open %s"), filename);
|
||||
rc = open_fetch_head(&fetch_head);
|
||||
if (rc)
|
||||
return -1;
|
||||
|
||||
if (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++) {
|
||||
for (rm = ref_map; rm; rm = rm->next) {
|
||||
struct ref *ref = NULL;
|
||||
const char *merge_status_marker = "";
|
||||
|
||||
if (rm->status == REF_STATUS_REJECT_SHALLOW) {
|
||||
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' of ", what);
|
||||
}
|
||||
switch (rm->fetch_head_status) {
|
||||
case FETCH_HEAD_NOT_FOR_MERGE:
|
||||
merge_status_marker = "not-for-merge";
|
||||
/* fall-through */
|
||||
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;
|
||||
}
|
||||
|
||||
append_fetch_head(&fetch_head, &rm->old_oid,
|
||||
rm->fetch_head_status,
|
||||
note.buf, url, url_len);
|
||||
|
||||
strbuf_reset(¬e);
|
||||
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)
|
||||
error(_("some local refs could not be updated; try running\n"
|
||||
" '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:
|
||||
strbuf_release(¬e);
|
||||
free(url);
|
||||
fclose(fp);
|
||||
close_fetch_head(&fetch_head);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
2
remote.h
2
remote.h
|
@ -134,7 +134,7 @@ struct ref {
|
|||
* should be 0, so that xcalloc'd structures get it
|
||||
* by default.
|
||||
*/
|
||||
enum {
|
||||
enum fetch_head_status {
|
||||
FETCH_HEAD_MERGE = -1,
|
||||
FETCH_HEAD_NOT_FOR_MERGE = 0,
|
||||
FETCH_HEAD_IGNORE = 1
|
||||
|
|
Загрузка…
Ссылка в новой задаче