Première publication libre de Multigit

Posté par  (site web personnel) . Édité par bobble bubble et Benoît Sibaud. Modéré par Julien Jorge. Licence CC By‑SA.
Étiquettes :
33
3
fév.
2025
Gestion de versions

Multigit est un outil graphique conçu pour simplifier la gestion de projets composés de beaucoup de dépôts git.

Une image et une vidéo valant mieux qu'un long discours, voici à quoi ça ressemble:

Screenshot

Je l'ai développé dans le cadre de mon travail chez IDEMIA où nous sommes souvent confrontés à plus de trente (voire plus de soixante) dépôts à gérer conjointement sur un projet. Dans ce contexte, la moindre opération git devient un mini-défi qu'il fallait relever quotidiennement.

Multigit est abouti et stable, il est utilisé au quotidien par plus d'une centaine de personnes (sous Windows), depuis plusieurs années. Mon employeur m'a aimablement autorisé à le publier en Open Source, ce dont je lui sais gré. Il est publié sous licence Apache 2.0

La problématique de gestion de plusieurs dépôts git conjoints pour un projet est assez peu répandue dans le monde du logiciel libre. Mais beaucoup plus dans le monde de l'entreprise. En effet, git ne gère pas la notion de droit d'accès à une partie d'un dépôt. La seule façon de restreindre l'accès à certains parties d'un projet est donc de créer un dépôt spécifique pour les y stocker, avec des droits d'accès au niveau du dépôt. Ajoutons à cela beaucoup de personnes, beaucoup de projets parfois complexes, beaucoup de sous-projets, beaucoup d'historique et on se retrouve avec une gestion des sources particulièrement complexe. Complexe … avant l'arrivée de Multigit en tout cas.

Installation

Sous Linux, la seule option d'installation disponible à l'heure actuelle est Python + pip, ou encore mieux avec pipx:

    $ sudo apt install python-pipx
    $ pipx install multigit_gx
    $ multigit

Sous Windows, un installeur graphique click-and-play vous permettra d'arriver au même résultat.

J'ai bien tenté de fournir un snap pour Linux mais snap est conçu pour empêcher à peu près tout ce que veut faire Multigit: accèder à tous vos fichiers et lancer des programmes de votre distribution (git, gitk, …)

Je ferai mieux dans la prochaine version. D'ailleurs, si vous avez des recommandations pour un packaging moderne, simple, facile à maintenir et couvrant toutes les distributions Linux, je suis preneur.

Contribution

Le projet est géré sous GitHub, les contributions ou les retours sont les bienvenus.

