VCS, Git, commit, working/staging area, Git directory, repository, remote, snapshot, tree, blob, checksum, SHA-1, checkout, merge, rebase, fetch, push, pull, master, HEAD, origin, tag, annotated, lightweight, signed, log, glob, branch, tracking
Un sistem de control al versiunilor (eng. VCS - Version Control System) este un mecanism prin intermediul căruia sunt gestionate fișiere / proiecte în dinamică (pe măsură ce sunt modificate), în scopul de a se putea realiza revenirea la o anumită stare în caz de eroare (restaurarea unei versiuni stabile) și pentru a permite colaborarea între mai multe echipe care lucrează concomitent la diferite funcționalități ale unui același proiect.
Deși în mod curent astfel de produse sunt folosite pentru dezvoltarea de aplicații (urmărindu-se o gestionare eficientă a codului sursă și a utilizatorilor care implementează anumite funcționalități sau corectează defecte), ele pot fi folosite și pentru alte tipuri de proiecte, ce implică lucrul cu fișiere binare, pentru care diferențele între diferite versiuni se realizează mai dificil.
În prezent, sunt folosite trei tipuri de sisteme de control a versiunilor, fiecare dintre acestea fiind adecvate unei anumite situații:
diff
) dintre versiuni succesive, astfel că se poate reveni oricând la o stare anterioară (exemplu: rcs);Apariția Git în 2005 este strâns legată de dezvoltarea kernelului pentru Linux, proiect open-source la care lucra o comunitate destul de numeroasă de programatori. Dacă anterior actualizările erau distribuite sub forma unor arhive ce conțineau modificările (1991-2002), respectiv prin intermediul unui sistem distribuit de control al versiunilor denumit BitKeeper (2002-2005), pe măsură ce proiectul a devenit mai complex și din ce în ce mai multe persoane și-au exprimat disponibilitatea de a contribui la dezvoltarea acestuia, s-a pus problema conceperii unui produs care să satisfacă cerințele legate de viteză, arhitectură scalabilă, suport pentru dezvoltare non-liniară (numeroase ramificații la care se lucrează concomitent), distribuire totală, capacitate de a gestiona proiecte de dimensiuni mari.
Git se diferențiază de alte sisteme de control al versiunilor prin câteva caracteristici:
commit
ce conține o referință către părintele sau părinții săi (consemnarea / consemnările anterioare, din care a fost obținut, prin modificarea fișierelor) și o referință către instantaneul propriu-zis. De asemenea, în cadrul acestui obiect se rețin și informații despre dimensiunea totală și suma de control SHA-1, autor și contributor (nume și adresă de poștă electronică), data la care a fost realizată consemnarea, mesajul asociat.tree
, o structură de date ce conține referințe spre fiecare resursă ce au fost modificată, aceasta fiind identificată prin denumire și sumă de control SHA-1; și în cazul unui astfel astfel de obiect se rețin dimensiunea totală și suma de control SHA-1;blob
, corespunzătoare fiecărei resurse din cadrul consemnării respective; acestea sunt identificate prin denumire, dimensiune și suma de control SHA-1.Un scenariu tipic de utilizare a sistemului de versiune Git implică:
Instrucțiunile pentru instalarea și configurarea Git sunt disponibile aici. Informații suplimentare cu privire la comenzile Git și sintaxa acestora pot fi obținute folosind paginile de manual:
aipi2014@ubuntu:~$ git help <command> aipi2014@ubuntu:~$ git <command> --help aipi2014@ubuntu:~$ man git-<command>
În cazul în care se dorește monitorizarea unui proiect nou / existent prin sistemul de control al versiunilor Git, directorul în care se găsește acesta va trebui inițializat folosind comanda:
aipi2014@ubuntu:~$ git init
Astfel, se creează un director .git
în care vor fi plasate toate versiunile fișierelor care sunt monitorizate. Inițial, acesta este vid.
Indicarea fișierelor care sunt monitorizate se face prin intermediul comenzii git add <file>
, fiind permisă și folosirea de expresii regulate folosind măști pentru a indica conținutul unui întreg director. Prin intermediul acestei comenzi, fișierele sunt transferate din directorul de lucru în zona de așteptare.
Consemnarea propriu-zisă a fișierelor se face rulând comanda git commit -m "<message>"
, mesajul care o însoțește trebuind să fie relevant pentru modificările care au fost realizate. Se recomandă ca această operație să fie realizată cât mai des pentru actualizări de dimensiuni relativ reduse ale codului sursă. În acest moment, fișierele trec din zona de așteptare în directorul Git.
În situația în care utilizatorul vrea să lucreze pe un proiect găzduit pe un server la distanță, poate descărca întregul conținut în zona de lucru, inclusiv istoricul complet al versiunilor anterioare (care poate fi ulterior reconstituit după această copie, în cazul coruperii informațiilor stocate pe serverul la distanță), prin intermediul comenzii:
git clone <URL> [<local_directory>]
unde:
URL
- reprezintă adresa serverului la distanță care găzduiește proiectul, putând fi utilizate în acest sens mai multe protocoale pentru transferul de informațieaipi2014@ubuntu:~$ git clone git://github.com/aipi2014/Laborator00.git
aipi2014@ubuntu:~$ git clone https://github.com/aipi2014/Laborator00
aipi2014@ubuntu:~$ git clone git@github.com:aipi2014/Laborator00.git
local_directory
(opțional) - denumirea directorului local în care va fi stocată versiunea curentă a proiectului (precum și istoricul din directorul Git), în cazul în care se dorește schimbarea acestuia
Fișierele astfel descărcate, aflate atât în zona de lucru cât și în directorul Git pot fi modificate în funcție de necesități și transferate, succesiv, în zona de așteptare (prin git add
) și în baza de date locală (prin git commit -m
).
Dacă este necesar ca fișierele modificate să fie încărcate pe serverul de unde au fost preluate, trebuie ca mai întâi să se actualizeze modificările care se vor fi produs pe acesta între timp (folosind comanda git pull --rebase
) - rezolvând eventualele conflicte - și apoi să se transfere efectiv prin intermediul comenzii git push origin master
.
Comanda git status
furnizează informații cu privire la starea fișierelor aflate în zona de lucru, fie că este vorba de resurse deja monitorizate (care se găsesc în directorul Git) care au fost modificate între timp, fie că este vorba despre date care au fost adăugate (și care nu au fost marcate în mod explicit pentru a fi ignorate). De asemenea, comanda indică și ramificația (eng. branch) pe care se găsește utilizatorul în mod curent.
O astfel de comandă poate întoarce mai multe rezultate:
aipi2014@ubuntu:~$ git status On branch master Your branch is up-to-date with 'origin/master'. nothing to commit, working directory clean
aipi2014@ubuntu:~$ git status On branch master Your branch is up-to-date with 'origin/master'. Untracked files: (use "git add <file>..." to include in what will be committed) MiniShell/ nothing added to commit but untracked files present (use "git add" to track)
Monitorizarea acestor date se face prin intermediul comenzii git add <files/directory>
, care suportă specificarea de expresii regulate desemnând măști pentru indicarea mai multor fișiere/directoare.
Changes to be committed
); eventualele fișiere care au existat în instantanee anterioare și au fost modificate în directorul de lucru nu vor fi marcate ca făcând parte din zona de așteptare (trecute în secțiunea Changes not staged for commit
) dacă versiunile respective nu vor fi incluse în mod explicit prin comanda git add <files/directory>
aipi2014@ubuntu:~$ git status On branch master Your branch is up-to-date with 'origin/master'. Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: Minishell/.classpath new file: Minishell/.project new file: Minishell/.settings/org.eclipse.jdt.core.prefs new file: Minishell/src/ro/pub/cs/aipi/lab00/applicationlogic/CommandParser.java new file: Minishell/src/ro/pub/cs/aipi/lab00/applicationlogic/FileSystemOperations.java new file: Minishell/src/ro/pub/cs/aipi/lab00/general/Constants.java new file: Minishell/src/ro/pub/cs/aipi/lab00/main/MiniShell.java Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: README.md
Fișierele care nu au existat într-un instataneu anterior sunt marcate prin new file
, iar cele care au fost modificate față de versiunile precedente sunt marcate prin modified
.
Changes to be committed
, respectiv Changes not staged for commit
în situația în care au mai fost realizate modificări asupra acestuia după rularea comenzii git add <files/directory>
.
Prin intermediul comenzii git add <files/directory>
se specifică fișiere / directoare care vor fi transferate din zona de lucru în zona de așteptare, pentru a fi incluse în următorul instantaneu în momentul în care se va realiza consemnarea acestora.
git add
. Dacă ele sunt modificate după precizarea acestei comenzi, actualizările nu vor fi consemnate în directorul Git decât dacă se rulează din nou comanda git add
.
aipi2014@ubuntu:~$ git add Minishell/*
În cazul în care un fișier este transferat din greșeală din zona de lucru în zona de așteptare, parcursul invers poate fi realizat prin intermediul comenzii git reset HEAD <file>
.
Dacă se dorește eliminarea modificărilor realizate asupra unui fișier din zona de lucru, se poate folosi comanda git checkout -- <file>
(disponibilă începând cu versiunea 1.6.1). O astfel de operație este totuși periculoasă, în sensul că actualizările respective sunt pierdute fără posibilitatea de a mai putea fi recuperate (întrucât nu au fost consemnate niciodată în directorul Git).
Alte operații care pot fi realizate asupra fișierelor din zona de lucru / zona de așteptare sunt mutarea (redenumirea), respectiv ștergerea acestora.
Comanda git mv <source> <target>
este folosită pentru operația de mutare (redenumire) a unui fișier. Necesitatea sa este dată de faptul că Git nu detectează în mod automat fișierele care sunt redenumite pe discul local. Dacă se verifică starea fișierelor din zona de lucru / zona de așteptare, fișierele redenumite apar în zona Changes to be committed
, în secțiunea renamed
.
git mv <source> <target>
este echivalentă cu aceeași succesiune de operații: aipi2014@ubuntu:~$ mv <source> <target> aipi2014@ubuntu:~$ git rm <source> aipi2014@ubuntu:~$ git add <target>
aipi2014@ubuntu:~$ git mv README.md README.txt
Prin intermediul comenzii git rm <file/directory>
, resursa specificată este eliminată nu numai de pe discul local, ci și din zona de așteptare, astfel încât atunci când se realizează operația de consemnare, acesta este eliminat din directorul Git și din lista fișierelor care sunt monitorizate. În cazul în care resursa este eliminată manual, doar de pe discul local, aceasta va apărea ca modificată în zona de lucru, dar nemarcată pentru a fi consemnată în zona de așteptare. Dacă se verifică starea fișierelor din zona de lucru / zona de așteptare, fișierele șterse apar în zona Changes to be committed
, în secțiunea deleted
.
-f
, pentru a preveni eliminarea accidentală a unor resurse.-r
(recursiv).
.gitignore
), comanda trebuie rulată cu opțiunea --cached
.
aipi2014@ubuntu:~$ git rm LICENSE rm 'LICENSE'
Este recomandat ca fișierele generate să nu fie incluse în directorul Git (binare, jurnale), acestea fiind rezultatul procesului de compilare / rulare a proiectului. Mai mult, nu se dorește ca informații cu privire la modificarea lor să fie incluse în raportul rezultat ca rulare a comenzii git status
.
Un astfel de comportament poate fi obținut prin specificarea acestui tip de fișiere în .gitignore
, respectându-se următoarele reguli:
#
nu sunt luate în considerare*
desemnează 0 sau mai multe caractere (trebuie precedat de \
)**
(suportat din versiunea 1.8.2) referă conținutul unui director?
indică un singur caracter{
și }
sunt trecute colecții de șabloane[
și ]
sunt trecute seturi de caractere (sau intervale între două caractere, separate prin -
)!
înaintea unui șablon îl neagă
La crearea unui proiect nou, GitHub oferă posibilitatea de a include în mod automat un fișier .gitignore
în funcție de limbajul de programare utilizat.
Pentru Java, conținutul fișierului .gitignore
generat este:
*.class # Mobile Tools for Java (J2ME) .mtj.tmp/ # Package Files # *.jar *.war *.ear # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid*
Prin intermediul comenzii git diff
, utilizatorul are posibilitatea de a vizualiza în ce constau actualizările pentru fiecare fișier în parte.
aipi2014@ubuntu:~$ git diff diff --git a/README.md b/README.md index 6094ea3..84033c8 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,15 @@ -Laborator00 -=========== +Minishell +========= + +Minishell is a platform-independent shell writen in Java (exploring the New I/O API), supporting commands (though without any options) to manipulate the content of the file system (files and directories). + +In the case of overwriting existing files or deleting non-empty directories, the user will be prompted whether he/she is sure to do so. + +There is no support at the moment for symbolic links. + +* v1.0 +The commands supported so far are: + - cd <new_directory>: changes the current directory to the new_directory, which may denote a relative or an absolute path + - mkdir <new_directory> / md <new_directory>: creates a new directory within the current directory, if the parameter is a relative path, or at the exact location specified by an absolute path + - touch <new_file> / touch <existing_file>: creates a new file with the specified content; appends the specified content to an existing file; the content is to be added line by line until /quit is entered + - quit / exit: terminates the program
<note>Dacă toate fișierele din zona de lucru au fost fie marcate pentru a fi incluse în următorul instantaneu, fie sunt ignorate, rezultatul comenzii git diff
va fi vid.</note>
--cached
sau --staged
(disponibil din versiunea 1.6.1, efectul este identic), comanda indică diferențele pentru fișierele care au fost marcate pentru a fi incluse în următorul instantaneu, față de situația existentă în directorul Git (cu alte cuvinte, sunt indicate diferențele dintre zona de așteptare și directorul Git) aipi2014@ubuntu:~$ git diff --staged diff --git a/Minishell/.settings/org.eclipse.jdt.core.prefs b/Minishell/.setting new file mode 100644 index 0000000..3a21537 --- /dev/null +++ b/Minishell/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 ...
Dacă toate fișierele din zona de lucru au fost marcate spre a fi incluse în următorul instantaneu (rezultatul comenzii git status
nu conține nici un fișier în secțiunea Changes not staged for commit
), ele pot fi consemnate, adică trecute din zona de așteptare în directorul Git, prin intermediul comenzii git commit
.
Rulată fără parametru, comanda deschide editorul de text implicit (sau cel indicat de proprietatea core.editor
) completat cu rezultat comenzii git status
, care poate fi modificat pentru a constitui mesajul asociat fișierelor consemnate în directorul Git. Dacă se dorește ca în acest mesaj să se includă și rezulatul comenzii git diff
, se poate utiliza parametrul -v
.
Mesajul care însoțește consemnarea fișierelor poate fi inclusă direct în cadrul comenzii prin intermediul parametrului -m
.
aipi2014@ubuntu:~$ git commit -m "initial commit: cd, mkdir, touch, quit" [master 60a2501] initial commit: cd, mkdir, touch, quit 7 files changed, 224 insertions(+) create mode 100644 Minishell/.classpath create mode 100644 Minishell/.project create mode 100644 Minishell/.settings/org.eclipse.jdt.core.prefs create mode 100644 Minishell/src/ro/pub/cs/aipi/lab00/applicationlogic/CommandParser.java create mode 100644 Minishell/src/ro/pub/cs/aipi/lab00/applicationlogic/FileSystemOperations.java create mode 100644 Minishell/src/ro/pub/cs/aipi/lab00/general/Constants.java create mode 100644 Minishell/src/ro/pub/cs/aipi/lab00/main/MiniShell.java
Se observă că în rezultatul acestei comenzi sunt indicate ramificația pe care se face consemnarea (master
), suma de control SHA-1 a consemnării (60a2501
), numărul de fișiere modificate (7
) precum și statistici cu privire la numărul de linii adăugate (224
), respectiv eliminate (0
).
git commit -m "<message>"
trebuie rulată și cu parametrul -a
. aipi2014@ubuntu:~$ git commit -a -m "initial commit: cd, mkdir, touch, quit"
Într-un astfel de caz, nu mai este necesar ca în prealabil să se ruleze comanda git add
.
Dacă s-au omis resurse în cadrul celei mai recente versiuni transmise către directorul Git, se poate încerca consemnarea modificărilor respective prin intermediul comenzii git commit --amend
(ulterioară marcării fișierelor în cauză în zona de așteptare) prin intermediul căreia se suprascrie varianta anterioară cu actualizările din zona de așteptare curentă. Ca și în cazul precedent, este afișat un editor de text care conține mesajul corespunzător versiunii consemnate, acesta putând fi actualizat.
Unei versiuni consemnate în directorul Git îi poate fi asociată o etichetă (eng. tag) prin care se desemnează, de regulă, o anumită funcționalitate.
Git oferă posibilitatea de a defini două tipuri de etichete, prin intermediul comenzii git tag
:
aipi2014@ubuntu:~$ git tag v1.0
aipi2014@ubuntu:~$ git tag -a v1.0 -m 'an annotated tag for the first release version'
Etichetele de acest tip pot fi semnate folosind GPG (GNU Privacy Guard), dacă utilizatorul dispune de o cheie privată, acestea având avantajul că pot fi verificate
aipi2014@ubuntu:~$ git tag -s v1.0 -m 'a signed tag for the first release version'
Verificarea unei etichete semnate se face prin intermediul comenzii git tag -v <tag_name>
, fiind necesară existența cheii publice a utilizatorului care a semnat-o.
Așadar, opțiunile cu care se poate rulează comanda git tag
în cazul unei etichete adnotate sunt:
-a
: specifică faptul că este vorba despre o etichetă nesemnată-s
: specifică faptul că este vorba despre o etichetă semnată-m
: indică mesajul asociat etichetei (dacă se rulează comanda fără acest parametru, va fi deschis editorul de text pentru ca acesta să fie introdus)-v
: dacă se dorește verificarea unei etichete semnate
Lista tuturor etichetelor asociate unui proiect poate fi consultată dacă se rulează comanda git tag
fără nici un parametru:
aipi2014@ubuntu:~$ git tag v1.0
Aceasta suportă opțiunea -l <tag_mask>
pentru a se afișa doar lista etichetelor care respectă o anumită expresie regulată.
Dacă se dorește consultarea conținutului unei etichete, se poate utiliza comanda git show <tag>
, prin care sunt listate toate informațiile asociate versiunii respective.
aipi2014@ubuntu:~$ git show v1.0 commit 5e4b82a0783e5841e5c5241d44104e8b15e4270f Author: aipi2014 <aipi2014@andreirosucojocaru.ro> Date: Tue Sep 30 01:28:08 2014 +0300 Initial commit
În situația în care o versiune a fost consemnată fără a i se asocia o etichetă, o astfel de operație poate fi realizată și ulterior, adăugând la comanda git tag
suma de control asociată respectivei versiuni (obținută ca rezultat al comenzii git log
).
aipi2014@ubuntu:~$ git tag -a v1.1 -m 'version 1.1' e71c1c4b1b9818292b4cda084e47e25bfb573507
De regulă, etichetele nu sunt transferate în cadrul depozitelor găzduite de serverele la distanță, fiind necesar ca acest lucru să fie realizat manual:
git push <remote_name> <tag>
;git push <remote_name> --tags
.
Istoricul versiunilor consemnate pentru un proiect poate fi consultat, în ordine invers cronologică (de la cele mai noi la cele mai vechi) rulând comanda git log
. Pentru fiecare versiune a proiectului, vor fi afișate următoarele informații:
Cele mai folosite opțiuni ale acestei comenzi sunt:
OPȚIUNE | FUNCȚIONALITATE |
---|---|
-p | afișează diferențele (rezultatul comenzii git diff ) realizate în consemnarea curentă față de consemnarea anterioară |
--word_diff | afișează diferențele între versiuni, la nivel de cuvânt (mai ales pentru fișiere text de dimensiuni mari, mai rar pentru cod sursă): cuvintele adăugate sunt cuprinse între {+ +}, iar cuvintele șterse între [- -]; dacă se dorește omiterea contextului în care apare diferența (linia precedentă și linia care o succede), se poate folosi opțiunea -U1 (se afișează doar linia curentă) |
--stat | afișează statistici cu privire la fiecare consemnare în parte: numărul de fișiere modificate, lista acestora și numărul de modificări (adăugări / ștergeri) - la nivel de linie - pentru fiecare dintre acestea |
--shortstat | afișează doar statistici generale: numărul total de fișiere modificate, adăugări și ștergeri (la nivel de linie) |
--name-only | afișează lista fișierelor modificate |
--name-status | afișează lista fișierelor modificate împreună cu natura actualizării (actualizat, adăugat, șters) |
--abbrev-commit | afișează numai câteva caractere (din cele 40) ale sumei de control SHA-1 asociată fiecărei consemnări |
--relative-date | afișează momentul la care a fost realizată consemnarea relativ la data curentă |
--graph | afișează un graf (în format ASCII) al ramificațiilor, ilustrând momentul la care acestea au fost combinate |
--pretty | modifică formatul în care sunt afișate informațiile despre versiuni: ♦ oneline - informațiile despre fiecare consemnare sunt afișate pe o singură linie♦ short , full , fuller - controlează cantitatea de informație♦ format - permite personalizarea informațiilor, util pentru situația în care conținutul urmează să fie prelucrat în mod automat:✔ %H - suma de control, %h - suma de control prescurtată✔ %T - arborele sumei de control, %t - arborele sumei de control prescurtat✔ %P - suma de control a părintelui, %p - suma de control prescurtată a părintelui✔ %an - numele autorului, %ae - adresa de poștă electonică a autorului✔ %ad - data autorului, %ar - data autorului (relativă)✔ %cn - numele contributorului*), %ce - adresa de poștă electronică a contributorului✔ %cd - data contributorului, %cr - data contributorului (relativă)✔ %s - subiectul |
--oneline | afișează numai câteva caractere (din cele 40) ale sumei de control SHA-1 asociată fiecărei consemnări, pe o singură linie prescurtare pentru --pretty=online abbrev-commit |
*) Distincția dintre autor și contributor este următoarea:
În situația în care pentru un proiect au fost realizate foarte multe consemnări, există posibilitatea ca rezultatul comenzii git log
să fie limitat doar la cele care sunt de interes (oricum, în mod implicit, se realizeză o paginare astfel încât este imposibil ca acestea să fie afișate toate dintr-o dată):
OPȚIUNE | FUNCȚIONALITATE |
---|---|
-<n> | afișează doar cele mai recente n consemnări |
--since , --after | afișează consemnările realizate după cu o anumită dată / oră absolută / relativă *) |
--until , --before | afișează consemnările realizate înainte de o anumită dată/oră absolută / relativă |
--author | afișează doar consemnările având un anumit autor |
--committer | afișează doar consemnările având un anumit contributor |
--grep | afișează doar consemnările având anumite cuvinte în mesajul asociat; dacă se dorește indicarea mai multor cuvinte, trebuie utilizată împreună cu opțiunea --all-match |
--<path> | afișează doar consemnările care au realizat modificări asupra fișierelor localizate în calea specificată; această opțiune trebuie inclusă întotdeauna ultima |
*) Formatul în care se afișează data este specificat de opțiunea --date
, care poate lua valorile iso
(ISO 8601), rfc
(RFC 2822), raw
(număr de secunde de la 01/01/1970 UTC), local
(în conformitate cu zona de timp), relative
(raportat la momentul curent de timp).
Dacă se utilizează mai multe opțiuni, se vor lua în considerare consemnările care îndeplinesc toate criteriile specificate.
aipi2014@ubuntu:~$ git log --pretty=format:"%h - %an [%ae] - %ar -> %s" --graph * 7224392 - Aplicatii Integrate pentru Intreprinderi 2014 [aipi2014@andreirosucojocaru.ro] - 6 minutes ago -> resolved conflicts: contents in delete branch included functionality of copy_move |\ | * 30fe2f3 - Aplicatii Integrate pentru Intreprinderi 2014 [aipi2014@andreirosucojocaru.ro] - 25 minutes ago -> added support for delete commands * | 477dcd6 - Aplicatii Integrate pentru Intreprinderi 2014 [aipi2014@andreirosucojocaru.ro] - 29 minutes ago -> added support for copy and move commands |/ * d6c99c9 - andreirosucojocaru [andrei.rosucojocaru@gmail.com] - 42 minutes ago -> added support for touch command * 3b6355b - Aplicatii Integrate pentru Intreprinderi 2014 [aipi2014@andreirosucojocaru.ro] - 77 minutes ago -> removed LICENSE * c387a66 - Aplicatii Integrate pentru Intreprinderi 2014 [aipi2014@andreirosucojocaru.ro] - 78 minutes ago -> initial commit: support for cd, mkdir, md * d438c81 - aipi2014 [aipi2014@andreirosucojocaru.ro] - 83 minutes ago -> Initial commit
Mai mulți utilizatori pot colabora în cadrul unui proiect Git aflat pe un server la distanță (eng. remote repository), pe care pot avea fie doar drepturi de citire fie atât drepturi de citire cât și de scriere. Operațiile pe care le pot realiza sunt descărcarea de cod sursă, respectiv încărcarea (în situația în care au drepturi suficiente).
Prin intermediul comenzii git remote
, pot fi consultate depozitele la distanță cu care se lucrează în mod curent. În mod implicit, sunt afișate doar denumirile scurte asociate acestora. În cazul în care se dorește să se afișeze și URL-ul locației corespunzătoare fiecărui depozit la distanță, se va folosi opțiunea -v
.
Dacă directorul pe care se lucrează în mod curent a fost obținut ca urmare a clonării unui depozit la distanță, acesta va fi afișat ca având denumirea origin
.
aipi2014@ubuntu:~$ git remote -v Laborator00_andreirosucojocaru git@github.com:andreirosucojocaru/Laborator00.git (fetch) Laborator00_andreirosucojocaru git@github.com:andreirosucojocaru/Laborator00.git (push) origin https://github.com/aipi2014/Laborator00.git (fetch) origin https://github.com/aipi2014/Laborator00.git (push)
Utilizatorul va avea drepturi de scriere numai pe depozitele la distanță pentru care dispune de cheile SSH corespunzătoare.
Pentru a putea referi un depozit la distanță prin intermediul unei denumiri (mai scurte) se va rula comenda git remote add
:
git remote add <remote_name> <URL>
Astfel, nu va mai fi necesară introducerea întregului URL corespunzător locației la care se găsește depozitul la distanță (pentru comenzile de încărcare/descărcare, respectiv consultare a acestuia), fiind suficientă precizarea denumirii asociate.
aipi2014@ubuntu:~$ git remote add Laborator00_andreirosucojocaru git@github.com:andreirosucojocaru/Laborator00.git
Pentru a descărca cod sursă aflat într-un depozit găzduit de un server la distanță pe discul local există trei posibilități:
git clone <URL>
realizează o copie a datelor aflate la locația indicată de URL, inclusiv a tuturor ramificațiilor (eng. branches), ramificația master
(în situația în care există) de pe server fiind monitorizată pentru modificări, astfel încât acestea să fie integrate automat (eng. merged) în codul sursă din directorul de lucru aipi@ubuntu:~$ git clone https://github.com/aipi2014/Laborator00.git Cloning into 'Laborator00'... remote: Counting objects: 30, done. remote: Compressing objects: 100% (19/19), done. remote: Total 30 (delta 3), reused 30 (delta 3) Unpacking objects: 100% (30/30), done. Checking connectivity... done.
git fetch <remote_name>
descarcă toate informațiile din depozitul de pe serverul la distanță care nu se regăsesc pe discul local, creându-se referințe către ramificația de la locația respectivă, care pot fi consultate pentru ca ulterior să fie integrate aipi@ubuntu:~$ git fetch Laborator00_andreirosucojocaru remote: Counting objects: 39, done. remote: Compressing objects: 100% (19/19), done. remote: Total 39 (delta 5), reused 39 (delta 5) Unpacking objects: 100% (39/39), done. From https://github.com/andreirosucojocaru/Laborator00 * [new branch] master -> Laborator00_andreirosucojocaru/master
git fetch
nu integrează modificările existente în depozitul existent pe serverul la distanță în mod automat, fiind necesar ca această operație să fie realizată manual.
git pull
descarcă modificările dintr-o ramificație monitorizată (de exemplu, ramificația master
în situația în care depozitul este clonat) din depozitul de pe serverul la distanță, încercând să le integreze în mod automat în codul sursă din directorul de lucru.
Transmiterea modificărilor operate asupra unui cod sursă pe un depozit găzduit de un server la distanță se face prin intermediul comenzii git push <remote_name> <branch>
, care primește ca parametrii denumirea referinței către depozitul la distanță (aceasta este origin
în situația în care proiectul a fost clonat) și ramificația pe care se găsește codul sursă care urmează a fi clonat.
aipi2014@ubuntu:~$ git push origin master Counting objects: 31, done. Delta compression using up to 2 threads. Compressing objects: 100% (9/9), done. Writing objects: 100% (16/16), 1.41 KiB | 0 bytes/s, done. Total 16 (delta 5), reused 0 (delta 0)
git push
va eșua în situația în care depozitul din directorul de lucru local nu este actualizat, adică dacă pe serverul la distanță se găsesc modificări (încărcate de alți utilizatori) care nu au fost descărcate încă. În această situație, înainte de a se transmite propriile modificări, va trebui să se ruleze comanda git pull
.
Dacă se dorește obținerea de informații cu privire la conținutul unui proiect găzduit de un server la distanță, se poate rula comanda git remote show <remote-name>
, care afișează:
git push
git pull
aipi2014@ubuntu:~$ git remote show origin * remote origin Fetch URL: https://github.com/aipi2014/Laborator00.git Push URL: https://github.com/aipi2014/Laborator00.git HEAD branch: master Remote branch: master tracked Local branch configured for 'git pull': master merges with remote master Local ref configured for 'git push': master pushes to master (up to date)
În cazul în care se dorește să se schimbe denumirea asociată unei referințte către un depozit la distanță, acest lucru poate fi realizat prin comanda git remote rename <old_remote_name> <new_remote_name>
.
aipi2014@ubuntu:~$ git remote rename Laborator00_andreirosucojocaru Lab00_arc
O referință către un depozit la distanță poate fi ștearsă în situația în care codul sursă nu mai este disponibil la adresa respectivă sau dacă utilizatorul respectiv nu mai lucrează pe proiectul în cauză. Comanda utilizată într-o astfel de situație este git remote rm <remote_name>
.
aipi2014@ubuntu:~$ git remote rm Lab00_arc
O ramificație (eng. branch) marchează un punct din care dezvoltarea unui proiect se realizează în zone diferite (corespunzătoare unor anumite funcționalități sau unor anumite echipe care le implementează), în scopul obținerii unui nivel de izolare cât mai mare până la obținerea rezultatului dorit, când mai multe ramificații pot fi integrate împreună.
De regulă, există o ramificație corespunzătoare unei versiuni stabile, aflată în exploatare și o ramificație pentru versiunea de lucru a proiectului în care sunt integrate, după ce au fost suficient testate, ramificațiile aferente diferitelor funcționalități. Acestea vor constitui o propunere de actualizare care va fi inclusă în următoarea versiune stabilă atunci când se consideră necesar.
Modul în care Git implementează gestiunea ramificațiilor îl evidențiază printre celelalte sisteme de versionare a codului sursă, operațiile care le vizează realizându-se foarte rapid.
O ramificație în Git nu este altceva decât o referință (un pointer) către cea mai recentă consemnare (obiect de tip commit
) pe care o urmează pe măsură ce sunt dezvoltate noi versiuni. Astfel, o ramificație este un fișier ce conține cele 40 de caractere ale sumei de control SHA-1 corespunzătoarele consemnării curente, motiv pentru care crearea și distrugerea acesteia se realizează foarte rapid (comparativ cu alte sisteme de versionare a codului sursă care copiau întregul proiect). De asemenea, referințele către consemnările părinte face ca operația de integrare a modificărilor să fie foarte rapidă, întrucât se poate identifica ușor care este consemnarea de bază (comună tuturor ramificațiilor implicate) peste care se pot aplica actualizările.
Implicit, ramificația curentă pe care se lucrează în Git poartă denumirea de master
.
Ramificația pe care se găsește utilizatorul în mod curent este indicată de o altă referință, denumită HEAD
.
Pentru a se lista ramificațiile din cadrul proiectului curent, se rulează comanda git branch
, fără nici un parametru:
aipi2014@ubuntu:~$ git branch copy_move delete * master
În cadrul rezultatului, ramura pe care se găsește utilizatorul în mod curent este marcată prin caracterul *
, al cărui conținut este reflectat de directorul de lucru și pe care urmează să se realizeze următoarea operație de consemnare.
Opțiunile cu care se poate rula această comandă sunt:
-v
pentru a se indica cea mai recentă consemnare corespunzătoare fiecărei ramificații (se afișează o parte din suma de control și mesajul asociat)--merged
pentru a se afișa doar ramificațiile care au fost deja integrate în cadrul ramificației pe care utilizatorul se găsește în mod curent--no-merged
pentru a se afișa ramificațiile al căror conținut nu a fost încă integrat în cadrul ramificației pe care utilizatorul se găsește în mod curentgit branch -D <branch_name>
, cu pierderea modificărilor realizate în cadrul acesteia.
Comanda git branch <branch_name>
are rolul de a crea o nouă ramificație în cadrul proiectului, având o denumirea dată. Inițial, aceasta va fi o referință către aceeași versiune ca și master
, urmând ca pe măsură ce sunt realizate noi consemnări, să indice obiectele de tip commit
corespunzătoare acestora.
aipi2014@ubuntu:~$ git branch copy_move aipi2014@ubuntu:~$ git branch delete
Prin intermediul comenzii git checkout <branch_name>
se realizează mutarea din ramificația curentă în ramificația indicată ca parametru, prin modificarea pointerului HEAD
. Totodată, se actualizează și conținutul directorului de lucru de pe discul local, corespunzător cu structura ramificației pe care s-a trecut.
aipi2014@ubuntu:~$ git checkout copy_move Switched to branch 'copy_move'
Operațiile de creare a unei noi ramificații și de mutare în cadrul acesteia (obținută prin rularea succesivă a comenzilor git branch
și git checkout
) pot fi realizate concomitent prin intermediul comenzii git checkout -b <branch_name>
.
aipi2014@ubuntu:~$ git checkout -b copy_move Switched to a new branch 'copy_move'
Integrarea modificărilor realizate pe o ramificație poate fi realizată în Git prin:
Rezultatul celor două tipuri de operații este întotdeauna același, în sensul că versiunea obținută va avea același conținut, distincția constând în modul în care este consemnat istoricul: în cazul merge acesta reflectă exact ce s-a întâmplat (care este punctul din care proiectul a fost dezvoltat în paralel și care este momentul de timp în care modificările au fost integrate), fiind însă mai dificil de gestionat, în timp ce în cazul rebase, dezvoltarea proiectului apare ca fiind liniară, ceea ce face ca referințele către stările proiectului să poată fi mutate mai ușor, putând fi introduse însă probleme serioase în cazul în care această operație este realizată pentru o consemnare existentă pe un server la distanță (aceasta dispare de pe server, în timp ce poate să existe în directoarele de lucru de pe discul local al utilizatorilor).
Integrarea modificărilor realizate pe o ramificație în varianta MERGE se face prin comanda git merge <branch_name>
, după ce toate modificările din cadrul acesteia au fost consemnate și s-a trecut înapoi pe ramificația master
.
aipi2014@ubuntu:~$ git checkout master Switched to branch 'master' aipi2014@ubuntu:~$ git merge copy_move Updating d6c99c9..477dcd6 Fast-forward .../aipi/lab00/applicationlogic/CommandParser.java | 8 ++ .../applicationlogic/FileSystemOperations.java | 117 +++++++++++++++++++++ .../ro/pub/cs/aipi/lab00/general/Constants.java | 7 ++ README.txt | 8 ++ 4 files changed, 140 insertions(+) aipi2014@ubuntu:~$ git merge delete Auto-merging README.txt CONFLICT (content): Merge conflict in README.txt Auto-merging MiniShell/src/ro/pub/cs/aipi/lab00/general/Constants.java CONFLICT (content): Merge conflict in MiniShell/src/ro/pub/cs/aipi/lab00/general/Constants.java Auto-merging MiniShell/src/ro/pub/cs/aipi/lab00/applicationlogic/FileSystemOperations.java CONFLICT (content): Merge conflict in MiniShell/src/ro/pub/cs/aipi/lab00/applicationlogic/FileSystemOperations.java Auto-merging MiniShell/src/ro/pub/cs/aipi/lab00/applicationlogic/CommandParser.java CONFLICT (content): Merge conflict in MiniShell/src/ro/pub/cs/aipi/lab00/applicationlogic/CommandParser.java Automatic merge failed; fix conflicts and then commit the result.
commit
corespunzător ramificației care este integrată referă ramificația cu care se integrează (urmărind pointerul părinte), întrucât în acest caz pointer-ii master
și HEAD
sunt pur și simplu mutați înainte;commit
corespunzător ramificației care este integrată nu referă ramificația cu care se integrează (urmărind pointerul părinte), se procedează la următorul algoritm:<<<<<<< HEAD
și =======
) și a versiunii care se dorește a fi integrată (între =======
și <<<<<<< <branch_name>
).git add
;merge.tool
)
Integrarea modificărilor realizate pe o ramificație în varianta REBASE se face prin comanda git rebase master
, după ce toate modificările din cadrul acesteia au fost consemnate. În acest caz, se identifică modificările realizate în cadrul ramificației de la cel mai recent strămoș comun, acestea fiind aplicate versiunii indicate de ramificația master
și introduse într-o consemnare care o va referi direct pe aceasta, ca părinte. După această operație, se poate trece înapoi pe ramificația master
, integrându-se printr-o operație de tip fast-forward modificările din ramificația obținută (spre care va indica referința ramificației care a fost integrată).
aipi2014@ubuntu:~$ git checkout copy_move aipi2014@ubuntu:~$ git rebase master First, rewinding head to replay your work on top of it... Applying: added staged command aipi2014@ubuntu:~$ git checkout master Switched to branch 'master' aipi2014@ubuntu:~$ git merge copy_move
Dacă nu se dorește ca inițial să se treacă pe ramificația care urmează a fi integrată, se poate rula comanda git rebase <branch_to_integrate_in> <branch_to_be_integrated>
. Prin aceasta, se trece pe ramificația ce urmează a fi integrată, realizându-se apoi operația de integrare a acesteia în ramura de bază, în care se face integrarea.
Astfel,
aipi2014@ubuntu:~$ git rebase master copy_move
este echivalentă cu
aipi2014@ubuntu:~$ git checkout copy_move aipi2014@ubuntu:~$ git rebase master
În situația în care proiectul conține mai multe ramificații de dezvoltare în paralel, dorindu-se doar integrarea unora și omiterea (pe moment) a altora, se poate folosi comanda git rebase --onto <branch_to_integrate_in> <branch_to_be_ommitted> <branch_to_be_included>
. Prin intermediul acestei comenzi, se identifică cel mai recent strămoș comun între ramificația care trebuie omisă și cea care trebuie inclusă, aplicându-se modificările realizate din acest punct asupra ramurii pe care se realizează integrarea.
Varianta REBASE se folosește de obicei atunci când se realizează contribuții pe depozite la distanță, astfel încât istoricul acestora să nu devină mai complex prin introducerea de ramificații suplimentare, existente doar în directorul de lucru local, integrarea constând într-o simplă operație de tip fast-forward.
Ștergerea unei ramificaţii este necesară în momentul în care aceasta referă o consemnare spre care mai există și alte referințe, ale unor ramificaţii în care au fost integrate (de regulă master
). O astfel de operaţie poate fi realizată prin comanda git branch -d <branch_name>
.
aipi2014@ubuntu:~$ git branch -d copy_move Deleted branch copy_move (was 477dcd6). aipi2014@ubuntu:~$ git branch -d delete Deleted branch delete (was 30fe2f3).
O ramificație la distanță este o referință către starea unei ramificații aflată într-un depozit găzduit de un server la distanță. Ele sunt stocate în directorul local, fiind descărcate în mod automat în momentul în care se realizează comunicații prin rețea. Nu se pot realiza nici un fel de operații asupra lor, cu excepția consultării, pentru a se realiza mai ușor comparația față de directorul de lucru de pe discul local.
Modul în care sunt referite ramificațiile la distanță sunt <remote-name>
/ <branch-name>
.
origin/master
în mod automat.
În momentul în care se rulează comanda git fetch
, sunt descărcate toate consemnările din depozitul la distanță, actualizându-se în mod corespunzător referințele către ramificații, așa cum se găsesc acolo.
git merge <remote-name>/<branch-name>
urmată de încărcarea versunii obținute pe server, după rezolvarea eventualelor conflicte.
git checkout -b <local_branch> <remote_name>/<remote_branch>
(cu forma git checkout --track <remote_name>/<branch_name>
, dacă denumirile ramificațiilor local și la distanță corespund).git push
și git pull
încarcă / descarcă informații în/din ramificația corespunzătoare de pe serverul la distanță în mod automat, fără a mai avea nevoie de alți parametri.
De regulă, ramificațiile create în directorul de lucru de pe discul local nu sunt vizibile la distanță. Dacă se dorește totuși să se partajeze o astfel de informație, se va rula comanda git push <remote_name> <local_branch>[:<remote_branch>]
. În acest mod, modificările din ramificația local_branch
din directorul de lucru de pe discul local vor fi încărcate pe ramificația remote_branch
din depozitul de pe serverul la distanță (cu condiția să existe drepturi de acces pe server).
În situația în care o ramificație de pe serverul la distanță nu mai este necesară (modificările sale au fost integrate în altă ramificație de pe serverul la distanță, de exemplu master
), se poate rula comanda git push <remote_name> :<remote_branch>
.
git push <remote_name> <branch_name>
.
user.name
, user.email
, core.editor
, merge.tool
. Verificați faptul că informațiile au fost introduse corect, prin două metode diferite.README.md
, nici a fișierului .gitignore
și a a fișierului LICENSE
).Minishell
, fișierul README.txt
și un fișier .gitignore
care indică tipurile de fișiere (extensiile) ignorate.list_directory_content
. master
.pwd
, derivată din master
, pe care să se implementeze rezultatul comenzii cu același nume, care afișează directorul curent.v4.0.1
și cu un mesaj care reflectă funcționalitatea sa.cat
, derivată din master
, pe care să se implementeze rezultatul comenzii cu același nume, care afișează conținutul fișierului indicat ca parametru.v4.0.2
și cu un mesaj care reflectă funcționalitatea sa.pwd
și cat
în ramificația master
, apoi să se elimine din depozitul la distanță asociat contului Github personal.
Scott CHACON, Pro Git, Apress, 2009 - capitolele 1-3
GitHub
3. Specificarea variabilelor de configurare se realizează prin comenzile:
aipi2014@ubuntu:~$ git configure --global user.name "Some Name" aipi2014@ubuntu:~$ git configure --global user.email somename@somedomain.com aipi2014@ubuntu:~$ git configure --global core.editor gedit aipi2014@ubuntu:~$ git configure --global merge.tool diff
Verificarea valorii pe care o au variabilele de configurare poate fi realizată în mai multe moduri:
aipi2014@ubuntu:~/GitHub$ git configure --list
aipi2014@ubuntu:~/GitHub$ cat .git/config
aipi2014@ubuntu:~/GitHub$ git config user.name aipi2014@ubuntu:~/GitHub$ git config user.email aipi2014@ubuntu:~/GitHub$ git config core.editor aipi2014@ubuntu:~/GitHub$ git config merge.tool
6.
aipi2014@ubuntu:~/GitHub$ git init aipi2014@ubuntu:~/GitHub$ git clone https://www.github.com/aipi2014/Laborator00.git
7.
aipi2014@ubuntu:~/GitHub$ cd Laborator00 aipi2014@ubuntu:~/GitHub/Laborator00$ git remote add Laborator00_somename git@github.com:somename/Laborator00.git aipi2014@ubuntu:~/GitHub/Laborator00$ git push Laborator00_somename master
8. I.
aipi2014@ubuntu:~/GitHub/Laborator00$ git checkout --track origin/list_directory_content
II.
aipi2014@ubuntu:~/GitHub/Laborator00$ git checkout master aipi2014@ubuntu:~/GitHub/Laborator00$ git merge list_directory_content
III.
aipi2014@ubuntu:~/GitHub/Laborator00$ git branch -d list_directory_content
IV.
aipi2014@ubuntu:~/GitHub/Laborator00$ git push Laborator00_somename master
9.
aipi2014@ubuntu:~/GitHub/Laborator00$ git log --pretty=format:"%h - %an [%ae] - %ar -> %s" --graph
10.
public interface Constants { // ... final public static String PRINT_WORKING_DIRECTORY_COMMAND = "pwd"; // ... }
// ... public Path analyze(String command, Path currentDirectory, Scanner scanner) { String[] parts = command.split(" "); FileSystemOperations fileSystemOperations = new FileSystemOperations(); switch (parts[0]) { // ... case Constants.PRINT_WORKING_DIRECTORY_COMMAND: fileSystemOperations.printWorkingDirectory(currentDirectory); break; // ... default: System.out.println("This command is not supported"); break; } return currentDirectory; }
// ... public void printWorkingDirectory(Path currentDirectory) { System.out.println(currentDirectory.toString()); } // ...
I.
aipi2014@ubuntu:~/GitHub/Laborator00$ git checkout -b pwd // implementare functionalitate pwd aipi2014@ubuntu:~/GitHub/Laborator00$ git add . aipi2014@ubuntu:~/GitHub/Laborator00$ git tag -a v4.0.1 -m "added support for pwd command"
II.
aipi2014@ubuntu:~/GitHub/Laborator00$ git push Laborator00_somename pwd
11.
public interface Constants { // ... final public static String CATENATE = "cat"; // ... }
// ... public Path analyze(String command, Path currentDirectory, Scanner scanner) { String[] parts = command.split(" "); FileSystemOperations fileSystemOperations = new FileSystemOperations(); switch (parts[0]) { // ... case Constants.CATENATE: fileSystemOperations.catenate(parts[1], currentDirectory); break; // ... default: System.out.println("This command is not supported"); break; } return currentDirectory; }
// ... public void catenate(String name, Path currentDirectory) { Path path = checkIfExists(name, currentDirectory); if (path != null && Files.isRegularFile(path) && Files.isReadable(path)) { Charset charset = Charset.forName("UTF-8"); try (BufferedReader bufferedReader = Files.newBufferedReader(path, charset)) { String line = null; while ((line = bufferedReader.readLine()) != null) System.out.println(line); } catch (IOException ioException) { System.out.println("Operation could not be performed !"+ioException.getMessage()); } } else System.out.format("Could not display the contents of file %s!%n",name); } // ...
I.
aipi2014@ubuntu:~/GitHub/Laborator00$ git branch cat aipi2014@ubuntu:~/GitHub/Laborator00$ git checkout cat // implementare functionalitate cat aipi2014@ubuntu:~/GitHub/Laborator00$ git add . aipi2014@ubuntu:~/GitHub/Laborator00$ git tag -a v4.0.2 -m "added support for cat command"
II.
aipi2014@ubuntu:~/GitHub/Laborator00$ git push Laborator00_somename cat
12.
aipi2014@ubuntu:~/GitHub/Laborator00$ git checkout master aipi2014@ubuntu:~/GitHub/Laborator00$ git merge pwd aipi2014@ubuntu:~/GitHub/Laborator00$ git push Laborator00_somename master aipi2014@ubuntu:~/GitHub/Laborator00$ git checkout master aipi2014@ubuntu:~/GitHub/Laborator00$ git merge cat aipi2014@ubuntu:~/GitHub/Laborator00$ git push Laborator00_somename master aipi2014@ubuntu:~/GitHub/Laborator00$ git mergetool aipi2014@ubuntu:~/GitHub/Laborator00$ git add . aipi2014@ubuntu:~/GitHub/Laborator00$ git commit -m "resolved conflicts between pwd and cat branches" aipi2014@ubuntu:~/GitHub/Laborator00$ git push Laborator00_somename master aipi2014@ubuntu:~/GitHub/Laborator00$ git push Laborator00_somename :pwd aipi2014@ubuntu:~/GitHub/Laborator00$ git push Laborator00_somename :cat