Usare i rami

At this point, you should understand how each commit creates an entire new filesystem tree (called a revision) in the repository. If not, go back and read about revisions in sezione chiamata «Revisioni».

A questo punto dovete già capire come ogni commit crea in deposito(repository) un albero di filesystem completamente nuovo (chiamato una «revisione»). Se no, tornate a leggere riguardo le revisioni la sezione chiamata «Revisioni».

For this chapter, we'll go back to the same example from Chapter 2. Remember that you and your collaborator, Sally, are sharing a repository that contains two projects, paint and calc. Notice that in Figura 4.2, «Forma iniziale del deposito(repository)», however, each project directory now contains subdirectories named trunk and branches. The reason for this will soon become clear.

Per questo capitolo torniamo allo stesso esempio del Capitolo 2. Ricordate che voi e vostra collaboratrice, Sally, state condividere un deposito(repository) che contiene due progetti, paint e calc. Da notare che nella Figura 4.2, «Forma iniziale del deposito(repository)», comunque, ogni cartella di progetto adesso contiene sottocartelle denominate trunk e branches. La ragione di ciò diventa presto chiara.

Figura 4.2. Forma iniziale del deposito(repository)

Forma iniziale del deposito(repository)

As before, assume that Sally and you both have working copies of the calc project. Specifically, you each have a working copy of /calc/trunk. All the files for the project are in this subdirectory rather than in /calc itself, because your team has decided that /calc/trunk is where the main line of development is going to take place.

Come prima, assumiamo che tutti e due, Sally e voi, avete copia di lavoro del progetto «calc». Specificamente, entrambi avete una copia di lavoro di /calc/trunk. Tutti i file del progetto sono in questa sottocartella invece nella cartella /calc stessa, perché vostro gruppo ha deciso che /calc/trunk è il posto dove va posizionata la «linea principale» dello sviluppo.

Let's say that you've been given the task of performing a radical reorganization of the project. It will take a long time to write, and will affect all the files in the project. The problem here is that you don't want to interfere with Sally, who is in the process of fixing small bugs here and there. She's depending on the fact that the latest version of the project (in /calc/trunk) is always usable. If you start committing your changes bit-by-bit, you'll surely break things for Sally.

Diciamo che vi hanno dato lavoro di radicale riorganizazione del progetto. Questo prende lungo tempo per scriverlo e toccherà tutti i file nell progetto. Il problema è che voi non volete interferire con lavoro di Sally, che sta correggendo piccoli errori qua e là. Ella dipende dall fatto che l'ultima versione del progetto (in /calc/trunk) è sempre usabile. Se voi cominciate pubblicare i vostri cambiamenti pezzo per pezzo, sicuramente rompete le cose a Sally.

One strategy is to crawl into a hole: you and Sally can stop sharing information for a week or two. That is, start gutting and reorganizing all the files in your working copy, but don't commit or update until you're completely finished with the task. There are a number of problems with this, though. First, it's not very safe. Most people like to save their work to the repository frequently, should something bad accidentally happen to their working copy. Second, it's not very flexible. If you do your work on different computers (perhaps you have a working copy of /calc/trunk on two different machines), you'll need to manually copy your changes back and forth, or just do all the work on a single computer. By that same token, it's difficult to share your changes-in-progress with anyone else. A common software development best practice is to allow your peers to review your work as you go. If nobody sees your intermediate commits, you lose potential feedback. Finally, when you're finished with all your changes, you might find it very difficult to re-merge your final work with the rest of the company's main body of code. Sally (or others) may have made many other changes in the repository that are difficult to incorporate into your working copy—especially if you run svn update after weeks of isolation.

