Casi di uso comuni

There are many different uses for branching and svn merge, and this section describes the most common ones you're likely to run into.

Ci sono molti usi diversi per ramificazione e fusione(svn merge) e questa sezione descrive i più comuni tra essi, che probabilmente potete incontrare.

Fondere ramo intero nel altro

To complete our running example, we'll move forward in time. Suppose several days have passed, and many changes have happened on both the trunk and your private branch. Suppose that you've finished working on your private branch; the feature or bug fix is finally complete, and now you want to merge all of your branch changes back into the trunk for others to enjoy.

Per complettare nostro esempio in corso, ci moviamo avanti nel tempo. Supponiamo che sono passati diversi giorni, e sono accadute molte modifiche su tutte e due strutture, il tronco e vostro ramo privato. Supponiamo che avete finito il lavoro su vostro ramo privato; nuova caratteristica o riparazione del bug è finalmente completta e adesso volete fondere tutte le modifiche del vostro ramo dietro tronco, così che gl'altri possono assaporarle.

So how do we use svn merge in this scenario? Remember that this command compares two trees, and applies the differences to a working copy. So to receive the changes, you need to have a working copy of the trunk. We'll assume that either you still have your original one lying around (fully updated), or that you recently checked out a fresh working copy of /calc/trunk.

Allora, come usiamo svn merge in questo scenario? Ricordate che questo comando compara due strutture ed applica le differenze alla copia di lavoro. Perciò per scoprire le modifiche, avete bisogno della copia di lavoro del tronco. Assumiamo che o avete ancora quella originale da qualche parte (complettamente aggiornata) o che di recente avete tirato fuori (checkout) una fresca copia di /calc/trunk.

But which two trees should be compared? At first glance, the answer may seem obvious: just compare the latest trunk tree with your latest branch tree. But beware—this assumption is wrong, and has burned many a new user! Since svn merge operates like svn diff, comparing the latest trunk and branch trees will not merely describe the set of changes you made to your branch. Such a comparison shows too many changes: it would not only show the addition of your branch changes, but also the removal of trunk changes that never happened on your branch.

Ma quale due strutture devono essere comparate? A primo sguardo, la risposta sembra ovvia: comparare l'ultima struttura di tronco con l'ultima del ramo. Ferma!—questa ??assunzione? è sbagliata, e ha bruciato molti principianti! Perché svn merge opera come svn diff, comparare le ultime strutture di tronco e ramo non descriverà soltanto l'insieme delle modifiche fatte sul ramo. Comparazione come questa mostra trope modifiche: mostrerebbe non solo le agguinte delle vostre modifiche del ramo, ma anche le rimozioni delle modifiche del tronco che non sono mai accadute su vostro ramo.

To express only the changes that happened on your branch, you need to compare the initial state of your branch to its final state. Using svn log on your branch, you can see that your branch was created in revision 341. And the final state of your branch is simply a matter of using the HEAD revision. That means you want to compare revisions 341 and HEAD of your branch directory, and apply those differences to a working copy of the trunk.

Per scoprire solo le modifiche che son accadute nel vostro ramo, dovete comparare lo stato iniziale del ramo con il suo stato finale. Usando svn log sul vostro ramo, potete vedere che vostro ramo era stato creato nella versione 341. E lo stato finale è semplicemente questione di usare la versione HEAD. Questo significa che dovete comparare versione 341 e HEAD della cartella del vostro ramo e applicare quelle differenze all copia di lavoro del tronco.

[Suggerimento] Suggerimento

A nice way of finding the revision in which a branch was created (the base of the branch) is to use the --stop-on-copy option to svn log. The log subcommand will normally show every change ever made to the branch, including tracing back through the copy which created the branch. So normally, you'll see history from the trunk as well. The --stop-on-copy will halt log output as soon as svn log detects that its target was copied or renamed.

Un bel modo per trovare la versione nella quale era stato creato un ramo (la «base» del ramo) è usare opzione --stop-on-copy nel svn log. Sottocomando log normalmente mostrerà ogni cambiamento fatto sul ramo, andando dietro anche prima della copiatura che ha creato il ramo. Così normalmente, vedremmo anche la parte della storia dal tronco. Lo --stop-on-copy fermerà output di log appena svn log scopre che il suo bersaglio era copiato o rinominato.

So in our continuing example,

Così nel nostro esempi continuo,

$ svn log --verbose --stop-on-copy \
          http://svn.example.com/repos/calc/branches/my-calc-branch
…
------------------------------------------------------------------------
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)

