git: Suggest merge commit message in remote (#36430)

Ben Kunkle created

Closes #ISSUE

Adds `merge_message` field to the `UpdateRepository` proto message so
that suggested merge messages are displayed in remote projects.

Release Notes:

- git: Fixed an issue where suggested merge commit messages would not
appear for remote projects

Change summary

crates/collab/migrations.sqlite/20221109000000_test_schema.sql    | 1 
crates/collab/migrations/20250818192156_add_git_merge_message.sql | 1 
crates/collab/src/db/queries/projects.rs                          | 7 
crates/collab/src/db/queries/rooms.rs                             | 1 
crates/collab/src/db/tables/project_repository.rs                 | 2 
crates/project/src/git_store.rs                                   | 3 
crates/proto/proto/git.proto                                      | 1 
7 files changed, 14 insertions(+), 2 deletions(-)

Detailed changes

crates/collab/migrations.sqlite/20221109000000_test_schema.sql 🔗

@@ -116,6 +116,7 @@ CREATE TABLE "project_repositories" (
     "scan_id" INTEGER NOT NULL,
     "is_deleted" BOOL NOT NULL,
     "current_merge_conflicts" VARCHAR,
+    "merge_message" VARCHAR,
     "branch_summary" VARCHAR,
     "head_commit_details" VARCHAR,
     PRIMARY KEY (project_id, id)

crates/collab/src/db/queries/projects.rs 🔗

@@ -349,11 +349,11 @@ impl Database {
                                     serde_json::to_string(&repository.current_merge_conflicts)
                                         .unwrap(),
                                 )),
-
-                                // Old clients do not use abs path, entry ids or head_commit_details.
+                                // Old clients do not use abs path, entry ids, head_commit_details, or merge_message.
                                 abs_path: ActiveValue::set(String::new()),
                                 entry_ids: ActiveValue::set("[]".into()),
                                 head_commit_details: ActiveValue::set(None),
+                                merge_message: ActiveValue::set(None),
                             }
                         }),
                     )
@@ -502,6 +502,7 @@ impl Database {
                 current_merge_conflicts: ActiveValue::Set(Some(
                     serde_json::to_string(&update.current_merge_conflicts).unwrap(),
                 )),
+                merge_message: ActiveValue::set(update.merge_message.clone()),
             })
             .on_conflict(
                 OnConflict::columns([
@@ -515,6 +516,7 @@ impl Database {
                     project_repository::Column::AbsPath,
                     project_repository::Column::CurrentMergeConflicts,
                     project_repository::Column::HeadCommitDetails,
+                    project_repository::Column::MergeMessage,
                 ])
                 .to_owned(),
             )
@@ -990,6 +992,7 @@ impl Database {
                         head_commit_details,
                         scan_id: db_repository_entry.scan_id as u64,
                         is_last_update: true,
+                        merge_message: db_repository_entry.merge_message,
                     });
                 }
             }

crates/collab/src/db/queries/rooms.rs 🔗

@@ -793,6 +793,7 @@ impl Database {
                             abs_path: db_repository.abs_path,
                             scan_id: db_repository.scan_id as u64,
                             is_last_update: true,
+                            merge_message: db_repository.merge_message,
                         });
                     }
                 }

crates/collab/src/db/tables/project_repository.rs 🔗

@@ -16,6 +16,8 @@ pub struct Model {
     pub is_deleted: bool,
     // JSON array typed string
     pub current_merge_conflicts: Option<String>,
+    // The suggested merge commit message
+    pub merge_message: Option<String>,
     // A JSON object representing the current Branch values
     pub branch_summary: Option<String>,
     // A JSON object representing the current Head commit values

crates/project/src/git_store.rs 🔗

@@ -2774,6 +2774,7 @@ impl RepositorySnapshot {
                 .iter()
                 .map(|repo_path| repo_path.to_proto())
                 .collect(),
+            merge_message: self.merge.message.as_ref().map(|msg| msg.to_string()),
             project_id,
             id: self.id.to_proto(),
             abs_path: self.work_directory_abs_path.to_proto(),
@@ -2836,6 +2837,7 @@ impl RepositorySnapshot {
                 .iter()
                 .map(|path| path.as_ref().to_proto())
                 .collect(),
+            merge_message: self.merge.message.as_ref().map(|msg| msg.to_string()),
             project_id,
             id: self.id.to_proto(),
             abs_path: self.work_directory_abs_path.to_proto(),
@@ -4266,6 +4268,7 @@ impl Repository {
             .map(proto_to_commit_details);
 
         self.snapshot.merge.conflicted_paths = conflicted_paths;
+        self.snapshot.merge.message = update.merge_message.map(SharedString::from);
 
         let edits = update
             .removed_statuses

crates/proto/proto/git.proto 🔗

@@ -121,6 +121,7 @@ message UpdateRepository {
     uint64 scan_id = 9;
     bool is_last_update = 10;
     optional GitCommitDetails head_commit_details = 11;
+    optional string merge_message = 12;
 }
 
 message RemoveRepository {