Una strategia è nascondersi in una buca: voi e Sally smettete di condividere le informazioni per una settimana o due. Proprio così, cominciate sventrare e riorganizzare tutti i file nella vostra copia di lavoro ma senza commit oppure update finché non avrete complettamente finito il vostro lavoro. C'è un numero di problemi con questo, comunque. Prima, non è molto sicuro. A molte persone piace conservare suo lavoro nel deposito(repository) frequentemente, nel caso che accidentalmente succede qualcosa brutto alla loro copia di lavoro. Seconda, non è molto flessibile. Se state svolgendo vostro lavoro su diversi computer (magari avete le copie di lavoro del /calc/trunk su due macchine diverse), avete bisogno di copiare manualmente i vostri cambiamenti avanti e dietro o fare il lavoro solo su un computer. E per la stessa ragione, è difficile condividere i vostri cambiamenti in corso con qualcun altro. «La regola d'arte» comune nel sviluppo del software è permettere ai vostri compagni di vedere il vostro lavoro man mano come procede. Se nessuno vede i vostri commit intermediari, avete perso le potenziali reazioni. Alla fine, quando avete finito con tutti i vostri cambiamenti, potete magari trovare molto difficile di ri-fondere vostro lavoro finale con il resto del corpo principale del codice della vostra società. Sally (o altri) poteva fare molti altri cambiamenti nel deposito(repository) che sono difficili da incorporare nella vostra copia di lavoro—specialmente se fatte svn update dopo settimane di isolamento.

The better solution is to create your own branch, or line of development, in the repository. This allows you to save your half-broken work frequently without interfering with others, yet you can still selectively share information with your collaborators. You'll see exactly how this works later on.

La soluzione migliore è creare vostro ramo, o linea di sviluppo, nel deposito(repository). Questo vi permette di salvare frequentemente vostro lavoro incompiuto senza interferire con altri, ma nello stesso tempo selettivamente condividere le informazioni con i vostri collaboratori. Vediamo in seguito esattamente come questo approcio funziona.

Creare un ramo

Creating a branch is very simple—you make a copy of the project in the repository using the svn copy command. Subversion is not only able to copy single files, but whole directories as well. In this case, you want to make a copy of the /calc/trunk directory. Where should the new copy live? Wherever you wish—it's a matter of project policy. Let's say that your team has a policy of creating branches in the /calc/branches area of the repository, and you want to name your branch my-calc-branch. You'll want to create a new directory, /calc/branches/my-calc-branch, which begins its life as a copy of /calc/trunk.

Creare un ramo è molto semplice—fatte una copia di progetto in deposito(repository) usando il comando svn copy. Subversion è capace non solo di copiare i singoli file, ma anche intere cartelle. Nel nostro caso, volete fare una copia della cartella /calc/trunk. Dove la mettiamo? Dovunque desideriate—è un aspetto di regole del progetto. Diciamo che il vostro gruppo ha stabilito la regola di creare rami nella area del deposito(repository) /calc/branches, e voi volete chiamare vostro ramo my-calc-branch. State per creare nuova cartella, /calc/branches/my-calc-branch, che nasce come copia di /calc/trunk.

There are two different ways to make a copy. We'll demonstrate the messy way first, just to make the concept clear. To begin, check out a working copy of the project's root directory, /calc:

Ci sono due modi diversi di fare una copia. Vi dimostriamo prima quello ingarbugliato, solo per fare chiaro il concetto. Per cominciare, tiriamo fuori una copia di lavoro della cartella principale del progetto, /calc:

$ svn checkout http://svn.example.com/repos/calc bigwc
A  bigwc/trunk/
A  bigwc/trunk/Makefile
A  bigwc/trunk/integer.c
A  bigwc/trunk/button.c
A  bigwc/branches/
Checked out revision 340.

Making a copy is now simply a matter of passing two working-copy paths to the svn copy command:

Creare una copia è adesso semplice questione di passare due percorsi di copia di lavoro al comando svn copy:

$ cd bigwc
$ svn copy trunk branches/my-calc-branch
$ svn status
A  +   branches/my-calc-branch

