Git
Chapters ▾ 2nd Edition

A1.9 Appendix A: Git в други среди - Git в PowerShell

Git в PowerShell

Стандартният команден терминал в Windows (cmd.exe) не е особено подходящ за комфортна работа с Git, но ако използвате PowerShell, тогава нещата са други. Това също работи ако използвате PowerShell Core на друга платформа, например Linux или macOS. Пакетът posh-git (https://github.com/dahlbyk/posh-git) осигурява мощна tab-completion функционалност, както и подобрен промпт за вашите хранилища. Изглежда така:

PowerShell с Posh-git.
Figure 166. PowerShell с Posh-git.

Инсталация

Изисквания (само за Windows)

Преди да можете да изпълнявате PowerShell скриптове, трябва да настроите вашата локална ExecutionPolicy на RemoteSigned (всичко с изключение на Undefined и Restricted). Ако изберете AllSigned вместо RemoteSigned, също и локалните скриптове (вашите собствени) трябва да бъдат цифрово подписани, за да могат да бъдат изпълнявани. С RemoteSigned, само скриптовете със "ZoneIdentifier" настроен на Internet (тоест, изтеглени от мрежата) е необходимо да са подписани, другите не. Ако сте администратор и искате да го зададете за всички потребители, използвайте "-Scope LocalMachine". Ако сте нормален потребител без административни права, използвайте "-Scope CurrentUser" за да зададете настройката само за вас.

За да зададете стойност RemoteSigned на ExecutionPolicy за всички потребители, използвайте командата:

> Set-ExecutionPolicy -Scope LocalMachine -ExecutionPolicy RemoteSigned -Force

Ако имате поне PowerShell 5 или PowerShell 4 с PackageManagement, можете да използвате пакетния мениджър за да издърпате posh-git.

Повече информация за PowerShell Gallery: https://docs.microsoft.com/en-us/powershell/scripting/gallery/overview

> Install-Module posh-git -Scope CurrentUser -Force
> Install-Module posh-git -Scope CurrentUser -AllowPrerelease -Force # Newer beta version with PowerShell Core support
-----

Ако искате да инсталирате posh-git за всички потребители, подайте "-Scope AllUsers" вместо това и изпълнете командата от elevated PowerShell конзола.
Ако втората команда завърши с грешка от рода на `Module 'PowerShellGet' was not installed by using Install-Module`, ще трябва първо да изпълните друга такава:

[source,powershell]

Install-Module PowerShellGet -Force -SkipPublisherCheck

След това може да се върнете и да опитате отново.
Това се случва, защото модулите, които идват с Windows Powershell са подписани с различни сертификати.

===== Обновяване на PowerShell промпта

За да включите Git информация в промпта, posh-git модулът трябва да бъде импортиран.
За да се импортира posh-git всеки път, когато се пусне PowerShell, изпълнете командата Add-PoshGitToProfile, която ще добави импортиращия израз във вашия $profile скрипт.
Той се изпълнява всеки път, когато отворите нов PowerShell промпт.
Не забравяйте, че има няколко $profile скриптове.
Например един за конзолата и друг отделен за ISE.

[source,powershell]

Import-Module posh-git Add-PoshGitToProfile -AllHosts

===== От изходен код

Просто изтеглете posh-git версия от (https://github.com/dahlbyk/posh-git[]), и я разархивирайте.
След това импортирайте модула използвайки пълния път до файла posh-git.psd1:

[source,powershell]

Import-Module <path-to-uncompress-folder>\src\posh-git.psd1 Add-PoshGitToProfile -AllHosts

Това ще добави правилния ред във файла `profile.ps1` и posh-git ще е активен следващия път, когато отворите вашия промпт.

За описание на Git status summary информацията, която се вижда в промпта, погледнете: https://github.com/dahlbyk/posh-git/blob/master/README.md#git-status-summary-information[]
За повече подробности как да настроите допълнително вашия posh-git промпт, вижте: https://github.com/dahlbyk/posh-git/blob/master/README.md#customization-variables[]


=== Обобщение

Научихте как да използвате силата на Git от инструментите, които ползвате в ежедневната си работа и как да получите достъп до Git хранилище от собствените си програми.

[[B-embedding-git-in-your-applications]]
[appendix]
== Вграждане на Git в приложения

Ако вашето приложение е предназначено за разработчици, твърде вероятно е интеграцията на сорс контрол възможности да му е от полза.
Дори и non-developer приложенията, като редактори на документи например, могат потенциално да се възползват от version-control функционалностите и моделът на Git работи добре в много и разнообразни сценарии.

Ако трябва да интегрирате Git във вашите собствени приложения, по същество разполагате с две опции: да извиквате шел сесия и да се обръщате към `git` от командния ред или да вградите Git библиотека в приложението си.
Тук ще погледнем интеграцията от команден ред и някои от най-популярните Git библиотеки, които могат да се вграждат.

=== Git от команден ред

Една възможност е да се стартира шел процес и да се използват Git командите за необходимите дейности.
Предимството на такъв подход е в последователността и всички Git функционалности са налице.
Освен това е сравнително лесно, защото повечето runtime среди разполагат със сравнително лесен механизъм за извикване на процеси с аргументи от команден ред.
Обаче, подходът си има и недостатъци.

Един от тях е, че изходът е в чист текст.
Това означава, че трябва да се обработва изходния формат на Git за прочитане на прогреса на дадено действие и информацията от резултатите, което може да е неефективен и податлив на грешки процес.

Друго неудобство е липсата на поддръжка за възстановяване на състоянието при възникнали грешки.
Ако дадено хранилище по някакъв начин се повреди или потребителят е подал неправилно форматирана конфигурационна стойност, то Git просто ще откаже да изпълни много операции.

Друг аспект е управлението на процесите.
Git изисква да поддържате шел обкръжение в отделен процес, което може да добави нежелана сложност.
Опитът да координирате много такива процеси (особено когато потенциално манипулирате едно и също хранилище от много процеси) може да бъде сериозно предизвикателство.


=== Libgit2

(((libgit2)))((("C")))
Библиотеката Libgit2 е друга опция на ваше разположение.
Libgit2 е dependency-free имплементация на Git, фокусирана в предоставянето на добър API за ползване от външни програми.
Налична е от https://libgit2.org[].

Първо нека видим как изглежда един C API.
Накратко:

[source,c]

git_repository *repo; int error = git_repository_open(&repo, "/path/to/repository");

git_object head_commit; error = git_revparse_single(&head_commit, repo, "HEAD^{commit}"); git_commit *commit = (git_commit)head_commit;

printf("%s", git_commit_message(commit)); const git_signature *author = git_commit_author(commit); printf("%s <%s>\n", author→name, author→email); const git_oid *tree_id = git_commit_tree_id(commit);

git_commit_free(commit); git_repository_free(repo);

Първите няколко реда отварят Git хранилище.
Типът `git_repository` представлява указател към хранилище с кеш в паметта.
Това е най-простият метод за случаите, когато знаете точния път към работната директория на хранилище или директорията `.git`.
Съществува и `git_repository_open_ext`, където имаме опции за търсене, `git_clone` и подобни команди за правене на локално копие на отдалечено хранилище, както и `git_repository_init` за създаване на изцяло ново хранилище.

Следващият елемент от кода използва rev-parse синтаксис (вижте <<_branch_references>> за подробности) за да вземе къмита, към който сочи HEAD.
Върнатият тип е `git_object` указател, който дава достъп до съдържанието на обектната база данни в Git хранилище.
`git_object` в действителност е ``parent'' тип за няколко различни вида обекти, разположението в паметта за всеки от ``child'' типовете е същото като на `git_object`, така че може безопасно да се cast-ва до правилния такъв.
В този случай, `git_object_type(commit)` ще върне `GIT_OBJ_COMMIT`, така че е възможно да се cast-не към `git_commit` указател.

Следващата част от кода показва как да се получи достъп до свойствата на къмита.
Последният ред използва типа `git_oid`, което е Libgit2 представянето на SHA-1 хеш.

От този пример можем да направим следните изводи:

* Ако декларирате указател и изпратите референция към него в Libgit2 повикване, това повикване вероятно ще върне целочислен код за грешка.
  Стойност `0` индикира успех, всичко по-малко е грешка.
* Ако Libgit2 инициализира указател за вас, ваша е отговорността да го освободите.
* Ако Libgit2 върне `const` указател от повикване, не трябва да го освобождавате, но той ще стане невалиден, когато обектът, към който принадлежи бъде освободен.
* Писането на код на C може да бъде доста болезнено.

(((Ruby)))
Последното означава, че не е много вероятно да пишете на C, когато използвате Libgit2.
За щастие, налични са много language-specific bindings, които правят сравнително лесно да работите с Git хранилища от вашия специфичен език за програмиране и среда.
Нека видим примера отгоре написан с помощта на Ruby bindings за Libgit2, наречени Rugged и налични от https://github.com/libgit2/rugged[].

[source,ruby]

repo = Rugged::Repository.new(path/to/repository) commit = repo.head.target puts commit.message puts "{commit.author[:name]} <{commit.author[:email]}>" tree = commit.tree

Както се вижда, кодът е доста по-прегледен.
Първо, Rugged използва изключения (exceptions), може да подава неща като `ConfigError` или `ObjectError` за да сигнализира за грешки.
Второ, няма изрично освобождаване на ресурси, понеже Ruby е garbage-collected.
Нека видим по-сложен пример: създаване на къмит от нулата

[source,ruby]

blob_id = repo.write("Blob contents", :blob) # <1>

index = repo.index index.read_tree(repo.head.target.tree) index.add(:path ⇒ newfile.txt, :oid ⇒ blob_id) # <2>

sig = { :email ⇒ "bob@example.com", :name ⇒ "Bob User", :time ⇒ Time.now, }

commit_id = Rugged::Commit.create(repo, :tree ⇒ index.write_tree(repo), # <3> :author ⇒ sig, :committer ⇒ sig, # <4> :message ⇒ "Add newfile.txt", # <5> :parents ⇒ repo.empty? ? [] : [ repo.head.target ].compact, # <6> :update_ref ⇒ HEAD, # <7> ) commit = repo.lookup(commit_id) # <8>

<1> Създаваме нов blob, който пази съдържанието на нов файл.
<2> Попълваме индекса с дървото на къмита на head и добавяме новия файл в пътя `newfile.txt`.
<3> Това създава ново дърво в ODB (базата данни с обекти) и го използва за новия къмит.
<4> Използваме една и съща сигнатура за author и committer полетата.
<5> Къмит съобщението.
<6> Когато създаваме къмит, трябва да укажем родителите му.
    В случая използваме върха на HEAD за единичен родител.
<7> Rugged (и Libgit2) може по желание да обнови референция, когато се прави къмит.
<8> Върнатата стойност е SHA-1 хеша на новия къмит обект и може да се използва за получаване на `Commit` обект.

Ruby кодът е чист и приятен, но понеже Libgit2 върши тежката работа, той също така ще работи и много бързо.
Ако не сте привърженик на Ruby, показваме накратко и някои други bindings в секцията <<_libgit2_bindings>>.

==== По-сложни функционалности

Libgit2 има доста възможности, които са извън обхвата на същността на Git.
Един пример е pluggability поддръжката: Libgit2 ви позволява да подадете специализирани ``backends'' за няколко различни типа операции, така че можете да съхранявате неща по различен начин от Git.
Libgit2 позволява custom backends за конфигурация, съхранение на референции и обектната база данни.

Нека видим как работи това.
Кодът отдолу е взаимстван от множеството backend примери, които екипът на Libgit2 предоставя (на адрес https://github.com/libgit2/libgit2-backends[]).
Ето как се настройва custom backend за базата данни с обекти:

[source,c]

git_odb *odb; int error = git_odb_new(&odb); // <1>

git_odb_backend my_backend; error = git_odb_backend_mine(&my_backend, /…*/); // <2>

error = git_odb_add_backend(odb, my_backend, 1); // <3>

git_repository *repo; error = git_repository_open(&repo, "some-path"); error = git_repository_set_odb(repo, odb); // <4>

_(Отбележете, че грешките се прихващат, но не се обработват. Надяваме се кодът ви да е по-добър от нашия.)_

<1> Инициализираме празен object database (ODB) ``frontend,'' който ще служи за контейнер за ``backend-те'', които всъщност вършат реалната работа
<2> Инициализираме custom ODB backend.
<3> Добавяме backend-а към frontend-а.
<4> Отваряме хранилище и го настройваме да използва нашата ODB за търсене на обекти.

Какво е `git_odb_backend_mine`?
Това е конструкторът за собствената ни ODB имплементация и тук може да правим каквото си искаме, стига да попълваме коректно структурата `git_odb_backend`.
Ето как _би могъл_ да изглежда:

[source,c]

typedef struct { git_odb_backend parent;

    // Some other stuff
    void *custom_context;
} my_backend_struct;

int git_odb_backend_mine(git_odb_backend *backend_out, /…*/) { my_backend_struct *backend;

backend = calloc(1, sizeof (my_backend_struct));
backend->custom_context = …;
backend->parent.read = &my_backend__read;
backend->parent.read_prefix = &my_backend__read_prefix;
backend->parent.read_header = &my_backend__read_header;
// …
*backend_out = (git_odb_backend *) backend;
    return GIT_SUCCESS;
}
Неуловимото ограничение тук е, че първият член на `my_backend_struct` трябва да е `git_odb_backend` структура -- това гарантира, че разположението в паметта е такова, каквото Libgit2 кода очаква.
Останалото е по избор, тази структура може да е толкова голяма или малка, колкото е нужно.

Инициализиращата функция запазва малко памет за структурата, настройва custom контекст и след това попълва членовете на `parent` структурата, която поддържа.
Погледнете файла `include/git2/sys/odb_backend.h` от сорс кода на Libgit2 за пълния набор от call signatures, вашият специфичен случай ще ви помогне да изберете коя точно ще искате да поддържате.

[[_libgit2_bindings]]
==== Други Bindings

Libgit2 има bindings за много езици.
Тук показваме малък пример за използване на някои от по-завършените (към момента на писането на книгата) bindings пакети. Библиотеки съществуват за много други платформи, включително C++, Go, Node.js, Erlang, и JVM, всяка от тях на различен етап от развитието си.
Официалната колекция bindings може да се намери като разгледате хранилищата на адрес https://github.com/libgit2[].
Кодът, който пишем ще върне къмит съобщението на къмита, към който сочи HEAD (нещо като `git log -1`).

===== LibGit2Sharp

(((.NET)))(((C#)))(((Mono)))
Ако пишете .NET или Mono приложение, LibGit2Sharp (https://github.com/libgit2/libgit2sharp[]) е нещото, което ви трябва.
Самите bindings са написани на C# и е обърнато сериозно внимание на добрата синхронизация между чистите Libgit2 повиквания с native-feeling CLR API-та.
Ето как би изглеждала примерната ни програма:

[source,csharp]

new Repository(@"C:\path\to\repo").Head.Tip.Message;

За desktop Windows приложения дори има и NuGet пакет, който помага да почнете по-лесно.

===== objective-git

(((Apple)))(((Objective-C)))(((Cocoa)))
Ако приложението ви работи на Apple платформа, вероятно ще използвате Objective-C като език за имплементация.
Objective-Git (https://github.com/libgit2/objective-git[]) е името на Libgit2 binding-те за тази среда.
Примерна програма:

[source,objc]

GTRepository *repo = [[GTRepository alloc] initWithURL:[NSURL fileURLWithPath: @"/path/to/repo"] error:NULL]; NSString *msg = [[[repo headReferenceWithError:NULL] resolvedTarget] message];

Objective-git е напълно оперативно съвместим със Swift, така че не се страхувайте, ако сте оставили Objective-C в миналото.

===== pygit2

(((Python)))
Binding-ите на Libgit2 за Python се наричат Pygit2, достъпни на https://www.pygit2.org[].
Примерна програма:

[source,python]

pygit2.Repository("/path/to/repo") # отваряме хранилище .head # вземаме текущия клон .peel(pygit2.Commit) # преминаваме към къмита .message # четем съобщението

==== Допълнителна информация

Разбира се, пълният преглед на Libgit2 възможностите е извън обхвата на книгата.
Ако се нуждаете от повече информация за самата Libgit2 имате API документация на адрес https://libgit2.github.com/libgit2[], както и набор от ръководства на https://libgit2.github.com/docs[].
За другите bindings, погледнете файла README и тестовете, често там има малки указания и насоки за получаване на допълнителна информация.


=== JGit

(((jgit)))(((java)))
Ако искате да ползвате Git от Java програма, налична е пълнофункционалната библиотека JGit.
JGit е Git имплементация написана на Java и е много популярна в Java общността.
Проектът JGit е под шапката на Eclipse и е на адрес https://www.eclipse.org/jgit/[].

==== Настройка

Има няколко начина да свържете проекта си с JGit.
Вероятно най-лесният е да използвате Maven -- интеграцията се извършва с добавяне на следното в `<dependencies>` тага на файла pom.xml:

[source,xml]

<dependency> <groupId>org.eclipse.jgit</groupId> <artifactId>org.eclipse.jgit</artifactId> <version>3.5.0.201409260305-r</version> </dependency>

Елементът `version` вероятно ще е различен по времето, когато четете това, проверете https://mvnrepository.com/artifact/org.eclipse.jgit/org.eclipse.jgit[] за актуална информация за хранилището.
След като това бъде направено, Maven автоматично ще намери и използва JGit библиотеките, които ви трябват.

Ако вместо това сами управлявате binary зависимостите, компилирани JGit binaries има на адрес https://www.eclipse.org/jgit/download[].
Може да ги интегрирате в проекта си с команди от рода на:

[source,console]

javac -cp .:org.eclipse.jgit-3.5.0.201409260305-r.jar App.java java -cp .:org.eclipse.jgit-3.5.0.201409260305-r.jar App

==== Plumbing

JGit има две основни API нива: plumbing и porcelain.
Терминологията им идва от самия Git и JGit е разделена на приблизително същите видове области: porcelain API-тата са friendly front-end за основните user-level действия (нещата, които нормално потребителят би използвал с Git в командния ред), докато plumbing API-тата са за директен контакт с low-level обекти в хранилище.

Отправната точка за повечето JGit сесии е класът `Repository` и първата ни задача е да го инстанциираме в обект.
За хранилище от файловата система (да, JGit позволява и други storage модели), това се прави с помощта на `FileRepositoryBuilder`:

[source,java]

Repository newlyCreatedRepo = FileRepositoryBuilder.create( new File("/tmp/new_repo/.git")); newlyCreatedRepo.create();

Repository existingRepo = new FileRepositoryBuilder() .setGitDir(new File("my_repo/.git")) .build();

Builder-ът има чудесен API за да осигури всички неща, необходими за намиране на Git хранилище без значение дали програмата ви знае къде точно се намира то.
Може да използва environment променливи (`.readEnvironment()`), да започне от място в работната директория и да търси (`.setWorkTree(…).findGitDir()`), или просто да отвори известна `.git` директория.

След като вече имате инстанция на `Repository`, можете да правите всякакви неща с обекта.
Бърз пример:

[source,java]

Ref master = repo.getRef("master");

ObjectId masterTip = master.getObjectId();

ObjectId obj = repo.resolve("HEAD^{tree}");

ObjectLoader loader = repo.open(masterTip); loader.copyTo(System.out);

RefUpdate createBranch1 = repo.updateRef("refs/heads/branch1"); createBranch1.setNewObjectId(masterTip); createBranch1.update();

RefUpdate deleteBranch1 = repo.updateRef("refs/heads/branch1"); deleteBranch1.setForceUpdate(true); deleteBranch1.delete();

Config cfg = repo.getConfig(); String name = cfg.getString("user", null, "name");

Тук има доста неща, нека ги разгледаме подред.

Първият ред взема указател към `master` референцията.
JGit автоматично намира _действителната_ `master` референция, която се пази в `refs/heads/master`, и връща обект, който позволява да извличате информация за нея.
Може да получите името ѝ (`.getName()`) както и целевия обект на директна референция (`.getObjectId()`) или референцията сочена от symbolic ref (`.getTarget()`).
Ref обектите се използват също за представяне на tag refs и objects, така че можете да питате дали тагът е ``peeled,'' което значи че сочи към финалната цел на (потенциално дълъг) стринг от таг обекти.

Вторият ред взема целта на `master` референцията, която се връща като ObjectId инстанция.
ObjectId представлява SHA-1 хеш на обект, който може да съществува или не в базата данни с обекти на Git.
Третият ред е подобен, но показва как JGit обработва rev-parse синтаксиса (за повече информация погледнете в <<_branch_references>>), можете да подадете произволен object specifier, който Git разбира, и JGit ще върне или валиден ObjectId за този обект или `null`.

Следващите два реда показват как да заредите raw съдържанието на обект.
В този пример ние извикваме `ObjectLoader.copyTo()` за да пратим съдържанието на обекта директно към stdout, но ObjectLoader има също методи за четене на типа и размера на обектa и може също така да ги върне като byte масив.
За големи обекти (където `.isLarge()` връща `true`), може да извикате `.openStream()` за да получите подобен на InputStream обект способен да чете object данни без да ги изтегля в паметта изцяло.

Следващите няколко реда показват какво е необходимо за създаване на клон.
Създаваме RefUpdate обект, конфигурираме малко параметри и извикваме `.update()` за да активираме промяната.
Директно след това идва кодът за изтриване на същия клон.
Отбележете, че `.setForceUpdate(true)` е необходимо условие за това, в противен случай `.delete()` повикването ще върне `REJECTED` и няма да се случи нищо.

Последният пример показва как да извлечем конфигурационна стойност на Git, `user.name`.
Тази Config инстанция използва отвореното по-рано хранилище за локалната конфигурация, но също така може да установи автоматично глобалните и системни конфигурационни файлове и да чете и от тях.

Това е само малка част от plumbing API-тата, съществуват много други методи и класове.
Тук също не показахме как JGit обработва грешки, това става с exceptions.
JGit API-тата понякога хвърлят стандартни Java exceptions (като например `IOException`), но съществуват и JGit-специфични типове изключения (като `NoRemoteRepositoryException`, `CorruptObjectException`, и `NoMergeBaseException`).

==== Porcelain

Plumbing API-тата са сравнително изчерпателни, но понякога може да е тромаво да се използват за постигане на тривиални задачи като добавяне на файл в индекса или създаването на нов къмит.
JGit предлага набор API-та от по-високо ниво посредством класа `Git`:

[source,java]

Repository repo; Git git = new Git(repo);

Git класът притежава чудесна колекция от high-level методи в _builder_ стил, които могат да се използват за реализиране на доволно сложни сценарии.
Да видим пример -- ще направим нещо еквивалентно на `git ls-remote`:

[source,java]

CredentialsProvider cp = new UsernamePasswordCredentialsProvider("username", "p4ssw0rd"); Collection<Ref> remoteRefs = git.lsRemote() .setCredentialsProvider(cp) .setRemote("origin") .setTags(true) .setHeads(false) .call(); for (Ref ref : remoteRefs) { System.out.println(ref.getName() + " → " + ref.getObjectId().name()); }

Това е стандартно използване на Git класа, методите връщат команден обект, който ви позволява да правите chaining на повикванията им за да задавате параметри и да ги стартирате с `.call()`.
В този случай ние питаме `origin` референцията за тагове, но не и за heads.
Също така, обърнете внимание на обекта `CredentialsProvider`, който се ползва за автентикация.

Много други команди са достъпни през класа Git, включително `add`, `blame`, `commit`, `clean`, `push`, `rebase`, `revert`, и `reset`.

==== Повече информация

Това е само малка демонстрация на възможностите на JGit.
Ако се интересувате и искате да научите повече, ето къде да потърсите допълнителна информация и вдъхновение:

* Официалната JGit API документация на адрес https://www.eclipse.org/jgit/documentation/[].
  Това са стандартни Javadoc, така че любимата ви JVM IDE ще може да ги инсталира и локално.
* Хранилището JGit Cookbook на адрес https://github.com/centic9/jgit-cookbook[] съдържа много примери за извършване на специфични дейности с JGit.

=== go-git

(((go-git)))(((Go)))
В случай, че желаете да интегрирате Git в услуга написана на Go, съществува pure Go библиотечна имплементация.
Тази имплементация няма никакви нативни зависимости и по тази причина е неподатлива на manual memory management грешки.
Също така е прозрачна за стандартните Golang performance analysis инструментариуми като CPU, Memory profilers, race detector, и т.н.

go-git акцентира върху разширяемостта и съвместимостта и поддържа повечето plumbing APIs, документирани на https://github.com/go-git/go-git/blob/master/COMPATIBILITY.md[].

Ето прост пример за използването на Go APIs:

[source, go]

import "github.com/go-git/go-git/v5"

r, err := git.PlainClone("/tmp/foo", false, &git.CloneOptions{ URL: "https://github.com/go-git/go-git", Progress: os.Stdout, })

След като имате инстанция `Repository`, можете да четете информация и да правите промени по нея:

[source, go]

ref, err := r.Head()

commit, err := r.CommitObject(ref.Hash())

history, err := commit.History()

for _, c := range history { fmt.Println(c) }

==== Разширена функционалност

go-git има няколко advanced възможности, които си заслужава да се посочат. Една от тях е pluggable storage системата, подобна на Libgit2 backend-ите.
Имплементацията по подразбиране е in-memory storage и тя е много бърза.

[source, go]

r, err := git.Clone(memory.NewStorage(), nil, &git.CloneOptions{ URL: "https://github.com/go-git/go-git", })

Pluggable storage системата осигурява много интересни опции.
Например, https://github.com/go-git/go-git/tree/master/_examples/storage[] позволява да съхранявате референции, обекти и конфигурационни настройки в Aerospike база данни.

Друга функция е гъвкавата абстракция на файловата система.
Посредством https://pkg.go.dev/github.com/go-git/go-billy/v5?tab=doc#Filesystem[] е лесно да се съхраняват всички файлове по различен начин, например като се пакетират в единичен архив на диска или като се запазват всички in-memory.

Друг advanced use-case включва HTTP клиент с фина настройка, като този от https://github.com/go-git/go-git/blob/master/_examples/custom_http/main.go[].

[source, go]

customClient := &http.Client{ Transport: &http.Transport{ // accept any certificate (might be useful for testing) TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }, Timeout: 15 * time.Second, // 15 second timeout CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse // don’t follow redirect }, }

client.InstallProtocol("https", githttp.NewClient(customClient))

r, err := git.Clone(memory.NewStorage(), nil, &git.CloneOptions{URL: url})

==== Допълнителна информация

Извън обхвата на тази книга е да разглеждаме всички поддържани от go-git възможности.
Ако се нуждаете от подробности, налична е API документация на адрес https://pkg.go.dev/github.com/go-git/go-git/v5[], както и комплект от примери на https://github.com/go-git/go-git/tree/master/_examples[].


=== Dulwich

(((Dulwich)))(((Python)))
Съществува и Git имплементация за Python - Dulwich.
Проектът се хоства на адрес https://www.dulwich.io/
Целта му е да предостави интерфейс за достъп до git хранилища (локални и отдалечени), който не се обръща към git директно, а вместо това използва чист Python.
Той също съдържа и опционални C разширения, които значително подобряват производителността.

Dulwich следва дизайна на git и разделя двете базови API: plumbing и porcelain.

Ето пример за използване на API от по-ниско ниво за достъп до съобщението на последния къмит:

[source, python]

from dulwich.repo import Repo r = Repo(.) r.head() # 57fbe010446356833a6ad1600059d80b1e731e15

c = r[r.head()] c # <Commit 015fc1267258458901a94d228e39f0a378370466>

c.message # Add note about encoding.\n

За да отпечатате commit log чрез porcelain API-то от по-високо ниво, може да използвате:

[source, python]

from dulwich import porcelain porcelain.log(., max_entries=1)

#commit: 57fbe010446356833a6ad1600059d80b1e731e15 #Author: Jelmer Vernooij <jelmer@jelmer.uk> #Date: Sat Apr 29 2017 23:57:34 +0000

==== Допълнителна информация

 * Официалната API документация е налична на https://www.dulwich.io/apidocs/dulwich.html[]
 * Официалното ръководство https://www.dulwich.io/docs/tutorial[] предлага много примери за това как да извършвате специфични задачи в Dulwich




[[C-git-commands]]
[appendix]
== Git команди

В книгата представихме много Git команди и се опитвахме това да бъде възможно най-информативно като постепенно добавяхме нови такива.
Обаче, това ни оставя с примери разпръснати из цялото съдържание.

В това приложение ще преминем накратко през всички изброени дотук Git команди и ще се постараем да ги групираме, доколкото е възможно, според предназначението им.
Ще говорим за това какво е общото предназначение на всяка команда и след това ще посочваме къде в книгата може да намерите примери с нея.

[TIP]
====
Можете да съкращавате дългите опции.
Например, може да напишете `git commit --a`, което е еквивалентно на `git commit --amend`.
Това работи само, ако символите след `--` са уникални за опцията.
Използвайте пълната опция в скриптове.
====

=== Настройки и конфигурация

Две от командите в Git се използват почти ежедневно, `config` и `help`.

==== git config

Git има начини за изпълнение по подразбиране на стотици операции.
За много от тях, можете да инструктирате Git да прави по подразбиране нещата по малко по-различен начин.
Това включва всичко, от това да кажете на Git какво е името ви до това да укажете специфични цветове в терминала или кой е редакторът ви, който предпочитате.
Тази команда чете и пише в няколко различни файла, така че можете да задавате стойности глобално или за определени хранилища.

Командата `git config` се използва в почти всяка глава от книгата.

В <<_first_time>> я ползвахме за задаване на име и имейл на потребителя, както и за указване на текстовия ни редактор -- преди още да бяхме започнали да използваме Git.

В <<_git_aliases>> показахме как бихте могли да я използвате за създаването на съкратени варианти на команди, които автоматично се разширяват до пълните еквиваленти, така че да не се налага да ги въвеждате изцяло всеки път.

В <<_rebasing>> използвахме командата за да направим `--rebase` опция по подразбиране при изпълнение на `git pull`.

В <<_credential_caching>> я използвахме за определяне на default store за HTTP пароли.

В <<_keyword_expansion>> показахме как се създават smudge and clean филтри за съдържанието влизащо или излизащо от Git.

Почти всичко в <<_git_config>> е посветено на тази команда.

[[_core_editor]]
==== git config core.editor команди

Съгласно конфигурационните инструкции в <<_editor>>, много редактори могат да се настроят както следва:

.Пълен списък на `core.editor` конфигурационните команди
[cols="1,2",options="header"]
|==============================
|Редактор | Конфигурационна команда
|Atom |`git config --global core.editor "atom --wait"`
|BBEdit (Mac, with command line tools) |`git config --global core.editor "bbedit -w"`
|Emacs |`git config --global core.editor emacs`
|Gedit (Linux) |`git config --global core.editor "gedit --wait --new-window"`
|Gvim (Windows 64-bit) |`git config --global core.editor "'C:\Program Files\Vim\vim72\gvim.exe' --nofork '%*'"` (вижте забележката отдолу)
|Kate (Linux) |`git config --global core.editor "kate"`
|nano |`git config --global core.editor "nano -w"`
|Notepad (Windows 64-bit) |`git config core.editor notepad`
|Notepad++ (Windows 64-bit) |`git config --global core.editor "'C:\Program Files\Notepad++\notepad++.exe' -multiInst -notabbar -nosession -noPlugin"` (вижте забележката отдолу)
|Scratch (Linux)|`git config --global core.editor "scratch-text-editor"`
|Sublime Text (macOS) |`git config --global core.editor "/Applications/Sublime\ Text.app/Contents/SharedSupport/bin/subl --new-window --wait"`
|Sublime Text (Windows 64-bit) |`git config --global core.editor "'C:\Program Files\Sublime Text 3\sublime_text.exe' -w"` (вижте забележката отдолу)
|TextEdit (macOS)|`git config --global --add core.editor "open -W -n"`
|Textmate |`git config --global core.editor "mate -w"`
|Textpad (Windows 64-bit) |`git config --global core.editor "'C:/Program Files/TextPad 5/TextPad.exe' -m` (вижте забележката отдолу)
|UltraEdit (Windows 64-bit) | `git config --global core.editor Uedit32`
|Vim |`git config --global core.editor "vim"`
|Visual Studio Code |`git config --global core.editor "code --wait"`
|VSCodium (Free/Libre Open Source Software Binaries of VSCode) | `git config --global core.editor "codium --wait"`
|WordPad |`git config --global core.editor '"C:\Program Files\Windows NT\Accessories\wordpad.exe"'"`
|Xi | `git config --global core.editor "xi --wait"`
|==============================

[NOTE]
====
Ако имате 32-битов редактор на 64-битов Windows, програмата ще бъде инсталирана в `C:\Program Files (x86)\` вместо в `C:\Program Files\`.
====

==== git help

Командата `git help` се използва за показване на документацията на командите в Git.
Ние правим кратък преглед тук, но ако искате пълния списък с всички възможни аргументи и флагове за коя да е команда, можете винаги да изпълните `git help <command>`.

Представихме `git help` в <<_git_help>> и показахме как да я използвате за да намерите повече информация за `git shell` в <<_setting_up_server>>.


=== Издърпване и създаване на проекти

Има два начина за сдобиване с Git хранилище.
Единият е да го копираме от налично такова в мрежата или където и да се намира. Другият е да си го създадем от налична директория.

==== git init

За да вземем произволна директория и да я превърнем в Git хранилище, просто изпълняваме `git init`.

Показахме това първо в <<_getting_a_repo>>, където създадохме ново хранилище, по което да работим.

Накратко споменахме как можем да сменим името на клона по подразбиране от ``master'' до друго такова в <<_remote_branches>>.

Използваме тази команда и за създаване на празно bare хранилище на сървъра в <<_bare_repo>>.

Последно, погледнахме какво командата прави задкулисно в <<_plumbing_porcelain>>.

==== git clone

Командата `git clone` по същество е нещо като wrapper около няколко други команди.
Тя създава нова директория, влиза в нея и изпълнява `git init` за да създаде празно Git хранилище, след това добавя remote (`git remote add`) към URL-а, който ѝ подавате (по подразбиране с име `origin`), изпълнява `git fetch` от това отдалечено хранилище и извлича в работната ви директория най-новия къмит с `git checkout`.

`git clone` се използва на цял куп места в книгата, ще изброим само най-интересните.

Командата бе представена и обяснена в <<_git_cloning>>, където дадохме няколко примера.

В <<_getting_git_on_a_server>> погледнахме опцията `--bare` за създаване на Git хранилище без работна директория.

В <<_bundling>> я използвахме за да възстановим пакетирано Git хранилище.

В <<_cloning_submodules>> научихме за опцията `--recurse-submodules`, с чиято помощ улесняваме клонирането на хранилище с подмодули.

Въпреки, че командата се използва на много други места в книгата, гореизброените са тези, при които тя се използва по по-различен и специфичен начин.


=== Snapshotting

За основния работен процес по индексиране на съдържание и къмитването му в историята, съществуват само няколко основни команди.

==== git add

Командата `git add` добавя съдържание от работната директория в staging area (или ``индексната област'') за следващия къмит.
Когато се изпълни `git commit`, по подразбиране тя гледа какво има само в индекса, така `git add` се използва за определяне на това какво точно искате да включите в следващия snapshot.

Това е много важна за Git команда и се споменава десетки пъти в книгата.
Ето по-важните места.

Представихме `git add` първо в <<_tracking_files>>.

Показахме как да я използваме за разрешаване на конфликти при сливане в <<_basic_merge_conflicts>>.

Демонстрирахме как да я използваме интерактивно за да индексираме само специфични части от модифициран файл в <<_interactive_staging>>.

Последно, емулирахме я на ниско ниво в <<_tree_objects>>, така че да получите представа какво се случва под повърхността.

==== git status

Командата `git status` ще ви покаже различните статуси на файловете в работната директория и индексната област.
Кои файлове са променени и неиндексирани и кои са индексирани, но все още не са къмитнати.
В нормалната си форма, тя също така подава основни съвети за това как да премествате файлове между тези етапи.

Видяхме `status` за първи път в <<_checking_status>>.
Използваме я почти навсякъде в книгата, но почти всичко, което можете да вършите с нея е обяснено там.

==== git diff

Командата `git diff` се използва, когато искате да видите разликите между кои да е две дървета.
Това би могло да бъде разликата между работната област и индексната (което прави `git diff` без аргументи), между индексната област и последния къмит (`git diff --staged`), или между два къмита (`git diff master branchB`).

За пръв път срещнахме `git diff` в <<_git_diff_staged>>, където показахме как да видим кои промени са индексирани и кои все още не са.

Използвахме я и за търсене на възможни whitespace проблеми преди къмитване с опцията `--check` в <<_commit_guidelines>>.

Видяхме как да проверим за разлики между клонове по ефективен начин използвайки синтаксиса `git diff A...B` в <<_what_is_introduced>>.

Използвахме я за да филтрираме whitespace разлики с флага `-b` а също и за сравнение на различни етапи от конфликтни файлове с `--theirs`, `--ours` и `--base` в <<_advanced_merging>>.

Накрая, използвахме я за ефективно сравнение на submodule промени със `--submodule` опцията в <<_starting_submodules>>.

==== git difftool

Командата `git difftool` просто стартира външен инструмент за показване на разликите между две дървета, в случай че предпочитате нещо различно от вградената `git diff`.

Споменахме я в <<_git_diff_staged>>.

==== git commit

Командата `git commit` взема съдържанието на всички файлове индексирани преди това с `git add` и записва нов перманентен snapshot в базата данни, след което премества указателя на текущия клон към него.

Основите на къмитването показахме в <<_committing_changes>>.
Там също така показахме как с удобния флаг `-a` можем да прескочим `git add` стъпката в ежедневния работен процес и как с `-m` да подадем къмит съобщението директно от командния ред, вместо да пускаме редактора.

Във <<_undoing>> разгледахме опцията `--amend` за да направим отново последния къмит.

В <<_git_branches_overview>> навлязохме в повече детайли за това какво прави `git commit` и защо го прави по дадения начин.

Видяхме как да подписваме къмити криптографски с флага `-S` в <<_signing_commits>>.

Последно, погледнахме какво прави зад кулисите `git commit` и как това е имплементирано в <<_git_commit_objects>>.

==== git reset

Командата `git reset` се използва основно за отмяна на действия, както подсказва името ѝ.
Тя премества указателя на `HEAD` и по избор може да променя индексната област, както и работната директория с опцията `--hard`.
Тази последна опция дава възможност за загуба на данни, ако се използва неправилно, така че трябва да я разберете добре преди да я ползвате.

За пръв път се срещнахме с простата форма на `git reset` в <<_unstaging>>, където я използвахме за да извадим от индекса файл, върху който е изпълнена `git add`.

След това я разгледахме в повече детайли в <<_git_reset>>, секцията беше изцяло посветена на нея.

Използвахме `git reset --hard` за да отменим сливане в <<_abort_merge>>, където също така използвахме и `git merge --abort`, която е един вид wrapper за `git reset`.

==== git rm

Използваме `git rm` за изваждане на файлове от индексната област и работната директория в Git.
Тя е подобна на `git add` в това, че индексира файл (само че за изваждане) от следващия къмит.

Погледнахме я в малко детайли в <<_removing_files>>, включително за рекурсивно изтриване на файлове и също така за изтриване на файлове само от индекса, но не и от работната директория с опцията `--cached`.

Единственият вид различно приложение на `git rm` в книгата е в <<_removing_objects>>, където използвахме и обяснихме опцията `--ignore-unmatch` при изпълнение на `git filter-branch`, която просто игнорира грешката, ако файлът който се опитваме да изтрием не съществува.
Това може да е полезно за ползване в скриптове.

==== git mv

Командата `git mv` е просто удобен заместител за ситуациите, когато трябва да преместите файл, да изпълните `git add` за новия файл и след това `git rm` за стария.

Само я споменахме накратко в <<_git_mv>>.

==== git clean

Командата `git clean` се използва за премахване на нежелани файлове от работната директория.
Това може да включва премахването на временни обекти от компилиране или merge conflict файлове.

Разглеждаме много от опциите и сценариите, в които може да се използва clean командата в <<_git_clean>>.

=== Клонове и сливане

Няколко команди имплементират по-голямата част от branching и merging функционалностите в Git.

==== git branch

`git branch` по същество е инструмент за управление на клоновете в Git.
Може да създава, изброява, изтрива и преименува клонове.

Повечето от написаното в <<ch03-git-branching>> е посветено на `branch` командата и тя се използва в цялата глава.
Първо я представихме в <<_create_new_branch>> и преминахме през повечето ѝ възможности (печатане и изтриване) в <<_branch_management>>.

В <<_tracking_branches>> използваме `git branch -u` за да укажем tracking клон.

Последно, погледнахме какво прави тя на заден план в <<_git_refs>>.

==== git checkout

Командата `git checkout` се използва за превключване на клонове и за извличане на съдържание в работната директория.

За пръв път я срещнахме в <<_switching_branches>> заедно с командата `git branch`.

Видяхме как да я използваме за да започнем да следим клонове с флага `--track` в <<_tracking_branches>>.

Използваме я за повторно въвеждане на конфликти във файлове с опцията `--conflict=diff3` в <<_checking_out_conflicts>>.

Навлизаме в по-дълбоки подробности за връзката ѝ с `git reset` в <<_git_reset>>.

Детайли по имплементацията ѝ показахме в <<ref_the_ref>>.

==== git merge

Инструментът `git merge` се използва за сливане на един или повече клонове в клона, който е текущо извлечен.
След сливането, текущият клон се премества напред с резултата от сливането.

Командата `git merge` видяхме за пръв път в <<_basic_branching>>.
Въпреки, че се използва на различни места в книгата, тя има много малко на брой вариации -- в общи линии само `git merge <branch>` с името на единичен клон, който искаме да слеем.

Видяхме как се прави squashed сливане (където Git слива работата, но го прави под формата на единичен обединяващ къмит, вместо да записва цялата история на сливащия се клон) в края на <<_public_project>>.

Преминахме през доста детайли по merge процеса и самата команда, включително `-Xignore-space-change` командата и флага `--abort` за отказ на проблематично сливане в <<_advanced_merging>>.

Видяхме как да проверяваме сигнатури преди сливане, ако проектът ви използва GPG подписване в <<_signing_commits>>.

Накрая научихме за Subtree сливането в <<_subtree_merge>>.

==== git mergetool

Командата `git mergetool` служи за стартиране на външен merge helper в случай, че не харесвате вграденото сливане в Git.

Погледнахме го набързо в <<_basic_merge_conflicts>> и навлязохме в детайли за това как да имплементирате собствен външен merge tool във <<_external_merge_tools>>.

==== git log

Командата `git log` се използва за показване на достъпна записана история на проект от най-късно записания къмит snapshot назад.
Тя по подразбиране ще покаже само историята на текущия клон, но може да ѝ се подадат различни или дори повече heads или клонове, от които да трасира.
Също често се използва за показване на разлики между два или повече клона на ниво къмит.

Тази команда се среща в почти всяка глава в книгата за демонстрация на различни аспекти от историята на проект.

Представихме я първо в <<_viewing_history>>.
Там разгледахме аргументите ѝ `-p` и `--stat` за да получим представа какво е било въведено във всеки къмит, както и `--pretty` и `--oneline` опциите за да видим историята по-стегнато заедно с някои прости опции за филтриране по дата и автор.

В <<_create_new_branch>> я използваме с аргумента `--decorate` за лесно визуализиране на това къде сочат указателите на клоновете, а също и с опцията `--graph` за да видим как изглеждат разклонени истории.

В <<_private_team>> и <<_commit_ranges>> обяснихме синтаксиса `branchA..branchB` за да инструктираме `git log` да намери кои къмити са уникални за даден клон в сравнение с друг клон.
В <<_commit_ranges>> разгледахме това сравнително обстойно.

В <<_merge_log>> и <<_triple_dot>> използваме формата `branchA...branchB` и `--left-right` синтаксиса, за да видим какво е налично в един от двата клона, но не и в двата едновременно.
В <<_merge_log>> също видяхме как да използваме `--merge` опцията за помощ при изследване на merge конфликти, както и `--cc` опцията за търсене на merge commit конфликти в историята.

В <<_git_reflog>> демонстрирахме флага `-g` за да видим Git reflog-а през този инструмент вместо да правим branch traversal.

В <<_searching>> използвахме флаговете `-S` и `-L` за търсене на неща, които са се случили хронологично в кода във времето, например историята на дадена функция.

В <<_signing_commits>> виждаме как да използваме `--show-signature` за да добавим верификационен стринг към всеки къмит в изхода от `git log` в зависимост от това дали той е валидно подписан или не.

==== git stash

Командата `git stash` се използва за временно съхранение/скриване на некъмитната работа с цел да се изчисти работната директория без да е необходимо къмитване на недовършената работа в клона.

Това е обяснено изцяло в <<_git_stashing>>.

==== git tag

`git tag` командата предоставя възможност за създаване на перманентен маркер (bookmark) към специфична точка в историята на кода.
В общи линии се използва за маркиране на неща от рода на releases.

Тази команда е представена и разгледана в подробности в <<_git_tagging>> и я използваме в действие в <<_tagging_releases>>.

Погледнахме как да създадем GPG signed таг с флага `-s` и също така как да проверим такъв с `-v` в <<_signing>>.


=== Споделяне и обновяване на проекти

Не са много командите в Git, които правят мрежови операции, почти всички работят с локалната база данни.
Когато сте готови да споделите работата си обаче, ето какви са възможностите:

==== git fetch

`git fetch` комуникира с отдалечено хранилище и издърпва всичката информация от него, която липсва в локалната база данни.

Представихме я във <<_fetching_and_pulling>> и продължихме с примери за нея в <<_remote_branches>>.

Използвахме я и в няколко примера в <<_contributing_project>>.

Използваме я за издърпваме на конкретна референция, която е извън областта по подразбиране в <<_pr_refs>> и видяхме как да издърпваме от bundle в <<_bundling>>.

Направихме строго потребителски refspecs с цел да накараме `git fetch` да направи нещо малко по-различно от подразбиращото се в <<_refspec>>.

==== git pull

Командата `git pull` по същество е комбинация от `git fetch` и `git merge` командите като Git ще се опита да изтегли и слее промените от отдалеченото хранилище в една стъпка.

Представихме я първоначално във <<_fetching_and_pulling>> и демонстрирахме как да видим какво ще бъде слято, ако бъде изпълнена в <<_inspecting_remote>>.

Видяхме как да я използваме в помощ при rebasing затруднения в <<_rebase_rebase>>.

Показваме как да я използваме с URL за да интегрираме промени в one-off маниер в <<_checking_out_remotes>>.

Набързо отбелязваме, че може да използвате опцията `--verify-signatures` за да проверите дали къмитите, които интегрирате са били GPG подписани в <<_signing_commits>>.

==== git push

Командата `git push` използваме за комуникация с отдалечено хранилище, за изчисляване и изпращане към него на информацията, която е налична локално, но не присъства на отдалечения сървър.
Тя изисква права за запис в отдалеченото хранилище и нормално се използва с някакъв вид автентикация.

За пръв път се срещнахме с `git push` в <<_pushing_remotes>>.
Там видяхме основите на публикуването на клонове в отдалечени хранилища.
В <<_pushing_branches>> навлязохме малко по-дълбоко в публикуването на специфични клонове, а в <<_tracking_branches>> видяхме как да направим tracking клонове, към които да публикуваме автоматично.
В <<_delete_branches>> използвахме флага `--delete` за да изтрием клон от сървъра с `git push`.

В <<_contributing_project>> видяхме множество примери на използване на командата за споделяне на работа с множество отдалечени хранилища.

Научихме как да я използваме за споделяне на тагове с опцията `--tags` в <<_sharing_tags>>.

В <<_publishing_submodules>> използвахме опцията `--recurse-submodules` за да проверим дали цялата ни работа по подмодулите е била публикувана преди да публикуваме основния проект. Това може да е много полезно, когато използваме подмодули.

В <<_other_client_hooks>> говорихме за `pre-push` hook скрипта, който можем да настроим да се стартира преди публикуването да завърши и да проверява дали съдържанието е валидно.

Последно, в <<_pushing_refspecs>> видяхме как се публикува с пълни refspec, вместо с общоприетите shortcuts, които се използват нормално.
Това може да ви помогне да сте много прецизни в това каква точно работа искате да публикувате.

==== git remote

`git remote` е управляващ инструмент за данните за отдалечени хранилища.
Позволява да изписвате дългите URL-и под формата на съкратени описателни имена като например ``origin''.
Може да имате много такива и `git remote` се използва за добавяне, редактиране и изтриването им.

Командата разгледахме в подробности в <<_remote_repos>>, включително показване, добавяне, изтриване и преименуване на отдалечени референции.

Тя също се използва почти във всяка глава в книгата, но винаги в стандартния формат `git remote add <name> <url>`.

==== git archive

Командата `git archive` използваме за създаване на архивен файл от специфичен snapshot на проекта.

Използвахме `git archive` за да създадем tarball на проект, подходящ за споделяне в <<_preparing_release>>.

==== git submodule

Командата `git submodule` се използва за управление на външни хранилища в рамките на основния проект.
Може да се използва за библиотеки или други споделени ресурси.
`submodule` има няколко подкоманди като (`add`, `update`, `sync`) за управлението им.

Присъства и се обяснява изцяло само в <<_git_submodules>>.

=== Инспекция и сравнение

==== git show

Командата `git show` може да покаже Git обект в опростен и по-интуитивен вид.
Нормално се използва за показване на информация за таг или къмит.

Използвахме я първо в <<_annotated_tags>> за да покажем информация за анотирани тагове.

По-късно я използвахме често в <<_revision_selection>> за да изведем къмитите, към които се отнасят конкретни ревизии на проекта.

Едно от по-интересните неща, които направихме с `git show` беше в <<_manual_remerge>>, когато я използвахме за да извлечем специфично съдържание от файлове от различни етапи по време на merge конфликт.

==== git shortlog

`git shortlog` се използва за съкратено обобщение на изхода от `git log`.
Тя приема много от аргументите на `git log`, но вместо да извежда всички къмити, ще отпечата обобщение на къмитите групирани по автор.

Показахме как да я използваме за да създадем прегледен changelog в <<_the_shortlog>>.

==== git describe

Командата `git describe` приема Git обект и извежда стринг, който е четим за хората и не се променя.
Това е начин да получим описание на къмит, което да е уникално като SHA-1 хеша, но малко по-разбираемо.

Използвахме `git describe` в <<_build_number>> и <<_preparing_release>> за да получим стринг, с който да именуваме нашия release файл.


=== Дебъгване

Git има няколко команди, които могат да бъдат от помощ при дебъгване на проблеми в кода.
Те помагат да определите къде нещо е било въведено и кой го е въвел.

==== git bisect

`git bisect` е много полезен инструмент за дебъгване, използва се за да определим кой специфичен къмит първи е въвел определена грешка в кода чрез автоматично двоично търсене.

Среща се в <<_binary_search>>, където е обяснен напълно.

==== git blame

Командата `git blame` анотира редовете на произволен файл с информация за това кой къмит последно е направил промяна по тях и кой е разработчика направил промените.
Това е удобно, когато трябва да намерите човека, когото да питате за повече информация по специфична секция от кода.

Среща се единствено в <<_file_annotation>>.

==== git grep

Командата `git grep` може да използвате за да търсите за произволен стринг или регулярен израз в произволен файл от кода, дори и в по-стари версии на проекта.

Използваме я единствено в <<_git_grep>>.

=== Patching

Има няколко команди в Git обединени около концепцията да възприемаме къмитите от гледна точка на това какви промени въвеждат, тоест като серия от пачове.
Тези команди помагат да управлявате клоновете си в подобен маниер.

==== git cherry-pick

`git cherry-pick` взема промяната въведена в единичен къмит и се опитва да я въведе наново като нов къмит в текущия клон.
Това може да е полезно в случай, че искате да вземете един или два къмита индивидуално от даден клон вместо да го слеете целия, което взема всичките му промени.

Cherry picking процесът е обяснен и демонстриран в <<_rebase_cherry_pick>>.

==== git rebase

Командата `git rebase` в общи линии е автоматична версия на `cherry-pick`.
Тя определя серия от къмити и след това ги cherry-pick-ва един по един в същия ред на друго място.

Пребазирането е разгледано в детайли в <<_rebasing>>, където освен това обръщаме внимание на възможните проблеми, които възникват в резултат на пребазиране на вече публикувани клонове.

Използваме я също в примера за разделяне на историята в две отделни хранилища в <<_replace>> с помощта на флага `--onto`.

Разглеждаме и ситуации, при които може да се окажем в merge конфликт по време на пребазиране в <<ref_rerere>>.

Използваме я в интерактивен скриптов режим с опцията `-i` в <<_changing_multiple>>.

==== git revert

Командата `git revert` по същество е обратна на `git cherry-pick`.
Тя създава нов къмит, който прилага точно обратното на промените въведени в посочения къмит и реално ги отменя.

Използваме я във <<_reverse_commit>> за да отменим merge къмит.

=== Email команди

Много Git проекти, включително и самия Git, се поддържат през мейлинг листи.
Git разполага с вградени инструменти за улеснение на процеса -- от такива за генериране на пачове, които можете да изпратите по пощата, до такива за прилагане на пачовете от имейл съобщение.

==== git apply

`git apply` прилага пач създаден с `git diff` или дори с GNU diff командите.
Тя е подобна на възможностите на `patch` командата с някои малки разлики.

Демонстрирахме я и показахме при какви обстоятелства е подходяща в <<_patches_from_email>>.

==== git am

Командата `git am` се използва за прилагане на пачове от email inbox с mbox формат.
Полезна е за получаване и лесно прилагане на пачове по имейл.

Видяхме използването ѝ и възможна работна последователност в <<_git_am>> включително с опциите ѝ `--resolved`, `-i` и `-3`.

Съществуват също няколко hooks, които може да използвате в помощ на работния процес с `git am`, разгледани са в <<_email_hooks>>.

Също така я използваме за прилагане на форматирани като пач GitHub Pull Request промени в <<_email_notifications>>.

==== git format-patch

`git format-patch` се използва за генериране на серии от пачове в mbox формат, които може да използвате за изпращане към мейлинг лист със съответното коректно форматиране.

Разгледахме пример за сътрудничество в проект с `git format-patch` инструмента в <<_project_over_email>>.

==== git imap-send

`git imap-send` командата качва информация генерирана с `git format-patch` в IMAP drafts папка.

Видяхме пример за сътрудничество в проект чрез изпращане на пачове с `git imap-send` в <<_project_over_email>>.

==== git send-email

`git send-email` се използва за изпращане по имейл на пачове генерирани с `git format-patch`.

Отново, пример за сътрудничество по проект с нейна помощ разгледахме в <<_project_over_email>>.

==== git request-pull

Командата `git request-pull` просто генерира примерно тяло на имейл съобщение, което може да изпратите някому.
Ако имате клон на публичен сървър и искате да покажете на някого как да интегрира промените ви без изпращане на пачове по имейл, може да изпълните тази команда и да изпратите изхода ѝ до колегата.

Демонстрирахме това генерирайки pull message в <<_public_project>>.

=== Външни системи

Git има няколко команди подпомагащи интеграцията с други version control системи.

==== git svn

`git svn` се използва за комуникация със Subversion като клиент.
Това значи, че може да използвате Git за да извличате съдържание и да изпращате къмити към Subversion сървър.

Командата е подробно разгледана в <<_git_svn>>.

==== git fast-import

За други видове системи или за импорт от почти всякакъв формат, може да използвате `git fast-import`, която бързо преработва чуждия формат до нещо, което Git може лесно да запише.

Разглеждаме я в <<_custom_importer>>.

=== Административни команди

Ако администрирате Git хранилище или трябва да поправите нещо по драстичен начин, ето какви команди са налични.

==== git gc

`git gc` изпълнява ``garbage collection'' процедури върху вашето хранилище премахвайки ненужни файлове от базата данни и пакетирайки останалите файлове в по-ефективен формат.

Тя нормално работи незабележимо на заден план, въпреки че може да се пуска и ръчно, ако желаете.
Видяхме някои примери в <<_git_gc>>.

==== git fsck

Командата `git fsck` се използва за проверка на вътрешната база данни за проблеми или несъответствия.

Използвахме я само във <<_data_recovery>> за търсене на т.нар. dangling обекти.

==== git reflog

`git reflog` преглежда дневника на това къде са били указателите на клоновете ви във времето за да намери евентуално загубени къмити -- нещо, което може да се случи при пренаписване на историите.

Работихме с тази команда главно в <<_git_reflog>>, където също видяхме как бихме могли да използваме `git log -g` за да получим същата информация форматирана като `git log` изход.

Показахме практически пример за възстановяване на подобен загубен клон във <<_data_recovery>>.

==== git filter-branch

Командата `git filter-branch` се използва за прилагане на специфични действия към набор от къмити, например премахване на файл от всеки от тях или филтриране на цялото хранилище назад до определена поддиректория за извличане на проект.

В <<_removing_file_every_commit>> обясняваме командата и изследваме няколко нейни опции като `--commit-filter`, `--subdirectory-filter` и `--tree-filter`.

В <<_git_p4>> и в <<_git_tfs>> я използваме за коригиране на импортирани външни хранилища.


=== Plumbing команди

В книгата използвахме и доста на брой команди от по-ниско ниво, plumbing командите.

Първата такава беше `ls-remote` в <<_pr_refs>>, за разглеждане на raw референциите на сървъра.

Използвахме `ls-files` в <<_manual_remerge>>, <<ref_rerere>> и <<_the_index>> за да видим как изглежда индексната област в по-суров вид.

Споменахме `rev-parse` в <<_branch_references>>. С нея видяхме как да превърнем всеки стринг в SHA-1 хеш на Git обект.

Повечето low level plumbing команди са в <<ch10-git-internals>>, цялата глава е малко или много посветена на тях.
В останалите части на книгата умишлено се старахме да не ги използваме.