web-dev-qa-db-fra.com

Git pour les utilisateurs de Perforce

J'utilise Perforce depuis un certain nombre d'années. J'aimerais passer à l'utilisation de git pour mon code personnel, mais tous les didacticiels git que j'ai vus supposent que vous êtes un contrôle de source complet n00b (ce qui les rend incroyablement fastidieux) ou que vous êtes habitué à svn (ce que je ne suis pas).

Je connais p4, et je comprends aussi l'idée derrière un système de contrôle de source distribué (donc je n'ai pas besoin d'un argumentaire de vente, merci). Ce que j'aimerais, c'est une table de traduction de la commande p4 en commandes git équivalentes, ainsi que les commandes "ne peut pas vivre sans" qui n'ont pas d'équivalent p4.

Étant donné que je soupçonne que chaque utilisateur de p4 utilise un sous-ensemble différent de p4, voici certaines des choses que je fais régulièrement en p4 que j'aimerais pouvoir faire dans git qui ne sont pas immédiatement évidentes d'après les documents que j'ai examinés :

  1. créer plusieurs listes de modifications en attente dans un seul client. (p4 change)
  2. modifier une liste de modifications en attente. (aussi p4 change)
  3. voir la liste de toutes mes listes de modifications en attente (p4 changes -s pending)
  4. liste de tous les fichiers modifiés dans mon client (p4 opened) ou dans une liste de modifications en attente (p4 describe)
  5. voir un diff d'une liste de modifications en attente (j'utilise un script wrapper pour cela qui utilise p4 diff et p4 describe)
  6. pour un fichier donné, voyez quelles listes de modifications soumises affectent quelles lignes (p4 annotate)
  7. pour un fichier donné, consultez la liste des descriptions des listes de modifications qui ont affecté le fichier (p4 log)
  8. soumettre une liste de modifications en attente (p4 submit -c)
  9. abandonner une liste de modifications en attente (p4 revert)

Beaucoup d'entre eux tournent autour de "listes de modifications". "changelist" est la terminologie p4. Quel est le terme équivalent git?

Il semble que les branches soient ce que les utilisateurs de git utilisent à la place de ce que p4 appelle les listes de modifications. Un peu déroutant, car p4 a aussi quelque chose appelé une branche bien qu'ils ne semblent être que des concepts vaguement liés. (Bien que j'ai toujours pensé que le concept de branche de p4 était assez bizarre, il est encore différent du concept RCS classique de branche.)

Quoi qu'il en soit ... Je ne sais pas comment accomplir ce que je fais normalement dans les listes de modifications p4 avec les branches de git. En p4, je peux faire quelque chose comme ceci:

$ p4 edit a.txt
$ p4 change a.txt
Change 12345 created.

À ce stade, j'ai une liste de modifications qui contient a.txt. Je peux modifier la description et continuer à travailler sans soumettre la liste des modifications. De plus, s'il s'avère que je dois apporter des modifications à d'autres fichiers, comme par exemple un correctif dans une autre couche du code, je peux le faire dans le même client:

$ p4 edit z.txt
$ p4 change z.txt
Change 12346 created.

Maintenant, j'ai deux listes de modifications distinctes dans le même client. Je peux travailler dessus en même temps et je n'ai rien à faire pour les "basculer" entre eux. Quand vient le temps de s'engager, je peux les soumettre séparément:

$ p4 submit -c 12346  # this will submit the changes to z.txt
$ p4 submit -c 12345  # this will submit the changes to a.txt

Je ne peux pas comprendre comment reproduire cela dans git. D'après mes expériences, il ne semble pas que git add est associé à la branche actuelle. Autant que je sache, quand je git commit ça va valider tous les fichiers que je git add- ed quelle que soit la branche dans laquelle j'étais à l'époque:

$ git init
Initialized empty Git repository in /home/laurence/git-playground/.git/
$ ls
a.txt  w.txt  z.txt
$ git add -A .
$ git commit
 Initial commit.
 3 files changed, 3 insertions(+), 0 deletions(-)
 create mode 100644 a.txt
 create mode 100644 w.txt
 create mode 100644 z.txt
