Le chaos des dépôts Git

Au fil des années, j’ai vu mon répertoire personnel se transformer en un véritable dédale de projets Git clonés pas toujours avec une organisation optimale et la perte de temps que ca engendrait.

Frustré par cette situation et par l’absence d’outils adaptés à ma façon de travailler, j’ai écrit un script bash qui clone de manière plus pratique.

Il organise intelligemment les projets par fournisseur, crée des liens symboliques à la demande et s’intègre avec zoxide pour une navigation ultra-rapide. Une solution simple mais efficace qui a amélioré ma façon de gérer mes projets.

Fonctionnalités clés du script

  • Organisation automatique par fournisseur : Le script détecte le fournisseur Git et structure vos dépôts dans des répertoires dédiés.
  • Intégration avec zoxide : Chaque répertoire cloné est ajouté à zoxide pour une navigation simplifiée entre projets.
  • Gestion des liens symboliques : Créez optionnellement un lien dans votre répertoire courant pour accéder rapidement au projet.
  • Support des tokens : L’option --token permet de cloner facilement des dépôts privés sans compromettre vos identifiants.
  • Multi-fournisseurs : Compatible avec GitHub, GitLab, Bitbucket et d’autres plateformes de gestion de code source.

Installation et prérequis

Dépendances

Avant d’installer le script “clone”, assurez-vous d’avoir les outils suivants :

  • git : Évidemment nécessaire pour cloner les dépôts
  • fzf : Utilisé pour les confirmations interactives
  • zoxide (optionnel) : Améliore la navigation entre les projets

Installation

# Téléchargez le script
curl -o ~/.local/bin/clone https://chemin-vers-votre-script/clone

# Rendez-le exécutable
chmod +x ~/.local/bin/clone

# Assurez-vous que ~/.local/bin est dans votre PATH
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

Configuration

Le script utilise par défaut ~/repos comme répertoire de base pour tous vos dépôts. Vous pouvez modifier cette valeur en éditant la variable BASE_DIR au début du script si vous préférez une autre structure.

Exemples d’utilisation

Cloner un dépôt GitHub public

$ clone https://github.com/username/project.git
Repository cloned into /home/user/repos/github/project
Directory added to zoxide

Le script détecte automatiquement qu’il s’agit d’un dépôt GitHub, le place dans ~/repos/github/project et l’ajoute à zoxide pour une navigation rapide.

Travailler avec des dépôts privés

$ clone https://gitlab.com/username/private-project.git --token
Enter your token: [votre token masqué]
Repository cloned into /home/user/repos/gitlab/private-project
Directory added to zoxide

L’option --token vous permet d’entrer un token d’accès de manière sécurisée, sans l’exposer dans votre historique de commandes ou vos logs.

Créer un lien symbolique

Quand vous clonez un projet, le script vous propose de créer un lien symbolique dans votre répertoire courant :

Add symlink in current dir ?
> Yes
  No
Symlink created in current directory

Cette option est pratique pour accéder rapidement au projet depuis votre emplacement actuel.

Gérer les dépôts existants

Si vous tentez de cloner un dépôt déjà présent dans votre structure :

⚠️ github/project already exists, overwrite it ?
> Yes
  No

Vous pouvez choisir de conserver votre version locale ou de l’écraser avec la nouvelle.

Une fois vos projets clonés, naviguez facilement entre eux grâce à l’intégration zoxide :

$ z project
# Vous amène directement à ~/repos/github/project

Le code 🤖

Voici le script en question :

#!/bin/bash

################################################################################
# Script Name: clone.sh
# Description: This script clones a Git repository from a given URL and organizes
#              it into a directory structure based on the provider. It also adds
#              the cloned directory to zoxide if zoxide is installed.
#
# Usage: clone <repository-url>
#
# Providers Supported:
# - GitHub (github.com)
# - GitLab (gitlab.com)
# - Bitbucket (bitbucket.org)
# - Azure DevOps (dev.azure.com)
# - AWS CodeCommit (git-codecommit.<region>.amazonaws.com)
# - Gitea (custom domains)
# - GitBucket (custom domains)
# - Custom providers with subdomains (e.g., <provider>.<company>.com)
#
# Requirements:
# - git (for cloning repositories)
# - zoxide (optional, for adding directories)
#
# License: MIT
# Copyright (c) 2025 🦊 Flobsx
#
################################################################################

