Skip to content

Commit

Permalink
git colocate: new command
Browse files Browse the repository at this point in the history
  • Loading branch information
samueltardieu committed Oct 17, 2024
1 parent 33f9b89 commit 7f34602
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 0 deletions.
6 changes: 6 additions & 0 deletions cli/src/cli_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2871,6 +2871,12 @@ impl From<String> for RevisionArg {
}
}

impl From<&'static str> for RevisionArg {
fn from(s: &'static str) -> Self {
RevisionArg(s.into())
}
}

impl AsRef<str> for RevisionArg {
fn as_ref(&self) -> &str {
&self.0
Expand Down
104 changes: 104 additions & 0 deletions cli/src/commands/git/colocate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright 2024 The Jujutsu Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::ffi::OsStr;
use std::fs;
use std::path::PathBuf;

use crate::cli_util::CommandHelper;
use crate::cli_util::WorkspaceCommandHelper;
use crate::command_error::user_error;
use crate::command_error::CommandError;
use crate::ui::Ui;

use super::export;
use super::export::GitExportArgs;

/// Transform a non-colocated git repository into a colocated one,
/// and vice-versa.
#[derive(clap::Args, Clone, Debug)]
pub struct GitColocateArgs {
/// Transform a colocated repository into a non-colocated one.
#[arg(long)]
undo: bool,
}

pub fn cmd_git_colocate(
ui: &mut Ui,
command: &CommandHelper,
args: &GitColocateArgs,
) -> Result<(), CommandError> {
let workspace_command = command.workspace_helper(ui)?;
if workspace_command.git_backend().is_none() {
return Err(user_error("The repo is not backed by a git repo"));
}
if args.undo {
undo_git_colocate(&workspace_command)
} else {
do_git_colocate(&workspace_command)?;
export::cmd_git_export(ui, command, &GitExportArgs {})
}
}

/// Returns a tuple with:
/// - the colocated git repo path
/// - the uncolocated repo path
/// - the .jj/repo/store/git_target path
/// - the .jj/.gitignore path
fn git_repo_paths(
workspace_command: &WorkspaceCommandHelper,
) -> Result<(PathBuf, PathBuf, PathBuf, PathBuf), CommandError> {
let workspace_root = workspace_command.workspace_root();
let store_path = workspace_command
.repo_path()
.to_owned()
.clone()
.join("store");
Ok((
workspace_root.join(".git"),
store_path.clone().join("git"),
store_path.join("git_target"),
workspace_root.join(".jj").join(".gitignore"),
))
}

fn do_git_colocate(workspace_command: &WorkspaceCommandHelper) -> Result<(), CommandError> {
let (colocated_path, uncolocated_path, git_target_path, gitignore_path) =
git_repo_paths(workspace_command)?;
if fs::exists(&colocated_path)? {
return Err(user_error("The repo is already colocated"));
}
fs::rename(uncolocated_path, &colocated_path)?;
git2::Repository::open_bare(&colocated_path)?
.config()?
.remove("core.bare")?;
fs::write(git_target_path, "../../../.git")?;
if !fs::exists(&gitignore_path)? {
fs::write(gitignore_path, "/*\n")?;
}
Ok(())
}

fn undo_git_colocate(workspace_command: &WorkspaceCommandHelper) -> Result<(), CommandError> {
let (colocated_path, uncolocated_path, git_target_path, _) = git_repo_paths(workspace_command)?;
if !fs::exists(&colocated_path)? {
return Err(user_error("The repo is not colocated"));
}
fs::rename(colocated_path, &uncolocated_path)?;
git2::Repository::open_ext::<_, &OsStr, _>(uncolocated_path, git2::RepositoryOpenFlags::NO_DOTGIT, vec![])?
.config()?
.set_bool("core.bare", true)?;
fs::write(git_target_path, "git")?;
Ok(())
}
5 changes: 5 additions & 0 deletions cli/src/commands/git/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

pub mod clone;
pub mod colocate;
pub mod export;
pub mod fetch;
pub mod import;
Expand All @@ -25,6 +26,8 @@ use clap::Subcommand;

use self::clone::cmd_git_clone;
use self::clone::GitCloneArgs;
use self::colocate::cmd_git_colocate;
use self::colocate::GitColocateArgs;
use self::export::cmd_git_export;
use self::export::GitExportArgs;
use self::fetch::cmd_git_fetch;
Expand Down Expand Up @@ -54,6 +57,7 @@ use crate::ui::Ui;
#[derive(Subcommand, Clone, Debug)]
pub enum GitCommand {
Clone(GitCloneArgs),
Colocate(GitColocateArgs),
Export(GitExportArgs),
Fetch(GitFetchArgs),
Import(GitImportArgs),
Expand All @@ -72,6 +76,7 @@ pub fn cmd_git(
) -> Result<(), CommandError> {
match subcommand {
GitCommand::Clone(args) => cmd_git_clone(ui, command, args),
GitCommand::Colocate(args) => cmd_git_colocate(ui, command, args),
GitCommand::Export(args) => cmd_git_export(ui, command, args),
GitCommand::Fetch(args) => cmd_git_fetch(ui, command, args),
GitCommand::Import(args) => cmd_git_import(ui, command, args),
Expand Down

0 comments on commit 7f34602

Please sign in to comment.