$ vi a.txt z.txt 
2 files to edit
$ git status
# On branch master
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   a.txt
#   modified:   z.txt
#
no changes added to commit (use "git add" and/or "git commit -a")
$ git branch aardvark
$ git checkout aardvark
M   a.txt
M   z.txt
Switched to branch 'aardvark'
$ git add a.txt 
$ git checkout master
M   a.txt
M   z.txt
Switched to branch 'master'
$ git branch zebra
$ git checkout zebra
M   a.txt
M   z.txt
Switched to branch 'zebra'
$ git add z.txt 
$ git status
# On branch zebra
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   a.txt
#   modified:   z.txt
#
$ git checkout aardvark
M   a.txt
M   z.txt
Switched to branch 'aardvark'
$ git status
# On branch aardvark
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   a.txt
#   modified:   z.txt

Dans cet exemple, les branches aardvark et zebra semblent contenir exactement le même ensemble de modifications et basées sur la sortie de git status il semble que faire un commit dans l'un ou l'autre aura le même effet. Est-ce que je fais quelque chose de mal?

83
Laurence Gonsalves

Je n'ai pas beaucoup utilisé perforce, ce n'est peut-être pas exactement une traduction 1: 1. Là encore, les systèmes de contrôle de source distribués comme git et Mercurial ont un workflow différent de toute façon, donc il n'y a vraiment pas (et il ne devrait pas y avoir) de traduction 1: 1. Quoi qu'il en soit, voici:

  • Créez plusieurs listes de modifications en attente -> Utilisez plutôt des branches. Dans git, les branches sont légères et rapides, prennent moins d'une seconde à créer et généralement moins de deux secondes pour fusionner. N'ayez pas peur de vous ramifier et de rebaser souvent.

    git branch new-branch-name
    git checkout new-branch-name
    

    Ou tout faire en une seule ligne:

    git checkout -b new-branch-name
    
  • Voir une liste de toutes les listes de modifications en attente -> Étant donné que l'équivalent de plusieurs listes de modifications en attente est plusieurs branches, il suffit de visualiser les branches:

    git branch
    

    Si vous souhaitez également afficher les succursales distantes:

    git branch -a
    

    Il est considéré comme une bonne pratique de supprimer immédiatement une branche après une fusion réussie afin que vous n'ayez pas à garder une trace de la branche en attente de fusion et de celles qui ont déjà été fusionnées.

  • Lister tous les fichiers modifiés -> Pour une seule "liste des modifications" en attente dans une branche spécifique, git a un concept d'index ou de cache. Pour valider une modification, vous devez d'abord ajouter des fichiers à cet index. Cela vous permet de sélectionner manuellement le groupe de fichiers qui représente une seule modification ou d'ignorer les fichiers non pertinents. Pour voir l'état des fichiers ajoutés ou non à cet index, il suffit de faire:

    git status
    
  • Voir un diff d'une liste de modifications en attente -> Il y a deux parties à cela. D'abord pour voir un diff entre le répertoire de travail et l'index:

    git diff
    

    Mais si vous voulez connaître le diff entre ce que vous tapez maintenant et le dernier commit, vous demandez vraiment un diff entre le répertoire + index de travail et le HEAD:

    git diff HEAD
    
  • Pour un fichier donné, voir quelles listes de modifications soumises ont affecté quelles lignes -> C'est simple:

    git blame filename
    

    ou encore mieux, si vous êtes dans un environnement fenêtré:

    git gui blame filename
    

    Git gui prend plus de temps pour analyser le fichier (il a été écrit en tcl au lieu de C) mais il a beaucoup de fonctionnalités intéressantes, y compris la possibilité de "voyager dans le temps" dans le passé en cliquant sur un ID de validation. Je souhaite seulement qu'ils implémentent une fonctionnalité pour "voyager dans le temps" vers le futur afin que je puisse découvrir comment un bug donné sera finalement résolu ;-)

  • Pour un fichier donné, voir une liste des descriptions des listes de modifications qui ont affecté le fichier -> aussi simple:

    git log filename
    

    Mais git log est un outil beaucoup plus puissant que cela. En fait, la plupart de mes scripts personnels se superposent au journal git pour lire le référentiel. Lisez la page de manuel.

  • Soumettre une liste de modifications en attente -> Aussi simple:

    git commit
    

