Skip to content

Commit 7b886cf

Browse files
committed
gvfs-helper: prevent and/or give advice on repeated downloads to shared object cache (git-for-windows#840)
There have been a number of customer-reported problems with errors of the form ``` error: inflate: data stream error (unknown compression method) error: unable to unpack a163b1302d4729ebdb0a12d3876ca5bca4e1a8c3 header error: files 'D:/.scalarCache/id_49b0c9f4-555f-4624-8157-a57e6df513b3/pack/tempPacks/t-20260106-014520-049919-0001.temp' and 'D:/.scalarCache/id_49b0c9f4-555f-4624-8157-a57e6df513b3/a1/63b1302d4729ebdb0a12d3876ca5bca4e1a8c3' differ in contents error: gvfs-helper error: 'could not install loose object 'D:/.scalarCache/id_49b0c9f4-555f-4624-8157-a57e6df513b3/a1/63b1302d4729ebdb0a12d3876ca5bca4e1a8c3': from GET a163b1302d4729ebdb0a12d3876ca5bca4e1a8c3' ``` or ``` Receiving packfile 1/1 with 1 objects (bytes received): 17367934, done. Receiving packfile 1/1 with 1 objects [retry 1/6] (bytes received): 17367934, done. Waiting to retry after network error (sec): 100% (8/8), done. Receiving packfile 1/1 with 1 objects [retry 2/6] (bytes received): 17367934, done. Waiting to retry after network error (sec): 100% (16/16), done. Receiving packfile 1/1 with 1 objects [retry 3/6] (bytes received): 17367934, done. ``` These are not actually due to network issues, but they look like it based on the stack that is doing retries. Instead, these actually have problems when installing the loose object or packfile into the shared object cache. The loose objects are hitting issues when installing and the target loose object is corrupt in some way, such as all NUL bytes because the disk wasn't flushed when the machine shut down. The error results because we are doing a collision check without confirming that the existing contents are valid. The packfiles may be hitting similar comparison cases, but it is less likely. We update these packfile installations to also skip the collision check. In both cases, if we have a transient network error, we add a new advice message that helps users with the two most common workarounds: 1. Your disk may be full. Make room. 2. Your shared object cache may be corrupt. Push all branches, delete it, and fetch to refill it. I make special note of when the shared object cache doesn't exist and point that it probably should so the whole repo is suspect at that point. * [X] This change only applies to interactions with Azure DevOps and the GVFS Protocol. Resolves git-for-windows#837.
2 parents ad6f04c + e5f698d commit 7b886cf

File tree

3 files changed

+59
-4
lines changed

3 files changed

+59
-4
lines changed

advice.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ static struct {
9393
[ADVICE_USE_CORE_FSMONITOR_CONFIG] = { "useCoreFSMonitorConfig" },
9494
[ADVICE_WAITING_FOR_EDITOR] = { "waitingForEditor" },
9595
[ADVICE_WORKTREE_ADD_ORPHAN] = { "worktreeAddOrphan" },
96+
97+
/* microsoft/git custom advice below: */
98+
[ADVICE_GVFS_HELPER_TRANSIENT_RETRY] = { "gvfs.transientRetry"},
9699
};
97100

98101
static const char turn_off_instructions[] =

advice.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ enum advice_type {
6060
ADVICE_USE_CORE_FSMONITOR_CONFIG,
6161
ADVICE_WAITING_FOR_EDITOR,
6262
ADVICE_WORKTREE_ADD_ORPHAN,
63+
64+
/* microsoft/git custom advice below: */
65+
ADVICE_GVFS_HELPER_TRANSIENT_RETRY,
6366
};
6467

6568
int git_default_advice_config(const char *var, const char *value);

gvfs-helper.c

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@
255255
#include "packfile.h"
256256
#include "date.h"
257257
#include "versioncmp.h"
258+
#include "advice.h"
258259

259260
#define TR2_CAT "gvfs-helper"
260261

@@ -1939,12 +1940,21 @@ static void my_finalize_packfile(struct gh__request_params *params,
19391940
* files, do we create the matching .keep (when requested).
19401941
*
19411942
* If we get an error and the target files already exist, we
1942-
* silently eat the error. Note that finalize_object_file()
1943+
* silently eat the error. Note that finalize_object_file_flags()
19431944
* has already munged errno (and it has various creation
19441945
* strategies), so we don't bother looking at it.
1946+
*
1947+
* We use FOF_SKIP_COLLISION_CHECK in case the same packfile was
1948+
* attempted for install earlier but got corrupted or failed to
1949+
* flush due to a disk issue. This prevents a narrow failure case
1950+
* but is better than failing for silly reasons.
19451951
*/
1946-
if (finalize_object_file(the_repository, temp_path_pack->buf, final_path_pack->buf) ||
1947-
finalize_object_file(the_repository, temp_path_idx->buf, final_path_idx->buf)) {
1952+
if (finalize_object_file_flags(the_repository,
1953+
temp_path_pack->buf, final_path_pack->buf,
1954+
FOF_SKIP_COLLISION_CHECK) ||
1955+
finalize_object_file_flags(the_repository,
1956+
temp_path_idx->buf, final_path_idx->buf,
1957+
FOF_SKIP_COLLISION_CHECK)) {
19481958
unlink(temp_path_pack->buf);
19491959
unlink(temp_path_idx->buf);
19501960

@@ -2459,7 +2469,16 @@ static void install_loose(struct gh__request_params *params,
24592469
goto cleanup;
24602470
}
24612471

2462-
if (finalize_object_file(the_repository, tmp_path.buf, loose_path.buf)) {
2472+
/*
2473+
* We skip collision check because the loose object in the target
2474+
* may be corrupt and we should override it with a better value
2475+
* instead of failing at this point.
2476+
*
2477+
* See https://github.com/microsoft/git/issues/837
2478+
*/
2479+
if (finalize_object_file_flags(the_repository,
2480+
tmp_path.buf, loose_path.buf,
2481+
FOF_SKIP_COLLISION_CHECK)) {
24632482
unlink(tmp_path.buf);
24642483
strbuf_addf(&status->error_message,
24652484
"could not install loose object '%s'",
@@ -3007,6 +3026,32 @@ static int compute_transient_delay(int attempt)
30073026
return v;
30083027
}
30093028

3029+
static void gvfs_advice_on_retry(void)
3030+
{
3031+
static int advice_given = 0;
3032+
3033+
if (advice_given)
3034+
return;
3035+
advice_given = 1;
3036+
3037+
if (gvfs_shared_cache_pathname.len) {
3038+
advise_if_enabled(ADVICE_GVFS_HELPER_TRANSIENT_RETRY,
3039+
"These retries may hint towards issues with your disk or\n"
3040+
"shared object cache. Check to see if your disk is full.\n"
3041+
"If your disk has space, then your shared object cache\n"
3042+
"may have corrupt files. Push all local branches then\n"
3043+
"delete '%s'\n"
3044+
"and run 'git fetch' to reload the cache.",
3045+
gvfs_shared_cache_pathname.buf);
3046+
} else {
3047+
advise_if_enabled(ADVICE_GVFS_HELPER_TRANSIENT_RETRY,
3048+
"These retries may hint towards issues with your disk.\n"
3049+
"Check to see if your disk is full. Note also that you\n"
3050+
"do not have a gvfs.sharedCache config, which is not\n"
3051+
"normal. You may need to delete and reclone this repo.");
3052+
}
3053+
}
3054+
30103055
/*
30113056
* Robustly make an HTTP request. Retry if necessary to hide common
30123057
* transient network errors and/or 429 blockages.
@@ -3049,6 +3094,10 @@ static void do_req__with_robust_retry(const char *url_base,
30493094
/*fallthru*/
30503095

30513096
case GH__RETRY_MODE__TRANSIENT:
3097+
/*
3098+
* Give advice for common reasons this could happen:
3099+
*/
3100+
gvfs_advice_on_retry();
30523101
params->k_transient_delay_sec =
30533102
compute_transient_delay(params->k_attempt);
30543103
continue;

0 commit comments

Comments
 (0)