Allow specifying SSH keys and clean up config further.
parent
9fdbb34967
commit
014a7d72e5
|
@ -2,39 +2,35 @@
|
||||||
user:
|
user:
|
||||||
name: jrpotter
|
name: jrpotter
|
||||||
email: jrpotter@github.io
|
email: jrpotter@github.io
|
||||||
local: $HOME/.homesync
|
ssh:
|
||||||
remote:
|
private: $HOME/.ssh/id_ed25519
|
||||||
|
repos:
|
||||||
|
local: $HOME/.homesync
|
||||||
|
remote:
|
||||||
name: origin
|
name: origin
|
||||||
branch: master
|
branch: master
|
||||||
url: "https://github.com/jrpotter/home-config.git"
|
url: "git@github.com:jrpotter/home-config.git"
|
||||||
packages:
|
packages:
|
||||||
alacritty:
|
alacritty:
|
||||||
configs:
|
|
||||||
- $HOME/.alacritty.yml
|
- $HOME/.alacritty.yml
|
||||||
- $HOME/.config/alacritty/alacritty.yml
|
- $HOME/.config/alacritty/alacritty.yml
|
||||||
- $XDG_CONFIG_HOME/alacritty.yml
|
- $XDG_CONFIG_HOME/alacritty.yml
|
||||||
- $XDG_CONFIG_HOME/alacritty/alacritty.yml
|
- $XDG_CONFIG_HOME/alacritty/alacritty.yml
|
||||||
bash:
|
bash:
|
||||||
configs:
|
|
||||||
- $HOME/.bash_profile
|
- $HOME/.bash_profile
|
||||||
- $HOME/.bashrc
|
- $HOME/.bashrc
|
||||||
home-manager:
|
home-manager:
|
||||||
configs:
|
|
||||||
- $HOME/home.nix
|
- $HOME/home.nix
|
||||||
homesync:
|
homesync:
|
||||||
configs:
|
|
||||||
- $HOME/.homesync.yml
|
- $HOME/.homesync.yml
|
||||||
- $HOME/.config/homesync/homesync.yml
|
- $HOME/.config/homesync/homesync.yml
|
||||||
- $XDG_CONFIG_HOME/homesync.yml
|
- $XDG_CONFIG_HOME/homesync.yml
|
||||||
- $XDG_CONFIG_HOME/homesync/homesync.yml
|
- $XDG_CONFIG_HOME/homesync/homesync.yml
|
||||||
neovim:
|
neovim:
|
||||||
configs:
|
|
||||||
- $HOME/.config/nvim/init.vim
|
- $HOME/.config/nvim/init.vim
|
||||||
- $XDG_CONFIG_HOME/nvim/init.vim
|
- $XDG_CONFIG_HOME/nvim/init.vim
|
||||||
termite:
|
termite:
|
||||||
configs:
|
|
||||||
- $HOME/.config/termite/config
|
- $HOME/.config/termite/config
|
||||||
- $XDG_CONFIG_HOME/termite/config
|
- $XDG_CONFIG_HOME/termite/config
|
||||||
tmux:
|
tmux:
|
||||||
configs:
|
|
||||||
- $HOME/.tmux.conf
|
- $HOME/.tmux.conf
|
||||||
|
|
|
@ -2,14 +2,17 @@
|
||||||
user:
|
user:
|
||||||
name: name
|
name: name
|
||||||
email: email@email.com
|
email: email@email.com
|
||||||
local: $HOME/.homesync
|
ssh:
|
||||||
remote:
|
public: $HOME/.ssh/id_ed25519.pub
|
||||||
|
private: $HOME/.ssh/id_ed25519
|
||||||
|
repos:
|
||||||
|
local: $HOME/.homesync
|
||||||
|
remote:
|
||||||
name: origin
|
name: origin
|
||||||
branch: master
|
branch: master
|
||||||
url: "https://github.com/owner/repo.git"
|
url: "https://github.com/owner/repo.git"
|
||||||
packages:
|
packages:
|
||||||
homesync:
|
homesync:
|
||||||
configs:
|
|
||||||
- $HOME/.homesync.yml
|
- $HOME/.homesync.yml
|
||||||
- $HOME/.config/homesync/homesync.yml
|
- $HOME/.config/homesync/homesync.yml
|
||||||
- $XDG_CONFIG_HOME/homesync.yml
|
- $XDG_CONFIG_HOME/homesync.yml
|
||||||
|
|
|
@ -3,7 +3,6 @@ use paris::formatter::colorize_string;
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use simplelog::{info, paris};
|
use simplelog::{info, paris};
|
||||||
use std::{collections::BTreeMap, env::VarError, error, fmt, fs, io, io::Write, path::PathBuf};
|
use std::{collections::BTreeMap, env::VarError, error, fmt, fs, io, io::Write, path::PathBuf};
|
||||||
use url::{ParseError, Url};
|
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// Error
|
// Error
|
||||||
|
@ -15,7 +14,6 @@ pub type Result<T> = std::result::Result<T, Error>;
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
IOError(io::Error),
|
IOError(io::Error),
|
||||||
MissingConfig,
|
MissingConfig,
|
||||||
ParseError(ParseError),
|
|
||||||
SerdeError(serde_yaml::Error),
|
SerdeError(serde_yaml::Error),
|
||||||
VarError(VarError),
|
VarError(VarError),
|
||||||
}
|
}
|
||||||
|
@ -41,12 +39,6 @@ impl From<path::Error> for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ParseError> for Error {
|
|
||||||
fn from(err: ParseError) -> Error {
|
|
||||||
Error::ParseError(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<VarError> for Error {
|
impl From<VarError> for Error {
|
||||||
fn from(err: VarError) -> Error {
|
fn from(err: VarError) -> Error {
|
||||||
Error::VarError(err)
|
Error::VarError(err)
|
||||||
|
@ -58,7 +50,6 @@ impl fmt::Display for Error {
|
||||||
match self {
|
match self {
|
||||||
Error::IOError(e) => write!(f, "{}", e),
|
Error::IOError(e) => write!(f, "{}", e),
|
||||||
Error::MissingConfig => write!(f, "Could not find configuration file"),
|
Error::MissingConfig => write!(f, "Could not find configuration file"),
|
||||||
Error::ParseError(e) => write!(f, "{}", e),
|
|
||||||
Error::SerdeError(e) => write!(f, "{}", e),
|
Error::SerdeError(e) => write!(f, "{}", e),
|
||||||
Error::VarError(e) => write!(f, "{}", e),
|
Error::VarError(e) => write!(f, "{}", e),
|
||||||
}
|
}
|
||||||
|
@ -78,23 +69,36 @@ pub struct User {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct Package {
|
pub struct SSH {
|
||||||
pub configs: Vec<PathBuf>,
|
pub public: Option<PathBuf>,
|
||||||
|
pub private: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct Remote {
|
pub struct Remote {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub branch: String,
|
pub branch: String,
|
||||||
pub url: Url,
|
pub url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Remote {
|
||||||
|
pub fn tracking_branch(&self) -> String {
|
||||||
|
format!("{}/{}", &self.name, &self.branch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct Repos {
|
||||||
|
pub local: PathBuf,
|
||||||
|
pub remote: Remote,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub user: User,
|
pub user: User,
|
||||||
pub local: PathBuf,
|
pub ssh: SSH,
|
||||||
pub remote: Remote,
|
pub repos: Repos,
|
||||||
pub packages: BTreeMap<String, Package>,
|
pub packages: BTreeMap<String, Vec<PathBuf>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
|
@ -160,7 +164,7 @@ pub fn load(candidates: &Vec<ResPathBuf>) -> Result<PathConfig> {
|
||||||
pub fn reload(pc: &PathConfig) -> Result<PathConfig> {
|
pub fn reload(pc: &PathConfig) -> Result<PathConfig> {
|
||||||
info!(
|
info!(
|
||||||
"<green>{}</> configuration reloaded.",
|
"<green>{}</> configuration reloaded.",
|
||||||
pc.config.local.display()
|
pc.config.repos.local.display()
|
||||||
);
|
);
|
||||||
load(&vec![pc.homesync_yml.clone()])
|
load(&vec![pc.homesync_yml.clone()])
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,8 +123,8 @@ impl<'a> WatchState<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.watching.clear();
|
self.watching.clear();
|
||||||
for (_, package) in &pc.config.packages {
|
for (_, packages) in &pc.config.packages {
|
||||||
for path in &package.configs {
|
for path in packages {
|
||||||
match path::soft_resolve(&path) {
|
match path::soft_resolve(&path) {
|
||||||
Ok(None) => self.send_poll(PollEvent::Pending(path.clone())),
|
Ok(None) => self.send_poll(PollEvent::Pending(path.clone())),
|
||||||
Ok(Some(n)) => self.watch(n),
|
Ok(Some(n)) => self.watch(n),
|
||||||
|
|
192
src/git.rs
192
src/git.rs
|
@ -1,10 +1,10 @@
|
||||||
use super::{config::PathConfig, path};
|
use super::{config::PathConfig, path};
|
||||||
use git2::{
|
use git2::{
|
||||||
Branch, BranchType, Commit, DiffOptions, Direction, IndexAddOption, ObjectType, Remote,
|
Branch, BranchType, Commit, Cred, DiffOptions, Direction, FetchOptions, Index, IndexAddOption,
|
||||||
Repository, Signature,
|
ObjectType, Remote, RemoteCallbacks, Repository, Signature,
|
||||||
};
|
};
|
||||||
use path::ResPathBuf;
|
use path::ResPathBuf;
|
||||||
use simplelog::{info, paris, warn};
|
use simplelog::{info, paris};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashSet,
|
collections::HashSet,
|
||||||
env::VarError,
|
env::VarError,
|
||||||
|
@ -75,30 +75,12 @@ impl error::Error for Error {}
|
||||||
// Initialization
|
// Initialization
|
||||||
// ========================================
|
// ========================================
|
||||||
|
|
||||||
fn clone_or_init(pc: &PathConfig, expanded: &Path) -> Result<Repository> {
|
fn clone(pc: &PathConfig, expanded: &Path) -> Result<Repository> {
|
||||||
match Repository::clone(&pc.config.remote.url.to_string(), &expanded) {
|
let fetch_options = get_fetch_options(&pc)?;
|
||||||
Ok(repo) => {
|
let mut builder = git2::build::RepoBuilder::new();
|
||||||
info!(
|
builder.fetch_options(fetch_options);
|
||||||
"Cloned remote repository <green>{}</>.",
|
|
||||||
&pc.config.remote.url
|
Ok(builder.clone(&pc.config.repos.remote.url, &expanded)?)
|
||||||
);
|
|
||||||
Ok(repo)
|
|
||||||
}
|
|
||||||
Err(e) if e.code() == git2::ErrorCode::NotFound || e.code() == git2::ErrorCode::Auth => {
|
|
||||||
// TODO(jrpotter): Setup authentication callbacks so private
|
|
||||||
// repositories work.
|
|
||||||
// https://docs.rs/git2/0.13.25/git2/build/struct.RepoBuilder.html#example
|
|
||||||
if e.code() == git2::ErrorCode::Auth {
|
|
||||||
warn!("Could not authenticate against remote. Are you using a public repository?");
|
|
||||||
}
|
|
||||||
info!(
|
|
||||||
"Creating local repository at <green>{}</>.",
|
|
||||||
pc.config.local.display()
|
|
||||||
);
|
|
||||||
Ok(Repository::init(&expanded)?)
|
|
||||||
}
|
|
||||||
Err(e) => Err(e)?,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets up a local github repository all configuration files will be synced to.
|
/// Sets up a local github repository all configuration files will be synced to.
|
||||||
|
@ -112,7 +94,7 @@ pub fn init(pc: &PathConfig) -> Result<Repository> {
|
||||||
// Permit the use of environment variables within the local configuration
|
// Permit the use of environment variables within the local configuration
|
||||||
// path (e.g. `$HOME`). Unlike with resolution, we want to fail if the
|
// path (e.g. `$HOME`). Unlike with resolution, we want to fail if the
|
||||||
// environment variable is not defined.
|
// environment variable is not defined.
|
||||||
let expanded = path::expand(&pc.config.local)?;
|
let expanded = path::expand(&pc.config.repos.local)?;
|
||||||
// Attempt to open the local path as a git repository if possible. The
|
// Attempt to open the local path as a git repository if possible. The
|
||||||
// `NotFound` error is thrown if:
|
// `NotFound` error is thrown if:
|
||||||
//
|
//
|
||||||
|
@ -125,11 +107,29 @@ pub fn init(pc: &PathConfig) -> Result<Repository> {
|
||||||
Ok(repo) => {
|
Ok(repo) => {
|
||||||
info!(
|
info!(
|
||||||
"Opened local repository <green>{}</>.",
|
"Opened local repository <green>{}</>.",
|
||||||
&pc.config.local.display()
|
&pc.config.repos.local.display()
|
||||||
);
|
);
|
||||||
Ok(repo)
|
Ok(repo)
|
||||||
}
|
}
|
||||||
Err(e) if e.code() == git2::ErrorCode::NotFound => Ok(clone_or_init(&pc, &expanded)?),
|
Err(e) if e.code() == git2::ErrorCode::NotFound => match clone(&pc, &expanded) {
|
||||||
|
Ok(repo) => {
|
||||||
|
info!(
|
||||||
|
"Cloned remote repository <green>{}</>.",
|
||||||
|
&pc.config.repos.remote.url
|
||||||
|
);
|
||||||
|
Ok(repo)
|
||||||
|
}
|
||||||
|
Err(Error::GitError(e))
|
||||||
|
if e.code() == git2::ErrorCode::Eof && e.class() == git2::ErrorClass::Ssh =>
|
||||||
|
{
|
||||||
|
info!(
|
||||||
|
"Creating local repository at <green>{}</>.",
|
||||||
|
pc.config.repos.local.display()
|
||||||
|
);
|
||||||
|
Ok(Repository::init(&expanded)?)
|
||||||
|
}
|
||||||
|
Err(e) => Err(e)?,
|
||||||
|
},
|
||||||
Err(e) => Err(e)?,
|
Err(e) => Err(e)?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,8 +159,8 @@ fn find_repo_files(path: &Path) -> Result<Vec<ResPathBuf>> {
|
||||||
|
|
||||||
fn find_package_files(pc: &PathConfig) -> Vec<ResPathBuf> {
|
fn find_package_files(pc: &PathConfig) -> Vec<ResPathBuf> {
|
||||||
let mut seen = Vec::new();
|
let mut seen = Vec::new();
|
||||||
for (_, package) in &pc.config.packages {
|
for (_, packages) in &pc.config.packages {
|
||||||
for path in &package.configs {
|
for path in packages {
|
||||||
if let Ok(resolved) = path::resolve(path) {
|
if let Ok(resolved) = path::resolve(path) {
|
||||||
seen.push(resolved);
|
seen.push(resolved);
|
||||||
}
|
}
|
||||||
|
@ -214,16 +214,11 @@ pub fn stage(pc: &PathConfig, repo: &Repository) -> Result<()> {
|
||||||
// Syncing
|
// Syncing
|
||||||
// ========================================
|
// ========================================
|
||||||
|
|
||||||
pub fn push(pc: &PathConfig, repo: &mut Repository) -> Result<()> {
|
/// Adds all files to our index.
|
||||||
// First pull to make sure there are no conflicts when we push our changes.
|
///
|
||||||
// This will also perform validation and construct our local and remote
|
/// Checks explicitly if any changes have been detected in the newly constructed
|
||||||
// environment.
|
/// index. If not, return `None`.
|
||||||
let _local_branch = pull(&pc, &repo)?;
|
pub fn index_add(repo: &Repository) -> Result<Option<Index>> {
|
||||||
let mut remote = get_remote(&pc, &repo)?;
|
|
||||||
|
|
||||||
// The index corresponds to our staging area. We add all files and write out
|
|
||||||
// to a tree. The resulting tree can be found using `git ls-tree <oid>`.
|
|
||||||
// https://git-scm.com/book/en/v2/Git-Internals-Git-Objects
|
|
||||||
let mut index = repo.index()?;
|
let mut index = repo.index()?;
|
||||||
index.add_all(["."].iter(), IndexAddOption::DEFAULT, None)?;
|
index.add_all(["."].iter(), IndexAddOption::DEFAULT, None)?;
|
||||||
let diff_stats = repo
|
let diff_stats = repo
|
||||||
|
@ -240,11 +235,48 @@ pub fn push(pc: &PathConfig, repo: &mut Repository) -> Result<()> {
|
||||||
&& diff_stats.insertions() == 0
|
&& diff_stats.insertions() == 0
|
||||||
&& diff_stats.deletions() == 0
|
&& diff_stats.deletions() == 0
|
||||||
{
|
{
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
Ok(Some(index))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create or retrieve the remote specified within our configuration.
|
||||||
|
///
|
||||||
|
/// This method also configures the fetchspec for the remote, explicitly mapping
|
||||||
|
/// the remote branch against our local one.
|
||||||
|
///
|
||||||
|
/// https://git-scm.com/book/en/v2/Git-Internals-The-Refspec
|
||||||
|
fn get_remote<'repo>(pc: &PathConfig, repo: &'repo Repository) -> Result<Remote<'repo>> {
|
||||||
|
repo.remote_set_url(&pc.config.repos.remote.name, &pc.config.repos.remote.url)?;
|
||||||
|
repo.remote_add_fetch(
|
||||||
|
&pc.config.repos.remote.name,
|
||||||
|
// We could go with "*" instead of {branch} for all remote branches.
|
||||||
|
&format!(
|
||||||
|
"+refs/heads/{}:refs/remotes/{}",
|
||||||
|
pc.config.repos.remote.branch,
|
||||||
|
pc.config.repos.remote.tracking_branch(),
|
||||||
|
),
|
||||||
|
)?;
|
||||||
|
Ok(repo.find_remote(&pc.config.repos.remote.name)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(pc: &PathConfig, repo: &mut Repository) -> Result<()> {
|
||||||
|
// First pull to make sure there are no conflicts when we push our changes.
|
||||||
|
// This will also perform validation and construct our local and remote
|
||||||
|
// environment.
|
||||||
|
let _local_branch = pull(&pc, &repo)?;
|
||||||
|
|
||||||
|
// The index corresponds to our staging area. We add all files and write out
|
||||||
|
// to a tree. The resulting tree can be found using `git ls-tree <oid>`.
|
||||||
|
// https://git-scm.com/book/en/v2/Git-Internals-Git-Objects
|
||||||
|
let mut index = match index_add(&repo)? {
|
||||||
|
Some(index) => index,
|
||||||
|
None => {
|
||||||
info!("Nothing to push. Have you run `homesync stage`?");
|
info!("Nothing to push. Have you run `homesync stage`?");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
};
|
||||||
let signature = get_signature(&pc)?;
|
|
||||||
// Retrieve the latest commit before writing to the object database.
|
// Retrieve the latest commit before writing to the object database.
|
||||||
let parent_commit = get_commit(&repo)?;
|
let parent_commit = get_commit(&repo)?;
|
||||||
let index_oid = index.write_tree()?;
|
let index_oid = index.write_tree()?;
|
||||||
|
@ -252,7 +284,8 @@ pub fn push(pc: &PathConfig, repo: &mut Repository) -> Result<()> {
|
||||||
info!("Writing index to tree `{}`.", index_oid);
|
info!("Writing index to tree `{}`.", index_oid);
|
||||||
|
|
||||||
// Commit our changes and push them to our remote.
|
// Commit our changes and push them to our remote.
|
||||||
let refspec = format!("refs/heads/{}", &pc.config.remote.branch);
|
let refspec = format!("refs/heads/{}", &pc.config.repos.remote.branch);
|
||||||
|
let signature = get_signature(&pc)?;
|
||||||
repo.commit(
|
repo.commit(
|
||||||
Some(&refspec),
|
Some(&refspec),
|
||||||
&signature,
|
&signature,
|
||||||
|
@ -262,11 +295,13 @@ pub fn push(pc: &PathConfig, repo: &mut Repository) -> Result<()> {
|
||||||
&index_tree,
|
&index_tree,
|
||||||
&[&parent_commit],
|
&[&parent_commit],
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
let mut remote = get_remote(&pc, &repo)?;
|
||||||
remote.connect(Direction::Push)?;
|
remote.connect(Direction::Push)?;
|
||||||
remote.push(&[&format!("{r}:{r}", r = refspec)], None)?;
|
remote.push(&[&format!("{r}:{r}", r = refspec)], None)?;
|
||||||
info!(
|
info!(
|
||||||
"Pushed changes to remote `{}/{}`.",
|
"Pushed changes to remote `{}`.",
|
||||||
&pc.config.remote.name, &pc.config.remote.branch
|
pc.config.repos.remote.tracking_branch(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -275,17 +310,25 @@ pub fn push(pc: &PathConfig, repo: &mut Repository) -> Result<()> {
|
||||||
pub fn pull<'repo>(pc: &PathConfig, repo: &'repo Repository) -> Result<Branch<'repo>> {
|
pub fn pull<'repo>(pc: &PathConfig, repo: &'repo Repository) -> Result<Branch<'repo>> {
|
||||||
validate_repo(&repo)?;
|
validate_repo(&repo)?;
|
||||||
|
|
||||||
|
// TODO(jrpotter): Configure our remote to point to the same URL mentioned
|
||||||
|
// in our config.
|
||||||
|
// TODO(jrpotter): If changes are available, need to stage them and then
|
||||||
|
// reapply.
|
||||||
|
|
||||||
// Establish our remote. If the remote already exists, re-configure it
|
// Establish our remote. If the remote already exists, re-configure it
|
||||||
// blindly to point to the appropriate url. Our results should now exist
|
// blindly to point to the appropriate url. Our results should now exist
|
||||||
// in a branch called `remotes/origin/<branch>`.
|
// in a branch called `remotes/origin/<branch>`.
|
||||||
// https://git-scm.com/book/it/v2/Git-Basics-Working-with-Remotes
|
// https://git-scm.com/book/it/v2/Git-Basics-Working-with-Remotes
|
||||||
// TODO(jrpotter): Configure our remote to point to the same URL mentioned
|
|
||||||
// in our config.
|
|
||||||
let mut remote = get_remote(&pc, &repo)?;
|
let mut remote = get_remote(&pc, &repo)?;
|
||||||
remote.fetch(&[&pc.config.remote.branch], None, None)?;
|
let mut fetch_options = get_fetch_options(&pc)?;
|
||||||
let remote_branch_name = format!("{}/{}", &pc.config.remote.name, &pc.config.remote.branch);
|
remote.fetch(
|
||||||
|
&[&pc.config.repos.remote.branch],
|
||||||
|
Some(&mut fetch_options),
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
|
let remote_branch_name = pc.config.repos.remote.tracking_branch();
|
||||||
let remote_branch = repo.find_branch(&remote_branch_name, BranchType::Remote)?;
|
let remote_branch = repo.find_branch(&remote_branch_name, BranchType::Remote)?;
|
||||||
info!("Fetched remote branch `{}`.", remote_branch_name);
|
info!("Fetched remote branch `{}`.", &remote_branch_name);
|
||||||
|
|
||||||
// There are two cases we need to consider:
|
// There are two cases we need to consider:
|
||||||
//
|
//
|
||||||
|
@ -293,11 +336,8 @@ pub fn pull<'repo>(pc: &PathConfig, repo: &'repo Repository) -> Result<Branch<'r
|
||||||
// available. These should be rebased relative to remote (our upstream).
|
// available. These should be rebased relative to remote (our upstream).
|
||||||
// 2. Our repository has been initialized in an empty state. The branch we
|
// 2. Our repository has been initialized in an empty state. The branch we
|
||||||
// are interested in is unborn, so we can just copy the branch from remote.
|
// are interested in is unborn, so we can just copy the branch from remote.
|
||||||
//
|
|
||||||
// TODO(jrpotter): If changes are available, need to stage them and then
|
|
||||||
// reapply.
|
|
||||||
let remote_ref = repo.reference_to_annotated_commit(remote_branch.get())?;
|
let remote_ref = repo.reference_to_annotated_commit(remote_branch.get())?;
|
||||||
if let Ok(local_branch) = repo.find_branch(&pc.config.remote.branch, BranchType::Local) {
|
if let Ok(local_branch) = repo.find_branch(&pc.config.repos.remote.branch, BranchType::Local) {
|
||||||
let local_ref = repo.reference_to_annotated_commit(local_branch.get())?;
|
let local_ref = repo.reference_to_annotated_commit(local_branch.get())?;
|
||||||
let signature = get_signature(&pc)?;
|
let signature = get_signature(&pc)?;
|
||||||
repo.rebase(Some(&local_ref), Some(&remote_ref), None, None)?
|
repo.rebase(Some(&local_ref), Some(&remote_ref), None, None)?
|
||||||
|
@ -306,7 +346,7 @@ pub fn pull<'repo>(pc: &PathConfig, repo: &'repo Repository) -> Result<Branch<'r
|
||||||
Ok(local_branch)
|
Ok(local_branch)
|
||||||
} else {
|
} else {
|
||||||
let local_branch =
|
let local_branch =
|
||||||
repo.branch_from_annotated_commit(&pc.config.remote.branch, &remote_ref, false)?;
|
repo.branch_from_annotated_commit(&pc.config.repos.remote.branch, &remote_ref, false)?;
|
||||||
info!("Created new local branch from `{}`.", remote_branch_name);
|
info!("Created new local branch from `{}`.", remote_branch_name);
|
||||||
Ok(local_branch)
|
Ok(local_branch)
|
||||||
}
|
}
|
||||||
|
@ -332,23 +372,27 @@ fn get_commit(repo: &Repository) -> Result<Commit> {
|
||||||
.map_err(|_| git2::Error::from_str("Couldn't find commit"))?)
|
.map_err(|_| git2::Error::from_str("Couldn't find commit"))?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create or retrieve the remote specified within our configuration.
|
/// Construct callbacks to supply authentication information on fetch/clone/etc.
|
||||||
///
|
fn get_fetch_options(pc: &PathConfig) -> Result<FetchOptions> {
|
||||||
/// This method also configures the fetchspec for the remote, explicitly mapping
|
let public_path = match &pc.config.ssh.public {
|
||||||
/// the remote branch against our local one.
|
Some(p) => Some(path::resolve(p)?),
|
||||||
///
|
None => None,
|
||||||
/// https://git-scm.com/book/en/v2/Git-Internals-The-Refspec
|
};
|
||||||
fn get_remote<'repo>(pc: &PathConfig, repo: &'repo Repository) -> Result<Remote<'repo>> {
|
let private_path = path::resolve(&pc.config.ssh.private)?;
|
||||||
repo.remote_set_url(&pc.config.remote.name, &pc.config.remote.url.to_string())?;
|
|
||||||
repo.remote_add_fetch(
|
let mut callbacks = RemoteCallbacks::new();
|
||||||
&pc.config.remote.name,
|
callbacks.credentials(move |_url, username_from_url, _allowed_types| {
|
||||||
// We could go with "*" instead of {branch} for all remote branches.
|
Cred::ssh_key(
|
||||||
&format!(
|
username_from_url.unwrap(),
|
||||||
"+refs/heads/{branch}:refs/remotes/origin/{branch}",
|
public_path.as_ref().map(|p| p.resolved().as_ref()),
|
||||||
branch = pc.config.remote.branch
|
private_path.as_ref(),
|
||||||
),
|
None,
|
||||||
)?;
|
)
|
||||||
Ok(repo.find_remote(&pc.config.remote.name)?)
|
});
|
||||||
|
|
||||||
|
let mut fetch_options = FetchOptions::new();
|
||||||
|
fetch_options.remote_callbacks(callbacks);
|
||||||
|
Ok(fetch_options)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a new signature at the current time.
|
/// Generate a new signature at the current time.
|
||||||
|
|
Loading…
Reference in New Issue