I have a local git repository that is being maintained through git2-rs, a pretty much one-to-one Rust wrapper around the C library libgit2. I have managed to stage and commit changes using the library. However, I cannot manage to push changes to the remote repository. When I try to connect to the remote I get an error with the following message:
request failed with status code: 401
Here is my code:
let repo: Repository = /* get repository */;
let mut remote = repo.find_remote("origin").unwrap();
// connect returns Err, and so this panics.
remote.connect(Direction::Push).unwrap();
I have also tried to pass various credentials, but the same error occurs:
let mut callbacks = RemoteCallbacks::new();
callbacks.credentials(|str, str_opt, cred_type| {
Ok(Cred::userpass_plaintext("natanfudge", env!("GITHUB_PASSWORD")).unwrap())
});
remote
.connect_auth(Direction::Push, Some(callbacks), None)
.unwrap();
let mut callbacks = RemoteCallbacks::new();
callbacks.credentials(|str, str_opt, cred_type| {
// This line does not panic, only the connect_auth!
Ok(Cred::ssh_key_from_agent("natanfudge").expect("Could not get ssh key from ssh agent"))
});
remote
.connect_auth(Direction::Push, Some(callbacks), None)
.unwrap();
What am I missing?
Ok, I solved the problem. There are 3 things that need to be done for this to work:
Using connect_auth
with credentials
is right.
You also need to specify the same credentials with remote.push
.
You must specify the same refspec string that you did in remote_add_push
in remote.push
.
So this code works:
fn create_callbacks<'a>() -> RemoteCallbacks<'a>{
let mut callbacks = RemoteCallbacks::new();
&callbacks.credentials(|str, str_opt, cred_type| {
Cred::userpass_plaintext("your-username",env!("GITHUB_PASSWORD"))
});
callbacks
}
fn main() {
let repo = /* get repository */
let mut remote = repo.find_remote("origin").unwrap();
remote.connect_auth(Direction::Push, Some(create_callbacks()), None).unwrap();
repo.remote_add_push("origin", "refs/heads/<branch-name>:refs/heads/<branch-name>").unwrap();
let mut push_options = PushOptions::default();
let mut callbacks = create_callbacks();
push_options.remote_callbacks(callbacks);
remote.push(&["refs/heads/<branch-name>:refs/heads/<branch-name>"], Some(&mut push_options)).unwrap();
std::mem::drop(remote);
Ok(())
}
For debugging, using the push_update_reference
callback is useful. It will say if there was a problem pushing.
let mut push_options = PushOptions::default();
let mut callbacks = create_callbacks();
callbacks.push_update_reference(|ref,error|{
println!("ref = {}, error = {:?}", ref, error);
Ok(())
});
remote.push(&["refs/heads/<branch-name>:refs/heads/<branch-name>"], Some(&mut
push_options)).unwrap();