In this case, the svn copy command recursively copies the trunk working directory to a new working directory, branches/my-calc-branch. As you can see from the svn status command, the new directory is now scheduled for addition to the repository. But also notice the + sign next to the letter A. This indicates that the scheduled addition is a copy of something, not something new. When you commit your changes, Subversion will create /calc/branches/my-calc-branch in the repository by copying /calc/trunk, rather than resending all of the working copy data over the network:

In questo caso, il comando svn copy copia ricorsivamente cartella di lavoro trunk nella nuova cartella di lavoro, branches/my-calc-branch. Come si può vedere dal comando svn status, la nuova cartella è adesso pianificata per essere aggiunta al deposito(repository). Notare anche il segno «+» vicino la lettera A. Questo indica che la aggiunta pianificata è una copia di qualcosa, non qualcosa di nuovo. Quando pubblicate i vostri cambiamenti, Subversion creerà /calc/branches/my-calc-branch nel deposito(repository) copiando /calc/trunk, invece di re-inviare tramite la rete tutti i dati dalla cartella di lavoro:

$ svn commit -m "Creating a private branch of /calc/trunk."
Adding         branches/my-calc-branch
Committed revision 341.
$ svn commit -m "Creato un ramo privato da /calc/trunk."
Adding         branches/my-calc-branch
Committed revision 341.

And now the easier method of creating a branch, which we should have told you about in the first place: svn copy is able to operate directly on two URLs.

E adesso il metodo più semplice di creare un ramo, di cui si doveva parlare per primo: svn copy può operare direttamente su due URL.

$ svn copy http://svn.example.com/repos/calc/trunk \
           http://svn.example.com/repos/calc/branches/my-calc-branch \
      -m "Creating a private branch of /calc/trunk."

Committed revision 341.
  $ svn copy http://svn.example.com/repos/calc/trunk \
  http://svn.example.com/repos/calc/branches/my-calc-branch \
  -m "Creato un ramo privato da /calc/trunk."

  Committed revision 341.

There's really no difference between these two methods. Both procedures create a new directory in revision 341, and the new directory is a copy of /calc/trunk. This is shown in Figura 4.3, «Deposito con la nuova copia». Notice that the second method, however, performs an immediate commit. [15] It's an easier procedure, because it doesn't require you to check out a large mirror of the repository. In fact, this technique doesn't even require you to have a working copy at all.

Realmente non c'è differenza tra questi due metodi. Entrambe le procedure creano una nuova cartella nella revisione 341 e la nuova cartella è una copia di /calc/trunk. Come mostrato nella Figura 4.3, «Deposito con la nuova copia». Notare che il secondo metodo, tuttavia, fa anche commit immediato. [16] Questa è una procedura più semplice, perché non richiede di fare checkout di grande parte del deposito(repository). Infatti, questa tecnica addirittura non richiede neanche di avere una copia di lavoro.

Figura 4.3. Deposito con la nuova copia

Deposito con la nuova copia

Lavorare con il vostro ramo

Now that you've created a branch of the project, you can check out a new working copy to start using it:

Adesso che avete creato un ramo del progetto, potete tirare fuori (check out) una nuova copia di lavoro per cominciar ad usarla:

$ svn checkout http://svn.example.com/repos/calc/branches/my-calc-branch
A  my-calc-branch/Makefile
A  my-calc-branch/integer.c
A  my-calc-branch/button.c
Checked out revision 341.

There's nothing special about this working copy; it simply mirrors a different directory in the repository. When you commit changes, however, Sally won't ever see them when she updates. Her working copy is of /calc/trunk. (Be sure to read sezione chiamata «Cambiare una copia di lavoro» later in this chapter: the svn switch command is an alternate way of creating a working copy of a branch.)

Non c'è niente speciale di questa copia di lavoro; semplicemente rispecchia una cartella diversa del deposito(repository). Quando pubblicate le vostre modifiche (commit), tuttavia, Sally non può nenche vederle quando fa aggiornamento (update). La sua copia di lavoro è di /calc/trunk. (Assicuratevi di leggere la sezione chiamata «Cambiare una copia di lavoro» più avanti in questo capitolo: il comando svn switch è un modo alternativo di creare copia di lavoro di un ramo.)