# Base directory for cloned repositories (configurable)
BASE_DIR="${HOME}/repos"

# Function to clone a repository and add it to zoxide if installed
clone() {
    local url="$1"
    local provider=""
    local projectpath=""

    # Extract the domain from the URL
    local domain=$(echo "$url" | sed -n 's|.*://\([^/]*\).*|\1|p')
    if [ -z "$domain" ]; then
        local domain=$(echo "$url" | sed -n 's/.*@\([^:/]*\).*/\1/p' || echo "$url" | sed -n 's|.*://\([^/]*\).*|\1|p')
    fi
    if [ -z "$domain" ]; then
        echo "❌ Invalid URL"
        return 1
    fi

    # Remove user:password@ or token@ if present
    if [[ "$domain" =~ .*@(.*) ]]; then
        domain="${BASH_REMATCH[1]}"
    fi

    # Determine the provider based on the domain
    case "$domain" in
        gitlab.com)
            provider="gitlab"
            ;;
        github.com)
            provider="github"
            ;;
        bitbucket.org)
            provider="bitbucket"
            ;;
        dev.azure.com)
            provider="azure"
            ;;
        git-codecommit.*.amazonaws.com)
            provider="aws"
            ;;
        *.gitea.*)
            provider=$(echo "$domain" | sed -n 's/.*\.\(.*\)\.gitea.*/\1/p')
            ;;
        *.gitbucket.*)
            provider=$(echo "$domain" | sed -n 's/.*\.\(.*\)\.gitbucket.*/\1/p')
            ;;
        *)
            if [[ "$domain" =~ (.*)\.(.*).com ]]; then
                provider="${BASH_REMATCH[2]}"
            else
                echo "Provider not recognized for domain: $domain"
                return 1
            fi
            ;;
    esac

    # Add token to the URL if provided
    if [ -n "$TOKEN" ]; then
        url=$(echo "$url" | sed "s|://$domain|://$TOKEN@$domain|")
    fi

    # Extract the project path from the URL
    projectpath=$(echo "$url" | sed -n 's|.*/\([^.]*\)|\1|p' | sed 's/\.git$//')

    # Define the target directory
    local target_dir="$BASE_DIR/$provider/$projectpath"

    # Create the provider directory if it doesn't exist
    mkdir -p "$BASE_DIR/$provider"

    # Check if the target directory already exists
    if [ -d "$target_dir" ]; then
        ADD_SYMLINK=$(fzf --header="⚠️ $provider/$projectpath already exists, overwrite it ?" --tac <<<$'Yes\nNo')
        if [[ $ADD_SYMLINK == Yes ]]; then
            echo "Overwriting the existing directory..."
            rm -rf "$target_dir"
        else
            echo "Clone aborted"
            return
        fi
    fi

    # Clone the repository into the target directory
    git clone "$url" "$target_dir"

    echo "Repository cloned into $target_dir"

    ADD_SYMLINK=$(fzf --header="Add symlink in current dir ?" --tac <<<$'Yes\nNo')
    if [[ $ADD_SYMLINK == Yes ]]; then
        ln -s "${target_dir}" .
        echo "Symlink created in current directory"
    fi

    # Check if zoxide is installed and add the directory to zoxide
    if command -v zoxide &> /dev/null; then
        zoxide add "$target_dir"
        echo "Directory added to zoxide"
    # else
    #     echo "zoxide is not installed. Directory not added to zoxide."
    fi
}

