Obsah
1. Programovací jazyk Clojure a práce s Gitem (3)
2. Použití funkce clj-jgit.porcelain/git-blame
3. Zdrojový kód demonstračního příkladu git-test12
5. Zdrojový kód demonstračního příkladu git-test13
7. Zdrojový kód demonstračního příkladu git-test14
8. Vytvoření naformátovaného patche s využitím funkce clj-jgit.querying/changed-files-with-patch
9. Zdrojový kód demonstračního příkladu git-test15
10. Repositář s dnešními demonstračními příklady
11. Odkazy na předchozí části tohoto seriálu
1. Programovací jazyk Clojure a práce s Gitem (3)
V dnešním článku o programovacím jazyce Clojure i o vybraných užitečných knihovnách, které jsou pro tento jazyk vytvořeny, se opět (již potřetí) budeme zabývat způsobem využití knihovny nazvané clj-jgit. Navážeme tak na sedmnáctou a osmnáctou část tohoto seriálu, v nichž jsme si nejprve řekli základní informace o knihovně clj-jgit a následně jsme si na celkem jedenácti demonstračních příkladech ukázali, jakým způsobem je možné získat obsah vzdáleného repositáře, jak se přečtou informace o lokálních i vzdálených větvích a taktéž jsme si předvedli způsoby přepínání mezi větvemi, přidávání a změnu souborů v repositáři, commit provedených změn a zjištění stavu repositáře v jakémkoli okamžiku (těsně po naklonování, po přepnutí větve, po přidání nového souboru do repositáře, změně souboru, commitu provedených změn apod.).
Prozatím jsme se však neseznámili se způsobem přečtení informací o všech provedených commitech ani o postupu, kterým je možné získat patch (změnový soubor) obsahující informace o tom, jaké změny byly ve vybraném commitu vlastně provedeny. S funkcemi a makry knihovny clj-jgit, které tyto operace implementují, se seznámíme v dnešním článku a především si ukážeme způsob jejich použití na čtveřici demonstračních příkladů. Popsány budou následující funkce a makra:
# | Funkce/makro | Stručný popis |
---|---|---|
1 | clj-jgit.porcelain/git-blame | obdoba příkazu git blame |
2 | clj-jgit.porcelain/rev-list | vrací seznam všech provedených změn v repositáři |
3 | clj-jgit.querying/commit-info | získání podrobnějších informací o zvoleném commitu |
4 | clj-jgit.querying/branches-for | informace o větvích, kterých se commit týká |
5 | clj-jgit.querying/changed-files | informace o změněných souborech ve zvoleném commitu |
6 | clj-jgit.querying/changed-files-with-patch | naformátování patche pro zvolený commit |
2. Použití funkce clj-jgit.porcelain/git-blame
První funkcí, s níž se dnes seznámíme, je funkce nazvaná clj-jgit.porcelain/git-blame, kterou je možné použít pro zjištění informací o autorech změn provedených v libovolném souboru, který je součástí repositáře. Této funkci se předávají dva argumenty – reference na repositář, tj. hodnota vrácená například funkcí clj-jgit.porcelain/with-repo a jméno souboru, o jehož změnách potřebujeme získat potřebné informace. Funkce clj-jgit.porcelain/git-blame vrací seznam změn, přičemž informace o každé změně je reprezentována následující datovou strukturou složenou z vnořených hashmap:
{ :author { :name :email :timezone } :commit :committer { :name :email :timezone } :line :source-path }
Pro zajímavost – zdrojový kód funkce clj-jgit.porcelain/git-blame vypadá následovně:
(defn git-blame ([^Git repo ^String path] (git-blame repo path false)) ([^Git repo ^String path ^Boolean follow-renames?] (-> repo .blame (.setFilePath path) (.setFollowFileRenames follow-renames?) .call blame-result)) ([^Git repo ^String path ^Boolean follow-renames? ^AnyObjectId start-commit] (-> repo .blame (.setFilePath path) (.setFollowFileRenames follow-renames?) (.setStartCommit start-commit) .call blame-result)))
Poznámka: pravděpodobně kvůli chybě v knihovně JGit, konkrétně ve funkci getSourceCommit(), dochází k výjimce typu ArrayIndexOutOfBoundsException při snaze o přečtení posledního záznamu v sekvenci.
3. Zdrojový kód demonstračního příkladu git-test12
V dnešním prvním demonstračním příkladu nazvaném git-test12 je ukázáno využití výše popsané funkce clj-jgit.porcelain/git-blame pro získání informací o změně provedené v souboru README.md umístěného v testovacím repositáři https://github.com/tisnik/testrepo. Vypíše se pouze první změna ze získané sekvence; při snaze o čtení dalších změn nezapomeňte celou smyčku uzavřít do makra try kvůli zpracování výše zmíněné výjimky typu ArrayIndexOutOfBoundsException.
Vytvoření kostry demonstračního příkladu:
lein new app git-12
Úprava projektového souboru project.clj:
(defproject git-test12 "0.1.0-SNAPSHOT" :description "FIXME: write description" :url "http://example.com/FIXME" :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[org.clojure/clojure "1.6.0"] [clj-jgit "0.8.0"] [clj-rm-rf "1.0.0-SNAPSHOT"]] :main ^:skip-aot git-test12.core :target-path "target/%s" :profiles {:uberjar {:aot :all}})
Zdrojový kód souboru src/git_test12/core.clj:
(ns git-test12.core (:gen-class)) (require '[clojure.pprint :as pprint]) (require '[clj-jgit.porcelain :as jgit]) (require '[clj-jgit.querying :as jgit-query]) (require '[hozumi.rm-rf :as rm-rf]) (def repository-url "Adresa GIT repositare vcetne specifikace protokolu." "https://github.com/tisnik/testrepo.git") (def directory-name "Jmeno adresare, do ktereho se GIT repositar naklonuje." "repo") (defn clone-repository "Naklonovani GIT repositare do specifikovaneho adresare." [url directory] (jgit/git-clone url directory)) (defn delete-directory "Smazani adresare vcetne podadresaru a souboru." [directory] (rm-rf/rm-r (java.io.File. directory))) (defn print-customized-log "Vypis logovacich informaci ve zvolenem formatu." [repository] (println "\nLog:") (doseq [log-entry (jgit/git-log repository)] (let [info (jgit-query/commit-info repository log-entry)] (println (:author info) ":" (:message info))))) (defn print-local-branches "Vypis vsech lokalnich vetvi pro vybrany repositar." [repo] (println "\nLocal branches") (doseq [branch (jgit/git-branch-list repo)] (println (.getName branch)))) (defn print-remote-branches "Vypis vsech vzdalenych vetvi pro vybrany repositar." [repo] (println "\nRemote branches") (doseq [branch (jgit/git-branch-list repo :remote)] (println (.getName branch)))) (defn print-git-blame "Ukazka pouziti funkce git-blame." [repo filename] (println "\nGit blame " filename) (let [blamelist (jgit/git-blame repo filename) first-blame (first blamelist)] ; ukazka pristupu ke vnorenemu prvku vracene struktury (println "Committer:" (:name (:committer first-blame))) ; vypis cele struktury (pprint/pprint first-blame) )) (defn git-test-12 "Naklonovani repositare, nacteni informaci z vytvoreneho adresare a vypis podrobnejsiho logu." [repository-url directory-name] ; naklonovani repositare do specifikovaneho adresare (clone-repository repository-url directory-name) ; nacteni informaci o repositari z lokalniho adresare ; povsimnete si pouziti "automagicky" vytvorene promenne, ; ktera se jmenuje 'repo' (try (jgit/with-repo directory-name (print-customized-log repo) (print-local-branches repo) (print-remote-branches repo) (print-git-blame repo "README.md")) (finally ; vymazani adresare s naklonovanym repositarem (delete-directory directory-name)))) (defn -main "Funkce zavolana po zadani prikazu 'lein run'." [& args] (git-test-12 repository-url directory-name))
Ukázka výstupu demonstračního příkladu po jeho spuštění (repositář ve stavu se třemi větvemi):
Log: Pavel Tisnovsky : Added garbage Pavel Tisnovsky : Updated README.md Pavel Tišnovský : Initial commit Local branches refs/heads/master Remote branches refs/remotes/origin/branch-1 refs/remotes/origin/branch-2 refs/remotes/origin/master Git blame README.md Committer: Pavel Tišnovský {:author {:name "Pavel Tišnovský", :email "ptisnovs@redhat.com", :timezone #<ZoneInfo sun.util.calendar.ZoneInfo[id="GMT-04:00",offset=-14400000,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]>}, :commit #<RevCommit commit badf2b12f9281a8581009bfa068a5147c4cbc87a 1434722046 -----p>, :committer {:name "Pavel Tišnovský", :email "ptisnovs@redhat.com", :timezone #<ZoneInfo sun.util.calendar.ZoneInfo[id="GMT-04:00",offset=-14400000,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]>}, :line 0, :source-path "README.md"}
Ukázka výstupu demonstračního příkladu po jeho spuštění (repositář ve stavu se čtyřmi větvemi, spojením větví a merge konfliktem):
Log: Pavel Tisnovsky : Updated README.md Pavel Tisnovsky : Added file-in-branch-3 into branch-3 Pavel Tisnovsky : Added garbage Pavel Tisnovsky : Updated README.md Pavel Tišnovský : Initial commit Local branches refs/heads/master Remote branches refs/remotes/origin/branch-1 refs/remotes/origin/branch-2 refs/remotes/origin/branch-3 refs/remotes/origin/master Git blame README.md Committer: Pavel Tišnovský {:author {:name "Pavel Tišnovský", :email "ptisnovs@redhat.com", :timezone #<ZoneInfo sun.util.calendar.ZoneInfo[id="GMT-04:00",offset=-14400000,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]>}, :commit #<RevCommit commit badf2b12f9281a8581009bfa068a5147c4cbc87a 1434722046 -----p>, :committer {:name "Pavel Tišnovský", :email "ptisnovs@redhat.com", :timezone #<ZoneInfo sun.util.calendar.ZoneInfo[id="GMT-04:00",offset=-14400000,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]>}, :line 0, :source-path "README.md"}
4. Použití funkcí clj-jgit.porcelain/rev-list a clj-jgit.querying/commit-info pro získání informací o commitech
Další informací, kterou je možné při práci s GITovskými repositáři relativně jednoduše přečíst, jsou informace o jednotlivých commitech, tj. o změnách provedených v průběhu historie existence aktivně používaného repositáře. V této kapitole si popíšeme dvojici funkcí, které lze využít pro přečtení podobných informací, jaké se z příkazového řádku získají příkazem git log (ve skutečnosti dále popsané funkce vrátí ještě více informací). První užitečnou funkcí je clj-jgit.porcelain/rev-list, jejíž zdrojový kód vypadá následovně:
(defn rev-list ([^Git repo] (rev-list repo (new-rev-walk repo))) ([^Git repo ^RevWalk rev-walk] (.reset rev-walk) (mark-all-heads-as-start-for! repo rev-walk) (doto (RevCommitList.) (.source rev-walk) (.fillTo Integer/MAX_VALUE))))
V nejjednodušším případě se do této funkce předá pouze jediný parametr: reference na repositář. Následně se vrátí seznam změn provedených v repositáři, ovšem my musíme mít možnost informace o jednotlivých změnách smysluplně zpracovat. Právě pro tento účel se používá druhá funkce nazvaná clj-jgit.querying/commit-info, která pro předaný objekt typu RevCommit (ten si můžeme představit jako otisk commitu, tj. jeho hešovací kód, ten je mimochodem uložený pod klíčem :id) vrátí mapu se strukturovanými informacemi. Zdrojový kód funkce clj-jgit.querying/commit-info je poněkud komplikovanější, neboť se podporuje různý typ (a počet) parametrů použitý při jejím volání:
(defn commit-info ([^Git repo, ^RevCommit rev-commit] (commit-info repo (new-rev-walk repo) rev-commit)) ([^Git repo, ^RevWalk rev-walk, ^RevCommit rev-commit] (merge (commit-info-without-branches repo rev-walk rev-commit) {:branches (branches-for repo rev-commit)})) ([^Git repo ^RevWalk rev-walk ^HashMap commit-map ^RevCommit rev-commit] (merge (commit-info-without-branches repo rev-walk rev-commit) {:branches (map #(.getName ^Ref %) (or (.get commit-map rev-commit) []))})))
Z vrácené mapy lze získat důležité hodnoty uložené pod těmito klíči:
1 | :id |
2 | :author |
3 | :message |
4 | :changed_files |
5. Zdrojový kód demonstračního příkladu git-test13
Výše popsané funkce clj-jgit.porcelain/rev-list a clj-jgit.querying/commit-info jsou použity v dnešním druhém demonstračním příkladu, kde se s jejich využitím získají informace o historii změn provedených v testovacím repositáři https://github.com/tisnik/testrepo.git.
Vytvoření kostry demonstračního příkladu:
lein new app git-13
Úprava projektového souboru project.clj:
(defproject git-test13 "0.1.0-SNAPSHOT" :description "FIXME: write description" :url "http://example.com/FIXME" :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[org.clojure/clojure "1.6.0"] [clj-jgit "0.8.0"] [clj-rm-rf "1.0.0-SNAPSHOT"]] :main ^:skip-aot git-test13.core :target-path "target/%s" :profiles {:uberjar {:aot :all}})
Zdrojový kód souboru src/git_test13/core.clj:
(ns git-test13.core (:gen-class)) (require '[clojure.pprint :as pprint]) (require '[clj-jgit.porcelain :as jgit]) (require '[clj-jgit.querying :as jgit-query]) (require '[hozumi.rm-rf :as rm-rf]) (def repository-url "Adresa GIT repositare vcetne specifikace protokolu." "https://github.com/tisnik/testrepo.git") (def directory-name "Jmeno adresare, do ktereho se GIT repositar naklonuje." "repo") (defn clone-repository "Naklonovani GIT repositare do specifikovaneho adresare." [url directory] (jgit/git-clone url directory)) (defn delete-directory "Smazani adresare vcetne podadresaru a souboru." [directory] (rm-rf/rm-r (java.io.File. directory))) (defn rev-list [repo] (println "\nList of all revision objects\n") (let [rev-list (jgit-query/rev-list repo)] (doseq [rev rev-list] (let [info (jgit-query/commit-info repo rev)] (println "commit" (:id info)) (println "Author: " (:author info)) (println "Files: " (:changed_files info)) (println "\n" (:message info) "\n\n"))))) (defn git-test-13 "Naklonovani repositare, nacteni informaci z vytvoreneho adresare a vypis podrobnejsiho logu." [repository-url directory-name] ; naklonovani repositare do specifikovaneho adresare (clone-repository repository-url directory-name) ; nacteni informaci o repositari z lokalniho adresare ; povsimnete si pouziti "automagicky" vytvorene promenne, ; ktera se jmenuje 'repo' (try (jgit/with-repo directory-name (rev-list repo)) (finally ; vymazani adresare s naklonovanym repositarem (delete-directory directory-name)))) (defn -main "Funkce zavolana po zadani prikazu 'lein run'." [& args] (git-test-13 repository-url directory-name))
Ukázka výstupu demonstračního příkladu po jeho spuštění (repositář ve stavu se třemi větvemi):
List of all revision objects commit 07f9a94593ec3e065a7eeaedbbc5220ffe5c5722 Author: Pavel Tisnovsky Files: ([file-in-branch-2 :add]) Added file-in-branch-2 commit f84564ce7fa81f865e60d3d2e18f5789539a237a Author: Pavel Tisnovsky Files: ([file-in-branch-1 :add]) Added file-in-branch-1 commit 394ce42cb3bcf44ff4e9a94949814bacddcdfe9b Author: Pavel Tisnovsky Files: ([garbage :add]) Added garbage commit 0fd398ab16e53ea0b783aef2413d75b75c6462cd Author: Pavel Tisnovsky Files: ([README.md :edit]) Updated README.md commit badf2b12f9281a8581009bfa068a5147c4cbc87a Author: Pavel Tisnovsky Files: [[README.md :add]] Initial commit
Ukázka výstupu demonstračního příkladu po jeho spuštění (repositář ve stavu se čtyřmi větvemi, spojením větví a merge konfliktem):
List of all revision objects commit ebffda7b2832c220e591d077ba448a0983fb1a47 Author: Pavel Tisnovsky Files: ([README.md :edit]) Merge branch 'master' into branch-3 Conflicts: README.md commit 19be06f06b8c5cb4a0dee5969d9a30c17f2cbc7d Author: Pavel Tisnovsky Files: ([README.md :edit]) Change in README.md commit 4be57bb203050da628b66a045610bb6e0e7b243f Author: Pavel Tisnovsky Files: ([README.md :edit]) Updated README.md commit 038bdf4191194b78fc683e40493c1ecace4f1e40 Author: Pavel Tisnovsky Files: ([file-in-branch-3 :add]) Added file-in-branch-3 into branch-3 commit 07f9a94593ec3e065a7eeaedbbc5220ffe5c5722 Author: Pavel Tisnovsky Files: ([file-in-branch-2 :add]) Added file-in-branch-2 commit f84564ce7fa81f865e60d3d2e18f5789539a237a Author: Pavel Tisnovsky Files: ([file-in-branch-1 :add]) Added file-in-branch-1 commit 394ce42cb3bcf44ff4e9a94949814bacddcdfe9b Author: Pavel Tisnovsky Files: ([garbage :add]) Added garbage commit 0fd398ab16e53ea0b783aef2413d75b75c6462cd Author: Pavel Tisnovsky Files: ([README.md :edit]) Updated README.md commit badf2b12f9281a8581009bfa068a5147c4cbc87a Author: Pavel Tisnovsky Files: [[README.md :add]] Initial commit
6. Podrobnější informace o změnách získané s využitím funkcí clj-jgit.querying/branches-for a clj-jgit.querying/changed-files
Knihovna clj-jgit nabízí svým uživatelům i několik dalších funkcí, které je možné využít pro získání podrobnějších informací o jednotlivých commitech. Některé z dále zmíněných funkcí se používají interně (tj. dalšími funkcemi z knihovny clj-jgit), ovšem díky tomu, že se nepoužívá žádný režim „skrývání“ těchto funkcí před programátory, lze je přímo volat i z vyvíjených aplikací. Jedná se především o následující funkce:
# | Funkce | Stručný popis |
---|---|---|
1 | branches-for | větev či větve, kterých se commit týká |
2 | changed-files | soubory změněné v rámci jednoho commitu |
3 | changed-files-between-commits | soubory změněné mezi libovolnými dvěma commity |
4 | changed-files-with-patch | změny provedené v rámci commitu naformátované jako patch |
Funkce clj-jgit.querying/branches-for vrací sekvenci větví, kterých se daný commit týká:
(defn branches-for [^Git repo ^ObjectId rev-commit] (let [rev-walk (new-rev-walk repo) bound-commit (bound-commit repo rev-walk rev-commit) branch-list (branch-list-with-heads repo rev-walk)] (->> (for [[^ObjectIdRef branch-ref ^RevCommit branch-tip-commit] branch-list :when branch-tip-commit] (do (when (commit-in-branch? repo rev-walk branch-tip-commit bound-commit) (.getName branch-ref)))) (remove nil?) doall)))
Význam funkce clj-jgit.querying/changed-files je jednoduchý – lze ji použít pro přečtení změněných, smazaných či naopak vytvořených souborů pro jeden commit:
(defn changed-files [^Git repo ^RevCommit rev-commit] (if-let [parent (first (.getParents rev-commit))] (changed-files-between-commits repo parent rev-commit) (changed-files-in-first-commit repo rev-commit)))
V některých případech je užitečné zjistit změny mezi libovolnými dvěma commity (ty spolu nemusí nijak souviset). Tehdy přichází na řadu funkce clj-jgit.querying/changed-files-between-commits, které se předá reference na repositář a reference na dva commity:
(defn changed-files-between-commits [^Git repo ^RevCommit old-rev-commit ^RevCommit new-rev-commit] (let [df ^DiffFormatter (diff-formatter-for-changes repo) entries (.scan df old-rev-commit new-rev-commit)] (map parse-diff-entry entries)))
Funkce vrátí kumulativní změny.
7. Zdrojový kód demonstračního příkladu git-test14
Dvojici výše zmíněných funkcí clj-jgit.querying/branches-for a clj-jgit.querying/changed-files použijeme v dnešním předposledním demonstračním příkladu nazvaném git-test14. Tento příklad je vlastně pouhým (velmi jednoduchým) rozšířením příkladu předchozího, což je ostatně patrné i při pohledu na jeho zdrojový kód, protože byly přidány pouze tyto dva nové řádky:
(println "Branches: " (jgit-query/branches-for repo (:raw info))) (println "Changed files: " (jgit-query/changed-files repo (:raw info)))
Vytvoření kostry demonstračního příkladu:
lein new app git-14
Úprava projektového souboru project.clj:
(defproject git-test14 "0.1.0-SNAPSHOT" :description "FIXME: write description" :url "http://example.com/FIXME" :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[org.clojure/clojure "1.6.0"] [clj-jgit "0.8.0"] [clj-rm-rf "1.0.0-SNAPSHOT"]] :main ^:skip-aot git-test14.core :target-path "target/%s" :profiles {:uberjar {:aot :all}})
Zdrojový kód souboru src/git_test14/core.clj:
(ns git-test14.core (:gen-class)) (require '[clojure.pprint :as pprint]) (require '[clj-jgit.porcelain :as jgit]) (require '[clj-jgit.querying :as jgit-query]) (require '[hozumi.rm-rf :as rm-rf]) (def repository-url "Adresa GIT repositare vcetne specifikace protokolu." "https://github.com/tisnik/testrepo.git") (def directory-name "Jmeno adresare, do ktereho se GIT repositar naklonuje." "repo") (defn clone-repository "Naklonovani GIT repositare do specifikovaneho adresare." [url directory] (jgit/git-clone url directory)) (defn delete-directory "Smazani adresare vcetne podadresaru a souboru." [directory] (rm-rf/rm-r (java.io.File. directory))) (defn rev-list [repo] (println "\nList of all revision objects\n") (let [rev-list (jgit-query/rev-list repo)] (doseq [rev rev-list] (let [info (jgit-query/commit-info repo rev)] (println "commit" (:id info)) (println "Author: " (:author info)) (println "Files: " (:changed_files info)) (println "\n" (:message info) "\n") (println "Branches: " (jgit-query/branches-for repo (:raw info))) (println "Changed files: " (jgit-query/changed-files repo (:raw info))) (println) )))) (defn git-test-14 "Naklonovani repositare, nacteni informaci z vytvoreneho adresare a vypis podrobnejsiho logu." [repository-url directory-name] ; naklonovani repositare do specifikovaneho adresare (clone-repository repository-url directory-name) ; nacteni informaci o repositari z lokalniho adresare ; povsimnete si pouziti "automagicky" vytvorene promenne, ; ktera se jmenuje 'repo' (try (jgit/with-repo directory-name (rev-list repo)) (finally ; vymazani adresare s naklonovanym repositarem (delete-directory directory-name)))) (defn -main "Funkce zavolana po zadani prikazu 'lein run'." [& args] (git-test-14 repository-url directory-name))
Ukázka výstupu demonstračního příkladu po jeho spuštění (repositář ve stavu se třemi větvemi):
List of all revision objects commit 07f9a94593ec3e065a7eeaedbbc5220ffe5c5722 Author: Pavel Tisnovsky Files: ([file-in-branch-2 :add]) Added file-in-branch-2 Branches: () Changed files: ([file-in-branch-2 :add]) commit f84564ce7fa81f865e60d3d2e18f5789539a237a Author: Pavel Tisnovsky Files: ([file-in-branch-1 :add]) Added file-in-branch-1 Branches: () Changed files: ([file-in-branch-1 :add]) commit 394ce42cb3bcf44ff4e9a94949814bacddcdfe9b Author: Pavel Tisnovsky Files: ([garbage :add]) Added garbage Branches: (refs/heads/master) Changed files: ([garbage :add]) commit 0fd398ab16e53ea0b783aef2413d75b75c6462cd Author: Pavel Tisnovsky Files: ([README.md :edit]) Updated README.md Branches: (refs/heads/master) Changed files: ([README.md :edit]) commit badf2b12f9281a8581009bfa068a5147c4cbc87a Author: Pavel Tisnovsky Files: [[README.md :add]] Initial commit Branches: (refs/heads/master) Changed files: [[README.md :add]]
Ukázka výstupu demonstračního příkladu po jeho spuštění (repositář ve stavu se čtyřmi větvemi, spojením větví a merge konfliktem):
List of all revision objects commit ebffda7b2832c220e591d077ba448a0983fb1a47 Author: Pavel Tisnovsky Files: ([README.md :edit]) Merge branch 'master' into branch-3 Conflicts: README.md Branches: () Changed files: ([README.md :edit]) commit 19be06f06b8c5cb4a0dee5969d9a30c17f2cbc7d Author: Pavel Tisnovsky Files: ([README.md :edit]) Change in README.md Branches: () Changed files: ([README.md :edit]) commit 4be57bb203050da628b66a045610bb6e0e7b243f Author: Pavel Tisnovsky Files: ([README.md :edit]) Updated README.md Branches: (refs/heads/master) Changed files: ([README.md :edit]) commit 038bdf4191194b78fc683e40493c1ecace4f1e40 Author: Pavel Tisnovsky Files: ([file-in-branch-3 :add]) Added file-in-branch-3 into branch-3 Branches: (refs/heads/master) Changed files: ([file-in-branch-3 :add]) commit 07f9a94593ec3e065a7eeaedbbc5220ffe5c5722 Author: Pavel Tisnovsky Files: ([file-in-branch-2 :add]) Added file-in-branch-2 Branches: () Changed files: ([file-in-branch-2 :add]) commit f84564ce7fa81f865e60d3d2e18f5789539a237a Author: Pavel Tisnovsky Files: ([file-in-branch-1 :add]) Added file-in-branch-1 Branches: () Changed files: ([file-in-branch-1 :add]) commit 394ce42cb3bcf44ff4e9a94949814bacddcdfe9b Author: Pavel Tisnovsky Files: ([garbage :add]) Added garbage Branches: (refs/heads/master) Changed files: ([garbage :add]) commit 0fd398ab16e53ea0b783aef2413d75b75c6462cd Author: Pavel Tisnovsky Files: ([README.md :edit]) Updated README.md Branches: (refs/heads/master) Changed files: ([README.md :edit]) commit badf2b12f9281a8581009bfa068a5147c4cbc87a Author: Pavel Tisnovsky Files: [[README.md :add]] Initial commit Branches: (refs/heads/master) Changed files: [[README.md :add]]
8. Vytvoření naformátovaného patche s využitím funkce clj-jgit.querying/changed-files-with-patch
Poslední funkcí z knihovny clj-jgit, kterou si dnes popíšeme, je funkce nazvaná clj-jgit.querying/changed-files-with-patch. Této funkci je nutné předat dva parametry: referenci na repositář a referenci na commit. Hodnotou, kterou tato funkce vrátí, je textová (řetězcová) podoba patche (změnového souboru), kterou je možné například s využitím standardní clojurovské funkce spit (http://clojuredocs.org/clojure.core/spit) uložit do externího textového souboru pro další použití, například pro pozdější aplikaci s využitím příkazů patch či git apply. Důležité je, že funkce clj-jgit.querying/changed-files-with-patch vrací skutečně pouze změny provedené v rámci specifikovaného commitu a nikoli rozdíl mezi libovolnými dvěma commity (i když i to by bylo užitečné).
Opět se pro zajímavost podívejme na zdrojový kód funkce clj-jgit.querying/changed-files-with-patch:
(defn changed-files-with-patch [^Git repo ^RevCommit rev-commit] (if-let [parent (first (.getParents rev-commit))] (let [rev-parent ^RevCommit parent out ^ByteArrayOutputStream (new ByteArrayOutputStream) df ^DiffFormatter (byte-array-diff-formatter-for-changes repo out)] (.format df rev-parent rev-commit) (.toString out))))
9. Zdrojový kód demonstračního příkladu git-test15
V dnešním posledním demonstračním příkladu nazvaném git-test15 je ukázáno použití funkce clj-jgit.querying/changed-files-with-patch v praxi. Vlastně se jedná o rozšíření předchozího příkladu s tím, že se kromě informací o jednotlivých commitech na standardní výstup vypíše i příslušný patch (který by samozřejmě bylo možné funkcí spit uložit do textového souboru).
Vytvoření kostry demonstračního příkladu:
lein new app git-15
Úprava projektového souboru project.clj:
(defproject git-test15 "0.1.0-SNAPSHOT" :description "FIXME: write description" :url "http://example.com/FIXME" :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[org.clojure/clojure "1.6.0"] [clj-jgit "0.8.0"] [clj-rm-rf "1.0.0-SNAPSHOT"]] :main ^:skip-aot git-test15.core :target-path "target/%s" :profiles {:uberjar {:aot :all}})
Zdrojový kód souboru src/git_test15/core.clj:
(ns git-test15.core (:gen-class)) (require '[clojure.pprint :as pprint]) (require '[clj-jgit.porcelain :as jgit]) (require '[clj-jgit.querying :as jgit-query]) (require '[hozumi.rm-rf :as rm-rf]) (def repository-url "Adresa GIT repositare vcetne specifikace protokolu." "https://github.com/tisnik/testrepo.git") (def directory-name "Jmeno adresare, do ktereho se GIT repositar naklonuje." "repo") (defn clone-repository "Naklonovani GIT repositare do specifikovaneho adresare." [url directory] (jgit/git-clone url directory)) (defn delete-directory "Smazani adresare vcetne podadresaru a souboru." [directory] (rm-rf/rm-r (java.io.File. directory))) (defn rev-list [repo] (println "\nList of all revision objects\n") (let [rev-list (jgit-query/rev-list repo)] (doseq [rev rev-list] (let [info (jgit-query/commit-info repo rev)] (println "commit" (:id info)) (println "Author: " (:author info)) (println "Files: " (:changed_files info)) (println "\n" (:message info) "\n") (println "Branches: " (jgit-query/branches-for repo (:raw info))) (println "Changed files: " (jgit-query/changed-files repo (:raw info))) (println) (println (jgit-query/changed-files-with-patch repo (:raw info))) (println) )))) (defn git-test-15 "Naklonovani repositare, nacteni informaci z vytvoreneho adresare a vypis podrobnejsiho logu." [repository-url directory-name] ; naklonovani repositare do specifikovaneho adresare (clone-repository repository-url directory-name) ; nacteni informaci o repositari z lokalniho adresare ; povsimnete si pouziti "automagicky" vytvorene promenne, ; ktera se jmenuje 'repo' (try (jgit/with-repo directory-name (rev-list repo)) (finally ; vymazani adresare s naklonovanym repositarem (delete-directory directory-name)))) (defn -main "Funkce zavolana po zadani prikazu 'lein run'." [& args] (git-test-15 repository-url directory-name))
Ukázka výstupu demonstračního příkladu po jeho spuštění (repositář ve stavu se třemi větvemi):
List of all revision objects commit 07f9a94593ec3e065a7eeaedbbc5220ffe5c5722 Author: Pavel Tisnovsky Files: ([file-in-branch-2 :add]) Added file-in-branch-2 Branches: () Changed files: ([file-in-branch-2 :add]) diff --git a/file-in-branch-2 b/file-in-branch-2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/file-in-branch-2 commit f84564ce7fa81f865e60d3d2e18f5789539a237a Author: Pavel Tisnovsky Files: ([file-in-branch-1 :add]) Added file-in-branch-1 Branches: () Changed files: ([file-in-branch-1 :add]) diff --git a/file-in-branch-1 b/file-in-branch-1 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/file-in-branch-1 commit 394ce42cb3bcf44ff4e9a94949814bacddcdfe9b Author: Pavel Tisnovsky Files: ([garbage :add]) Added garbage Branches: (refs/heads/master) Changed files: ([garbage :add]) diff --git a/garbage b/garbage new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/garbage commit 0fd398ab16e53ea0b783aef2413d75b75c6462cd Author: Pavel Tisnovsky Files: ([README.md :edit]) Updated README.md Branches: (refs/heads/master) Changed files: ([README.md :edit]) diff --git a/README.md b/README.md index 6edd528..04aafb8 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ # testrepo testrepo +hello! commit badf2b12f9281a8581009bfa068a5147c4cbc87a Author: Pavel Tisnovsky Files: [[README.md :add]] Initial commit Branches: (refs/heads/master) Changed files: [[README.md :add]] nil
Ukázka výstupu demonstračního příkladu po jeho spuštění (repositář ve stavu se čtyřmi větvemi, spojením větví a merge konfliktem):
List of all revision objects commit ebffda7b2832c220e591d077ba448a0983fb1a47 Author: Pavel Tisnovsky Files: ([README.md :edit]) Merge branch 'master' into branch-3 Conflicts: README.md Branches: () Changed files: ([README.md :edit]) diff --git a/README.md b/README.md index a999387..50943b6 100644 --- a/README.md +++ b/README.md @@ -2,5 +2,9 @@ testrepo hello! +<<<<<<< HEAD change incompatible with master +======= +update +>>>>>>> master commit 19be06f06b8c5cb4a0dee5969d9a30c17f2cbc7d Author: Pavel Tisnovsky Files: ([README.md :edit]) Change in README.md Branches: () Changed files: ([README.md :edit]) diff --git a/README.md b/README.md index 04aafb8..a999387 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ # testrepo testrepo hello! + +change incompatible with master + commit 4be57bb203050da628b66a045610bb6e0e7b243f Author: Pavel Tisnovsky Files: ([README.md :edit]) Updated README.md Branches: (refs/heads/master) Changed files: ([README.md :edit]) diff --git a/README.md b/README.md index 04aafb8..a2e71dd 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ # testrepo testrepo hello! + +update commit 038bdf4191194b78fc683e40493c1ecace4f1e40 Author: Pavel Tisnovsky Files: ([file-in-branch-3 :add]) Added file-in-branch-3 into branch-3 Branches: (refs/heads/master) Changed files: ([file-in-branch-3 :add]) diff --git a/file-in-branch-3 b/file-in-branch-3 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/file-in-branch-3 commit 07f9a94593ec3e065a7eeaedbbc5220ffe5c5722 Author: Pavel Tisnovsky Files: ([file-in-branch-2 :add]) Added file-in-branch-2 Branches: () Changed files: ([file-in-branch-2 :add]) diff --git a/file-in-branch-2 b/file-in-branch-2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/file-in-branch-2 commit f84564ce7fa81f865e60d3d2e18f5789539a237a Author: Pavel Tisnovsky Files: ([file-in-branch-1 :add]) Added file-in-branch-1 Branches: () Changed files: ([file-in-branch-1 :add]) diff --git a/file-in-branch-1 b/file-in-branch-1 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/file-in-branch-1 commit 394ce42cb3bcf44ff4e9a94949814bacddcdfe9b Author: Pavel Tisnovsky Files: ([garbage :add]) Added garbage Branches: (refs/heads/master) Changed files: ([garbage :add]) diff --git a/garbage b/garbage new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/garbage commit 0fd398ab16e53ea0b783aef2413d75b75c6462cd Author: Pavel Tisnovsky Files: ([README.md :edit]) Updated README.md Branches: (refs/heads/master) Changed files: ([README.md :edit]) diff --git a/README.md b/README.md index 6edd528..04aafb8 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ # testrepo testrepo +hello! commit badf2b12f9281a8581009bfa068a5147c4cbc87a Author: Pavel Tisnovsky Files: [[README.md :add]] Initial commit Branches: (refs/heads/master) Changed files: [[README.md :add]] nil
10. Repositář s dnešními demonstračními příklady
Všechny čtyři dnes popsané demonstrační příklady byly, podobně jako v předchozích částech tohoto seriálu, uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/clojure-examples. V tabulce zobrazené pod tímto odstavcem naleznete na zdrojové kódy jednotlivých demonstračních příkladů přímé odkazy:
# | Příklad | Github |
---|---|---|
1 | git-test12 | https://github.com/tisnik/clojure-examples/tree/master/git-test12 |
2 | git-test13 | https://github.com/tisnik/clojure-examples/tree/master/git-test13 |
3 | git-test14 | https://github.com/tisnik/clojure-examples/tree/master/git-test14 |
4 | git-test15 | https://github.com/tisnik/clojure-examples/tree/master/git-test15 |
11. Odkazy na předchozí části tohoto seriálu
Stalo se již zvykem uvést odkazy na všechny předchozí části tohoto seriálu. Tento zvyk samozřejmě dodržíme i dnes:
- Leiningen: nástroj pro správu projektů napsaných v Clojure
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure/ - Leiningen: nástroj pro správu projektů napsaných v Clojure (2)
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-2/ - Leiningen: nástroj pro správu projektů napsaných v Clojure (3)
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-3/ - Leiningen: nástroj pro správu projektů napsaných v Clojure (4)
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-4/ - Leiningen: nástroj pro správu projektů napsaných v Clojure (5)
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-5/ - Leiningen: nástroj pro správu projektů napsaných v Clojure (6)
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-6/ - Programovací jazyk Clojure a databáze (1.část)
http://www.root.cz/clanky/programovaci-jazyk-clojure-a-databaze-1-cast/ - Pluginy pro Leiningen
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-pluginy-pro-leiningen/ - Programovací jazyk Clojure a knihovny pro práci s vektory a maticemi
http://www.root.cz/clanky/programovaci-jazyk-clojure-a-knihovny-pro-praci-s-vektory-a-maticemi/ - Programovací jazyk Clojure a knihovny pro práci s vektory a maticemi (2)
http://www.root.cz/clanky/programovaci-jazyk-clojure-a-knihovny-pro-praci-s-vektory-a-maticemi-2/ - Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk
http://www.root.cz/clanky/programovaci-jazyk-clojure-synteza-proceduralnich-textur-s-vyuzitim-knihovny-clisk/ - Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk (2)
http://www.root.cz/clanky/programovaci-jazyk-clojure-synteza-proceduralnich-textur-s-vyuzitim-knihovny-clisk-2/ - Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk (dokončení)
http://www.root.cz/clanky/programovaci-jazyk-clojure-synteza-proceduralnich-textur-s-vyuzitim-knihovny-clisk-dokonceni/ - Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure
http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure/ - Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure (2)
http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure-2/ - Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure (3)
http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure-3/ - Programovací jazyk Clojure a práce s Gitem
http://www.root.cz/clanky/programovaci-jazyk-clojure-a-prace-s-gitem/ - Programovací jazyk Clojure a práce s Gitem (2)
http://www.root.cz/clanky/programovaci-jazyk-clojure-a-prace-s-gitem-2/
12. Odkazy na Internetu
- Seriál o Gitu: 1. díl – Git – Historie a principy
http://www.itnetwork.cz/git-tutorial-historie-a-principy - 2. díl – Git – Instalace a vytvoření repositáře
http://www.itnetwork.cz/git-tutorial-instalace-a-stazeni-repositare - 3. díl – Git – Základy
http://www.itnetwork.cz/git-tutorial-zaklady - 4. díl – Git – Zkoumání historie
http://www.itnetwork.cz/git-tutorial-historie - 5. díl – Git – Rozděluj a panuj
http://www.itnetwork.cz/git-tutorial-vetve - 6. díl – Git – práce se vzdáleným repositářem
http://www.itnetwork.cz/git-prace-se-vzdalenym-repositarem - 3.1 Git Branching – What a Branch Is
http://git-scm.com/book/en/v1/Git-Branching-What-a-Branch-Is - Building User Interfaces with Seesaw (slajdy k přednášce)
http://darevay.com/talks/clojurewest2012/#/title-slide - Seesaw na GitHubu
https://github.com/daveray/seesaw - Seesaw API Documentation
http://daveray.github.io/seesaw/ - Seesaw wiki
https://github.com/daveray/seesaw/wiki - seesaw-repl-tutorial.clj
https://gist.github.com/daveray/1441520 - Témata o Seesaw na Google groups
https://groups.google.com/forum/#!forum/seesaw-clj - Threading macro (dokumentace k jazyku Clojure)
https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/-> - Understanding the Clojure → macro
http://blog.fogus.me/2009/09/04/understanding-the-clojure-macro/ - Clisk
https://github.com/mikera/clisk - clojars: net.mikera/clisk
https://clojars.org/net.mikera/clisk - clojure.inspector
http://clojure.github.io/clojure/clojure.inspector-api.html - Clisk: wiki
https://github.com/mikera/clisk/wiki - Dokumentace vygenerovaná pro knihovnu core.matrix
https://cloojure.github.io/doc/core.matrix/index.html - Size and Dimensionality
https://groups.google.com/forum/#!topic/numerical-clojure/zebBCa68eTw/discussion - Towards core.matrix for Clojure?
https://clojurefun.wordpress.com/2013/01/05/towards-core-matrix-for-clojure/ - The Clojure Toolbox
http://www.clojure-toolbox.com/ - Neanderthal
http://neanderthal.uncomplicate.org/ - Hello world project
https://github.com/uncomplicate/neanderthal/blob/master/examples/hello-world/project.clj - vectorz-clj
https://github.com/mikera/vectorz-clj - vectorz – Examples
https://github.com/mikera/vectorz-clj/wiki/Examples - gloss
https://github.com/ztellman/gloss - HTTP client/server for Clojure
http://www.http-kit.org/ - Array Programming
https://en.wikipedia.org/wiki/Array_programming - Discovering Array Languages
http://archive.vector.org.uk/art10008110 - no stinking loops – Kalothi
http://www.nsl.com/ - Vector (obsahuje odkazy na články, knihy a blogy o programovacích jazycích APL, J a K)
http://www.vector.org.uk/ - APL Interpreters
http://www.vector.org.uk/?area=interpreters - APL_(programming_language
http://en.wikipedia.org/wiki/APL_(programming_language - APL FAQ
http://www.faqs.org/faqs/apl-faq/ - APL FAQ (nejnovější verze)
http://home.earthlink.net/~swsirlin/apl.faq.html - A+
http://www.aplusdev.org/ - APLX
http://www.microapl.co.uk/ - FreeAPL
http://www.pyr.fi/apl/index.htm - J: a modern, high-level, general-purpose, high-performance programming language
http://www.jsoftware.com/ - K, Kdb: an APL derivative for Solaris, Linux, Windows
http://www.kx.com - openAPL (GPL)
http://sourceforge.net/projects/openapl - Parrot APL (GPL)
http://www.parrotcode.org/ - Learning J (Roger Stokes)
http://www.jsoftware.com/help/learning/contents.htm - Rosetta Code
http://rosettacode.org/wiki/Main_Page - Why APL
http://www.acm.org/sigapl/whyapl.htm - java.jdbc API Reference
https://clojure.github.io/java.jdbc/ - Hiccup
https://github.com/weavejester/hiccup - Clojure Ring na GitHubu
https://github.com/ring-clojure/ring - A brief overview of the Clojure web stack
https://brehaut.net/blog/2011/ring_introduction - Getting Started with Ring
http://www.learningclojure.com/2013/01/getting-started-with-ring.html - Getting Started with Ring and Compojure – Clojure Web Programming
http://www.myclojureadventure.com/2011/03/getting-started-with-ring-and-compojure.html - Unit Testing in Clojure
http://nakkaya.com/2009/11/18/unit-testing-in-clojure/ - Testing in Clojure (Part-1: Unit testing)
http://blog.knoldus.com/2014/03/22/testing-in-clojure-part-1-unit-testing/ - API for clojure.test – Clojure v1.6 (stable)
https://clojure.github.io/clojure/clojure.test-api.html - Leiningen: úvodní stránka
http://leiningen.org/ - Leiningen: Git repository
https://github.com/technomancy/leiningen - leiningen-win-installer
http://leiningen-win-installer.djpowell.net/ - Clojure 1: Úvod
http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm/ - Clojure 2: Symboly, kolekce atd.
http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-2-cast/ - Clojure 3: Funkcionální programování
http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-3-cast-funkcionalni-programovani/ - Clojure 4: Kolekce, sekvence a lazy sekvence
http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-4-cast-kolekce-sekvence-a-lazy-sekvence/ - Clojure 5: Sekvence, lazy sekvence a paralelní programy
http://www.root.cz/clanky/clojure-a-bezpecne-aplikace-pro-jvm-sekvence-lazy-sekvence-a-paralelni-programy/ - Clojure 6: Podpora pro paralelní programování
http://www.root.cz/clanky/programovaci-jazyk-clojure-6-futures-nejsou-jen-financni-derivaty/ - Clojure 7: Další funkce pro paralelní programování
http://www.root.cz/clanky/programovaci-jazyk-clojure-7-dalsi-podpurne-prostredky-pro-paralelni-programovani/ - Clojure 8: Identity, stavy, neměnné hodnoty a reference
http://www.root.cz/clanky/programovaci-jazyk-clojure-8-identity-stavy-nemenne-hodnoty-a-referencni-typy/ - Clojure 9: Validátory, pozorovatelé a kooperace s Javou
http://www.root.cz/clanky/programovaci-jazyk-clojure-9-validatory-pozorovatele-a-kooperace-mezi-clojure-a-javou/ - Clojure 10: Kooperace mezi Clojure a Javou
http://www.root.cz/clanky/programovaci-jazyk-clojure-10-kooperace-mezi-clojure-a-javou-pokracovani/ - Clojure 11: Generátorová notace seznamu/list comprehension
http://www.root.cz/clanky/programovaci-jazyk-clojure-11-generatorova-notace-seznamu-list-comprehension/ - Clojure 12: Překlad programů z Clojure do bajtkódu JVM I:
http://www.root.cz/clanky/programovaci-jazyk-clojure-12-preklad-programu-z-clojure-do-bajtkodu-jvm/ - Clojure 13: Překlad programů z Clojure do bajtkódu JVM II:
http://www.root.cz/clanky/programovaci-jazyk-clojure-13-preklad-programu-z-clojure-do-bajtkodu-jvm-pokracovani/ - Clojure 14: Základy práce se systémem maker
http://www.root.cz/clanky/programovaci-jazyk-clojure-14-zaklady-prace-se-systemem-maker/ - Clojure 15: Tvorba uživatelských maker
http://www.root.cz/clanky/programovaci-jazyk-clojure-15-tvorba-uzivatelskych-maker/ - Clojure 16: Složitější uživatelská makra
http://www.root.cz/clanky/programovaci-jazyk-clojure-16-slozitejsi-uzivatelska-makra/ - Clojure 17: Využití standardních maker v praxi
http://www.root.cz/clanky/programovaci-jazyk-clojure-17-vyuziti-standardnich-maker-v-praxi/ - Clojure 18: Základní techniky optimalizace aplikací
http://www.root.cz/clanky/programovaci-jazyk-clojure-18-zakladni-techniky-optimalizace-aplikaci/ - Clojure 19: Vývojová prostředí pro Clojure
http://www.root.cz/clanky/programovaci-jazyk-clojure-19-vyvojova-prostredi-pro-clojure/ - Clojure 20: Vývojová prostředí pro Clojure (Vimu s REPL)
http://www.root.cz/clanky/programovaci-jazyk-clojure-20-vyvojova-prostredi-pro-clojure-integrace-vimu-s-repl/ - Clojure 21: ClojureScript aneb překlad Clojure do JS
http://www.root.cz/clanky/programovaci-jazyk-clojure-21-clojurescript-aneb-preklad-clojure-do-javascriptu/