Differential D20892 Diff 49802 src/applications/repository/engine/PhabricatorRepositoryPullEngine.php
Changeset View
Changeset View
Standalone View
Standalone View
src/applications/repository/engine/PhabricatorRepositoryPullEngine.php
Show First 20 Lines • Show All 347 Lines • ▼ Show 20 Lines | if ($err && $repository->canDestroyWorkingCopy()) { | ||||
$this->executeGitCreate(); | $this->executeGitCreate(); | ||||
} else if ($err) { | } else if ($err) { | ||||
throw new Exception($message); | throw new Exception($message); | ||||
} | } | ||||
// Load the refs we're planning to fetch from the remote repository. | // Load the refs we're planning to fetch from the remote repository. | ||||
$remote_refs = $this->loadGitRemoteRefs( | $remote_refs = $this->loadGitRemoteRefs( | ||||
$repository, | $repository, | ||||
$repository->getRemoteURIEnvelope()); | $repository->getRemoteURIEnvelope(), | ||||
$is_local = false); | |||||
// Load the refs we're planning to fetch from the local repository, by | // Load the refs we're planning to fetch from the local repository, by | ||||
// using the local working copy path as the "remote" repository URI. | // using the local working copy path as the "remote" repository URI. | ||||
$local_refs = $this->loadGitRemoteRefs( | $local_refs = $this->loadGitRemoteRefs( | ||||
$repository, | $repository, | ||||
new PhutilOpaqueEnvelope($path)); | new PhutilOpaqueEnvelope($path), | ||||
$is_local = true); | |||||
// See T13448. The "git fetch --prune ..." flag only prunes local refs | |||||
// matching the refspecs we pass it. If "Fetch Refs" is configured, we'll | |||||
// pass it a very narrow list of refspecs, and it won't prune older refs | |||||
// that aren't currently subject to fetching. | |||||
// Since we want to prune everything that isn't (a) on the fetch list and | |||||
// (b) in the remote, handle pruning of any surplus leftover refs ourselves | |||||
// before we fetch anything. | |||||
// (We don't have to do this if "Fetch Refs" isn't set up, since "--prune" | |||||
// will work in that case, but it's a little simpler to always go down the | |||||
// same code path.) | |||||
$surplus_refs = array(); | |||||
foreach ($local_refs as $local_ref => $local_hash) { | |||||
$remote_hash = idx($remote_refs, $local_ref); | |||||
if ($remote_hash === null) { | |||||
$surplus_refs[] = $local_ref; | |||||
} | |||||
} | |||||
if ($surplus_refs) { | |||||
$this->log( | |||||
pht( | |||||
'Found %s surplus local ref(s) to delete.', | |||||
phutil_count($surplus_refs))); | |||||
foreach ($surplus_refs as $surplus_ref) { | |||||
$this->log( | |||||
pht( | |||||
'Deleting surplus local ref "%s" ("%s").', | |||||
$surplus_ref, | |||||
$local_refs[$surplus_ref])); | |||||
$repository->execLocalCommand( | |||||
'update-ref -d %R --', | |||||
$surplus_ref); | |||||
unset($local_refs[$surplus_ref]); | |||||
} | |||||
} | |||||
if ($remote_refs === $local_refs) { | if ($remote_refs === $local_refs) { | ||||
$this->log( | $this->log( | ||||
pht( | pht( | ||||
'Skipping fetch because local and remote refs are already '. | 'Skipping fetch because local and remote refs are already '. | ||||
'identical.')); | 'identical.')); | ||||
return false; | return false; | ||||
} | } | ||||
$this->logRefDifferences($remote_refs, $local_refs); | $this->logRefDifferences($remote_refs, $local_refs); | ||||
$fetch_rules = $this->getGitFetchRules($repository); | $fetch_rules = $this->getGitFetchRules($repository); | ||||
// For very old non-bare working copies, we need to use "--update-head-ok" | // For very old non-bare working copies, we need to use "--update-head-ok" | ||||
// to tell Git that it is allowed to overwrite whatever is currently | // to tell Git that it is allowed to overwrite whatever is currently | ||||
// checked out. See T13280. | // checked out. See T13280. | ||||
$future = $repository->getRemoteCommandFuture( | $future = $repository->getRemoteCommandFuture( | ||||
'fetch --prune --update-head-ok -- %P %Ls', | 'fetch --update-head-ok -- %P %Ls', | ||||
$repository->getRemoteURIEnvelope(), | $repository->getRemoteURIEnvelope(), | ||||
$fetch_rules); | $fetch_rules); | ||||
$future | $future | ||||
->setCWD($path) | ->setCWD($path) | ||||
->resolvex(); | ->resolvex(); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | if ($repository->isWorkingCopyBare()) { | ||||
$repository->execLocalCommand( | $repository->execLocalCommand( | ||||
'symbolic-ref HEAD %s', | 'symbolic-ref HEAD %s', | ||||
'refs/heads/'.$repository->getDefaultBranch()); | 'refs/heads/'.$repository->getDefaultBranch()); | ||||
} | } | ||||
} | } | ||||
private function loadGitRemoteRefs( | private function loadGitRemoteRefs( | ||||
PhabricatorRepository $repository, | PhabricatorRepository $repository, | ||||
PhutilOpaqueEnvelope $remote_envelope) { | PhutilOpaqueEnvelope $remote_envelope, | ||||
$is_local) { | |||||
// See T13448. When listing local remotes, we want to list everything, | |||||
// not just refs we expect to fetch. This allows us to detect that we have | |||||
// undesirable refs (which have been deleted in the remote, but are still | |||||
// present locally) so we can update our state to reflect the correct | |||||
// remote state. | |||||
if ($is_local) { | |||||
$ref_rules = array(); | |||||
} else { | |||||
$ref_rules = $this->getGitRefRules($repository); | $ref_rules = $this->getGitRefRules($repository); | ||||
// NOTE: "git ls-remote" does not support "--" until circa January 2016. | // NOTE: "git ls-remote" does not support "--" until circa January 2016. | ||||
// See T12416. None of the flags to "ls-remote" appear dangerous, but | // See T12416. None of the flags to "ls-remote" appear dangerous, but | ||||
// refuse to list any refs beginning with "-" just in case. | // refuse to list any refs beginning with "-" just in case. | ||||
foreach ($ref_rules as $ref_rule) { | foreach ($ref_rules as $ref_rule) { | ||||
if (preg_match('/^-/', $ref_rule)) { | if (preg_match('/^-/', $ref_rule)) { | ||||
throw new Exception( | throw new Exception( | ||||
pht( | pht( | ||||
'Refusing to list potentially dangerous ref ("%s") beginning '. | 'Refusing to list potentially dangerous ref ("%s") beginning '. | ||||
'with "-".', | 'with "-".', | ||||
$ref_rule)); | $ref_rule)); | ||||
} | } | ||||
} | } | ||||
} | |||||
list($stdout) = $repository->execxRemoteCommand( | list($stdout) = $repository->execxRemoteCommand( | ||||
'ls-remote %P %Ls', | 'ls-remote %P %Ls', | ||||
$remote_envelope, | $remote_envelope, | ||||
$ref_rules); | $ref_rules); | ||||
// Empty repositories don't have any refs. | // Empty repositories don't have any refs. | ||||
if (!strlen(rtrim($stdout))) { | if (!strlen(rtrim($stdout))) { | ||||
▲ Show 20 Lines • Show All 267 Lines • Show Last 20 Lines |