Aller plus loin

  • # submodules

    Posté par  . Évalué à 3 (+1/-0).

    Cool ! Très intéressant.

    Question : est-ce que vous avez tenté les git submodules ?
    J'ai joué un peu avec pendant un temps. Mais ça me rapportait, si ce n'est plus de pépins que de solutions, au moins beaucoup de … Zut, le terme de l'erreur la plus fréquente sur laquelle je tombais m'échappe. Mais ça se produisait souvent, et c'était plus une question de "quand", et pas "si" ça va se produire.

    En tout cas, je me rappelle vaguement avoir à :
    - soit virer le submodule et le "tirer" à nouveau du repo
    - soit "tricoter" avec des branches pour le remettre d'aplomb
    - et parfois, je foirais la gestion des submodules à partir du répertoire de base quand je tentais de résoudre les pépins dans les sous répertoires …

    Ça vous parle ? C'est moi qui ne sais pas m'y prendre ?

    En tout, il va falloir que j'essaye cette appli !

    Là, tout ce que j'ai automatisé, c'est la liste des repos dans une arborescence, avec branche courante, remote repos, et status (et options pour masquer l'un ou l'autre. Avec en plus la génération d'un script pour tout recréer dans un autre répertoire, à l'identique (même branche, même liste de remote). C'est pas rocket science, mais ça intéresse du monde ?

    • [^] # Re: submodules

      Posté par  (site web personnel) . Évalué à 2 (+1/-0).

      J'ai vraiment l'impression de pouvoir faire la même chose avec les submodules. J'ai pas de difficulté particulière à les utiliser, je dirais même qu'à l'usage c'est une solution plutôt bien pensée.

    • [^] # Re: submodules

      Posté par  (site web personnel) . Évalué à 5 (+3/-0).

      J'ai essayé les sous-modules ainsi qu'une ou deux autres solutions mais je les ai rapidement rejetées par rapport à mon contexte. En effet :

      • mes collègues utilisent exclusivement une interface graphique pour interagir avec git (TortoiseGit majoritairement). A l'époque où j'ai écrit Multigit, aucun client git graphique ne fournissait une gestion des sous-modules. Pour eux, ça n'auraient pas été utilisable.

      • les sous-modules demandent un apprentissage nouveau au dessus de git. Les commandes n'ont pas les mêmes noms, et il y a des opérations en plus à effectuer. Une grande partie de mes collègues (surtout il y a 4 ans) étaient déjà à la peine avec l'utilisation de git toute simple. Mon objectif est de leur simplifier la vie, pas de la leur rendre plus complexe.

      • comme le décrit si bien AncalagonTotof, avec les sous-modules, on peut se rater. Ça offre encore plus de façon de se planter avant d'arriver à un résultat attendu. Notamment, il faut être très attentif au répertoire dans lequel on lance sa commande car le résultat peut n'avoir rien à voir.

      Mon objectif en écrivant Multigit était vraiment la simplicité pour tout le monde:

      • pouvoir d'un coup d'oeil identifier les 7 dépôts modifiés parmi les 67 dépôts du projet et prendre quelques secondes pour vérifier le diff sur chacun

      • pouvoir faire les opérations du quotidien sur tous les dépôts ou un sous-ensemble de dépôt de façon très simple

      • utiliser uniquement des commandes git standard, visibles dans le log, de façon à ce que tout le monde comprenne ce qui se passe

      D'après mes collègues, j'ai plutôt réussi ma mission. Et je suis tout fier quand je vois qu'il est sur tous les écrans des développeurs de mon boulot.

      Cela dit, si les sous-modules marchent bien pour vous, continuez avec. J'ai aussi deux collègues (sur 300) qui en sont très contents et m'ont demandé d'en rajouter le support dans Multigit. J'attends d'avoir plus de demande avant de me lancer

      Un truc que les sous-modules font de façon élégante, c'est l'atomicité: un changement sur plusieurs dépôts n'est réellement publié qu'après la mise à jour de dépôt parent. C'est cool, sauf que dans nos projets, on a jamais eu de problème d'atomicité. Par contre, on a très souvent eu des modifs oubliées parce qu'elles étaient dans un dépôt profond que le développeur avait oublié qu'il avait modifié. Et pas mal d'autres problématiques liées uniquement au fait que quand on manipule beaucoup de dépôts, on a vite fait d'en rater un.

      • [^] # Re: submodules

        Posté par  . Évalué à 5 (+3/-0).

        A l'époque où j'ai écrit Multigit, aucun client git graphique ne fournissait une gestion des sous-modules. Pour eux, ça n'auraient pas été utilisable.

        Je me permet quand même de remettre en cause cette affirmation (pas les autres arguments) : GitExtensions ( un client graphique pour Windows) possède le support des sous-modules depuis plus de 13 ans.

        Note: pour l'exhaustivité de la discussion, l'autre solution (que certains disent plus facile que les sous-modules -mais que j'ai jamais testé--): git subtree

        Par contre, l'intérêt des sous-modules, c'est que le commit est ajouté à un commit du dépôt parent comme cela tu garantie que la dépendance est dans la bonne version. Comment tu fais avec Multigit?

        • [^] # Re: submodules

          Posté par  (site web personnel) . Évalué à 4 (+2/-0).

          Merci, je trouvais plus le nom de l'autre solution que j'avais regardé. Je l'ai rejetée pour les mêmes raisons, elle introduit de la complexité par rapport aux commandes git classiques et ne permet pas d'interaction graphique simple.

          Par contre, l'intérêt des sous-modules, c'est que le commit est ajouté à un commit du dépôt parent comme cela tu garantie que la dépendance est dans la bonne version.

          On fonctionne plutôt avec des conventions: l'ensemble des dépôts doit être fonctionnel et stable sur la branche principale (dev chez nous) à sa dernière version. Pour introduire des changements, on va passer par des "features branch" (le git flow quoi) et quand elle est stable, on va merger toute ces branches dans dev. Et au moment où on appuie sur le bouton git push, on publie.

          Il y a bien quelques secondes par jour où tu risques de tomber sur une dépendance désynchronisée, mais notre niveau d'activité est suffisamment faible pour que ça n'arrive jamais en pratique. Et même si ça arrivait, tu relances un git pull et tout retombe dans l'ordre.

          C'est pas tellement distinct de la façon font fonctionne n'importe quel dépôt git, la branche principale doit être stable

    • [^] # Re: submodules

      Posté par  . Évalué à 3 (+1/-0). Dernière modification le 08 février 2025 à 16:57.

      Question : est-ce que vous avez tenté les git submodules ?

      Pour avoir été dans la situation de l'op, à plusieurs reprises.

      La problématique souvent rencontré, c'est que plusieurs équipes d'une même entreprise développent des outils ou librairies qui s'utilisent les unes les autres.

      Donc à la fois, les process et les pratiques développement sont indépendantes, et il n'y a aucun "projet unique" qui regroupe tout (et surtout qui pourrait donner des directives). Et personne ne veut qu'une autre équipe puisse commiter sur le projet de son équipe, sauf à être explicitement autorisée. Des fois certaines équipes ne souhaitent même pas utiliser Git par exemple.

      Et en même temps, il faut que tout le monde puisse accéder à toutes les versions du code de toute le monde, et ce rapidement.

      Typiquement, utiliser les submodules, ben une équipe peut faire ce choix. J'ai essayé une fois. Personne n'a rien compris. On a continué à utiliser notre script qui récupérait les multiples repo git (et TFS, et les copies de fichiers en dur) nécessaires.

      Un outil comme multi-git, chiadé et visuel aurait peut être pu prendre pour le coup.

      • [^] # Re: submodules

        Posté par  (site web personnel) . Évalué à 2 (+0/-0).

        Merci, ça fait plaisir de voir que l'outil a un sens dans d'autres organisations que la mienne!

        Sur les interdépendances de librairies auxquelles tu dois pas toucher parce que tu risques de casser le projet d'un autre, mais que tu dois quand même modifier parce que t'en a besoin dans ton projet… on a exactement la même.

        En théorie, on a résolu ça avec du gitflow, et des branches stables. Sauf que la définition de "stable" est très subjective. Stable, ça veut dire que ça casse pas mon projet. Mais j'ai aucun moyen de savoir si ça a cassé un des deux autres projets qui utilisent la même lib. Les trois projets sont bien sûrs en pression temporelle max, sans aucune marge de manoeuvre pour débugger un truc qui ne concerne pas directement le client.

        On a proposé au management de mettre de la CI sur des projets de référence qu'on ne doit jamais casser mais ils ont refusé: ça coût trop cher d'avoir des intégrateurs qui feraient juste ça. Mieux vaut casser les projets de façon aveugle et les réparer à l'arrache, ça marche déjà très bien comme ça.

        (je forcis un peu le trait, dans faits, on s'en sort mais il y a toujours un moment où tu es dans une zone aveugle en terme de modifications)

    • [^] # Re: submodules

      Posté par  (site web personnel) . Évalué à 1 (+0/-0).

      La notion de submodules n'a rien à voir avec les multiples dépôts Git. Ça peut être des projets différents.

      Oui pour le partage de ton script !!

      • [^] # Re: submodules

        Posté par  . Évalué à 2 (+0/-0).

        OK, mais portez des lunettes de protection avant de regarder …

        As is, avec ses tests commentés (en particulier appendRecreateScript() que j'ai déjà écrit un paquet de fois dans d'autres script mais pas réussi à refaire ici; pas grave, pas le bout du monde; echo ... >> $recreateScript un peu partout du coup), ses bouts de doc de getopt, toussa.

        NB: le projet sur lequel je suis est poussé à la fois sur mon serveur perso et sur GitHub, d'où le traitement des multiples "remote" potentiellement définis.

        #!/bin/bash
        
        # getopts
        #     OPTARG The value of the last option argument processed by the getopts builtin command (see SHELL BUILTIN COMMANDS below).
        #     OPTIND The index of the next argument to be processed by the getopts builtin command (see SHELL BUILTIN COMMANDS below).
        #     OPTERR If set to the value 1, bash displays error messages generated by the getopts builtin command (see SHELL BUILTIN COMMANDS below).  OPTERR is initialized to 1 each time the shell is invoked or a shell script is executed.
        
        showBranch=1
        showRemote=1
        showStatus=1
        
        function usage()
        {
                echo "$(basename $0) [-brs]"
                echo "options:"
                echo "  -b      Hide branch"
                echo "  -r      Hide remote repo"
                echo "  -s      Hide repo status"
                echo "  -h      This help message"
        }
        
        while getopts "brsh" opt
        do
                case ${opt} in
                        b)
                                # echo "Option -b is triggered."
                                showBranch=0
                                ;;
                        r)
                                # echo "Option -r is triggered."
                                showRemote=0
                                ;;
                        s)
                                # echo "Option -s is triggered."
                                showStatus=0
                                ;;
                        h)
                                # echo "Option -h is triggered."
                                usage
                                exit 0
                                ;;
                        ?)
                                echo "Invalid option: -${name} ${OPTARG}."
                                exit 1
                                ;;
                esac
        done
        
        
        if [[ -t 1 ]]
        then
            ANSI_COLOR_NONE=$'\033[30m'
        
            ANSI_COLOR_RED=$'\033[31m'
            ANSI_COLOR_GREEN=$'\033[32m'
            ANSI_COLOR_YELLOW=$'\033[33m'
            ANSI_COLOR_BLUE=$'\033[34m'
            ANSI_COLOR_MAGENTA=$'\033[35m'
            ANSI_COLOR_CYAN=$'\033[36m'
        
            ANSI_COLOR_BG_RED=$'\033[41m'
            ANSI_COLOR_BG_GREEN=$'\033[42m'
            ANSI_COLOR_BG_YELLOW=$'\033[43m'
            ANSI_COLOR_BG_BLUE=$'\033[44m'
            ANSI_COLOR_BG_MAGENTA=$'\033[45m'
            ANSI_COLOR_BG_CYAN=$'\033[46m'
        
            ANSI_COLOR_RESET=$'\033[0m'
        else
            ANSI_COLOR_NONE=""
        
            ANSI_COLOR_RED=""
            ANSI_COLOR_GREEN=""
            ANSI_COLOR_YELLOW=""
            ANSI_COLOR_BLUE=""
            ANSI_COLOR_MAGENTA=""
            ANSI_COLOR_CYAN=""
        
            ANSI_COLOR_BG_RED=""
            ANSI_COLOR_BG_GREEN=""
            ANSI_COLOR_BG_YELLOW=""
            ANSI_COLOR_BG_BLUE=""
            ANSI_COLOR_BG_MAGENTA=""
            ANSI_COLOR_BG_CYAN=""
        
            ANSI_COLOR_RESET=""
        fi
        
        gitRepoList=""
        
        export recreateScript="$(pwd)/recreateScript.sh"
        
        function initRecreateScript()
        {
            echo "#!/bin/bash" > recreateScript.sh
            echo "" >> $recreateScript
            echo "" >> $recreateScript
        }
        
        #function appendRecreateScript()
        #{
        #}
        
        initRecreateScript
        
        while read folder
        do
            if [[ "_"$folder"_" == "_._" ]]
            then
                echo "Skipping folder ."
            else
                (
                    cd $folder
                    echo $ANSI_COLOR_YELLOW"Folder [$folder]"$ANSI_COLOR_RESET
        
                    ####################################################################
                    branchName=$(git branch | grep '^*' | sed -e 's#^*##g' -e 's#^ *##')
        
                    if [[ $showBranch -eq 1 ]]
                    then
                        echo -n $ANSI_COLOR_CYAN"    Branch: "$ANSI_COLOR_RESET
                        echo $ANSI_COLOR_BG_BLUE$ANSI_COLOR_YELLOW$branchName$ANSI_COLOR_RESET
        
                        echo
                    fi
        
                    ####################################################################
                    if [[ $showRemote -eq 1 ]]
                    then
                        echo $ANSI_COLOR_CYAN"    Repo:"$ANSI_COLOR_RESET
                        git remote -v | sed -e 's# (fetch)##g' -e 's# (push)##g' | sort | uniq |
                                while read name url
                                do
                                        echo "        $name $url"
                                done
        
                        echo
                    fi
        
                    export repoCount=1
        
                    echo "" >> $recreateScript
                    echo "echo" >> $recreateScript
                    echo "echo" >> $recreateScript
                    echo "echo \"Folder: $folder\"" >> $recreateScript
                    echo "(" >> $recreateScript
                    echo "    mkdir -p $folder" >> $recreateScript
        
                    git remote -v | sed -e 's# (fetch)##g' -e 's# (push)##g' | sort | uniq |
                            while read name url
                            do
                                if [[ $repoCount -eq 1 ]]
                                then
                                    echo "    # Repo #1" >> $recreateScript
                                    echo "    echo \"Initial clone of: $url, branch $branchName as $name into $folder\"" >> $recreateScript
                                    echo "    git clone -o $name -b $branchName $url $folder" >> $recreateScript
                                else
                                    echo "    (" >> $recreateScript
                                        echo "echo" >> $recreateScript
                                        echo "        # Repo #$repoCount" >> $recreateScript
                                        echo "        cd $folder" >> $recreateScript
                                        echo "        echo \"Additional remote from: $url as $name\"" >> $recreateScript
                                        echo "        git remote add $name $url" >> $recreateScript
                                    echo "    )" >> $recreateScript
                                fi
                                repoCount=$((repoCount+1))
                            done
        
                    echo ")" >> $recreateScript
                    echo "" >> $recreateScript
        
        
                    ####################################################################
                    if [[ $showStatus -eq 1 ]]
                    then
                        echo $ANSI_COLOR_CYAN"    Status: "$ANSI_COLOR_RESET
                        git status |
                            while read line
                            do
                                specialColor=""
        
                                echo $line | grep 'added' > /dev/null
                                if [[ $? -eq 0 ]]
                                then
                                    specialColor=$ANSI_COLOR_GREEN"    "
                                fi
        
                                echo $line | grep 'renamed' > /dev/null
                                if [[ $? -eq 0 ]]
                                then
                                    specialColor=$ANSI_COLOR_CYAN"    "
                                fi
        
                                echo $line | grep 'modified' > /dev/null
                                if [[ $? -eq 0 ]]
                                then
                                    specialColor=$ANSI_COLOR_YELLOW"    "
                                fi
        
                                echo $line | grep 'deleted' > /dev/null
                                if [[ $? -eq 0 ]]
                                then
                                    specialColor=$ANSI_COLOR_RED"    "
                                fi
        
                                echo "        $specialColor$line"$ANSI_COLOR_RESET
                            done
                        echo
                    fi
                )
            fi
        
            gitRepoList="$gitRepoList $folder"
        done< <(find . -type d -name .git | sed 's#/.git$##g')
        
        echo
        echo "Git repos: $gitRepoList"
        
        echo
        echo "Complete recreation script:"
        ls -AlF recreateScript.sh
        #echo "####################################################################"
        #cat recreateScript.sh
        #echo "####################################################################"
        echo
        
  • # Et pour l'initialisation ?

    Posté par  . Évalué à 3 (+3/-0).

    Bonjour et merci pour ce partage !

    J'ai exactement la même problématique au boulot pour 2 grosses applications maison composée chacune de multiples repo git.

    On a développé un outil maison qui gère les git clone/pull/… un peu comme multigit. On se base sur un fichier de config qui ressemble furieusement à celui de multigit.
    Les principales différences, c'est qu'on n'a pas d'IHM, et que notre outil gère en plus l'aspect installation via npm (tous nos modules sont en NodeJS), plus quelques subtilités liées à l'architecture de nos 2 applis.

    Je me demandais : comment gérez-vous le deploiement initial ? Est-ce que chacun de vos développeurs gère lui-même ses propres git clone pour chacun de ses repositories ? Ou bien avez-vous des fichiers de config prêts à l'emploi que vous vous partagez selon le périmètre de travail de chacun ?

    De notre côté, on a intégré 2 listes de modules pré-configurés, ce qui permet d'avoir rapidement un environnement prêt à l'emploi selon que l'on travaille sur l'une ou l'autre de nos 2 applications.

    En tous cas, c'est intéressant de voir qu'à l'heure où les monorepos sont à la mode, le multirepos a toujours son mot à dire.

    PS : j'ai tenté l'installation sous Ubuntu 24.04 via pipx. Malheureusement multigit renvoie une erreur de segmentation au démarrage.

    • [^] # Re: Et pour l'initialisation ?

      Posté par  (site web personnel) . Évalué à 4 (+2/-0).

      Oui, depuis quelques années, il y a pas mal d'outils qui fleurissent en ligne de commande pour gérer tout cela. Il y en avait un déjà quand j'ai commencé, c'était repo fourni par google pour Android.

      De mon côté, j'ai peu développé l'aspect ligne de commande parce que c'est pas là où il y avait des besoins. Cela dit, ça revient lentement.

      Pour le "bootstrap" d'un projet, deux approches possibles:

      • la plus simple : un développeur crée l'arborescence de dépôt dont il a besoin sur son poste. Multigit va découvrir tous les dépôts et il pourra exporter le fichier multigit pour ses camarades

      • le plus geek: tu prépares à la main le bon fichier multigit en JSON que tu refiles à tes camarades.

      Dans les deux cas, chaque projet est défini par un fichier multigit que nous partageons dans un lieu central. Chacun va ensuite faire "clone from multigit file" et se retrouver avec un projet prêt à l'emploi.

      Une autre fonction qui a été demandée et qui est bien pratique, c'est de mettre l'ensemble des dépôts dans le même état exact que celui de son voison, ou celui qui est sorti de la CI. C'est "Apply multigit file"

      Pour ton segfault, c'est pas cool. Tu peux me contacter en privé stp à phil.fremy at free.fr ?

  • # En ligne de commande

    Posté par  . Évalué à 2 (+1/-0).

    Pour les adeptes de la ligne de commande, moi j'utilise https://github.com/tkrajina/git-plus qui permet des commandes genre "git multi pull".

  • # Repo

    Posté par  . Évalué à 2 (+2/-0).

    Et par rapport a repo (l'outil de Google), cela apporte quoi de plus ?

    • [^] # Re: Repo

      Posté par  (site web personnel) . Évalué à 4 (+2/-0).

      Je dirais pas que ça apporte en plus, c'est une approche différente:

      • on continue à utiliser des commandes git contrairement à repo qui utilise une nomenclature différente: sync, upload, … . Pas besoin d'apprendre un nouvel outil et une nouvelle façon de fonctionner

      • il y a une interface graphique qui permet de repérer visuellement plus facilement l'état global d'un projet et d'agir facilement sur un sous-ensemble de dépôts

  • # Excellent

    Posté par  (site web personnel) . Évalué à 1 (+0/-0).

    Bravo pour l'outil, qui semble bien fini et ergonomique (j'aime bien le popup du double click qui t'invite à configurer le comportement).

    J'ai toujours voulu coder un outil similaire mais flemme, là très bien c'est fait.

    J'ai une soixantaines de repos git donc c'est bien pratique en un coup d'oeil on voit le statut.

    J'ai eu un popup d'erreur concernant l'objet HEAD sur quelques repositories mais ça n'a pas crashé.

Envoyer un commentaire

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.