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
22 changes: 22 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [

{
"name": "remap",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/main.go",
"args": [
"-m",
"/absolute/path/to/migration-archive.tar.gz",
"-c",
"/absolute/path/to/commit-map"
]
}
]
}
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,15 @@ Usage:
Flags:
-h, --help help for gh-commit-remap
-c, --mapping-file string Path to the commit map file Example: /path/to/commit-map
-m, --migration-archive string Path to the migration archive Example: /path/to/migration-archive
-m, --migration-archive string Path to the migration archive Example: /path/to/migration-archive or /path/to/migration-archive.tar.gz
```

The mapping file should be in the format produced by [git-filter-repo](https://github.com/newren/git-filter-repo). For example:
```
old new
892d146b368d74a64ad8a83af8ecc949ff20a408 0000000000000000000000000000000000000000
12f9bdd865d5f8b19eaaff8c94af10ec1d7f6e27 26b42504e90f3cb96e684be18ffbc3ff42d12383
...
```

You can provide a directory where your migration archive has been expanded, or a `.tar.gz` file, via the `--migration-archive` argument. In either case, this tool will produce a new migration archive in the current directory, appending `-REMAPPED` to the base filename of the original migration archive.
35 changes: 27 additions & 8 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package cmd
import (
"log"
"os"
"strings"

"github.com/mona-actions/gh-commit-remap/internal/archive"
"github.com/mona-actions/gh-commit-remap/internal/commitremap"
Expand Down Expand Up @@ -38,18 +39,36 @@ var rootCmd = &cobra.Command{

archivePath, _ := cmd.Flags().GetString("migration-archive")

err = commitremap.ProcessFiles(archivePath, types, commitMap)
if err != nil {
log.Fatal(err)
var extractedDir string
untarAndRetar := strings.HasSuffix(archivePath, ".tar.gz")

if untarAndRetar {
// Extract the provided migration archive so we can modify its JSON contents
extractedDir, err = archive.UnTar(archivePath, "")
if err != nil {
log.Fatalf("Error extracting migration archive: %v", err)
}
} else {
// Treat provided path as an already-extracted directory
extractedDir = archivePath
}

tarPath, err := archive.ReTar(archivePath)
if err != nil {
if err := commitremap.ProcessFiles(extractedDir, types, commitMap); err != nil {
log.Fatal(err)
} else {
// Re-package the modified directory into a new archive
tarPath, err := archive.ReTar(extractedDir)
if err != nil {
log.Fatal(err)
}
log.Printf("New archive created: %s", tarPath)
if untarAndRetar {
// Cleanup extracted directory after successful re-tar
if err := os.RemoveAll(extractedDir); err != nil {
log.Printf("Warning: failed to remove extracted directory %s: %v", extractedDir, err)
}
}
}

log.Printf("New archive created: %s", tarPath)

},
}

Expand Down
52 changes: 50 additions & 2 deletions internal/archive/archive.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package archive

import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
)

// reTarFiles creates a new tar archive from the files in the given directory.
Expand All @@ -12,8 +14,8 @@ func ReTar(archivePath string) (string, error) {
// Extract the directory name from the archivePath
dirName := filepath.Base(archivePath)

// Create the name of the new archive
archiveName := dirName + ".tar.gz"
// Create the name of the new archive with -REMAPPED suffix
archiveName := fmt.Sprintf("%s-REMAPPED.tar.gz", dirName)

err := checkTarAvailability()
if err != nil {
Expand All @@ -30,6 +32,52 @@ func ReTar(archivePath string) (string, error) {
return archiveName, nil
}

// UnTar extracts the given .tar.gz archive into the specified destination directory.
// If destDir is empty, a directory will be created in the current working directory
// using the archive base name (without the .tar.gz suffix). It returns the directory
// where the archive was extracted.
func UnTar(archiveFile, destDir string) (string, error) {
if archiveFile == "" {
return "", fmt.Errorf("archive file path is empty")
}

if err := checkTarAvailability(); err != nil {
return "", err
}

// Ensure the archive exists before attempting extraction
if _, err := os.Stat(archiveFile); err != nil {
return "", fmt.Errorf("cannot stat archive file: %w", err)
}

// Determine destination directory if not provided
if destDir == "" {
base := filepath.Base(archiveFile)
// strip .tar.gz if present
base = strings.TrimSuffix(base, ".tar.gz")
if base == filepath.Base(archiveFile) { // no .tar.gz suffix
base = strings.TrimSuffix(base, ".tgz")
}
Comment thread
robandpdx marked this conversation as resolved.
if base == "" {
base = "extracted"
}
destDir = base
}

// Create destination directory if it does not exist
if err := os.MkdirAll(destDir, 0o755); err != nil {
return "", fmt.Errorf("failed to create destination directory: %w", err)
}

// Run tar extraction
tarCmd := exec.Command("tar", "-xzf", archiveFile, "-C", destDir)
if err := tarCmd.Run(); err != nil {
return "", fmt.Errorf("error extracting archive: %w", err)
}

return destDir, nil
}

// checkTarAvailability checks if the 'tar' command is available in the system's PATH.
func checkTarAvailability() error {
_, err := exec.LookPath("tar")
Expand Down
47 changes: 47 additions & 0 deletions internal/archive/archive_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,50 @@ func TestReTar(t *testing.T) {
t.Fatalf("Original file content and extracted file content do not match")
}
}

func TestUnTar(t *testing.T) {
// Setup: create temp dir with a file, tar it using ReTar, then UnTar into new dir
srcDir, err := os.MkdirTemp("", "untar-src")
if err != nil {
t.Fatalf("failed to create temp dir: %v", err)
}
defer os.RemoveAll(srcDir)

content := []byte("hello world")
if err := os.WriteFile(filepath.Join(srcDir, "file.txt"), content, 0o644); err != nil {
t.Fatalf("failed to write file: %v", err)
}

archiveName, err := ReTar(srcDir)
if err != nil {
t.Fatalf("ReTar failed: %v", err)
}
defer os.Remove(archiveName)

// Extract using UnTar with empty destination (auto-generated)
destDir, err := UnTar(archiveName, "")
if err != nil {
t.Fatalf("UnTar failed: %v", err)
}
defer os.RemoveAll(destDir)

extractedContent, err := os.ReadFile(filepath.Join(destDir, "file.txt"))
if err != nil {
t.Fatalf("failed to read extracted file: %v", err)
}
if !bytes.Equal(content, extractedContent) {
t.Fatalf("extracted content mismatch")
}
}

func TestUnTarErrors(t *testing.T) {
// Non-existent archive
if _, err := UnTar("does-not-exist.tar.gz", ""); err == nil {
t.Fatalf("expected error for non-existent archive")
}

// Empty archive path
if _, err := UnTar("", ""); err == nil {
t.Fatalf("expected error for empty archive path")
}
}
Loading