Skip to content

Commit

Permalink
Add git notes functionalities: add, append, list, show. Closes #52
Browse files Browse the repository at this point in the history
  • Loading branch information
Dario Civallero authored and edannenberg committed Nov 8, 2017
1 parent 06d53be commit 6537302
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 33 deletions.
76 changes: 50 additions & 26 deletions README.md
@@ -1,4 +1,4 @@
# clj-jgit
# clj-jgit

Clojure wrapper for using the JGit library to manipulate Git repositories in a "pure Java" fashion.

Expand All @@ -12,7 +12,7 @@ Last stable version is available on [Clojars](http://clojars.org/clj-jgit).

## Quickstart Tutorial ##

This brief tutorial will show you how to:
This brief tutorial will show you how to:

1. Clone a remote repository
2. Create a local branch for your changes
Expand Down Expand Up @@ -77,14 +77,14 @@ This brief tutorial will show you how to:

;; Blame
(first (git-blame my-repo "README.md"))
;=> {:author {:name "Ilya Sabanin",
:email "ilya@sabanin.com",
:timezone #<ZoneInfo ...>},
:commit #<RevCommit commit fabdf5cf4abb72231461177238349c21e23fa46a 1352414190 -----p>,
:committer {:name "Ilya Sabanin",
:email "ilya@wildbit.com",
:timezone #<ZoneInfo ...>},
:line 66,
;=> {:author {:name "Ilya Sabanin",
:email "ilya@sabanin.com",
:timezone #<ZoneInfo ...>},
:commit #<RevCommit commit fabdf5cf4abb72231461177238349c21e23fa46a 1352414190 -----p>,
:committer {:name "Ilya Sabanin",
:email "ilya@wildbit.com",
:timezone #<ZoneInfo ...>},
:line 66,
:source-path "README.md"}

;; Tagging
Expand Down Expand Up @@ -160,6 +160,30 @@ This uses internal JGit API, so it may require some additional knowledge of JGit
(git-log repo))
```

```clj
;; Notes Add
(git-notes-add my-repo "my note")
;=> (#object[org.eclipse.jgit.notes.Note 0x1237ddce "Note[3c14d1f8917761rc71db03170866055ef88b585f -> 10500018fca9b3425b50de67a7258a12cba0c076]"])
```

```clj
;; Notes Append
(git-notes-add my-repo "my appended note")
;=> (#object[org.eclipse.jgit.notes.Note 0x1237ddce "Note[3c14d1f8917761rc71db03170866055ef88b585f -> 10500018fca9b3425b50de67a7258a12cba0c076]"])
```

```clj
;; Notes List
(git-notes my-repo)
;=> (#object[org.eclipse.jgit.notes.Note 0x1237ddce "Note[3c14d1f8917761rc71db03170866055ef88b585f -> 10500018fca9b3425b50de67a7258a12cba0c076]"])
```

```clj
;; Notes Show
(git-notes-show my-repo)
;=> ["my note" "my appended note"]
```

```clj
(ns clj-jgit.querying)

Expand All @@ -170,7 +194,7 @@ This uses internal JGit API, so it may require some additional knowledge of JGit

;; List of pairs of branch refs and RevCommits associated with them
(branch-list-with-heads repo rev-walk)
;=> ([#<org.eclipse.jgit.storage.file.RefDirectory$LooseUnpeeled, Name: refs/heads/master, ObjectId: 3b9c98bc151bb4920f9799cfa6c32c536ed64348>
;=> ([#<org.eclipse.jgit.storage.file.RefDirectory$LooseUnpeeled, Name: refs/heads/master, ObjectId: 3b9c98bc151bb4920f9799cfa6c32c536ed64348>
#<RevCommit commit 3b9c98bc151bb4920f9799cfa6c32c536ed64348 1339922123 -----p>])

;; Find an ObjectId instance for a repo and given commit-ish, tree-ish or blob
Expand All @@ -191,26 +215,26 @@ This uses internal JGit API, so it may require some additional knowledge of JGit
(commit-info repo (find-rev-commit repo rev-walk "38dd57264cf"))

; Returns
{:repo #<Git org.eclipse.jgit.api.Git@21d306ef>,
:changed_files [[".gitignore" :add]
["README.md" :add]
["project.clj" :add]
["src/clj_jgit/core.clj" :add]
["src/clj_jgit/util/print.clj" :add]
["test/clj_jgit/test/core.clj" :add]],
:raw #<RevCommit commit 38dd57264cf5c05fb77211c8347d1f16e4474623 1304645414 ----sp>,
:author "Daniel Gregoire",
:email "daniel.l.gregoire@gmail.com",
:message "Initial commit",
:branches ("refs/heads/master"),
:merge false,
:time #<Date Fri May 06 09:30:14 KRAT 2011>,
{:repo #<Git org.eclipse.jgit.api.Git@21d306ef>,
:changed_files [[".gitignore" :add]
["README.md" :add]
["project.clj" :add]
["src/clj_jgit/core.clj" :add]
["src/clj_jgit/util/print.clj" :add]
["test/clj_jgit/test/core.clj" :add]],
:raw #<RevCommit commit 38dd57264cf5c05fb77211c8347d1f16e4474623 1304645414 ----sp>,
:author "Daniel Gregoire",
:email "daniel.l.gregoire@gmail.com",
:message "Initial commit",
:branches ("refs/heads/master"),
:merge false,
:time #<Date Fri May 06 09:30:14 KRAT 2011>,
:id "38dd57264cf5c05fb77211c8347d1f16e4474623"}

;; You can also combine this with Porcelain API, to get a list of all commits in a repo with detailed information
(with-repo "/path/to/repo.git"
(map #(commit-info repo %) (git-log repo)))

;; Branches lookup is an expensive operation, especially for repos with many branches.
;; commit-info spends most of it time trying to detect list of branches commit belongs to.

Expand Down
9 changes: 9 additions & 0 deletions src/clj_jgit/internal.clj
Expand Up @@ -71,3 +71,12 @@
(defn get-refs
[^Git repo ^String prefix]
(.getRefs (ref-database repo) prefix))

(defn get-head-commit "Return HEAD RevCommit instance" [^Git repo]
(let [rev-walk (new-rev-walk repo)]
(try
(as-> repo $
(.getRepository $)
(.resolve $ "HEAD")
(bound-commit repo rev-walk $))
(finally (close-rev-walk rev-walk)))))
57 changes: 56 additions & 1 deletion src/clj_jgit/porcelain.clj
Expand Up @@ -19,7 +19,9 @@
[java.util List]
[org.eclipse.jgit.api.errors JGitInternalException]
[org.eclipse.jgit.transport UsernamePasswordCredentialsProvider URIish]
[org.eclipse.jgit.treewalk TreeWalk]))
[org.eclipse.jgit.treewalk TreeWalk]
[java.nio.charset StandardCharsets]
[org.eclipse.jgit.revwalk RevWalk RevCommit]))

(declare log-builder)

Expand Down Expand Up @@ -795,3 +797,56 @@
[repo commit path]
(when-let [blob-id (get-blob-id repo commit path)]
(.getName blob-id)))

(defn git-notes
"Return the list of notes object for a given ref (defaults to 'commits')."
([^Git repo ^String ref]
(-> repo
.notesList
(.setNotesRef (str "refs/notes/" ref))
.call))
([^Git repo]
(git-notes repo "commits")))

(defn git-notes-show
"Return notes strings for the given ref (defaults to 'commits')."
([^Git repo ^String ref]
(let [repository (-> repo .getRepository)]
(->> (git-notes repo ref)
(map #(String. (.getBytes (.open repository (.getData %))) (StandardCharsets/UTF_8)))
(map #(str/split % #"\n"))
(first))))
([^Git repo]
(git-notes-show repo "commits")))

(defn git-notes-add
"Add note for a given commit (defaults to HEAD) with the given ref (defaults to 'commits')
It overwrites existing note for the commit"
([^Git repo ^String message ^String ref ^RevCommit commit]
(-> repo
.notesAdd
(.setMessage message)
(.setNotesRef (str "refs/notes/" ref))
(.setObjectId commit)
.call))
([^Git repo ^String message ^String ref]
(->> repo
get-head-commit
(git-notes-add repo message ref)))
([^Git repo ^String message]
(git-notes-add repo message "commits")))

(defn git-notes-append
"Append note for a given commit (defaults to HEAD) with the given ref (defaults to 'commits')
It concatenates notes with \n char"
([^Git repo ^String message ^String ref ^RevCommit commit]
(as-> (git-notes-show repo ref) $
(conj $ message)
(str/join "\n" $)
(git-notes-add repo $ ref commit)))
([^Git repo ^String message ^String ref]
(->> repo
get-head-commit
(git-notes-append repo message ref)))
([^Git repo ^String message]
(git-notes-append repo message "commits")))
16 changes: 10 additions & 6 deletions test/clj_jgit/test/internal.clj
@@ -1,11 +1,11 @@
(ns clj-jgit.test.internal
(:use
(:use
[clj-jgit.test.helpers]
[clj-jgit.porcelain]
[clj-jgit.querying]
[clj-jgit.internal]
[clojure.test])
(:import
(:import
[org.eclipse.jgit.lib ObjectId]
[org.eclipse.jgit.revwalk RevWalk RevCommit]
[org.eclipse.jgit.treewalk TreeWalk]))
Expand All @@ -24,15 +24,19 @@
(testing "bound-commit"
(read-only-repo
(are
[commit-ish] (instance? RevCommit
(bound-commit repo
[commit-ish] (instance? RevCommit
(bound-commit repo
(new-rev-walk repo)
(resolve-object commit-ish repo)))
"38dd57264cf5c05fb77211c8347d1f16e4474623" ; initial commit
"master" ; branch name
"master^" ; commit before master's head
)))

(testing "new-tree-walk"
(testing "new-tree-walk"
(read-only-repo
(is (instance? TreeWalk (new-tree-walk repo (find-rev-commit repo (new-rev-walk repo) "master")))))))
(is (instance? TreeWalk (new-tree-walk repo (find-rev-commit repo (new-rev-walk repo) "master"))))))

(testing "get-head-commit"
(read-only-repo
(is (instance? RevCommit (get-head-commit repo))))))
30 changes: 30 additions & 0 deletions test/clj_jgit/test/porcelain.clj
Expand Up @@ -96,3 +96,33 @@
(git-remote-add repo-b "origin" (.getAbsolutePath bare-dir))
(is (instance? PullResult (git-pull repo-b)))
(is (= commit-msg (-> repo-b git-log first .getFullMessage))))))

(deftest test-git-notes-functions
(with-tmp-repo "target/tmp"
(git-commit repo "init commit")

(testing "No notes for a fresh new repository on ref commits"
(is (= 0 (count (git-notes repo)))))
(testing "No notes for not existing ref"
(is (= 0 (count (git-notes repo "not-valid-ref")))))
(testing "Add and retrieve a note on default ref"
(git-notes-add repo "note test 1")
(is (= 1 (count (git-notes repo)))))
(testing "Add and retrieve a note on 'custom' ref"
(git-notes-add repo "note test 2" "custom")
(is (= 1 (count (git-notes repo "custom")))))
(testing "Read notes on default ref"
(is (= 1 (count (git-notes-show repo))))
(is (= "note test 1" (first (git-notes-show repo))))
(git-notes-add repo "note test 1\nnote test 1.1")
(is (= 2 (count (git-notes-show repo))))
(is (= "note test 1" (first (git-notes-show repo))))
(is (= "note test 1.1" (second (git-notes-show repo)))))
(testing "Read notes on 'custom' ref"
(is (= 1 (count (git-notes-show repo "custom"))))
(is (= "note test 2" (first (git-notes-show repo "custom")))))
(testing "Append note on default ref"
(is (= 2 (count (git-notes-show repo))))
(git-notes-append repo "append test")
(is (= 3 (count (git-notes-show repo))))
(is (= "append test" (last (git-notes-show repo)))))))

0 comments on commit 6537302

Please sign in to comment.