mz_deploy/cli/git.rs
1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10//! Git metadata helpers.
11//!
12//! Retrieves the current commit hash and detects uncommitted changes in the
13//! project directory. Used to tag deployments with source-control context.
14
15use std::process::Command;
16
17/// Get the current git commit hash of the project directory.
18///
19/// Returns Some(commit_hash) if the directory is a git repository with a valid HEAD,
20/// otherwise returns None.
21///
22/// # Arguments
23/// * `directory` - Path to the project directory
24pub fn get_git_commit(directory: &std::path::Path) -> Option<String> {
25 let output = Command::new("git")
26 .arg("rev-parse")
27 .arg("HEAD")
28 .current_dir(directory)
29 .output()
30 .ok()?;
31
32 if !output.status.success() {
33 return None;
34 }
35
36 let commit = String::from_utf8(output.stdout).ok()?;
37 Some(commit.trim().to_string())
38}
39
40/// Returns `true` if the repo contains any uncommitted or unstaged changes.
41///
42/// This uses `git status --porcelain`, which outputs a line for each changed file.
43/// Any non-empty output means the repo is dirty.
44///
45/// # Arguments
46/// * `directory` - Path to the project directory
47pub fn is_dirty(directory: &std::path::Path) -> bool {
48 let output = Command::new("git")
49 .args(["status", "--porcelain"])
50 .current_dir(directory)
51 .output();
52
53 let out = match output {
54 Ok(out) => out,
55 Err(_) => return false,
56 };
57
58 !String::from_utf8_lossy(&out.stdout).trim().is_empty()
59}