Voir ma réponse à une question précédente pour voir mon flux de travail git typique: Learning Git. Besoin de savoir si je suis sur la bonne voie

Si vous suivez le flux de travail que j'ai décrit, vous trouverez que les outils comme gitk sont beaucoup plus précieux car ils vous permettent de voir clairement les groupes de changements.


Réponse supplémentaire:

Git est très flexible et il existe plusieurs façons de faire ce que vous décrivez. La chose à retenir est de toujours démarrer une nouvelle branche pour chaque fonctionnalité sur laquelle vous travaillez. Cela signifie que la branche principale n'est pas touchée, vous pouvez donc toujours y revenir pour corriger les bogues. Travailler dans git on devrait presque toujours commencer par:

git checkout -b new-feature-a

Vous pouvez maintenant éditer le fichier a.txt. Pour travailler simultanément sur une autre fonctionnalité, procédez comme suit:

git checkout master
git checkout -b new-feature-z

Vous pouvez maintenant éditer le fichier z.txt. Pour revenir à a.txt:

git checkout new-feature-a

Mais attendez, il y a des changements dans new-feature-z et git ne vous permettra pas de changer de branche. À ce stade, vous avez deux choix. Le premier est le plus simple, validez toutes les modifications dans la branche actuelle:

git add .
git commit
git checkout new-feature-a

C'est ce que je recommanderais. Mais si vous n'êtes vraiment pas prêt à valider le code, vous pouvez le cacher temporairement:

git stash

Vous pouvez maintenant passer à la branche new-feature-a. Pour revenir au code sur lequel vous travailliez, il suffit de faire apparaître la cachette:

git checkout new-feature-z
git stash pop

Lorsque tout est terminé, fusionnez toutes les modifications vers le maître:

git merge --no-ff new-feature-a
git merge --no-ff new-feature-z

Parce que les fusions sont si rapides et faciles (faciles parce que les conflits sont si rares et la résolution des conflits, quand il se produit, pas trop difficile), nous utilisons des branches dans git pour tout.

Voici un autre exemple d'utilisation courante de branches dans git que vous ne voyez pas dans d'autres outils de contrôle de source (sauf peut-être Mercurial):

Besoin de continuer à changer vos fichiers de configuration pour refléter votre environnement de développement? Utilisez ensuite une branche:

git checkout -b dev-config

Maintenant éditez vos fichiers de configuration dans votre éditeur préféré puis validez les modifications:

git add .
git commit

Maintenant, chaque nouvelle branche peut commencer à partir de la branche dev-config au lieu de master:

git checkout dev-config
git checkout -b new-feature-branch

Une fois que vous avez terminé, supprimez les modifications dans dev-config de new-feature-branch à l'aide de rebase interactif:

git rebase -i master

Supprimez les validations dont vous ne voulez pas, puis enregistrez. Vous avez maintenant une branche propre sans modifications de configuration personnalisées. Il est temps de revenir au maître:

git checkout master
git merge --no-ff new-feature-branch
# because master have changed, it's a good idea to rebase dev-config:
git checkout dev-config
git rebase master

Il convient de noter que la suppression des modifications avec git rebase -i fonctionne même lorsque toutes les modifications se produisent dans le même fichier. Git se souvient des changements, pas du contenu des fichiers *.

* note: en fait, techniquement pas tout à fait vrai mais en tant qu'utilisateur c'est ce que l'on ressent


Plus de réponse supplémentaire:

Ainsi, d'après vos commentaires, il semble que vous souhaitiez que deux branches existent simultanément afin que vous puissiez tester le fonctionnement du code combiné. Eh bien, c'est un bon moyen d'illustrer la puissance et la flexibilité des succursales.