$

As expected, the final revision printed by this command is the revision in which my-calc-branch was created by copying.

Come aspettato, l'ultima versione stampata da questo comando è la versione in cui my-calc-branch era stato creato copiandolo.

Here's the final merging procedure, then:

Qui c'è la procedura di fusione finale, allora:

$ cd calc/trunk
$ svn update
At revision 405.

$ svn merge -r 341:405 http://svn.example.com/repos/calc/branches/my-calc-branch
U   integer.c
U   button.c
U   Makefile

$ svn status
M   integer.c
M   button.c
M   Makefile

# ...examine the diffs, compile, test, etc...

$ svn commit -m "Merged my-calc-branch changes r341:405 into the trunk."
Sending        integer.c
Sending        button.c
Sending        Makefile
Transmitting file data ...
Committed revision 406.
  $ cd calc/trunk
  $ svn update
  At revision 405.

  $ svn merge -r 341:405 http://svn.example.com/repos/calc/branches/my-calc-branch
  U   integer.c
  U   button.c
  U   Makefile

  $ svn status
  M   integer.c
  M   button.c
  M   Makefile

  # ...esaminare le diff, compilare, testare, ecc...

  $ svn commit -m "Fuso modifiche my-calc-branch r341:405 dentro tronco."
  Sending        integer.c
  Sending        button.c
  Sending        Makefile
  Transmitting file data ...
  Committed revision 406.

Again, notice that the commit log message very specifically mentions the range of changes that was merged into the trunk. Always remember to do this, because it's critical information you'll need later on.

Di nuovo, notare che messaggio di commit menziona molto specificamente intervallo delle modifiche che sono state fuse nel tronco. Ricordate sempre di farlo, perché più tardi avrete bisogno di questa critica informazione.

For example, suppose you decide to keep working on your branch for another week, in order to complete an enhancement to your original feature or bug fix. The repository's HEAD revision is now 480, and you're ready to do another merge from your private branch to the trunk. But as discussed in sezione chiamata «Regole d'arte per la fusione», you don't want to merge the changes you've already merged before; you only want to merge everything new on your branch since the last time you merged. The trick is to figure out what's new.

Per esempio, supponiamo che decidete di proseguire lavoro sul vostro ramo per altra settimana, onde complettare un miglioramento alla vostra caratteristica o bugfix. La versione HEAD del deposito(repository) è adesso 480, e voi siete pronti a fare altra fusione dal vostro ramo privato al tronco. Ma come era discusso nella sezione chiamata «Regole d'arte per la fusione», non volete fondere le modifiche che avevate già fuso prima; volete solo fondere tutto il «nuovo» dal vostro ramo a partire dalla ultima fusione. Il trucco sta nel scoprire che cosa è 'il nuovo'.

The first step is to run svn log on the trunk, and look for a log message about the last time you merged from the branch:

Il primo passo è avviare svn log su tronco e cercare messaggio riguardo l'ultima fusione dal ramo:

$ cd calc/trunk
$ svn log
…
------------------------------------------------------------------------
r406 | user | 2004-02-08 11:17:26 -0600 (Sun, 08 Feb 2004) | 1 line

Merged my-calc-branch changes r341:405 into the trunk.
------------------------------------------------------------------------
…
  $ cd calc/trunk
  $ svn log
  …
  ------------------------------------------------------------------------
  r406 | user | 2004-02-08 11:17:26 -0600 (Sun, 08 Feb 2004) | 1 line

  Fuso modifiche my-calc-branch r341:405 dentro tronco.
  ------------------------------------------------------------------------
  …

Aha! Since all branch-changes that happened between revisions 341 and 405 were previously merged to the trunk as revision 406, you now know that you want to merge only the branch changes after that—by comparing revisions 406 and HEAD.

Aha! Poiché tutte modifiche del ramo che sono state fatte tra le versioni 341 e 405 erano già precedentemente fuse nel tronco come versione 406, sapete adesso che volete fondere solo cambiamenti del ramo fatte dopo—comparando versioni 406 e HEAD.

$ cd calc/trunk
$ svn update
At revision 480.

# We notice that HEAD is currently 480, so we use it to do the merge:

$ svn merge -r 406:480 http://svn.example.com/repos/calc/branches/my-calc-branch
U   integer.c
U   button.c
U   Makefile

