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))) | |||||||||