Tout d'abord, un mot sur l'implication d'un branchement bon marché et d'un historique modifiable sur votre workflow. Lorsque j'utilisais CVS et SVN, j'étais toujours un peu réticent à s'engager. C'est parce que la validation d'un code instable entraînerait inévitablement le code de travail des autres. Mais avec git, j'ai perdu cette peur. C'est parce que dans git, les autres personnes n'obtiendront pas mes modifications avant que je ne les fusionne pour les maîtriser. Alors maintenant, je me retrouve à commettre du code toutes les 5 lignes que j'écris. Vous n'avez pas besoin d'une prévoyance parfaite pour vous engager. Vous avez juste besoin de changer votre état d'esprit: commit-to-branch == add-to-changeset, merge-to-master == commit-changeset.

Revenons donc aux exemples. Voici comment je le ferais. Disons que vous avez une succursale new-feature-z et vous voulez le tester avec new-feature-a. Je voudrais juste créer une nouvelle branche pour la tester:

# assume we are currently in branch new-feature-z
# branch off this branch for testing
git checkout -b feature-z-and-feature-a
# now temporarily merge new-feature-a
git merge --no-ff new-feature-a

Vous pouvez maintenant tester. Si vous devez modifier quelque chose pour que la fonctionnalité-z fonctionne avec la fonctionnalité-a, faites-le. Si c'est le cas, vous pouvez fusionner les modifications apportées à la branche appropriée. Utilisation git rebase -i pour supprimer les modifications non pertinentes de la fusion.

Alternativement, vous pouvez également utiliser git rebase pour modifier temporairement la base de new-feature-z pour pointer vers new-feature-a:

# assume we are currently in branch new-feature-z
git rebase new-feature-a

Maintenant, l'historique des branches est modifié de sorte que new-feature-z soit basé sur new-feature-a au lieu de master. Vous pouvez maintenant tester. Toutes les modifications validées dans cette branche appartiendront à la branche new-feature-z. Si vous devez modifier new-feature-a, revenez simplement dessus et rebasez pour obtenir les nouvelles modifications:

git checkout new-feature-a
# edit code, add, commit etc..
git checkout new-feature-z
git rebase new-feature-a
# now new-feature-z will contain new changes from new-feature-a

Lorsque vous avez terminé, il vous suffit de rebasculer vers le maître pour supprimer les modifications de new-feature-a:

# assume we are currently in branch new-feature-z
git rebase master

N'ayez pas peur de démarrer une nouvelle succursale. N'ayez pas peur de démarrer une branche jetable. N'ayez pas peur de jeter des branches. Et puisque fusionner == soumettre et valider == add-to-changeset, n'ayez pas peur de vous engager souvent. N'oubliez pas que la validation est l'outil ultime d'annulation d'un développeur.

Oh, et autre chose, dans git, les branches supprimées existent toujours dans votre référentiel. Donc, si vous avez accidentellement supprimé quelque chose que vous réalisez plus tard est utile après tout, vous pouvez toujours le récupérer en recherchant l'historique. Alors n'ayez pas peur de jeter des branches.

70
slebetman

Je souffre comme vous de l'absence du concept de "changelist" qui n'est pas exactement le même que celui des branches git.

J'écrirais un petit script qui créera un fichier de liste de modifications avec la liste des fichiers dans cette liste de modifications.

Une autre commande pour soumettre juste une certaine liste de modifications en appelant simplement git commit -a @ change_list_contents.txt puis "git commit"

J'espère que ça aide, Elias

1
Elias Bachaalany

Il existe une alternative plus légère dans git qui pourrait faire partie de votre flux de travail; en utilisant la zone de transit git.

Souvent, je fais juste des changements, puis je soumets en plusieurs validations (par exemple, ajoutez des instructions de débogage, refactorisez, corrigez en fait un bogue). Plutôt que de configurer vos listes de modifications perforce, puis de faire des modifications, puis de les soumettre, vous pouvez simplement apporter vos modifications, puis choisir la manière de les soumettre (en utilisant éventuellement la zone de transit git).

