clj-jgit1.0.0Clojure wrapper for JGit dependencies
| (this space intentionally left almost blank) | |||||||||
(ns clj-jgit.querying
(:require [clojure.string :as string]
[clj-jgit.internal :refer :all]
[clj-jgit.porcelain :as porcelain]
[clj-jgit.util :as util])
(:import (java.io ByteArrayOutputStream)
(java.util HashMap Date)
(org.eclipse.jgit.api Git)
(org.eclipse.jgit.diff DiffFormatter DiffEntry RawTextComparator)
(org.eclipse.jgit.internal.storage.file RefDirectory$LooseRef)
(org.eclipse.jgit.lib ObjectIdRef ObjectId AnyObjectId Ref)
(org.eclipse.jgit.revwalk RevWalk RevCommit RevCommitList)
(org.eclipse.jgit.util.io DisabledOutputStream))) | ||||||||||
(declare change-kind create-tree-walk diff-formatter-for-changes
byte-array-diff-formatter-for-changes changed-files-in-first-commit
parse-diff-entry mark-all-heads-as-start-for!) | ||||||||||
Find RevCommit instance in RevWalk by commit-ish. Returns nil if the commit is not found. | (defn find-rev-commit
[^Git repo ^RevWalk rev-walk commit-ish]
(let [object (resolve-object commit-ish repo)]
(if (nil? object)
nil
(bound-commit repo rev-walk object)))) | |||||||||
List of branches for a repo in pairs of [branch-ref branch-tip-commit] | (defn branch-list-with-heads
([^Git repo]
(branch-list-with-heads repo (new-rev-walk repo)))
([^Git repo ^RevWalk rev-walk]
(letfn [(zip-commits [^ObjectIdRef branch-ref]
[branch-ref (bound-commit repo rev-walk (.getObjectId branch-ref))])]
(let [branches (porcelain/git-branch-list repo :jgit? true)]
(doall (map zip-commits branches)))))) | |||||||||
Checks if commit is merged into branch | (defn commit-in-branch? [^Git repo ^RevWalk rev-walk ^RevCommit branch-tip-commit ^ObjectId bound-commit] (.isMergedInto rev-walk bound-commit branch-tip-commit)) | |||||||||
List of branches in which specific commit is present | (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))) | |||||||||
List of files changed between two RevCommit objects | (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))) | |||||||||
List of files changed in RevCommit object | (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))) | |||||||||
Patch with diff of all changes in RevCommit object | (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)))) | |||||||||
Find changes for commit-ish. Returns nil if the commit is not found. | (defn changes-for
[^Git repo commit-ish]
(let [rev-commit (->> commit-ish
(find-rev-commit repo (new-rev-walk repo)))]
(if (nil? rev-commit)
nil
(changed-files repo rev-commit)))) | |||||||||
List of all revision in repo | (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)))) | |||||||||
(defn commit-info-without-branches
[^Git repo ^RevWalk rev-walk ^RevCommit rev-commit]
(let [ident (.getAuthorIdent rev-commit)
time (-> (.getCommitTime rev-commit) (* 1000) Date.)
message (-> (.getFullMessage rev-commit) str string/trim)]
{:id (.getName rev-commit)
:repo repo
:author (.getName ident)
:email (.getEmailAddress ident)
:time time
:message message
:changed_files (changed-files repo rev-commit)
:merge (> (.getParentCount rev-commit) 1)
:raw rev-commit ; can't retain commit because then RevCommit can't be garbage collected
})) | ||||||||||
(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) []))}))) | ||||||||||
(defn- mark-all-heads-as-start-for!
[^Git repo ^RevWalk rev-walk]
(doseq [[objId ref] (.getAllRefsByPeeledObjectId (.getRepository repo))]
(.markStart rev-walk (.lookupCommit rev-walk objId)))) | ||||||||||
(defn- change-kind
[^DiffEntry entry]
(let [change (.. entry getChangeType name)]
(cond
(= change "ADD") :add
(= change "MODIFY") :edit
(= change "DELETE") :delete
(= change "COPY") :copy))) | ||||||||||
(defn- diff-formatter-for-changes
[^Git repo]
(doto
(DiffFormatter. DisabledOutputStream/INSTANCE)
(.setRepository (.getRepository repo))
(.setDiffComparator RawTextComparator/DEFAULT)
(.setDetectRenames false))) | ||||||||||
(defn- byte-array-diff-formatter-for-changes
[^Git repo ^ByteArrayOutputStream out]
(doto
(new DiffFormatter out)
(.setRepository (.getRepository repo))
(.setDiffComparator RawTextComparator/DEFAULT))) | ||||||||||
(defn- changed-files-in-first-commit
[^Git repo ^RevCommit rev-commit]
(let [tree-walk (new-tree-walk repo rev-commit)
changes (transient [])]
(while (.next tree-walk)
(conj! changes [(util/normalize-path (.getPathString tree-walk)) :add]))
(persistent! changes))) | ||||||||||
(defn- parse-diff-entry
[^DiffEntry entry]
(let [old-path (util/normalize-path (.getOldPath entry))
new-path (util/normalize-path (.getNewPath entry))
change-kind (change-kind entry)]
(cond
(= old-path new-path) [new-path change-kind]
(= old-path "dev/null") [new-path change-kind]
(= new-path "dev/null") [old-path change-kind]
:else [old-path change-kind new-path]))) | ||||||||||
(defn rev-list-for
([^Git repo ^RevWalk rev-walk ^RefDirectory$LooseRef object]
(.reset rev-walk)
(.markStart rev-walk (.lookupCommit ^RevWalk rev-walk ^AnyObjectId (.getObjectId object)))
(.toArray
(doto (RevCommitList.)
(.source rev-walk)
(.fillTo Integer/MAX_VALUE))))) | ||||||||||
(defn- add-branch-to-map
[^Git repo ^RevWalk rev-walk branch ^HashMap m]
(let [^"[Ljava.lang.Object;" revs (rev-list-for repo rev-walk branch)]
(dotimes [i (alength revs)]
(let [c (aget revs i)]
(.put m c (conj (or (.get m c) []) branch)))))) | ||||||||||
Build commit map, which is a map of commit IDs to the list of branches they are in. | (defn build-commit-map
([repo]
(build-commit-map repo (new-rev-walk repo)))
([^Git repo ^RevWalk rev-walk]
(let [^HashMap m (HashMap.)]
(loop [[branch & branches] (vals (get-refs repo "refs/heads/"))]
(add-branch-to-map repo rev-walk branch m)
(if branches
(recur branches)
m))))) | |||||||||
(ns clj-jgit.util
(:require [clojure.java.io :as io])
(:import (java.io IOException)
(org.eclipse.jgit.internal.storage.file RefDirectory$LooseRef)
(org.eclipse.jgit.lib PersonIdent))) | ||||||||||
Delete file | (defn recursive-delete-file
[f & [silently]]
(let [file (io/file f)]
(if (.exists file)
(do (doseq [child (reverse (file-seq file))]
(io/delete-file child silently))
true)
(or silently
(throw (IOException. (str "Couldn't find " f))))))) | |||||||||
If given | (defn seq?! [obj] (if (sequential? obj) obj (vector obj))) | |||||||||
Repeatedly executes function Example that executes
| (defn doseq-cmd-fn!
[cmd-instance f param-seq]
(doseq [p (seq?! param-seq)]
(f cmd-instance p))
cmd-instance) | |||||||||
Given a URI to a Git resource, derive the name (for use in cloning to a directory) | (defn name-from-uri [uri] (second (re-find #"/([^/]*)\.git$" uri))) | |||||||||
Special | (defmacro when-present
[obj & body]
`(when (not (or (nil? ~obj) (empty? ~obj)))
~@body)) | |||||||||
(defmethod print-method RefDirectory$LooseRef
[^RefDirectory$LooseRef o w]
(print-simple
(str "#<" (.replaceFirst (str (.getClass o)) "class " "") ", "
"Name: " (.getName o) ", "
"ObjectId: " (.getName (.getObjectId o)) ">") w)) | ||||||||||
Removes a leading slash from a path | (defn normalize-path
[path]
(if (= path "/")
"/"
(if (= (first path) \/)
(subs path 1)
path))) | |||||||||
Convert given JGit | (defn person-ident
[^PersonIdent person]
(when person
{:name (.getName person)
:email (.getEmailAddress person)
:date (.getWhen person)
:timezone (.getTimeZone person)})) | |||||||||
(ns clj-jgit.internal
(:import
(clojure.lang Sequential)
(org.eclipse.jgit.api Git)
(org.eclipse.jgit.lib ObjectId ObjectIdRef Repository RefDatabase)
(org.eclipse.jgit.transport RefSpec)
(org.eclipse.jgit.revwalk RevWalk RevCommit)
(org.eclipse.jgit.treewalk TreeWalk))) | ||||||||||
(defn ref-spec ^RefSpec [str] (RefSpec. str)) | ||||||||||
Creates a new RevWalk instance (mutable), it's a good idea to use | (defn new-rev-walk ^RevWalk [^Git repo] (RevWalk. (.getRepository repo))) | |||||||||
If given | (defn close-rev-walk
[rev-walk]
(when (instance? RevWalk rev-walk)
(.close ^RevWalk rev-walk))) | |||||||||
Create new recursive TreeWalk instance (mutable) | (defn new-tree-walk
^TreeWalk [^Git repo ^RevCommit rev-commit]
(doto
(TreeWalk. (.getRepository repo))
(.addTree (.getTree rev-commit))
(.setRecursive true))) | |||||||||
Find a RevCommit object in a RevWalk and bound to it. | (defn bound-commit ^RevCommit [^Git repo ^RevWalk rev-walk ^ObjectId rev-commit] (.parseCommit rev-walk rev-commit)) | |||||||||
Protocol for things that resolve ObjectId's. | (defprotocol Resolvable
(resolve-object ^ObjectId [commit-ish repo]
"Find ObjectId instance for any Git name: commit-ish, tree-ish or blob. Accepts ObjectId instances and just passes them through.")) | |||||||||
(extend-type String
Resolvable
(resolve-object
^ObjectId [^String commit-ish ^Git repo]
(.resolve (.getRepository repo) commit-ish))) | ||||||||||
(extend-type ObjectId
Resolvable
(resolve-object
^ObjectId [commit-ish ^Git repo]
commit-ish)) | ||||||||||
(extend-type ObjectIdRef
Resolvable
(resolve-object
^ObjectId [commit-ish ^Git repo]
(.getObjectId commit-ish))) | ||||||||||
(extend-type Sequential
Resolvable
(resolve-object [refs ^Git repo]
(map #(resolve-object % repo) refs))) | ||||||||||
(extend-type Git
Resolvable
(resolve-object
^ObjectId [^Git repo commit-ish]
"For compatibility with previous implementation of resolve-object, which would take repo as a first argument."
(resolve-object commit-ish repo))) | ||||||||||
(defn ref-database ^RefDatabase [^Git repo] (.getRefDatabase ^Repository (.getRepository repo))) | ||||||||||
(defn get-refs [^Git repo ^String prefix] (.getRefs (ref-database repo) prefix)) | ||||||||||
Return HEAD RevCommit instance | (defn get-head-commit [^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))))) | |||||||||
(ns clj-jgit.porcelain
(:require [clojure.java.io :as io]
[clojure.string :as str]
[clj-jgit.internal :refer :all]
[clj-jgit.util :as util :refer [seq?! doseq-cmd-fn!]])
(:import (java.io File FileNotFoundException IOException)
(java.nio.charset StandardCharsets)
(java.security GeneralSecurityException)
(java.util List)
(org.eclipse.jgit.api Git InitCommand StatusCommand AddCommand PullCommand MergeCommand LogCommand
LsRemoteCommand Status ResetCommand$ResetType FetchCommand PushCommand CloneCommand
RmCommand ResetCommand SubmoduleUpdateCommand SubmoduleSyncCommand SubmoduleInitCommand
StashCreateCommand StashApplyCommand BlameCommand ListBranchCommand$ListMode
CreateBranchCommand$SetupUpstreamMode CheckoutCommand$Stage
CommitCommand MergeCommand$FastForwardMode RevertCommand CreateBranchCommand
CheckoutCommand TransportConfigCallback TransportCommand ListBranchCommand TagCommand
CleanCommand DeleteTagCommand)
(org.eclipse.jgit.blame BlameResult)
(org.eclipse.jgit.diff DiffAlgorithm$SupportedAlgorithm)
(org.eclipse.jgit.lib RepositoryBuilder AnyObjectId PersonIdent BranchConfig$BranchRebaseMode ObjectId
SubmoduleConfig$FetchRecurseSubmodulesMode Ref Repository StoredConfig GpgConfig)
(org.eclipse.jgit.merge MergeStrategy)
(org.eclipse.jgit.notes Note)
(org.eclipse.jgit.revwalk RevCommit)
(org.eclipse.jgit.submodule SubmoduleWalk)
(org.eclipse.jgit.transport.sshd SshdSessionFactory DefaultProxyDataFactory JGitKeyCache KeyPasswordProvider)
(org.eclipse.jgit.transport FetchResult UsernamePasswordCredentialsProvider URIish RefSpec RefLeaseSpec TagOpt
RemoteConfig CredentialsProvider CredentialItem$CharArrayType
CredentialItem$YesNoType SshTransport)
(org.eclipse.jgit.treewalk TreeWalk))) | ||||||||||
Discover a Git repository in a path. | (defmulti discover-repo type) | |||||||||
(defmethod discover-repo File [^File file] (discover-repo (.getPath file))) | ||||||||||
(defmethod discover-repo String
[^String path]
(let [with-git (io/as-file (str path "/.git"))
bare (io/as-file (str path "/refs"))]
(cond
(.endsWith path ".git") (io/as-file path)
(.exists with-git) with-git
(.exists bare) (io/as-file path)))) | ||||||||||
(def ^:dynamic *cred-provider* nil) (def ^:dynamic *ssh-key-dir* (str (System/getProperty "user.home") (File/separator) ".ssh")) (def ^:dynamic *ssh-key-name* ["id_rsa", "id_dsa", "id_ecdsa", "id_ed25519"]) (def ^:dynamic *ssh-key-passphrase* ) (def ^:dynamic *known-hosts-file* ["known_hosts" "known_hosts2"]) | ||||||||||
Basic | (def ^CredentialsProvider trust-any-provider
(proxy [CredentialsProvider] []
(supports [items]
(if (some? (->> items
(filter #(when (instance? CredentialItem$YesNoType %) true))
first))
true
false))
(get [uri items]
(let [^CredentialItem$YesNoType yesno-item (->> items
(filter #(when (instance? CredentialItem$YesNoType %) true))
first)]
(if (some? yesno-item)
(do (.setValue yesno-item true) true)
false)))
(isInteractive []
false))) | |||||||||
Create a new
| (defn user-pass-provider
^UsernamePasswordCredentialsProvider [login password & {:keys [trust-all?] :or {trust-all? false}}]
(if trust-all?
(proxy [UsernamePasswordCredentialsProvider] [^String login ^String password]
(supports [items]
(if (some? (->> items
(filter #(when (instance? CredentialItem$YesNoType %) true))
first))
true
false))
(get [uri items]
(let [^CredentialItem$YesNoType yesno-item (->> items
(filter #(when (instance? CredentialItem$YesNoType %) true))
first)]
(if (some? yesno-item)
(do (.setValue yesno-item true) true)
false)))
(isInteractive []
false))
(UsernamePasswordCredentialsProvider. ^String login ^String password))) | |||||||||
Create a new | (defn key-pass-provider
^KeyPasswordProvider [key-pw]
(reify KeyPasswordProvider
(getPassphrase [_ uri attempt]
(char-array key-pw))
(setAttempts [_ attempts]
true)
(getAttempts [_]
1)
(keyLoaded [_ uri attempt ex]
(when ex (throw (GeneralSecurityException. "Key passphrase mismatch")))
; never retry on error
false))) | |||||||||
Handle SSH transport configuration. | (def ^SshdSessionFactory sshd-factory
(proxy [SshdSessionFactory] [(JGitKeyCache.) (DefaultProxyDataFactory.)]
(getSshDirectory []
(let [ssh-dir (io/as-file *ssh-key-dir*)]
(if (.isAbsolute ssh-dir)
ssh-dir
(.getAbsoluteFile ssh-dir))))
(createKeyPasswordProvider [^CredentialsProvider provider]
(key-pass-provider *ssh-key-passphrase*))
(getDefaultKnownHostsFiles [^File ssh-dir]
(let [ssh-dir (.toPath ssh-dir)
kh-files (->> (seq?! *known-hosts-file*)
(map #(.resolve ssh-dir ^String %)))]
(if (seq kh-files)
kh-files
(throw (IOException. (str "Couldn't find any known hosts file(s), tried: " *known-hosts-file*
" in " *ssh-key-dir*))))))
(getDefaultIdentities [^File ssh-dir]
(let [key-files (->> (seq?! *ssh-key-name*)
(map #(io/file ssh-dir %))
(filter #(.exists ^File %))
(map #(.toPath ^File %)))]
(if (seq key-files)
key-files
(throw (IOException. (str "Couldn't find any key file(s), tried: " *ssh-key-name*
" in " *ssh-key-dir*)))))))) | |||||||||
(-> (Runtime/getRuntime) (.addShutdownHook (Thread. #(.close sshd-factory)))) | ||||||||||
Default | (def ^TransportConfigCallback transport-callback
(reify ^TransportConfigCallback TransportConfigCallback
(configure [_ transport]
(when (instance? SshTransport transport)
(.setSshSessionFactory ^SshTransport transport sshd-factory))))) | |||||||||
(def ^:dynamic *transport-callback* transport-callback) | ||||||||||
Use given
| (defmacro with-credentials
[config & body]
`(let [user-name# (get ~config :login "")
user-pw# (get ~config :pw "")
key-dir# (get ~config :key-dir *ssh-key-dir*)
trust-all?# (get ~config :trust-all? false)
cred-provider# (get ~config :credentials *cred-provider*)]
(binding [*ssh-key-dir* key-dir#
*transport-callback* nil
*cred-provider* (if (some? cred-provider#)
cred-provider#
(user-pass-provider user-name# user-pw# :trust-all? trust-all?#))]
~@body))) | |||||||||
Use given
| (defmacro with-identity
[config & body]
`(let [key-name# (get ~config :name *ssh-key-name*)
key-pw# (get ~config :pw *ssh-key-passphrase*)
key-dir# (get ~config :key-dir *ssh-key-dir*)
trust-all?# (get ~config :trust-all? false)
cred-provider# (get ~config :cred-provider *cred-provider*)
transport-cb# (get ~config :transport-callback *transport-callback*)]
(binding [*ssh-key-name* key-name#
*ssh-key-dir* key-dir#
*ssh-key-passphrase* key-pw#
*cred-provider* (if (and trust-all?# (nil? cred-provider#))
trust-any-provider
cred-provider#)
*transport-callback* transport-cb#]
~@body))) | |||||||||
Given a | (defn load-repo
^Git [path]
(if-let [git-dir (discover-repo path)]
(-> (RepositoryBuilder.)
(.setGitDir git-dir)
(.readEnvironment)
(.findGitDir)
(.build)
(Git.))
(throw
(FileNotFoundException. (str "The Git repository at '" path "' could not be located."))))) | |||||||||
Load Git repository at | (defmacro with-repo
[path & body]
`(let [~'repo (load-repo ~path)
~'rev-walk (new-rev-walk ~'repo)]
(try ~@body
(finally (close-rev-walk ~'rev-walk))))) | |||||||||
Release all resources held by JGit process. Not mandatory, but prevents leaks when, for example, running in a webapp. | (defn git-shutdown [] (Git/shutdown)) | |||||||||
Add file contents to the index.
| (defn git-add
[^Git repo file-patterns & {:keys [update? working-tree-iterator]
:or {update? false
working-tree-iterator nil}}]
(as-> (.add repo) cmd
^AddCommand (doseq-cmd-fn! cmd #(.addFilepattern ^AddCommand %1 %2) file-patterns)
(.setUpdate cmd update?)
(if (some? working-tree-iterator)
(.setWorkingTreeIterator cmd working-tree-iterator) cmd)
(.call cmd))) | |||||||||
(defonce branch-list-modes
{:all ListBranchCommand$ListMode/ALL
:remote ListBranchCommand$ListMode/REMOTE}) | ||||||||||
(defn strip-refs-head [s] (str/replace s #"^refs/heads/" )) | ||||||||||
Get a list of branch names for given
| (defn git-branch-list
[^Git repo & {:keys [jgit? list-mode]
:or {jgit? false
list-mode :local}}]
(as-> (.branchList repo) ^ListBranchCommand cmd
(if-not (= list-mode :local)
(.setListMode cmd (list-mode branch-list-modes)) cmd)
(.call cmd)
(if jgit?
cmd
(map #(-> (.getName ^Ref %) strip-refs-head) cmd)))) | |||||||||
The current branch name of given
| (defn git-branch-current
[^Git repo & {:keys [jgit?] :or {jgit? false}}]
(as-> (.getRepository repo) ^Repository r
(.getFullBranch r)
(if jgit?
r
(strip-refs-head r)))) | |||||||||
Is the given | (defn git-branch-attached? [^Git repo] (some? (re-find #"^refs/heads/" (git-branch-current repo :jgit? true)))) | |||||||||
(defonce branch-upstream-modes
{:no-track CreateBranchCommand$SetupUpstreamMode/NOTRACK
:set-upstream CreateBranchCommand$SetupUpstreamMode/SET_UPSTREAM
:track CreateBranchCommand$SetupUpstreamMode/TRACK}) | ||||||||||
Create a new local branch for given
| (defn git-branch-create
[^Git repo branch-name & {:keys [force? ^String start-point upstream-mode]
:or {force? false
start-point nil
upstream-mode nil}}]
(as-> (.branchCreate repo) ^CreateBranchCommand cmd
(.setName cmd branch-name)
(.setForce cmd force?)
(if (some? start-point)
(.setStartPoint cmd start-point) cmd)
(if (some? upstream-mode)
(.setUpstreamMode cmd (upstream-mode branch-upstream-modes)) cmd)
(.call cmd))) | |||||||||
Delete one or several branches.
| (defn git-branch-delete
[^Git repo branch-names & {:keys [force?]
:or {force? false}}]
(-> (.branchDelete repo)
(.setBranchNames (into-array String (seq?! branch-names)))
(.setForce force?)
(.call))) | |||||||||
(defonce checkout-stage-modes
{:base CheckoutCommand$Stage/BASE
:ours CheckoutCommand$Stage/OURS
:theirs CheckoutCommand$Stage/THEIRS}) | ||||||||||
Checkout a branch to the working tree.
| (defn git-checkout
[^Git repo & {:keys [all-paths? create-branch? force? monitor name orphan? paths stage ^String start-point upstream-mode]
:or {all-paths? false
create-branch? false
force? false
monitor nil
name nil
orphan? false
paths nil
stage nil
start-point nil
upstream-mode nil}}]
(as-> (.checkout repo) ^CheckoutCommand cmd
(if (some? name)
(.setName cmd name) cmd)
(if (some? paths)
(.addPaths cmd (seq?! paths)) cmd)
(.setAllPaths cmd all-paths?)
(.setCreateBranch cmd create-branch?)
(.setForceRefUpdate cmd force?)
(if (some? monitor)
(.setProgressMonitor cmd monitor) cmd)
(.setOrphan cmd orphan?)
(if (some? stage)
(.setStage cmd (stage checkout-stage-modes)) cmd)
(if (some? start-point)
(.setStartPoint cmd start-point) cmd)
(if (some? upstream-mode)
(.setUpstreamMode cmd (upstream-mode branch-upstream-modes)) cmd)
(.call cmd))) | |||||||||
(declare git-cherry-pick) | ||||||||||
(defonce tag-opt
{:auto-follow TagOpt/AUTO_FOLLOW
:fetch-tags TagOpt/FETCH_TAGS
:no-tags TagOpt/NO_TAGS}) | ||||||||||
(defn clone-cmd ^CloneCommand [uri]
(-> (Git/cloneRepository)
(.setURI uri)
^TransportCommand (.setCredentialsProvider *cred-provider*)
(.setTransportConfigCallback *transport-callback*))) | ||||||||||
Clone a repository into a new working directory from given
| (defn git-clone
[uri & {:keys [bare? branch callback clone-all? clone-branches clone-subs? dir git-dir no-checkout? mirror? monitor
remote tags]
:or {bare? false
branch "master"
clone-all? true
clone-branches nil
clone-subs? false
callback nil
dir nil
git-dir nil
no-checkout? false
mirror? false
monitor nil
remote nil
tags :auto-follow}}]
(as-> (clone-cmd uri) cmd
(.setBare cmd bare?)
(.setBranch cmd branch)
(.setCloneAllBranches cmd clone-all?)
(if (some? clone-branches)
(.setBranchesToClone cmd (seq?! clone-branches)) cmd)
(.setCloneSubmodules cmd clone-subs?)
(if (some? callback)
(.setCallback cmd callback) cmd)
(if (some? dir)
(.setDirectory cmd (io/as-file dir)) cmd)
(if (some? git-dir)
(.setGitDir cmd (io/as-file git-dir)) cmd)
(.setNoCheckout cmd no-checkout?)
(.setMirror cmd mirror?)
(if (some? monitor)
(.setProgressMonitor cmd monitor) cmd)
(if (some? remote)
(.setRemote cmd remote) cmd)
(.setTagOption cmd (tags tag-opt))
(.call cmd))) | |||||||||
(defn ^CredentialsProvider signing-pass-provider [^String key-pw]
"Return a new `CredentialsProvider` instance for given `key-pw`."
(proxy [CredentialsProvider] []
(supports [items]
(if (some? (->> items
(filter #(when (instance? CredentialItem$CharArrayType %) true))
first))
true
false))
(get [uri items]
(let [^CredentialItem$CharArrayType pw-item (->> items
(filter #(when (instance? CredentialItem$CharArrayType %) true))
first)]
(if (some? pw-item)
(do (.setValue pw-item (char-array key-pw)) true)
false)))
(isInteractive []
false))) | ||||||||||
(declare git-config-load) | ||||||||||
(defn gpg-config [^Git repo]
"Return commit signing config for given `repo`."
(let [config (GpgConfig. (git-config-load repo))]
{:sign? (.isSignCommits config)
:signing-key (.getSigningKey config)
:key-format (.getKeyFormat config)})) | ||||||||||
Record changes to given
| (defn git-commit
[^Git repo message & {:keys [all? allow-empty? amend? author committer insert-change-id? no-verify? only
reflog-comment sign? signing-key signing-pw signing-provider]
:or {all? false
allow-empty? true
amend? false
author nil
committer nil
insert-change-id? false
no-verify? false
only nil
reflog-comment ""
sign? nil
signing-key nil
signing-pw nil
signing-provider nil}}]
(let [sign? (if (some? sign?)
sign?
(:sign? (gpg-config repo)))]
(as-> (.commit repo) ^CommitCommand cmd
(.setAll cmd all?)
(.setAllowEmpty cmd allow-empty?)
(.setAmend cmd amend?)
(if (some? author)
(.setAuthor cmd (:name author) (:email author)) cmd)
(if (some? committer)
(.setCommitter cmd (:name committer) (:email committer)) cmd)
(.setInsertChangeId cmd insert-change-id?)
(.setMessage cmd message)
(.setNoVerify cmd no-verify?)
(if (some? only)
(doseq-cmd-fn! cmd #(.setOnly ^CommitCommand %1 %2) only) cmd)
(if (or (nil? reflog-comment)
(not (clojure.string/blank? reflog-comment)))
(.setReflogComment cmd reflog-comment) cmd)
(.setSign cmd sign?)
(if (and sign? (some? signing-key))
(.setSigningKey cmd signing-key) cmd)
(if (and sign? (some? signing-pw))
; see https://bugs.eclipse.org/bugs/show_bug.cgi?id=553116
(do (.setCredentialsProvider cmd (signing-pass-provider signing-pw)) cmd)
cmd)
(if (and sign? (some? signing-provider))
; see https://bugs.eclipse.org/bugs/show_bug.cgi?id=553116
(do (.setCredentialsProvider cmd signing-provider) cmd)
cmd)
(.call cmd)))) | |||||||||
(defn ^StoredConfig git-config-load [^Git repo] "Return mutable JGit StoredConfig object for given `repo`." (-> repo .getRepository .getConfig)) | ||||||||||
(defn git-config-save [^StoredConfig git-config] "Save given `git-config` to repo's `.git/config` file." (.save git-config)) | ||||||||||
(defn parse-git-config-key [^String config-key]
"Parse given Git `config-key` and return a vector of format [section subsection name]."
(let [keys (str/split config-key #"\.")]
(case (count keys)
2 [(first keys) nil (last keys)]
3 keys
(throw (Exception. (str "Invalid config-key format: " config-key)))))) | ||||||||||
(defn git-config-get [^StoredConfig git-config ^String config-key]
"Return Git config value as string for given Git `config-key`. Note that config keys that are not explicitly set in
global/current repo config will always return nil and not the default value."
(->> (parse-git-config-key config-key)
(apply #(.getString git-config % %2 %3)))) | ||||||||||
(defn ^StoredConfig git-config-set [^StoredConfig git-config ^String config-key config-value]
"Set given `config-value` for given Git `config-key`, always returns the passed JGit StoredConfig object."
(->> (parse-git-config-key config-key)
(apply #(.setString git-config % %2 %3 (str config-value))))
git-config) | ||||||||||
(defn fetch-cmd ^FetchCommand [^Git repo]
(-> (.fetch repo)
^TransportCommand (.setCredentialsProvider *cred-provider*)
(.setTransportConfigCallback *transport-callback*))) | ||||||||||
(defonce fetch-recurse-submodules-modes
{:no SubmoduleConfig$FetchRecurseSubmodulesMode/NO
:on-demand SubmoduleConfig$FetchRecurseSubmodulesMode/ON_DEMAND
:yes SubmoduleConfig$FetchRecurseSubmodulesMode/YES}) | ||||||||||
(defonce transport-tag-opts
{:auto-follow TagOpt/AUTO_FOLLOW
:fetch-tags TagOpt/FETCH_TAGS
:no-tags TagOpt/NO_TAGS}) | ||||||||||
Fetch changes from upstream
| (defn git-fetch
^FetchResult [^Git repo & {:keys [callback check-fetched? dry-run? force? monitor recurse-subs ref-specs remote
rm-deleted-refs? tag-opt thin?]
:or {callback nil
check-fetched? false
dry-run? false
force? false
monitor nil
recurse-subs nil
ref-specs nil
remote nil
rm-deleted-refs? nil
tag-opt nil
thin? true}}]
(as-> (fetch-cmd repo) cmd
(if (some? callback)
(.setCallback cmd callback) cmd)
(.setCheckFetchedObjects cmd check-fetched?)
(.setDryRun cmd dry-run?)
(.setForceUpdate cmd force?)
(if (some? monitor)
(.setProgressMonitor cmd monitor) cmd)
(if (some? recurse-subs)
(.setRecurseSubmodules cmd (recurse-subs fetch-recurse-submodules-modes)) cmd)
(if (some? ref-specs)
(.setRefSpecs cmd ^"[Ljava.lang.String;" (into-array String (seq?! ref-specs))) cmd)
(if (some? remote)
(.setRemote cmd remote) cmd)
(if (some? rm-deleted-refs?)
(.setRemoveDeletedRefs cmd rm-deleted-refs?) cmd)
(if (some? tag-opt)
(.setTagOpt cmd (tag-opt transport-tag-opts)) cmd)
(.setThin cmd thin?)
(.call cmd))) | |||||||||
Fetch all refs from upstream | (defn git-fetch-all ([^Git repo] (git-fetch-all repo "origin")) ([^Git repo remote] (git-fetch repo :remote remote :ref-specs ["+refs/tags/*:refs/tags/*" "+refs/heads/*:refs/heads/*"]))) | |||||||||
Initialize and return a new Git repository, if no options are passed a non-bare repo is created at user.dir
| (defn git-init
[& {:keys [bare? dir git-dir]
:or {bare? false
dir "."
git-dir nil}}]
(as-> (InitCommand.) cmd
(.setBare cmd bare?)
(.setDirectory cmd (io/as-file dir))
(if (some? git-dir)
(.setGitDir cmd (io/as-file git-dir)) cmd)
(.call cmd))) | |||||||||
Add a new remote to given | (defn git-remote-add
[^Git repo name ^String uri]
(doto (.remoteAdd repo)
(.setName name)
(.setUri (URIish. uri))
(.call))) | |||||||||
Remove a remote with given name from | (defn git-remote-remove
[^Git repo name]
(doto (.remoteRemove repo)
(.setName name)
(.call))) | |||||||||
Returns a seq of vectors with format [name [^URIish ..]] representing all configured remotes for given | (defn git-remote-list
[^Git repo]
(->> (.remoteList repo)
.call
(map (fn [^RemoteConfig r]
[(.getName r) (.getURIs r)])))) | |||||||||
Returns a seq of maps representing the commit history for current branch of given
| (defn git-log
[^Git repo & {:keys [all? jgit? max-count paths range rev-filter since skip until]
:or {all? false
jgit? false
max-count nil
paths nil
range nil
rev-filter nil
since nil
skip nil
until nil}}]
(as-> (.log repo) ^LogCommand cmd
(if (some? until)
(.add cmd (resolve-object until repo)) cmd)
(if (some? since)
(.not cmd (resolve-object since repo)) cmd)
(if (some? range)
(.addRange cmd (resolve-object (:since range) repo) (resolve-object (:until range) repo)) cmd)
(if all?
(.all cmd) cmd)
(if (some? max-count)
(.setMaxCount cmd max-count) cmd)
(if (some? paths)
(doseq-cmd-fn! cmd #(.addPath ^LogCommand %1 %2) paths) cmd)
(if (some? rev-filter)
(.setRevFilter cmd rev-filter) cmd)
(if (some? skip)
(.setSkip cmd skip) cmd)
(.call cmd)
(if jgit?
cmd
(map #(do {:id (.getId ^RevCommit %)
:msg (.getShortMessage ^RevCommit %)
:author (util/person-ident (.getAuthorIdent ^RevCommit %))
:committer (util/person-ident (.getCommitterIdent ^RevCommit %))
}) cmd)))) | |||||||||
(defonce merge-ff-modes
{:ff MergeCommand$FastForwardMode/FF
:ff-only MergeCommand$FastForwardMode/FF_ONLY
:no-ff MergeCommand$FastForwardMode/NO_FF}) | ||||||||||
(defonce merge-strategies
{:ours MergeStrategy/OURS
:recursive MergeStrategy/RECURSIVE
:resolve MergeStrategy/RESOLVE
:simple-two-way MergeStrategy/SIMPLE_TWO_WAY_IN_CORE
:theirs MergeStrategy/THEIRS}) | ||||||||||
Merge given
| (defn git-merge
[^Git repo refs & {:keys [commit? ff-mode message monitor squash? strategy]
:or {commit? true
ff-mode nil
message nil
monitor nil
squash? false
strategy :recursive}}]
(as-> (.merge repo) ^MergeCommand cmd
(doseq-cmd-fn! cmd #(.include ^MergeCommand %1 ^AnyObjectId %2) (resolve-object refs repo))
(.setCommit cmd commit?)
(if (some? ff-mode)
(.setFastForward cmd (ff-mode merge-ff-modes)) cmd)
(if (some? message)
(.setMessage cmd message) cmd)
(if (some? monitor)
(.setProgressMonitor cmd monitor) cmd)
(.setSquash cmd squash?)
(.setStrategy cmd (strategy merge-strategies))
(.call cmd))) | |||||||||
(defonce branch-rebase-modes
{:interactive BranchConfig$BranchRebaseMode/INTERACTIVE
:none BranchConfig$BranchRebaseMode/NONE
:preserve BranchConfig$BranchRebaseMode/PRESERVE
:rebase BranchConfig$BranchRebaseMode/REBASE}) | ||||||||||
Fetch from and integrate with another repository or a local branch.
| (defn git-pull
[^Git repo & {:keys [ff-mode monitor rebase-mode recurse-subs remote remote-branch strategy tag-opt]
:or {ff-mode nil
monitor nil
rebase-mode nil
recurse-subs nil
remote nil
remote-branch nil
strategy :recursive
tag-opt nil}}]
(as-> (.pull repo) cmd
(if (some? ff-mode)
(.setFastForward cmd (ff-mode merge-ff-modes)) cmd)
(if (some? monitor)
(.setProgressMonitor cmd monitor) cmd)
(if (some? rebase-mode)
(.setRebase cmd ^BranchConfig$BranchRebaseMode (rebase-mode branch-rebase-modes)) cmd)
(if (some? recurse-subs)
(.setRecurseSubmodules cmd (recurse-subs fetch-recurse-submodules-modes)) cmd)
(if (some? remote)
(.setRemote cmd remote) cmd)
(if (some? remote-branch)
(.setRemoteBranchName cmd remote-branch) cmd)
(.setStrategy cmd (strategy merge-strategies))
(if (some? tag-opt)
(.setTagOpt cmd (tag-opt transport-tag-opts)) cmd)
^TransportCommand (.setCredentialsProvider cmd *cred-provider*)
(.setTransportConfigCallback cmd *transport-callback*)
(.call cmd))) | |||||||||
Update remote refs along with associated objects for given
| (defn git-push
[^Git repo & {:keys [all? atomic? dry-run? force? monitor options output-stream receive-pack ref-lease-specs
ref-specs refs remote tags? thin?]
:or {all? false
atomic? false
dry-run? false
force? false
monitor nil
options nil
output-stream nil
receive-pack nil
ref-lease-specs nil
ref-specs nil
refs nil
remote nil
tags? false
thin? false}}]
(as-> (.push repo) ^PushCommand cmd
(if all?
(.setPushAll cmd) cmd)
(.setAtomic cmd atomic?)
(.setDryRun cmd dry-run?)
(.setForce cmd force?)
(if (some? monitor)
(.setProgressMonitor cmd monitor) cmd)
(if (some? options)
(.setPushOptions cmd (seq?! options)) cmd)
(if (some? output-stream)
(.setOutputStream cmd output-stream) cmd)
(if (some? ref-lease-specs)
(.setRefLeaseSpecs cmd (->> ref-lease-specs
seq?!
(map #(RefLeaseSpec. (:ref %) (:expected %)))
^List (apply list)))
cmd)
(if (some? ref-specs)
(.setRefSpecs cmd ^List (map #(RefSpec. %) (seq?! ref-specs))) cmd)
(if (some? refs)
(doseq-cmd-fn! cmd #(.add ^PushCommand %1 ^String %2) refs) cmd)
(if (some? remote)
(.setRemote cmd remote) cmd)
(if tags? (.setPushTags cmd) cmd)
(.setThin cmd thin?)
^TransportCommand (.setCredentialsProvider cmd *cred-provider*)
(.setTransportConfigCallback cmd *transport-callback*)
(.call cmd))) | |||||||||
(defn git-rebase []) | ||||||||||
Revert given commits, which can either be a single resolvable ("HEAD", "a6efda", etc) or a coll of resolvables. Returns a map of format:
| (defn git-revert
[^Git repo commits & {:keys [monitor our-commit-name strategy]
:or {monitor nil
our-commit-name nil
strategy :recursive}}]
(let [revert-cmd (.revert repo)]
(as-> revert-cmd cmd
^RevertCommand (doseq-cmd-fn! cmd #(.include ^RevertCommand %1 ^AnyObjectId %2) (resolve-object commits repo))
(if (some? our-commit-name)
(.setOurCommitName cmd our-commit-name) cmd)
(if (some? monitor)
(.setProgressMonitor cmd monitor) cmd)
(.setStrategy cmd (strategy merge-strategies))
(.call cmd)
{:reverted (.getRevertedRefs revert-cmd)
:unmerged (.getUnmergedPaths revert-cmd)
:error (.getFailingResult revert-cmd)}))) | |||||||||
Remove files from the working tree and from the index.
| (defn git-rm
[^Git repo file-patterns & {:keys [cached?]
:or {cached? false}}]
(-> (.rm repo)
^RmCommand (doseq-cmd-fn! #(.addFilepattern ^RmCommand %1 %2) file-patterns)
(.setCached cached?)
(.call))) | |||||||||
Show the working tree status. Returns a map with keys corresponding to the passed
| (defn git-status
[^Git repo & {:keys [ignore-subs? jgit? monitor paths status status-fns working-tree-iterator]
:or {ignore-subs? nil
jgit? false
monitor nil
paths nil
status :all
status-fns nil
working-tree-iterator nil}}]
(let [status-instance (as-> (.status repo) ^StatusCommand cmd
(if (some? ignore-subs?)
(.setIgnoreSubmodules cmd ignore-subs?) cmd)
(if (some? monitor)
(.setProgressMonitor cmd monitor) cmd)
(if (some? paths)
(doseq-cmd-fn! cmd #(.addPath ^StatusCommand %1 %2) paths) cmd)
(if (some? working-tree-iterator)
(.setWorkingTreeIt cmd working-tree-iterator) cmd)
(.call cmd))
def-status-fns {:added #(into #{} (.getAdded ^Status %))
:changed #(into #{} (.getChanged ^Status %))
:missing #(into #{} (.getMissing ^Status %))
:modified #(into #{} (.getModified ^Status %))
:removed #(into #{} (.getRemoved ^Status %))
:untracked #(into #{} (.getUntracked ^Status %))}
selected-def-fns (if (some? status)
(if (= status :all)
def-status-fns
(select-keys def-status-fns (seq?! status)))
{})
output-fns (if (some? status-fns)
(merge selected-def-fns status-fns)
selected-def-fns)]
(if jgit?
status-instance
(apply merge (for [[k f] output-fns]
{k (f status-instance)}))))) | |||||||||
Creates an annotated tag with the provided name and (optional) message.
| (defn git-tag-create
[^Git repo tag-name & {:keys [annotated? force? message signed? tagger]
:or {annotated? true
force? false
message nil
signed? false
tagger nil}}]
(as-> (.tag repo) ^TagCommand cmd
(.setAnnotated cmd annotated?)
(.setForceUpdate cmd force?)
(if (some? message)
(.setMessage cmd message) cmd)
(.setName cmd tag-name)
(.setSigned cmd signed?)
(if (some? tagger)
(.setTagger cmd (PersonIdent. ^String (:name tagger) ^String (:email tagger))) cmd)
(.call cmd))) | |||||||||
Deletes tag(s) with the provided name(s). | (defn git-tag-delete
[^Git repo & tag-names]
(-> ^DeleteTagCommand (.tagDelete repo)
(.setTags (into-array String tag-names))
(.call))) | |||||||||
Lists the tags in a | (defn git-tag-list
[^Git repo]
(->> (.tagList repo)
(.call)
(map #(->> ^Ref % .getName (re-matches #"refs/tags/(.*)") second)))) | |||||||||
(defn ls-remote-cmd ^LsRemoteCommand [^Git repo]
(-> (.lsRemote repo)
^TransportCommand (.setCredentialsProvider *cred-provider*)
(.setTransportConfigCallback *transport-callback*))) | ||||||||||
List references in a remote
| (defn git-ls-remote
[^Git repo & {:keys [heads? remote tags? upload-pack]
:or {heads? false
remote nil
tags? false
upload-pack nil}}]
(as-> (ls-remote-cmd repo) cmd
(if (some? remote)
(.setRemote cmd remote) cmd)
(.setHeads cmd heads?)
(.setTags cmd tags?)
(if (some? upload-pack)
(.setUploadPack cmd upload-pack) cmd)
(.call cmd))) | |||||||||
(defonce reset-modes
{:hard ResetCommand$ResetType/HARD
:keep ResetCommand$ResetType/KEEP
:merge ResetCommand$ResetType/MERGE
:mixed ResetCommand$ResetType/MIXED
:soft ResetCommand$ResetType/SOFT}) | ||||||||||
Reset current HEAD to the specified
| (defn git-reset
[^Git repo & {:keys [mode monitor paths ref ref-log?]
:or {mode :mixed
monitor nil
paths nil
ref nil
ref-log? true}}]
(as-> (.reset repo) ^ResetCommand cmd
(if (nil? paths)
(.setMode cmd (mode reset-modes)) cmd)
(if (some? monitor)
(.setProgressMonitor cmd monitor) cmd)
(if (some? paths)
(doseq-cmd-fn! cmd #(.addPath ^ResetCommand %1 %2) paths) cmd)
(if (some? ref)
(.setRef cmd ref) cmd)
(.disableRefLog cmd (not ref-log?))
(.call cmd))) | |||||||||
(defn submodule-walk
([^Git repo]
(->> (submodule-walk (.getRepository repo) 0)
(flatten)
(filter identity)
(map #(Git/wrap %))))
([^Git repo level]
(when (< level 3)
(let [gen (SubmoduleWalk/forIndex repo)
repos (transient [])]
(while (.next gen)
(when-let [subm (.getRepository gen)]
(conj! repos subm)
(conj! repos (submodule-walk subm (inc level)))))
(->> (persistent! repos)
(flatten)))))) | ||||||||||
(defn git-submodule-fetch
[^Git repo]
(doseq [subm (submodule-walk repo)]
(git-fetch-all subm))) | ||||||||||
(defn submodule-update-cmd ^SubmoduleUpdateCommand [^Git repo]
(-> (.submoduleUpdate repo)
^TransportCommand (.setCredentialsProvider *cred-provider*)
(.setTransportConfigCallback *transport-callback*))) | ||||||||||
Update all submodules of given
| (defn git-submodule-update
[^Git repo & {:keys [callback fetch? fetch-callback monitor paths strategy]
:or {callback nil
fetch? true
fetch-callback nil
monitor nil
paths nil
strategy nil}}]
(as-> (submodule-update-cmd repo) ^SubmoduleUpdateCommand cmd
(if (some? callback)
(.setCallback cmd callback) cmd)
(.setFetch cmd fetch?)
(if (some? fetch-callback)
(.setFetchCallback cmd fetch-callback) cmd)
(if (some? monitor)
(.setProgressMonitor cmd monitor) cmd)
(if (some? paths)
(doseq-cmd-fn! cmd #(.addPath ^SubmoduleUpdateCommand %1 %2) paths) cmd)
(.setStrategy cmd (strategy merge-strategies))
(.call cmd))) | |||||||||
Set the remote URL in a submodule's repository to the current value in the .gitmodules file of given
| (defn git-submodule-sync
[^Git repo & {:keys [paths] :or {paths nil}}]
(as-> (.submoduleSync repo) ^SubmoduleSyncCommand cmd
(if (some? paths)
(doseq-cmd-fn! cmd #(.addPath ^SubmoduleSyncCommand %1 %2) paths) cmd)
(.call cmd))) | |||||||||
Copy the
| (defn git-submodule-init
[^Git repo & {:keys [paths] :or {paths nil}}]
(as-> (.submoduleInit repo) ^SubmoduleInitCommand cmd
(if (some? paths)
(doseq-cmd-fn! cmd #(.addPath ^SubmoduleInitCommand %1 %2) paths) cmd)
(.call cmd))) | |||||||||
Clone the submodule from given
| (defn git-submodule-add
[^Git repo uri path & {:keys [name monitor]
:or {name nil
monitor nil}}]
(as-> (.submoduleAdd repo) cmd
(.setURI cmd uri)
(.setPath cmd path)
(if (some? monitor)
(.setName cmd name) cmd)
(if (some? monitor)
(.setProgressMonitor cmd monitor) cmd)
(.call cmd))) | |||||||||
Stash changes in the working directory and index in a commit.
| (defn git-stash-create
[^Git repo & {:keys [index-msg person ref untracked? working-dir-msg]
:or {index-msg nil
person nil
ref nil
untracked? false
working-dir-msg nil}}]
(as-> (.stashCreate repo) cmd
(if (some? index-msg)
(.setIndexMessage cmd index-msg) cmd)
(if (some? person)
(.setPerson cmd (PersonIdent. ^String (:name person) ^String (:email person))) cmd)
(if (some? ref)
(.setRef cmd ref) cmd)
(.setIncludeUntracked cmd untracked?)
(if (some? working-dir-msg)
(.setWorkingDirectoryMessage cmd working-dir-msg) cmd)
(.call cmd))) | |||||||||
Behaves like git stash apply --index, i.e. it tries to recover the stashed index state in addition to the working tree state.
| (defn git-stash-apply
[^Git repo & {:keys [ignore-repo-state? index? untracked? stash-ref strategy]
:or {ignore-repo-state? false
index? true
stash-ref nil
strategy :recursive
untracked? true}}]
(as-> (.stashApply repo) cmd
(.ignoreRepositoryState cmd ignore-repo-state?)
(doto cmd
(.setApplyIndex index?)
(.setApplyUntracked untracked?))
(.setStashRef cmd stash-ref)
(.setStrategy cmd (strategy merge-strategies))
(.call cmd))) | |||||||||
List the stashed commits for given | (defn git-stash-list
[^Git repo]
(-> repo
.stashList
.call)) | |||||||||
Delete a stashed commit reference. Currently only supported on a traditional file repository using one-file-per-ref reflogs.
| (defn git-stash-drop
[^Git repo & {:keys [all? stash-id]
:or {all? false
stash-id 0}}]
(-> (.stashDrop repo)
(.setAll all?)
(.setStashRef stash-id)
(.call))) | |||||||||
Apply and then drop the latest stash commit. | (defn git-stash-pop [^Git repo] (git-stash-apply repo) (git-stash-drop repo)) | |||||||||
Remove untracked files from the working tree.
| (defn git-clean
[^Git repo & {:keys [dirs? dry-run? force? ignore? paths]
:or {dirs? false
dry-run? false
force? false
ignore? true
paths nil}}]
(as-> (.clean repo) ^CleanCommand cmd
(.setCleanDirectories cmd dirs?)
(.setDryRun cmd dry-run?)
(.setForce cmd force?)
(.setIgnore cmd ignore?)
(if (some? paths)
(.setPaths cmd (set (seq?! paths))) cmd)
(.call cmd))) | |||||||||
(defn blame-result
[^BlameResult blame]
(.computeAll blame)
(letfn [(blame-line [num]
(when-let [commit (try
(.getSourceCommit blame num)
(catch ArrayIndexOutOfBoundsException _ nil))]
{:author (util/person-ident (.getSourceAuthor blame num))
:commit commit
:committer (util/person-ident (.getSourceCommitter blame num))
:line (.getSourceLine blame num)
:line-contents (-> blame .getResultContents (.getString num))
:source-path (.getSourcePath blame num)}))
(blame-seq [num]
(when-let [cur (blame-line num)]
(cons cur
(lazy-seq (blame-seq (inc num))))))]
(blame-seq 0))) | ||||||||||
(defonce diff-supported-algorithms
{:histogram DiffAlgorithm$SupportedAlgorithm/HISTOGRAM
:myers DiffAlgorithm$SupportedAlgorithm/MYERS}) | ||||||||||
Show blame result for given
| (defn git-blame
[^Git repo path & {:keys [diff-algo follow-mv? jgit? reverse start text-comparator]
:or {diff-algo nil
follow-mv? false
jgit? false
reverse nil
start nil
text-comparator nil}}]
(as-> (.blame repo) cmd
(.setFilePath cmd path)
(if (some? diff-algo)
(.setDiffAlgorithm cmd (diff-algo diff-supported-algorithms)) cmd)
(.setFollowFileRenames cmd follow-mv?)
(if (some? reverse)
(.reverse cmd
^AnyObjectId (resolve-object (:start reverse) repo)
(->> (:end reverse) seq?! ^ObjectId (map #(resolve-object % repo))))
cmd)
(if (some? start)
(.setStartCommit cmd (resolve-object start repo)) cmd)
(if (some? text-comparator)
(.setTextComparator cmd text-comparator) cmd)
(.call cmd)
(if jgit?
cmd
(blame-result cmd)))) | |||||||||
(defn get-blob-id ^ObjectId [^Git repo ^RevCommit commit ^String path]
(let [tree-walk (TreeWalk/forPath (.getRepository repo) path (.getTree commit))]
(when tree-walk
(.getObjectId tree-walk 0)))) | ||||||||||
(defn get-blob
[repo commit path]
(when-let [blob-id (get-blob-id repo commit path)]
(.getName blob-id))) | ||||||||||
Return list of note objects for given
| (defn git-notes
([^Git repo & {:keys [^String ref]
:or {ref "commits"}}]
(-> (.notesList repo)
(.setNotesRef (str "refs/notes/" ref))
.call))) | |||||||||
Return note string for given
| (defn git-notes-show
[^Git repo & {:keys [^String ref]
:or {ref "commits"}}]
(let [repository (-> repo .getRepository)]
(->> (git-notes repo :ref ref)
(map #(String. (.getBytes (.open repository (.getData ^Note %))) (StandardCharsets/UTF_8)))
(map #(str/split % #"\n"))
first))) | |||||||||
Add a note for a given
| (defn git-notes-add
[^Git repo ^String message & {:keys [^RevCommit commit ^String ref]
:or {commit nil
ref "commits"}}]
(-> (.notesAdd repo)
(.setMessage message)
(.setNotesRef (str "refs/notes/" ref))
(.setObjectId (or commit (get-head-commit repo)))
.call)) | |||||||||
Append a note for a given
| (defn git-notes-append
"Append a note for a given `:commit` and `:ref`, given message is concatenated with a `\n` char.
Options:
:commit The RevCommit object the note should be added to. When nil the current \"HEAD\"
is used. (default: nil)
:ref The name of the ref in \"refs/notes/\" to read notes from. Note, the default
value of JGit's Constants.R_NOTES_COMMITS will be used if nil is passed.
(default: \"commits\")
"
[^Git repo ^String message & {:keys [^RevCommit commit ^String ref]
:or {commit nil
ref "commits"}}]
(as-> (git-notes-show repo :ref ref) $
(conj $ message)
(str/join "\n" $)
(git-notes-add repo $ :ref ref :commit commit))) | |||||||||