Let's pretend that a week goes by, and the following commits happen:

Facciamo finta che le settimane passano e succedono sequenti pubblicazioni (commit):

  • You make a change to /calc/branches/my-calc-branch/button.c, which creates revision 342.

  • You make a change to /calc/branches/my-calc-branch/integer.c, which creates revision 343.

  • Sally makes a change to /calc/trunk/integer.c, which creates revision 344.

  • Fatta la modifica di /calc/branches/my-calc-branch/button.c, che crea revisione 342.

  • Fatta la modifica di /calc/branches/my-calc-branch/integer.c, che crea revisione 343.

  • Sally modifica /calc/trunk/integer.c, che crea revisione 344.

There are now two independent lines of development, shown in Figura 4.4, «Ramificazione della storia d'un file», happening on integer.c.

Ci sono adesso due linee di sviluppo independenti, mostrate nella Figura 4.4, «Ramificazione della storia d'un file», che toccano integer.c.

Figura 4.4. Ramificazione della storia d'un file

Ramificazione della storia d'un file

Things get interesting when you look at the history of changes made to your copy of integer.c:

Le cose diventano interessanti quando guardate la storia delle modifiche della vostra copia di integer.c:

$ pwd
/home/user/my-calc-branch

$ svn log --verbose integer.c
------------------------------------------------------------------------
r343 | user | 2002-11-07 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
Changed paths:
   M /calc/branches/my-calc-branch/integer.c

* integer.c:  frozzled the wazjub.

------------------------------------------------------------------------
r341 | user | 2002-11-03 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
Changed paths:
   A /calc/branches/my-calc-branch (from /calc/trunk:340)

Creating a private branch of /calc/trunk.

------------------------------------------------------------------------
r303 | sally | 2002-10-29 21:14:35 -0600 (Tue, 29 Oct 2002) | 2 lines
Changed paths:
   M /calc/trunk/integer.c

* integer.c:  changed a docstring.

------------------------------------------------------------------------
r98 | sally | 2002-02-22 15:35:29 -0600 (Fri, 22 Feb 2002) | 2 lines
Changed paths:
   M /calc/trunk/integer.c

* integer.c:  adding this file to the project.

------------------------------------------------------------------------
  $ pwd
  /home/user/my-calc-branch

  $ svn log --verbose integer.c
  ------------------------------------------------------------------------
  r343 | user | 2002-11-07 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
  Changed paths:
  M /calc/branches/my-calc-branch/integer.c

  * integer.c:  frozzled the wazjub.

  ------------------------------------------------------------------------
  r341 | user | 2002-11-03 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
  Changed paths:
  A /calc/branches/my-calc-branch (from /calc/trunk:340)

  Creato un ramo privato da /calc/trunk

  ------------------------------------------------------------------------
  r303 | sally | 2002-10-29 21:14:35 -0600 (Tue, 29 Oct 2002) | 2 lines
  Changed paths:
  M /calc/trunk/integer.c

  * integer.c:  cambiato un docstring.

  ------------------------------------------------------------------------
  r98 | sally | 2002-02-22 15:35:29 -0600 (Fri, 22 Feb 2002) | 2 lines
  Changed paths:
  M /calc/trunk/integer.c

  * integer.c:  aggiunto questo file al progetto.

  ------------------------------------------------------------------------

Notice that Subversion is tracing the history of your branch's integer.c all the way back through time, even traversing the point where it was copied. It shows the creation of the branch as an event in the history, because integer.c was implicitly copied when all of /calc/trunk/ was copied. Now look what happens when Sally runs the same command on her copy of the file:

