Re: [PATCH v2 1/4] fetch: extract writing to FETCH_HEAD
From: Junio C Hamano <hidden>
Date: 2021-01-08 23:41:09
Patrick Steinhardt [off-list ref] writes:
+static int open_fetch_head(struct fetch_head *fetch_head)
+{
+ const char *filename = git_path_fetch_head(the_repository);
+
+ if (!write_fetch_head)
+ return 0;
+
+ fetch_head->fp = fopen(filename, "a");
+ if (!fetch_head->fp)
+ return error_errno(_("cannot open %s"), filename);
+
+ return 0;
+}So the difference from the original, which used to have a writable filehandle to /dev/null in the dry-run mode, is that fetch_head->fp is left as-is (not even NULLed out).
+static void append_fetch_head(struct fetch_head *fetch_head, const char *old_oid,
It is clear from the type these days but variable names like "old_oid" hint the readers that they are not a hexadecimal object name string but either an array of uchar[40] or a struct object_id; perhaps "old_oid_hex" would be less misleading. If the caller does have struct object_id, then it would be even better to take it as-is as a parameter and use oid_to_hex_r() on it in this function when it is given to fprintf(). [Nit #1]
+ const char *merge_status_marker, const char *note,
+ const char *url, size_t url_len)
+{
+ size_t i;
+
+ if (!write_fetch_head)
+ return;Presumably, this check is what makes sure that fetch_head->fp that is left uninitialized will never gets used.
+ fprintf(fetch_head->fp, "%s\t%s\t%s",
+ 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);
+}OK. This is the "case FETCH_HEAD_NOT_FOR_MERGE" and "case FETCH_HEAD_MERGE" parts in the original. As an abstraction, it may be better to make the caller pass a boolean "is this for merge?" and keep the knowledge of what exact string is used for merge_status_marker to this function, instead of letting the caller passing it as a parameter in the string form. After all, we never allow anything other than an empty string or a fixed "not-for-merge" string in that place in the file format. [Nit #2]
+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 (!write_fetch_head)
+ return;So is this check a protection against uninitialized fetch_head->fp. Both changes make sense.
+ fclose(fetch_head->fp); +}
quoted hunk
@@ -909,22 +959,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;
And that is why this variable is left uninitialised on stack and it is OK. An alternative design would be to initialize fetch_head.fp to NULL, and return early with "if (!fetch_head->fp)" in the two functions that take fetch_head struct. That way, we have less reliance on the global variable write_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;OK, we've already said "cannot open" in the open_fetch_head() function, so we just return an error silently.
quoted hunk
@@ -1016,16 +1063,10 @@ static int store_updated_refs(const char *raw_url, const char *remote_name, 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); + append_fetch_head(&fetch_head, + oid_to_hex(&rm->old_oid), + merge_status_marker, + note.buf, url, url_len);
Here, we can lose merge_status_marker variable from this caller, and
then this caller becomes:
switch (rm->fetch_head_status) {
case FETCH_HEAD_NOT_FOR_MERGE:
case FETCH_HEAD_MERGE:
append_fetch_head(&fetch_head, &rm->old_oid,
rm->fetch_head_status == FETCH_HEAD_MERGE,
note.buf, url, url_len);
Note that I am passing "is this a ref to be merged?" boolean to keep
the exact string of "not-for-merge" in the callee.
quoted hunk
@@ -1060,6 +1101,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 +1121,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; }
Other than the above two nits, this step looks good to me. Thanks.