# Function to display help
display_help() {
    echo "Usage: clone <repository-url> [--token] [--help]"
    echo
    echo "Description:"
    echo "This script clones a Git repository from a given URL and organizes"
    echo "it into a directory structure based on the provider. It also adds"
    echo "the cloned directory to zoxide if zoxide is installed."
    echo
    echo "Options:"
    echo "  --token    Prompt for a token to include in the repository URL."
    echo "  --help     Display this help message."
    echo
    echo "Providers Supported:"
    echo "  - GitHub (github.com)"
    echo "  - GitLab (gitlab.com)"
    echo "  - Bitbucket (bitbucket.org)"
    echo "  - Azure DevOps (dev.azure.com)"
    echo "  - AWS CodeCommit (git-codecommit.<region>.amazonaws.com)"
    echo "  - Gitea (custom domains)"
    echo "  - GitBucket (custom domains)"
    echo "  - Custom providers with subdomains (e.g., <provider>.<company>.com)"
}

# Check if an argument is provided
if [ -z "$1" ] || [[ "$@" == *"--help"* ]]; then
    display_help
    exit 0
fi

# Check if --token flag is provided
TOKEN=""
if [[ "$@" == *"--token"* ]]; then
    read -sp "Enter your token: " TOKEN
    echo
fi

# Call the clone function with the provided URL
clone "$1"

Comment ça fonctionne

Détection du fournisseur

Le script commence par extraire le domaine de l’URL fournie en utilisant des expressions régulières :

local domain=$(echo "$url" | sed -n 's|.*://\([^/]*\).*|\1|p')

Une fois le domaine isolé, une structure case permet d’identifier le fournisseur :

case "$domain" in
    gitlab.com)
        provider="gitlab"
        ;;
    github.com)
        provider="github"
        ;;
    # Autres fournisseurs...
esac

Pour les domaines personnalisés, le script utilise des expressions régulières plus complexes pour extraire la partie pertinente du nom de domaine.

Extraction du chemin du projet

Le script isole intelligemment le nom du projet depuis l’URL :

projectpath=$(echo "$url" | sed -n 's|.*/\([^.]*\)|\1|p' | sed 's/\.git$//')

Cette expression régulière extrait la dernière partie de l’URL (après le dernier slash) et supprime l’extension .git si elle est présente.

Organisation des répertoires

Une structure cohérente est créée pour chaque projet :

local target_dir="$BASE_DIR/$provider/$projectpath"
mkdir -p "$BASE_DIR/$provider"

Cette approche organise tous les projets selon ~/repos/fournisseur/ projet, ce qui facilite énormément la navigation et la gestion.

Gestion des liens symboliques

Le script propose de créer un lien symbolique dans le répertoire courant via une interface interactive utilisant fzf :

ADD_SYMLINK=$(fzf --header="Add symlink in current dir ?" --tac <<<$'Yes\nNo')
if [[ $ADD_SYMLINK == Yes ]]; then
    ln -s "${target_dir}" .
    echo "Symlink created in current directory"
fi

Intégration avec zoxide

Pour faciliter la navigation ultérieure, le script ajoute automatiquement le répertoire cloné à zoxide :

if command -v zoxide &> /dev/null; then
    zoxide add "$target_dir"
    echo "Directory added to zoxide"
fi

Cette fonctionnalité permet ensuite d’accéder au projet depuis n’importe où en utilisant simplement z nom-du-projet.

Comment ce script a transformé mon workflow

Ce script a été un véritable game-changer dans ma gestion quotidienne de projets. Fini les minutes perdues à rechercher mes repos !

À vous de jouer

Ce script reste volontairement simple et modulaire. N’hésitez pas à l’adapter à vos besoins ou à le faire évoluer.

Le script est disponible sur mon dépôt de dotfiles, et les contributions sont les bienvenues. Qu’il s’agisse de corriger un bug, d’ajouter une fonctionnalité ou simplement de suggérer une amélioration, votre retour m’intéresse.

Et vous ?

Et vous, comment organisez-vous vos dépôts Git en local ?

Partagez vos astuces et expériences dans les commentaires !