Hlavní navigace

Programovací jazyk Clojure a práce s Gitem (3)

Pavel Tišnovský

V devatenácté části seriálu o programovacím jazyku Clojure se již potřetí (a naposledy) budeme zabývat popisem knihovny nazvané clj-jgit odvozené od javovské knihovny JGit. Ukážeme si především použití funkce odpovídající příkazu git blame, procházení jednotlivými commity a taktéž tvorbu patchů.

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

4. Použití funkcí clj-jgit.porcelain/rev-list a clj-jgit.querying/commit-info pro získání informací o commitech

5. Zdrojový kód demonstračního příkladu git-test13

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

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

12. Odkazy na Internetu

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/tes­trepo.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/clo­jure.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:

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:

  1. Leiningen: nástroj pro správu projektů napsaných v Clojure
    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 (2)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-2/
  3. 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/
  4. 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/
  5. 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/
  6. 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/
  7. Programovací jazyk Clojure a databáze (1.část)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-databaze-1-cast/
  8. Pluginy pro Leiningen
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-pluginy-pro-leiningen/
  9. 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/
  10. 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/
  11. 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/
  12. 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/
  13. 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/
  14. Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure
    http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure/
  15. 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/
  16. 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/
  17. Programovací jazyk Clojure a práce s Gitem
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-prace-s-gitem/
  18. 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

  1. Seriál o Gitu: 1. díl – Git – Historie a principy
    http://www.itnetwork.cz/git-tutorial-historie-a-principy
  2. 2. díl – Git – Instalace a vytvoření repositáře
    http://www.itnetwork.cz/git-tutorial-instalace-a-stazeni-repositare
  3. 3. díl – Git – Základy
    http://www.itnetwork.cz/git-tutorial-zaklady
  4. 4. díl – Git – Zkoumání historie
    http://www.itnetwork.cz/git-tutorial-historie
  5. 5. díl – Git – Rozděluj a panuj
    http://www.itnetwork.cz/git-tutorial-vetve
  6. 6. díl – Git – práce se vzdáleným repositářem
    http://www.itnetwork.cz/git-prace-se-vzdalenym-repositarem
  7. 3.1 Git Branching – What a Branch Is
    http://git-scm.com/book/en/v1/Git-Branching-What-a-Branch-Is
  8. Building User Interfaces with Seesaw (slajdy k přednášce)
    http://darevay.com/talks/clo­jurewest2012/#/title-slide
  9. Seesaw na GitHubu
    https://github.com/daveray/seesaw
  10. Seesaw API Documentation
    http://daveray.github.io/seesaw/
  11. Seesaw wiki
    https://github.com/davera­y/seesaw/wiki
  12. seesaw-repl-tutorial.clj
    https://gist.github.com/da­veray/1441520
  13. Témata o Seesaw na Google groups
    https://groups.google.com/fo­rum/#!forum/seesaw-clj
  14. Threading macro (dokumentace k jazyku Clojure)
    https://clojure.github.io/clo­jure/clojure.core-api.html#clojure.core/->
  15. Understanding the Clojure → macro
    http://blog.fogus.me/2009/09/04/un­derstanding-the-clojure-macro/
  16. Clisk
    https://github.com/mikera/clisk
  17. clojars: net.mikera/clisk
    https://clojars.org/net.mikera/clisk
  18. clojure.inspector
    http://clojure.github.io/clo­jure/clojure.inspector-api.html
  19. Clisk: wiki
    https://github.com/mikera/clisk/wiki
  20. Dokumentace vygenerovaná pro knihovnu core.matrix
    https://cloojure.github.i­o/doc/core.matrix/index.html
  21. Size and Dimensionality
    https://groups.google.com/fo­rum/#!topic/numerical-clojure/zebBCa68eTw/discussion
  22. Towards core.matrix for Clojure?
    https://clojurefun.wordpres­s.com/2013/01/05/towards-core-matrix-for-clojure/
  23. The Clojure Toolbox
    http://www.clojure-toolbox.com/
  24. Neanderthal
    http://neanderthal.uncomplicate.org/
  25. Hello world project
    https://github.com/uncompli­cate/neanderthal/blob/mas­ter/examples/hello-world/project.clj
  26. vectorz-clj
    https://github.com/mikera/vectorz-clj
  27. vectorz – Examples
    https://github.com/mikera/vectorz-clj/wiki/Examples
  28. gloss
    https://github.com/ztellman/gloss
  29. HTTP client/server for Clojure
    http://www.http-kit.org/
  30. Array Programming
    https://en.wikipedia.org/wi­ki/Array_programming
  31. Discovering Array Languages
    http://archive.vector.org­.uk/art10008110
  32. no stinking loops – Kalothi
    http://www.nsl.com/
  33. Vector (obsahuje odkazy na články, knihy a blogy o programovacích jazycích APL, J a K)
    http://www.vector.org.uk/
  34. APL Interpreters
    http://www.vector.org.uk/?a­rea=interpreters
  35. APL_(programming_language
    http://en.wikipedia.org/wi­ki/APL_(programming_langu­age
  36. APL FAQ
    http://www.faqs.org/faqs/apl-faq/
  37. APL FAQ (nejnovější verze)
    http://home.earthlink.net/~swsir­lin/apl.faq.html
  38. A+
    http://www.aplusdev.org/
  39. APLX
    http://www.microapl.co.uk/
  40. FreeAPL
    http://www.pyr.fi/apl/index.htm
  41. J: a modern, high-level, general-purpose, high-performance programming language
    http://www.jsoftware.com/
  42. K, Kdb: an APL derivative for Solaris, Linux, Windows
    http://www.kx.com
  43. openAPL (GPL)
    http://sourceforge.net/pro­jects/openapl
  44. Parrot APL (GPL)
    http://www.parrotcode.org/
  45. Learning J (Roger Stokes)
    http://www.jsoftware.com/hel­p/learning/contents.htm
  46. Rosetta Code
    http://rosettacode.org/wiki/Main_Page
  47. Why APL
    http://www.acm.org/sigapl/whyapl.htm
  48. java.jdbc API Reference
    https://clojure.github.io/java.jdbc/
  49. Hiccup
    https://github.com/weavejester/hiccup
  50. Clojure Ring na GitHubu
    https://github.com/ring-clojure/ring
  51. A brief overview of the Clojure web stack
    https://brehaut.net/blog/2011/rin­g_introduction
  52. Getting Started with Ring
    http://www.learningclojure­.com/2013/01/getting-started-with-ring.html
  53. Getting Started with Ring and Compojure – Clojure Web Programming
    http://www.myclojureadven­ture.com/2011/03/getting-started-with-ring-and-compojure.html
  54. Unit Testing in Clojure
    http://nakkaya.com/2009/11/18/unit-testing-in-clojure/
  55. Testing in Clojure (Part-1: Unit testing)
    http://blog.knoldus.com/2014/03/22/tes­ting-in-clojure-part-1-unit-testing/
  56. API for clojure.test – Clojure v1.6 (stable)
    https://clojure.github.io/clo­jure/clojure.test-api.html
  57. Leiningen: úvodní stránka
    http://leiningen.org/
  58. Leiningen: Git repository
    https://github.com/techno­mancy/leiningen
  59. leiningen-win-installer
    http://leiningen-win-installer.djpowell.net/
  60. Clojure 1: Úvod
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm/
  61. Clojure 2: Symboly, kolekce atd.
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-2-cast/
  62. 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/
  63. 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/
  64. 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/
  65. Clojure 6: Podpora pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-6-futures-nejsou-jen-financni-derivaty/
  66. Clojure 7: Další funkce pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-7-dalsi-podpurne-prostredky-pro-paralelni-programovani/
  67. 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/
  68. 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/
  69. Clojure 10: Kooperace mezi Clojure a Javou
    http://www.root.cz/clanky/programovaci-jazyk-clojure-10-kooperace-mezi-clojure-a-javou-pokracovani/
  70. Clojure 11: Generátorová notace seznamu/list comprehension
    http://www.root.cz/clanky/programovaci-jazyk-clojure-11-generatorova-notace-seznamu-list-comprehension/
  71. 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/
  72. 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/
  73. Clojure 14: Základy práce se systémem maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-14-zaklady-prace-se-systemem-maker/
  74. Clojure 15: Tvorba uživatelských maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-15-tvorba-uzivatelskych-maker/
  75. Clojure 16: Složitější uživatelská makra
    http://www.root.cz/clanky/programovaci-jazyk-clojure-16-slozitejsi-uzivatelska-makra/
  76. Clojure 17: Využití standardních maker v praxi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-17-vyuziti-standardnich-maker-v-praxi/
  77. Clojure 18: Základní techniky optimalizace aplikací
    http://www.root.cz/clanky/programovaci-jazyk-clojure-18-zakladni-techniky-optimalizace-aplikaci/
  78. Clojure 19: Vývojová prostředí pro Clojure
    http://www.root.cz/clanky/programovaci-jazyk-clojure-19-vyvojova-prostredi-pro-clojure/
  79. 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/
  80. Clojure 21: ClojureScript aneb překlad Clojure do JS
    http://www.root.cz/clanky/programovaci-jazyk-clojure-21-clojurescript-aneb-preklad-clojure-do-javascriptu/
Našli jste v článku chybu?
Lupa.cz: Kdo pochopí vtip, může jít do ČT vyvíjet weby

Kdo pochopí vtip, může jít do ČT vyvíjet weby

120na80.cz: 5 nejčastějších mýtů o kondomech

5 nejčastějších mýtů o kondomech

Vitalia.cz: „Připluly“ z Německa a možná obsahují jed

„Připluly“ z Německa a možná obsahují jed

Lupa.cz: Teletext je „internetem hipsterů“

Teletext je „internetem hipsterů“

Vitalia.cz: Cena stejného léku se liší i o tisíce

Cena stejného léku se liší i o tisíce

Podnikatel.cz: Udávání a účtenková loterie, hloupá komedie

Udávání a účtenková loterie, hloupá komedie

Podnikatel.cz: Podnikatelům dorazí varování od BSA

Podnikatelům dorazí varování od BSA

Root.cz: Certifikáty zadarmo jsou horší než za peníze?

Certifikáty zadarmo jsou horší než za peníze?

120na80.cz: Pánové, pečujte o svoje přirození a prostatu

Pánové, pečujte o svoje přirození a prostatu

Lupa.cz: Proč firmy málo chrání data? Chovají se logicky

Proč firmy málo chrání data? Chovají se logicky

Vitalia.cz: Láska na vozíku: Přitažliví jsme pro tzv. pečovatelky

Láska na vozíku: Přitažliví jsme pro tzv. pečovatelky

Podnikatel.cz: Chtějte údaje k dani z nemovitostí do mailu

Chtějte údaje k dani z nemovitostí do mailu

Podnikatel.cz: Udávání kvůli EET začalo

Udávání kvůli EET začalo

Měšec.cz: Jak vymáhat výživné zadarmo?

Jak vymáhat výživné zadarmo?

Podnikatel.cz: Snížení DPH na 15 % se netýká všech

Snížení DPH na 15 % se netýká všech

Vitalia.cz: 4 příčiny zápachu z úst a jak s ním zatočit

4 příčiny zápachu z úst a jak s ním zatočit

120na80.cz: Co všechno ovlivňuje ženskou plodnost?

Co všechno ovlivňuje ženskou plodnost?

Vitalia.cz: Jsou čajové sáčky toxické?

Jsou čajové sáčky toxické?

Podnikatel.cz: Chaos u EET pokračuje. Jsou tu další návrhy

Chaos u EET pokračuje. Jsou tu další návrhy

Měšec.cz: U levneELEKTRO.cz už reklamaci nevyřídíte

U levneELEKTRO.cz už reklamaci nevyřídíte