Vous pouvez valider des fichiers particuliers à partir de la ligne de commande avec:

git commit a.txt
git commit z.txt

Ou en organisant explicitement les fichiers en premier:

git add a.txt
git commit
git add z.txt
git commit

git gui vous permettra de sélectionner des lignes ou des morceaux à partir de fichiers pour créer un commit dans la zone de transfert. Ceci est très utile si vous avez des modifications dans un fichier que vous souhaitez faire dans différentes validations. Être passé de git à perforce et c'est une chose qui me manque vraiment.

Il y a une petite mise en garde à garder à l'esprit avec ce flux de travail. Si vous apportez les modifications A et B à un fichier, testez le fichier, puis validez A, puis vous n'avez pas testé cette validation (indépendamment de B).

1
Russell Gallop

Je n'ai pas assez d'expérience en p4 pour produire une véritable feuille de triche, mais il y a au moins quelques similitudes sur lesquelles se rabattre. Un "changeset" p4 est un "commit" git.

Les modifications apportées à votre espace de travail local sont ajoutées à l '"index" avec git add, et l'index est ensuite validé avec git commit. L'index est donc votre liste de modifications en attente, à toutes fins utiles.

Vous regardez les changements avec git diff et git status, où git diff montre généralement les changements entre l'espace de travail et l'index, mais git diff --cached montre les changements entre l'index et le référentiel (= votre liste de modifications en attente).

Pour des informations plus détaillées, je recommande http://progit.org/book/ . Puisque vous connaissez le contrôle de version en général, vous pouvez probablement en survoler une grande partie et extraire les informations spécifiques à git ...

1
Jakob Borg

Cela ne répond pas spécifiquement à votre question, mais je ne sais pas si vous savez qu'une version 2 utilisateurs, 5 espaces de travail de perforce est téléchargeable et utilisable gratuitement depuis le site Web perforce .

De cette façon, vous pouvez utiliser perforce à la maison pour vos projets personnels si vous le souhaitez. Le seul inconvénient est les 5 espaces de travail qui peuvent être un peu limitants, mais c'est assez incroyable d'avoir forcément disponible pour un usage domestique.

0
Toby Allen

Ayant utilisé à la fois Perforce et git assez largement, je ne peux voir qu'une seule façon de me rapprocher des listes de modifications Perforce avec git.

La première chose à comprendre est que pour implémenter correctement cette fonctionnalité dans git de telle sorte que ce ne soit pas un kluge complet, par ex. essayer de le chausse-pied en branches, nécessiterait le changement suivant: git nécessiterait plusieurs zones de transit pour une seule branche.

Les listes de modifications Perforce permettent un workflow qui n'a tout simplement pas d'équivalent dans git. Considérez le workflow suivant:

Check out a branch
Modify file A and add it to changelist 1
Modify file B and add it to changelist 2

Si vous essayez de le faire en utilisant des branches dans git, vous vous retrouverez avec deux branches, dont l'une a les modifications du fichier A, l'autre a les modifications du fichier B, mais non endroit où vous pouvez voir les modifications des deux fichiers A et B en même temps.

L'approximation la plus proche que je peux voir est d'utiliser git add . -p puis utilisez le 'a' et 'd' sous-commandes pour sélectionner ou rejeter des fichiers entiers. Cependant, ce n'est pas tout à fait la même chose, et la différence ici découle d'une disparité fondamentale dans le mode de fonctionnement général des deux systèmes.

Git (et Subversion, ce n'est pas important pour cette discussion) permet de modifier un fichier sans en parler à personne à l'avance. Vous venez de modifier un fichier, puis laissez git trier le tout lorsque vous validez les modifications. Perforce vous oblige à extraire activement un fichier avant d'autoriser les modifications, et c'est pour cette raison que les listes de modifications doivent exister. En substance, Perforce vous oblige à ajouter un fichier à l'index avant de le modifier. D'où la nécessité de plusieurs listes de modifications dans Perforce, et aussi la raison pour laquelle git n'a pas d'équivalent. Il n'en a tout simplement pas besoin.

0
dgnuff