Notare che Subversion tiene traccia della storia di integer.c del vostro ramo dietro tutto il tempo, attraversando anche il punto dov'è stato copiato. Mostra la creazione del ramo come evento nella storia, perché integer.c era stato implicitamente copiato quando tutto il /calc/trunk/ era stato copiato. Adesso guardate che sucede quando Sally avvia lo stesso comando sulla sua copia del file:

  $ pwd
  /home/sally/calc

  $ svn log --verbose integer.c
  ------------------------------------------------------------------------
  r344 | sally | 2002-11-07 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
  Changed paths:
  M /calc/trunk/integer.c

  * integer.c:  fix a bunch of spelling errors.

  ------------------------------------------------------------------------
  r303 | sally | 2002-10-29 21:14:35 -0600 (Tue, 29 Oct 2002) | 2 lines
  Changed paths:
  M /calc/trunk/integer.c

  * integer.c:  changed a docstring.

  ------------------------------------------------------------------------
  r98 | sally | 2002-02-22 15:35:29 -0600 (Fri, 22 Feb 2002) | 2 lines
  Changed paths:
  M /calc/trunk/integer.c

  * integer.c:  adding this file to the project.

  ------------------------------------------------------------------------
  $ pwd
  /home/sally/calc

  $ svn log --verbose integer.c
  ------------------------------------------------------------------------
  r344 | sally | 2002-11-07 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
  Changed paths:
  M /calc/trunk/integer.c

  * integer.c:  riparata una manciata di errori ortografici.

  ------------------------------------------------------------------------
  r303 | sally | 2002-10-29 21:14:35 -0600 (Tue, 29 Oct 2002) | 2 lines
  Changed paths:
  M /calc/trunk/integer.c

  * integer.c:  cambiato un docstring.

  ------------------------------------------------------------------------
  r98 | sally | 2002-02-22 15:35:29 -0600 (Fri, 22 Feb 2002) | 2 lines
  Changed paths:
  M /calc/trunk/integer.c

  * integer.c:  aggiunto questo file al progetto.

  ------------------------------------------------------------------------

Sally sees her own revision 344 change, but not the change you made in revision 343. As far as Subversion is concerned, these two commits affected different files in different repository locations. However, Subversion does show that the two files share a common history. Before the branch-copy was made in revision 341, they used to be the same file. That's why you and Sally both see the changes made in revisions 303 and 98.

Sally vede la sua modifica nella versione 344, ma non la modifica che voi avete fatto nella versione 343. Per quel che riguarda Subversion, questi due commit riguardano i file diversi nelle diverse locazioni del deposito(repository). Tuttavia, Subversion mostra che questi due file condividono una storia comune. Prima che era fatta copia del ramo nella versione 341, essi erano l'unico file. E per questo entrambi, voi e Sally, vedete le modifiche fatte nelle versioni 303 e 98.

Concetti chiave dietro i rami

There are two important lessons that you should remember from this section.

Ci sono due lezioni importanti che dovete ricordare da questa sezione.

  1. Unlike many other version control systems, Subversion's branches exist as normal filesystem directories in the repository, not in an extra dimension. These directories just happen to carry some extra historical information.

    Diversamente da molti altri sistemi di controllo delle versioni, rami di Subversion esistono come normali cartelle del filesystem in deposito(repository), non in una dimensione extra. Succede solo che queste cartelle portano qualche storica informazione extra.

  2. Subversion has no internal concept of a branch—only copies. When you copy a directory, the resulting directory is only a branch because you attach that meaning to it. You may think of the directory differently, or treat it differently, but to Subversion it's just an ordinary directory that happens to have been created by copying.

    Subversion non ha un concetto interno dei rami—solo copie. Quando copiate una cartella, la cartella risultante è un «ramo» solo perché voi le date questo significato. Potete pensare alla cartella diversamente o trattarla diversamente, ma per Subversion essa è solo normale cartella, a quall'è successo di esser creata tramite copiatura.



[15] Subversion does not support cross-repository copying. When using URLs with svn copy or svn move, you can only copy items within the same repository.

[16] Subversion non supporta la copia tra due depositi (cross-repository). Usando gli URL con svn copy o svn move, si possono copiare elementi solo dentro lo stesso deposito(repository).