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.
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 |
---|---|
A nice way of finding the revision in which a branch was
created (the “base” of the branch) is to use the
Un bel modo per trovare la versione nella quale era stato
creato un ramo (la «base» del ramo) è usare opzione
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 Come aspettato, l'ultima versione stampata da questo comando
è la versione in cui |
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.
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]
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.
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.
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.
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 «sì», 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).)