Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock

# These are backup files generated by rustfmt
**/*.rs.bk
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ You may need to close and reopen your terminal after installation. Alternatively
export OPENAI_API_KEY='sk-XXXXXXXX'
```

`auto-commit` uses the `AUTO_COMMIT_MODEL` environment variable to choose which OpenAI model to use when generating commit messages. If this variable is not set, the tool defaults to `gpt-4.1-nano`.

```bash
export AUTO_COMMIT_MODEL='gpt-4.1-nano'
```

Once you have configured your environment, stage some changes by running, for example, `git add .`, and then run `auto-commit`.

Of course, `auto-commit` also includes some options, for editing the message before commiting, or just printing the message to the terminal.
Expand Down
10 changes: 7 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ pub fn truncate_to_n_tokens(text: &str, limit: usize) -> String {
text.split_whitespace().take(limit).collect::<Vec<_>>().join(" ")
}

pub const DEFAULT_MODEL: &str = "gpt-4.1-nano";

pub fn get_model_from_env() -> String {
std::env::var("AUTO_COMMIT_MODEL").unwrap_or_else(|_| "gpt-4.1-nano".to_string())
std::env::var("AUTO_COMMIT_MODEL").unwrap_or_else(|_| DEFAULT_MODEL.to_string())

}

#[cfg(test)]
Expand All @@ -19,7 +22,7 @@ mod tests {
#[test]
fn test_get_model_from_env_default() {
std::env::remove_var("AUTO_COMMIT_MODEL");
assert_eq!(get_model_from_env(), "gpt-4.1-nano");
assert_eq!(get_model_from_env(), DEFAULT_MODEL);
}

#[test]
Expand Down Expand Up @@ -81,4 +84,5 @@ mod tests {
assert_eq!(get_model_from_env(), custom);
std::env::remove_var("AUTO_COMMIT_MODEL");
}
}
}

69 changes: 50 additions & 19 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ impl ToString for Commit {
}
}

const MAX_DIFF_TOKENS: usize = 20_000;

#[tokio::main]
async fn main() -> Result<(), ()> {
let cli = Cli::parse();
Expand All @@ -80,10 +82,19 @@ async fn main() -> Result<(), ()> {
.arg("diff")
.arg("--staged")
.output()
.expect("Couldn't find diff.")
.map_err(|e| {
error!("Failed to get staged diff: {}", e);
()
})?
.stdout;

let git_staged_cmd = str::from_utf8(&git_staged_cmd).unwrap();
let git_staged_cmd = match str::from_utf8(&git_staged_cmd) {
Ok(v) => v,
Err(e) => {
error!("Staged diff output was not valid UTF-8: {}", e);
""
}
};

if git_staged_cmd.is_empty() {
error!("There are no staged files to commit.\nTry running `git add` to stage some files.");
Expand All @@ -93,10 +104,22 @@ async fn main() -> Result<(), ()> {
.arg("rev-parse")
.arg("--is-inside-work-tree")
.output()
.expect("Failed to check if this is a git repository.")
.map_err(|e| {
error!("Failed to check if this is a git repository: {}", e);
()
})?
.stdout;

if str::from_utf8(&is_repo).unwrap().trim() != "true" {
if match str::from_utf8(&is_repo) {
Ok(v) => v.trim() != "true",
Err(e) => {
error!("Git repository check output was not valid UTF-8: {}", e);
true // Treat as not a repo if output is invalid
}
} {
error!("It looks like you are not in a git repository.\nPlease run this command from the root of a git repository, or initialize one using `git init`.");
std::process::exit(1);
}
error!("It looks like you are not in a git repository.\nPlease run this command from the root of a git repository, or initialize one using `git init`.");
std::process::exit(1);
}
Expand All @@ -108,29 +131,37 @@ async fn main() -> Result<(), ()> {
.arg("--name-only")
.arg("--staged")
.output()
.expect("Couldn't get changed files.")
.map_err(|e| {
error!("Couldn't get changed files: {}", e);
()
})?
.stdout;
let files_changed = str::from_utf8(&files_output).map_err(|e| {
error!("Git diff --name-only output is not valid UTF-8: {}. Attempting lossy conversion.", e);
// Decide on error strategy, e.g. propagate with `?` after mapping to `()`
// For a direct replacement that avoids panic but might have imperfect strings:
// String::from_utf8_lossy(&files_output).into_owned()
// Or, to propagate the error if main returns Result:
// return Err(()); // after mapping error to () for main's signature
// For now, let's suggest a pattern that would fit with `?` if error mapped
panic!("Non-UTF8 output from git: {}", e); // Placeholder, better to map and use `?`
}).unwrap_or_else(|_| String::new()); // Fallback to empty or handle error properly
let files_changed = match str::from_utf8(&files_output) {
Ok(v) => v,
Err(e) => {
error!("Changed files output was not valid UTF-8: {}", e);
""
}
};

let diff_output = Command::new("git")
.arg("diff")
.arg("--staged")
.output()
.expect("Couldn't find diff.")
.map_err(|e| {
error!("Couldn't find diff: {}", e);
()
})?
.stdout;
let diff_output = str::from_utf8(&diff_output).unwrap();
let diff_output = match str::from_utf8(&diff_output) {
Ok(v) => v,
Err(e) => {
error!("Diff output was not valid UTF-8: {}", e);
""
}
};

let combined = format!("Changed files:\n{}\n\nDiff:\n{}", files_changed, diff_output);
const MAX_DIFF_TOKENS: usize = 20_000; // Or define elsewhere
let output = truncate_to_n_tokens(&combined, MAX_DIFF_TOKENS);

if !cli.dry_run {
Expand Down Expand Up @@ -186,7 +217,7 @@ async fn main() -> Result<(), ()> {
ChatCompletionRequestMessage {
role: Role::System,
content: Some(
"You are an experienced programmer who writes great commit messages."
"You are an experienced developer who writes great commit messages."
.to_string(),
),
..Default::default()
Expand Down