diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:41:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:41:41 +0000 |
commit | 10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 (patch) | |
tree | bdffd5d80c26cf4a7a518281a204be1ace85b4c1 /vendor/gix/src/clone/fetch/util.rs | |
parent | Releasing progress-linux version 1.70.0+dfsg1-9~progress7.99u1. (diff) | |
download | rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.tar.xz rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.zip |
Merging upstream version 1.70.0+dfsg2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/gix/src/clone/fetch/util.rs')
-rw-r--r-- | vendor/gix/src/clone/fetch/util.rs | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/vendor/gix/src/clone/fetch/util.rs b/vendor/gix/src/clone/fetch/util.rs new file mode 100644 index 000000000..ac8943f6e --- /dev/null +++ b/vendor/gix/src/clone/fetch/util.rs @@ -0,0 +1,229 @@ +use std::{borrow::Cow, convert::TryInto, io::Write}; + +use gix_odb::Find; +use gix_ref::{ + transaction::{LogChange, RefLog}, + FullNameRef, +}; + +use super::Error; +use crate::{ + bstr::{BStr, BString, ByteSlice}, + Repository, +}; + +enum WriteMode { + Overwrite, + Append, +} + +#[allow(clippy::result_large_err)] +pub fn write_remote_to_local_config_file( + remote: &mut crate::Remote<'_>, + remote_name: BString, +) -> Result<gix_config::File<'static>, Error> { + let mut config = gix_config::File::new(local_config_meta(remote.repo)); + remote.save_as_to(remote_name, &mut config)?; + + write_to_local_config(&config, WriteMode::Append)?; + Ok(config) +} + +fn local_config_meta(repo: &Repository) -> gix_config::file::Metadata { + let meta = repo.config.resolved.meta().clone(); + assert_eq!( + meta.source, + gix_config::Source::Local, + "local path is the default for new sections" + ); + meta +} + +fn write_to_local_config(config: &gix_config::File<'static>, mode: WriteMode) -> std::io::Result<()> { + assert_eq!( + config.meta().source, + gix_config::Source::Local, + "made for appending to local configuration file" + ); + let mut local_config = std::fs::OpenOptions::new() + .create(false) + .write(matches!(mode, WriteMode::Overwrite)) + .append(matches!(mode, WriteMode::Append)) + .open(config.meta().path.as_deref().expect("local config with path set"))?; + local_config.write_all(config.detect_newline_style())?; + config.write_to_filter(&mut local_config, |s| s.meta().source == gix_config::Source::Local) +} + +pub fn append_config_to_repo_config(repo: &mut Repository, config: gix_config::File<'static>) { + let repo_config = gix_features::threading::OwnShared::make_mut(&mut repo.config.resolved); + repo_config.append(config); +} + +/// HEAD cannot be written by means of refspec by design, so we have to do it manually here. Also create the pointed-to ref +/// if we have to, as it might not have been naturally included in the ref-specs. +pub fn update_head( + repo: &mut Repository, + remote_refs: &[gix_protocol::handshake::Ref], + reflog_message: &BStr, + remote_name: &BStr, +) -> Result<(), Error> { + use gix_ref::{ + transaction::{PreviousValue, RefEdit}, + Target, + }; + let (head_peeled_id, head_ref) = match remote_refs.iter().find_map(|r| { + Some(match r { + gix_protocol::handshake::Ref::Symbolic { + full_ref_name, + target, + object, + } if full_ref_name == "HEAD" => (Some(object.as_ref()), Some(target)), + gix_protocol::handshake::Ref::Direct { full_ref_name, object } if full_ref_name == "HEAD" => { + (Some(object.as_ref()), None) + } + gix_protocol::handshake::Ref::Unborn { full_ref_name, target } if full_ref_name == "HEAD" => { + (None, Some(target)) + } + _ => return None, + }) + }) { + Some(t) => t, + None => return Ok(()), + }; + + let head: gix_ref::FullName = "HEAD".try_into().expect("valid"); + let reflog_message = || LogChange { + mode: RefLog::AndReference, + force_create_reflog: false, + message: reflog_message.to_owned(), + }; + match head_ref { + Some(referent) => { + let referent: gix_ref::FullName = referent.try_into().map_err(|err| Error::InvalidHeadRef { + head_ref_name: referent.to_owned(), + source: err, + })?; + repo.refs + .transaction() + .packed_refs(gix_ref::file::transaction::PackedRefs::DeletionsAndNonSymbolicUpdates( + Box::new(|oid, buf| { + repo.objects + .try_find(oid, buf) + .map(|obj| obj.map(|obj| obj.kind)) + .map_err(|err| Box::new(err) as Box<dyn std::error::Error + Send + Sync + 'static>) + }), + )) + .prepare( + { + let mut edits = vec![RefEdit { + change: gix_ref::transaction::Change::Update { + log: reflog_message(), + expected: PreviousValue::Any, + new: Target::Symbolic(referent.clone()), + }, + name: head.clone(), + deref: false, + }]; + if let Some(head_peeled_id) = head_peeled_id { + edits.push(RefEdit { + change: gix_ref::transaction::Change::Update { + log: reflog_message(), + expected: PreviousValue::Any, + new: Target::Peeled(head_peeled_id.to_owned()), + }, + name: referent.clone(), + deref: false, + }); + }; + edits + }, + gix_lock::acquire::Fail::Immediately, + gix_lock::acquire::Fail::Immediately, + ) + .map_err(crate::reference::edit::Error::from)? + .commit( + repo.committer() + .transpose() + .map_err(|err| Error::HeadUpdate(crate::reference::edit::Error::ParseCommitterTime(err)))?, + ) + .map_err(crate::reference::edit::Error::from)?; + + if let Some(head_peeled_id) = head_peeled_id { + let mut log = reflog_message(); + log.mode = RefLog::Only; + repo.edit_reference(RefEdit { + change: gix_ref::transaction::Change::Update { + log, + expected: PreviousValue::Any, + new: Target::Peeled(head_peeled_id.to_owned()), + }, + name: head, + deref: false, + })?; + } + + setup_branch_config(repo, referent.as_ref(), head_peeled_id, remote_name)?; + } + None => { + repo.edit_reference(RefEdit { + change: gix_ref::transaction::Change::Update { + log: reflog_message(), + expected: PreviousValue::Any, + new: Target::Peeled( + head_peeled_id + .expect("detached heads always point to something") + .to_owned(), + ), + }, + name: head, + deref: false, + })?; + } + }; + Ok(()) +} + +/// Setup the remote configuration for `branch` so that it points to itself, but on the remote, if and only if currently +/// saved refspecs are able to match it. +/// For that we reload the remote of `remote_name` and use its ref_specs for match. +fn setup_branch_config( + repo: &mut Repository, + branch: &FullNameRef, + branch_id: Option<&gix_hash::oid>, + remote_name: &BStr, +) -> Result<(), Error> { + let short_name = match branch.category_and_short_name() { + Some((cat, shortened)) if cat == gix_ref::Category::LocalBranch => match shortened.to_str() { + Ok(s) => s, + Err(_) => return Ok(()), + }, + _ => return Ok(()), + }; + let remote = repo + .find_remote(remote_name) + .expect("remote was just created and must be visible in config"); + let group = gix_refspec::MatchGroup::from_fetch_specs(remote.fetch_specs.iter().map(|s| s.to_ref())); + let null = gix_hash::ObjectId::null(repo.object_hash()); + let res = group.match_remotes( + Some(gix_refspec::match_group::Item { + full_ref_name: branch.as_bstr(), + target: branch_id.unwrap_or(&null), + object: None, + }) + .into_iter(), + ); + if !res.mappings.is_empty() { + let mut config = repo.config_snapshot_mut(); + let mut section = config + .new_section("branch", Some(Cow::Owned(short_name.into()))) + .expect("section header name is always valid per naming rules, our input branch name is valid"); + section.push("remote".try_into().expect("valid at compile time"), Some(remote_name)); + section.push( + "merge".try_into().expect("valid at compile time"), + Some(branch.as_bstr()), + ); + write_to_local_config(&config, WriteMode::Overwrite)?; + config.commit().expect("configuration we set is valid"); + } + Ok(()) +} |