$ svn commit -m "Merged my-calc-branch changes r406:480 into the trunk."
Sending        integer.c
Sending        button.c
Sending        Makefile
Transmitting file data ...
Committed revision 481.
  $ cd calc/trunk
  $ svn update
  At revision 480.

  # Vediamo che HEAD è al momento 480, usiamo questa info per la fusione:

  $ svn merge -r 406:480 http://svn.example.com/repos/calc/branches/my-calc-branch
  U   integer.c
  U   button.c
  U   Makefile

  $ svn commit -m "Fuso modifiche my-calc-branch r406:480 dentro tronco."
  Sending        integer.c
  Sending        button.c
  Sending        Makefile
  Transmitting file data ...
  Committed revision 481.

Now the trunk contains the complete second wave of changes made to the branch. At this point, you can either delete your branch (we'll discuss this later on), or continue working on your branch and repeat this procedure for subsequent merges.

Adesso il tronco contiene la completta seconda ondata delle modifiche fatte sul ramo. A questo punto, potete o cancellare vostro ramo (discutteremmo questo più avanti), o continuare lavoro su vostro ramo e ripetere questa procedura con le fusioni venture.

Disfare i cambiamenti

Another common use for svn merge is to roll back a change that has already been committed. Suppose you're working away happily on a working copy of /calc/trunk, and you discover that the change made way back in revision 303, which changed integer.c, is completely wrong. It never should have been committed. You can use svn merge to undo the change in your working copy, and then commit the local modification to the repository. All you need to do is to specify a reverse difference:

Altro uso comune per svn merge è di riavvolgere dietro (disfare) le modifiche che sono state già depositate. Supponiamo che state proseguendo beatamente lavori sulla copia di lavoro del /calc/trunk, e avete scoperto che la modifica fatta nella versione 303, che ha cambiato integer.c, è complettamente sbagliata. Non doveva essere mai depositata. Potete usare svn merge per «disfare» la modifica nella vostra copia e dopo depositare la modifica locale. Tutto di cui avete bisogno è specificare la diffrenza rovesciata:

$ svn merge -r 303:302 http://svn.example.com/repos/calc/trunk
U  integer.c

$ svn status
M  integer.c

$ svn diff
…
# verify that the change is removed
…

$ svn commit -m "Undoing change committed in r303."
Sending        integer.c
Transmitting file data .
Committed revision 350.
  $ svn merge -r 303:302 http://svn.example.com/repos/calc/trunk
  U  integer.c

  $ svn status
  M  integer.c

  $ svn diff
  …
  # verificare che le modifiche sono state rimosse
  …

  $ svn commit -m "Disfatte le modifiche pubblicate nella r303."
  Sending        integer.c
  Transmitting file data .
  Committed revision 350.

One way to think about a repository revision is as a specific group of changes (some version control systems call these changesets). By using the -r switch, you can ask svn merge to apply a changeset, or whole range of changesets, to your working copy. In our case of undoing a change, we're asking svn merge to apply changeset #303 to our working copy backwards.

Un modo di pensare alle versioni del deposito(repository) è come allo specifico gruppo delle modifiche (alcuni sistemi di controllo delle versioni lo chiamano changeset). Usando opzione -r potete chiedere a svn merge di applicare un changeset, o tutto intervallo di changeset, alla vostra copia di lavoro. Nel nostro caso di disfare cambiamenti, stiamo chiedendo a svn merge di applicare changeset nr.303 sulla nostra copia di lavoro al rovescio.

Keep in mind that rolling back a change like this is just like any other svn merge operation, so you should use svn status and svn diff to confirm that your work is in the state you want it to be in, and then use svn commit to send the final version to the repository. After committing, this particular changeset is no longer reflected in the HEAD revision.

Tenete in mente che disfare (riavvolgere dietro) una modifica è come ogni altra operazione svn merge, così dovete usare svn status e svn diff per confermare che vostro lavoro è nello stato voluto, e poi usare svn commit per spedire la versione finale al deposito(repository). Dopo deposizione quello particolare changeset non è più presente nella versione HEAD.

Again, you may be thinking: well, that really didn't undo the commit, did it? The change still exists in revision 303. If somebody checks out a version of the calc project between revisions 303 and 349, they'll still see the bad change, right?

Di nuovo, potete pensare: Insomma, questo in verità non disfa una deposizione, dico bene? La modifica ancora esiste nella versione 303. Se qualcuno tira fuori una versione di progetto calc tra versioni 303 e 349, può ancora vedere la modifica errata, vero?

Yes, that's true. When we talk about removing a change, we're really talking about removing it from HEAD. The original change still exists in the repository's history. For most situations, this is good enough. Most people are only interested in tracking the HEAD of a project anyway. There are special cases, however, where you really might want to destroy all evidence of the commit. (Perhaps somebody accidentally committed a confidential document.) This isn't so easy, it turns out, because Subversion was deliberately designed to never lose information. Revisions are immutable trees which build upon one another. Removing a revision from history would cause a domino effect, creating chaos in all subsequent revisions and possibly invalidating all working copies. [19]

Sì, questo è vero. Quando abbiamo parlato di «rimuovere» una modifica, in verità abbiamo parlato di rimuoverla dalla versione HEAD. Il cambiamento originale ancora esiste nella storia del deposito(repository). Per la maggioranza delle situazioni questo basta. Tanto, molte persone sono interessate solo di usare HEAD del progetto. Ci sono, comunque, casi speciali, in cui veramente volete distruggere ogni traccia della deposizione avvenuta. (Magari qualcuno ha accidentalmente pubblicato un documento riservato.) Si scopre che non è così facile, perché Subversion era stato volutamente disegnato per non perdere mai le informazioni. Versioni sono strutture ad albero immutabili basati una sull'altra. Togliendo una revisione dalla storia può causare un effetto domino, creando chaos in tutte le subsequenti versioni e possibilmente invalidare tutte le copie di lavoro. [20]

Risuscitare elementi cancellati

The great thing about version control systems is that information is never lost. Even when you delete a file or directory, it may be gone from the HEAD revision, but the object still exists in earlier revisions. One of the most common questions new users ask is, How do I get my old file or directory back?.

La cosa grande nei sistemi di controllo delle versioni è che la informazione non si perde mai. Addirittura quando cancellate un file o cartella, può sparire dalla versione HEAD, ma l'elemento sempre esiste nelle versioni precedenti. Una delle più comuni domande degli nuovi utenti è «Come posso riavere mio vecchio file o cartella?».

The first step is to define exactly which item you're trying to resurrect. Here's a useful metaphor: you can think of every object in the repository as existing in a sort of two-dimensional coordinate system. The first coordinate is a particular revision tree, and the second coordinate is a path within that tree. So every version of your file or directory can be defined by a specific coordinate pair.

Il primo passo è di definire precisamente quale elemento state provando di risuscitare. Qui c'è una utile metafora: potete pensare ad ogni oggetto del deposito(repository) come essistesse in una sorta di spazio bidimensionale. La prima coordinata è la particolare versione e la seconda è il percorso e nome dentro questa versione. Così ogni versione del vostro file o cartella può essere definita da una copia di coordinate specifica.

Subversion has no Attic directory like CVS does, [21] so you need to use svn log to discover the exact coordinate pair you wish to resurrect. A good strategy is to run svn log --verbose in a directory which used to contain your deleted item. The --verbose option shows a list of all changed items in each revision; all you need to do is find the revision in which you deleted the file or directory. You can do this visually, or by using another tool to examine the log output (via grep, or perhaps via an incremental search in an editor).

Subversion no ha cartella Attic come la tiene CVS, [22] e perciò dovete usare svn log per scoprire la copia esatta di coordinate del elemento da risuscitare. Una strategia buona è di avviare svn log --verbose nella cartella che abitulamente conteneva vostro elemento cancellato. La opzione --verbose mostra la lista di tutte le modifiche in ogni versione; tutto di cui avete bisogno è trovare versione in cui l'elemento era stato cancellato. Potete farlo a vista o usando qualche strumento per essaminare output di log (tramite grep, o forse con una ricerca incrementale in un editore).

$ cd parent-dir
$ svn log --verbose
…
------------------------------------------------------------------------
r808 | joe | 2003-12-26 14:29:40 -0600 (Fri, 26 Dec 2003) | 3 lines
Changed paths:
   D /calc/trunk/real.c
   M /calc/trunk/integer.c

Added fast fourier transform functions to integer.c.
Removed real.c because code now in double.c.
…
  $ cd parent-dir
  $ svn log --verbose
  …
  ------------------------------------------------------------------------
  r808 | joe | 2003-12-26 14:29:40 -0600 (Fri, 26 Dec 2003) | 3 lines
  Changed paths:
  D /calc/trunk/real.c
  M /calc/trunk/integer.c

  Aggiunta la funzione di trasformazione Fourier veloce a integer.c.
  Rimosso real.c perché il codice è adesso in double.c.
  …

In the example, we're assuming that you're looking for a deleted file real.c. By looking through the logs of a parent directory, you've spotted that this file was deleted in revision 808. Therefore, the last version of the file to exist was in the revision right before that. Conclusion: you want to resurrect the path /calc/trunk/real.c from revision 807.

Nel esempio abbiamo assunto che state cercando file cancellato real.c. Scorrendo tra log della sua cartella parente, avete scoperto che questo file era stato cancellato nella versione 808. In consequenza, l'ultima versione che lo contiene è quella subito prima. Conclusione: dovete risuscitare /calc/trunk/real.c della versione 807.

That was the hard part—the research. Now that you know what you want to restore, you have two different choices.

Quella era la parte dura—la ricerca. Adesso che sappiamo che cosa riprendere, abbiamo due diverse scelte.

One option is to use svn merge to apply revision 808 in reverse. (We've already discussed how to undo changes, see sezione chiamata «Disfare i cambiamenti».) This would have the effect of re-adding real.c as a local modification. The file would be scheduled for addition, and after a commit, the file would again exist in HEAD.

Una opzione è usare svn merge per applicare versione 808 «al rovescio». (Abbiamo già discusso come disfare le modifiche, vedi sezione chiamata «Disfare i cambiamenti».) Questo potrebbe avere effetto di ri-aggiungere real.c come una modifica locale. Il file sarà pianificato per aggiunta e dopo deposizione essisterà di nuovo nel HEAD.

In this particular example, however, this is probably not the best strategy. Reverse-applying revision 808 would not only schedule real.c for addition, but the log message indicates that it would also undo certain changes to integer.c, which you don't want. Certainly, you could reverse-merge revision 808 and then svn revert the local modifications to integer.c, but this technique doesn't scale well. What if there were 90 files changed in revision 808?

In questo esempio particolare, ??however?, questa probabilmente non è la strategia migliore. Applicazione al rovescio della versione 808 non solo pianificherà real.c per aggiunta, ma il messagio di log indica che disfarà anche certe modifiche del file integer.c, ciò che non vogliamo. Naturalmente, potete fare fusione al rovescio di versione 808 e dopo disfare modifiche locali del file integer.c con svn revert, ma questa tecnica non si adatta bene al aumento del lavoro. Cosa fare se ci fossero 90 file modificati nella versione 808?

A second, more targeted strategy is not to use svn merge at all, but rather the svn copy command. Simply copy the exact revision and path coordinate pair from the repository to your working copy:

Una seconda strategia, più mirata, è di non usare del tutto svn merge, ma invece il comando svn copy. Semplicemete copiate esatte «coordinate» (revisone e percorso-nome) dal deposito(repository) alla vostra copia di lavoro:

$ svn copy --revision 807 \
           http://svn.example.com/repos/calc/trunk/real.c ./real.c

$ svn status
A  +   real.c

$ svn commit -m "Resurrected real.c from revision 807, /calc/trunk/real.c."
Adding         real.c
Transmitting file data .
Committed revision 1390.
$ svn copy --revision 807 \
           http://svn.example.com/repos/calc/trunk/real.c ./real.c

$ svn status
A  +   real.c

$ svn commit -m "Risuscitato real.c dalla versione 807, /calc/trunk/real.c."
Adding         real.c
Transmitting file data .
Committed revision 1390.

The plus sign in the status output indicates that the item isn't merely scheduled for addition, but scheduled for addition with history. Subversion remembers where it was copied from. In the future, running svn log on this file will traverse back through the file's resurrection and through all the history it had prior to revision 807. In other words, this new real.c isn't really new; it's a direct descendant of the original, deleted file.

Il segno più nel output di status indica che l'elemento non è solo pianificato per aggiunta, ma per aggiunta «con storico». Subversion si ricorda da dove era stato copiato. In futuro, usando svn log su di questo file attraversiammo dietro la sua resurrezione e vedremmo tutta la sua storia che aveva prima della versione 807. In altre parole, questo nuovo real.c non è veramente nuovo; è un discendente diretto del file originale cancellato.

Although our example shows us resurrecting a file, note that these same techniques work just as well for resurrecting deleted directories.

Nonostante nostro esempio ci mostra resurrezione di un file, notare che la stessa tecnica serve anche per resurrezione delle cartelle cancellate.

Tipi comuni di ramificazione

Version control is most often used for software development, so here's a quick peek at two of the most common branching/merging patterns used by teams of programmers. If you're not using Subversion for software development, feel free to skip this section. If you're a software developer using version control for the first time, pay close attention, as these patterns are often considered best practices by experienced folk. These processes aren't specific to Subversion; they're applicable to any version control system. Still, it may help to see them described in Subversion terms.

Controllo delle versioni è molto spesso usato per sviluppo del software, così abbiamo qui una veloce sbirciatina di due più comuni archetipi di ramificazione usati dai team di programmatori. Se non usate Subversion per sviluppo di software, sentitevi liberi di saltare questa sezione.[alternativa: Se non è il vostro caso, potete tranquilmente saltare questa sezione.] Se siete sviluppatore di software alle prime armi con l'uso di controllo delle versioni, prestate molta attenzione, perché questi archetipi sono spesso considerati regola d'arte dalla gente esperta. Queste procedure non sono specifiche di Subversion; sono applicabili a qualsiasi sistema di controllo delle versioni. Tuttavia, può essere d'aiuto vederle descritte in termini di Subversion.

Rami di rilascio

Most software has a typical lifecycle: code, test, release, repeat. There are two problems with this process. First, developers need to keep writing new features while quality-assurance teams take time to test supposedly-stable versions of the software. New work cannot halt while the software is tested. Second, the team almost always needs to support older, released versions of software; if a bug is discovered in the latest code, it most likely exists in released versions as well, and customers will want to get that bugfix without having to wait for a major new release.

Molti software hanno un tipico ciclo di vita: scrittura, test, rilascio; ripetere. Ci sono due problemi con questo processo. Prima, sviluppatori devono continuare a scrivere nuove funzioni mentre i team di controllo di qualità prendono tempo per testare presunte stabili versioni del software. Nuovo lavoro non si può fermare mentre si fanno i test. Secondo, il team quasi sempre deve fornire supporto per vecchie, già rilasciate versioni del software; quando si scopre un errore nel codice nuovo, con molta probabilità esiste anche nelle versioni rilasciate e i clienti vogliono avere questi bug risolti senza aspettare rilascio della nuova versione.

Here's where version control can help. The typical procedure looks like this:

Ed è qui che controllo delle versioni può aiutare. La tipica procedura assomiglia a questo:

  • Developers commit all new work to the trunk. Day-to-day changes are committed to /trunk: new features, bugfixes, and so on.

    Sviluppatori fanno commit di tutto lavoro nuovo nel tronco. Giorno dopo giorno modifiche sono pubblicate nel /trunk: nuove capacità, fix dei bug e così via.

  • The trunk is copied to a release branch. When the team thinks the software is ready for release (say, a 1.0 release), then /trunk might be copied to /branches/1.0.

    Il tronco è copiato nel ramo «rilascio». Quando il team pensa che il software è pronto per rilascio (diciamo, una vers. 1.0), il /trunk può essere copiato su /branches/1.0.

  • Teams continue to work in parallel. One team begins rigorous testing of the release branch, while another team continues new work (say, for version 2.0) on /trunk. If bugs are discovered in either location, fixes are ported back and forth as necessary. At some point, however, even that process stops. The branch is frozen for final testing right before a release.

    I team continuarono lavorare in parallelo. Un team comincia rigorosi test del ramo rilascio, mentre altro team continua nuovo lavoro (diciamo per la versione 2.0) su /trunk. Quando si scoprono i bug in entrambi locazioni, correzioni sono portate avanti e dietro secondo la necessità. Ad un certo punto, comunque, anche questo processo si ferma. Il ramo è «congelato» per test finale subito prima del rilascio.

  • The branch is tagged and released. When testing is complete, /branches/1.0 is copied to /tags/1.0.0 as a reference snapshot. The tag is packaged and released to customers.

    Il ramo è targato e rilasciato. Quando i test sono completati, /branches/1.0 è copiato su /tags/1.0.0 come instantanea di riferimento. Ramo targato è impachettato e rilasciato ai clienti.

  • The branch is maintained over time. While work continues on /trunk for version 2.0, bugfixes continue to be ported from /trunk to /branches/1.0. When enough bugfixes have accumulated, management may decide to do a 1.0.1 release: /branches/1.0 is copied to /tags/1.0.1, and the tag is packaged and released.

    Il ramo è mantenuto durante il tempo. Mentre lavoro continua su /trunk per la versione 2.0, i bugfix continuano ad essere portati da /trunk a /branches/1.0. Quando si accumulano abbastanza correzioni, i capi possono decidere di rilasciare vers. 1.0.1: /branches/1.0 è copiato su /tags/1.0.1, la targa è impachettata e rilasciata.

This entire process repeats as the software matures: when the 2.0 work is complete, a new 2.0 release branch is created, tested, tagged, and eventually released. After some years, the repository ends up with a number of release branches in maintenance mode, and a number of tags representing final shipped versions.

L'intero processo si ripete quando il software matura: quando lavoro 2.0 è completo, è creato nuovo ramo di rilascio 2.0, testato, targato e alla fine rilasciato. Dopo alcuni anni il deposito(repository) finisce con certo numero di rami di rilascio in stato di «mantenimento» e certo numero di targhe che rappresentano le versioni finali spedite.

Rami di 'caratteristica'

A feature branch is the sort of branch that's been the dominant example in this chapter, the one you've been working on while Sally continues to work on /trunk. It's a temporary branch created to work on a complex change without interfering with the stability of /trunk. Unlike release branches (which may need to be supported forever), feature branches are born, used for a while, merged back to the trunk, then ultimately deleted. They have a finite span of usefulness.

Un ramo di caratteristica è il tipo di ramo che era esempio dominante in questo capitolo, quello su quale voi avete lavorato mentre Sally continuava lavorare su /trunk. È un ramo temporaneo creato per lavorare su una modifica complessa senza interferire con la stabilità di /trunk. A differenza di rami di rilascio (che possono avere bisogno di supporto per sempre[23]), rami di caratteristica nascono, sono ussati per un po', fusi dietro il tronco e infine cancellati. Hanno un arco di vita utile limitato.

Again, project policies vary widely concerning exactly when it's appropriate to create a feature branch. Some projects never use feature branches at all: commits to /trunk are a free-for-all. The advantage to this system is that it's simple—nobody needs to learn about branching or merging. The disadvantage is that the trunk code is often unstable or unusable. Other projects use branches to an extreme: no change is ever committed to the trunk directly. Even the most trivial changes are created on a short-lived branch, carefully reviewed and merged to the trunk. Then the branch is deleted. This system guarantees an exceptionally stable and usable trunk at all times, but at the cost of tremendous process overhead.

Di nuovo, le regole del progetto variano largamente riguardo esattamente quando è appropriato create un ramo di caratteristica. Alcuni progetti non usano questi rami per niente: depositare (commit) su /trunk è libero per tutti. Vantaggio di questo sistema è la semplicità—nessuno deve imparare ramificazione e fusione. Svantaggio è che contenuto fresco del tronco (HEAD) è spesso instabile o non usabile. Altri progetti ramificano ad estremo: nessuna modifica è mai pubblicata su tronco direttamente. Anche la modifica più triviale è creata su ramo a vita breve, revisionata con cura e solo dopo fusa al tronco. Dopo quel ramo 'monouso' è cancellato. Questo sistema garantisce tronco eccezionalmente stabile e usabile in ogni momento, ma al costo di tremendo sovracarico del processo di sviluppo.

Most projects take a middle-of-the-road approach. They commonly insist that /trunk compile and pass regression tests at all times. A feature branch is only required when a change requires a large number of destabilizing commits. A good rule of thumb is to ask this question: if the developer worked for days in isolation and then committed the large change all at once (so that /trunk were never destabilized), would it be too large a change to review? If the answer to that question is yes, then the change should be developed on a feature branch. As the developer commits incremental changes to the branch, they can be easily reviewed by peers.

Molti progetti addottano approccio 'via di mezzo'. Comunemente insistono che /trunk si può compilare e passa tutti i test in ogni momento. Un ramo di caratteristica è richiesto solo quando modifica porta grande numero di commit destabilizzanti. Buona regola ferrea è di rispondere a questa domanda: se il sviluppatore lavora per giorni in isolamento e dopo pubblica larga modifica tutta in un colpo (così che /trunk non sarà mai destabilizzato), la modifica non sarebbe tropo grande per revisionare? Se la risposta a questa domanda è di «», allora le modifiche devono essere sviluppate in un ramo di caratteristica. Man mano che il sviluppatore pubblica modifiche incrementali sul ramo, queste possono essere facilmente revisionate dai collaboratori.

Finally, there's the issue of how to best keep a feature branch in sync with the trunk as work progresses. As we mentioned earlier, there's a great risk to working on a branch for weeks or months; trunk changes may continue to pour in, to the point where the two lines of development differ so greatly that it may become a nightmare trying to merge the branch back to the trunk.

All fine, c'è anche la questione come meglio tenere un ramo di caratteristica in «sincronia» col tronco man mano che lavoro procede. Abbiamo menzionato prima, c'è un grande rischio di lavorare su un ramo per settimane o mesi; modifiche continuano confluire anche al tronco, al punto che le due linee di sviluppo differiscono così tanto che può essere un incubo provare di fondere ramo dentro il tronco.

This situation is best avoided by regularly merging trunk changes to the branch. Make up a policy: once a week, merge the last week's worth of trunk changes to the branch. Take care when doing this; the merging needs to be hand-tracked to avoid the problem of repeated merges (as described in sezione chiamata «Tenere a mano traccia delle fusioni»). You'll need to write careful log messages detailing exactly which revision ranges have been merged already (as demonstrated in sezione chiamata «Fondere ramo intero nel altro»). It may sound intimidating, but it's actually pretty easy to do.

Questa situazione si può evitare meglio con regolare fusione delle modifiche del tronco dentro il ramo. Stabilite una regola: una volta alla settimana fondere ultime modifiche del tronco dentro il ramo. Prestate attenzione facendo questo; la fusione deve essere documentata a mano per evitare il problema delle fusioni ripetute (come descritto nella sezione chiamata «Tenere a mano traccia delle fusioni»). Dovete scrivere con cura messaggi di merge con esatti dettagli di quale intervallo di versioni era stato già fuso (come dimostrato nella sezione chiamata «Fondere ramo intero nel altro»). Può sembrare spaventoso, ma in verità è abbastanza semplice da fare.

At some point, you'll be ready to merge the synchronized feature branch back to the trunk. To do this, begin by doing a final merge of the latest trunk changes to the branch. When that's done, the latest versions of branch and trunk will be absolutely identical except for your branch changes. So in this special case, you would merge by comparing the branch with the trunk:

Ad un certo punto, sarete pronti per fondere il vostro ramo di caratteristica «sincronizato» dietro nel tronco. Per fare questo, cominciate facendo fusione finale delle ultime modifiche del tronco dentro vostro ramo. Qundo sarà fatto, le ultime versioni del ramo e del tronco saranno identiche, eccezion fatta per le vostre modifiche del ramo. Così in questo caso speciale, potete fondere comparando il ramo con il tronco:

$ cd trunk-working-copy

$ svn update
At revision 1910.

$ svn merge http://svn.example.com/repos/calc/trunk@1910 \
            http://svn.example.com/repos/calc/branches/mybranch@1910
U  real.c
U  integer.c
A  newdirectory
A  newdirectory/newfile
…

By comparing the HEAD revision of the trunk with the HEAD revision of the branch, you're defining a delta that describes only the changes you made to the branch; both lines of development already have all of the trunk changes.

Comparando versione HEAD del tronco con la versione HEAD del ramo, avete definito un delta che descrive solo le modifiche che avete fatto sul ramo; tutte e due linee dello sviluppo già hanno le modifiche del tronco.

Another way of thinking about this pattern is that your weekly sync of trunk to branch is analogous to running svn update in a working copy, while the final merge step is analogous to running svn commit from a working copy. After all, what else is a working copy but a very shallow private branch? It's a branch that's only capable of storing one change at a time.

Altro modo di pensare di questo archetipo è che la sincronizzazione settimanale del tronco sul ramo è analoga a svn update sulla copia di lavoro, mentre il passo di fusione finale è analogo a svn commit dalla copia di lavoro. Doppo tutto, che altro è una copia di lavoro se non un ramo privato pocco capiente. Un ramo capace di contenere solo una modifica alla volta.

Proofread stopflag #$#$#$#$#$#



[19] The Subversion project has plans, however, to someday implement an svnadmin obliterate command that would accomplish the task of permanently deleting information. In the meantime, see sezione chiamata «svndumpfilter» for a possible workaround.

[20] Progetto Subversion pianifica tuttavia di implementare un giorno il comando svnadmin obliterate che può accontentare una richiesta della cancellazione permanente. Ma prima ciò accada, vedi la sezione chiamata «svndumpfilter» per possibile raggiro.

[21] Because CVS doesn't version trees, it creates an Attic area within each repository directory as a way of remembering deleted files.

[22] Perché CVS non fa le versioni delle strutture, crea una area Attic in ogni cartella del deposito(repository) come modo di ricordare i file cancellati.

[23] Ndt. 'per sempre' mi sempre parola tropo grossa, il team può anche decidere e rendere noto diciamo dopo rilascio di versione 8.0 che a partire da 1 genaio del anno venturo cesserà il supporto per la versione 3.0 e precedenti. Già successo su molti software di grandi nomi. Poi le targhe 1.0, 2.0 e 3.0 possono essere cancellate dal deposito(repository).)