my xfce4 dotfiles
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

529 lines
25 KiB

3 years ago
  1. # gitstatus
  2. **gitstatus** is a 10x faster alternative to `git status` and `git describe`. Its primary use
  3. case is to enable fast git prompt in interactive shells.
  4. Heavy lifting is done by **gitstatusd** -- a custom binary written in C++. It comes with Zsh and
  5. Bash bindings for integration with shell.
  6. ## Table of Contents
  7. 1. [Using from Zsh](#using-from-zsh)
  8. 1. [Using from Bash](#using-from-bash)
  9. 2. [Using from other shells](#using-from-other-shells)
  10. 1. [How it works](#how-it-works)
  11. 1. [Benchmarks](#benchmarks)
  12. 1. [Why fast](#why-fast)
  13. 1. [Requirements](#requirements)
  14. 1. [Compiling](#compiling)
  15. 1. [License](#license)
  16. ## Using from Zsh
  17. The easiest way to take advantage of gitstatus from Zsh is to use a theme that's already integrated
  18. with it. For example, [Powerlevel10k](https://github.com/romkatv/powerlevel10k) is a flexible and
  19. fast theme with first-class gitstatus integration.
  20. ![Powerlevel10k Zsh Theme](
  21. https://raw.githubusercontent.com/romkatv/powerlevel10k-media/master/prompt-styles-high-contrast.png)
  22. For those who wish to use gitstatus without a theme, there is
  23. [gitstatus.prompt.zsh](gitstatus.prompt.zsh). Install it as follows:
  24. ```zsh
  25. git clone --depth=1 https://github.com/romkatv/gitstatus.git ~/gitstatus
  26. echo 'source ~/gitstatus/gitstatus.prompt.zsh' >>! ~/.zshrc
  27. ```
  28. Users in mainland China can use the official mirror on gitee.com for faster download.<br>
  29. 中国大陆用户可以使用 gitee.com 上的官方镜像加速下载.
  30. ```zsh
  31. git clone --depth=1 https://gitee.com/romkatv/gitstatus.git ~/gitstatus
  32. echo 'source ~/gitstatus/gitstatus.prompt.zsh' >>! ~/.zshrc
  33. ```
  34. Alternatively, if you have Homebrew installed:
  35. ```zsh
  36. brew install romkatv/gitstatus/gitstatus
  37. echo "source $(brew --prefix)/opt/gitstatus/gitstatus.prompt.zsh" >>! ~/.zshrc
  38. ```
  39. (If you choose this option, replace `~/gitstatus` with `$(brew --prefix)/opt/gitstatus/gitstatus`
  40. in all code snippets below.)
  41. _Make sure to disable your current theme if you have one._
  42. This will give you a basic yet functional prompt with git status in it. It's
  43. [over 10x faster](#benchmarks) than any alternative that can give you comparable prompt. In order
  44. to customize it, set `PROMPT` and/or `RPROMPT` at the end of `~/.zshrc` after sourcing
  45. `gitstatus.prompt.zsh`. Insert `${GITSTATUS_PROMPT}` where you want git status to go. For example:
  46. ```zsh
  47. source ~/gitstatus/gitstatus.prompt.zsh
  48. PROMPT='%~%# ' # left prompt: directory followed by %/# (normal/root)
  49. RPROMPT='$GITSTATUS_PROMPT' # right prompt: git status
  50. ```
  51. The expansion of `${GITSTATUS_PROMPT}` can contain the following bits:
  52. | segment | meaning |
  53. |-------------|-------------------------------------------------------|
  54. | `master` | current branch |
  55. | `#v1` | HEAD is tagged with `v1`; not shown when on a branch |
  56. | `@5fc6fca4` | current commit; not shown when on a branch or tag |
  57. | `⇣1` | local branch is behind the remote by 1 commit |
  58. | `⇡2` | local branch is ahead of the remote by 2 commits |
  59. | `⇠3` | local branch is behind the push remote by 3 commits |
  60. | `⇢4` | local branch is ahead of the push remote by 4 commits |
  61. | `*5` | there are 5 stashes |
  62. | `merge` | merge is in progress (could be some other action) |
  63. | `~6` | there are 6 merge conflicts |
  64. | `+7` | there are 7 staged changes |
  65. | `!8` | there are 8 unstaged changes |
  66. | `?9` | there are 9 untracked files |
  67. `$GITSTATUS_PROMPT_LEN` tells you how long `$GITSTATUS_PROMPT` is when printed to the console.
  68. [gitstatus.prompt.zsh](gitstatus.prompt.zsh) has an example of using it to truncate the current
  69. directory.
  70. If you'd like to change the format of git status, or want to have greater control over the
  71. process of assembling `PROMPT`, you can copy and modify parts of
  72. [gitstatus.prompt.zsh](gitstatus.prompt.zsh) instead of sourcing the script. Your `~/.zshrc`
  73. might look something like this:
  74. ```zsh
  75. source ~/gitstatus/gitstatus.plugin.zsh
  76. function my_set_prompt() {
  77. PROMPT='%~%# '
  78. RPROMPT=''
  79. if gitstatus_query MY && [[ $VCS_STATUS_RESULT == ok-sync ]]; then
  80. RPROMPT=${${VCS_STATUS_LOCAL_BRANCH:-@${VCS_STATUS_COMMIT}}//\%/%%} # escape %
  81. (( VCS_STATUS_NUM_STAGED )) && RPROMPT+='+'
  82. (( VCS_STATUS_NUM_UNSTAGED )) && RPROMPT+='!'
  83. (( VCS_STATUS_NUM_UNTRACKED )) && RPROMPT+='?'
  84. fi
  85. setopt no_prompt_{bang,subst} prompt_percent # enable/disable correct prompt expansions
  86. }
  87. gitstatus_stop 'MY' && gitstatus_start -s -1 -u -1 -c -1 -d -1 'MY'
  88. autoload -Uz add-zsh-hook
  89. add-zsh-hook precmd my_set_prompt
  90. ```
  91. This snippet is sourcing `gitstatus.plugin.zsh` rather than `gitstatus.prompt.zsh`. The former
  92. defines low-level bindings that communicate with gitstatusd over pipes. The latter is a simple
  93. script that uses these bindings to assemble git prompt.
  94. Unlike [Powerlevel10k](https://github.com/romkatv/powerlevel10k), code based on
  95. [gitstatus.prompt.zsh](gitstatus.prompt.zsh) is communicating with gitstatusd synchronously. This
  96. can make your prompt slow when working in a large git repository or on a slow machine. To avoid
  97. this problem, call `gitstatus_query` asynchronously as documented in
  98. [gitstatus.plugin.zsh](gitstatus.plugin.zsh). This can be quite challenging.
  99. ## Using from Bash
  100. The easiest way to take advantage of gitstatus from Bash is via
  101. [gitstatus.prompt.sh](gitstatus.prompt.sh). Install it as follows:
  102. ```bash
  103. git clone --depth=1 https://github.com/romkatv/gitstatus.git ~/gitstatus
  104. echo 'source ~/gitstatus/gitstatus.prompt.sh' >> ~/.bashrc
  105. ```
  106. Users in mainland China can use the official mirror on gitee.com for faster download.<br>
  107. 中国大陆用户可以使用 gitee.com 上的官方镜像加速下载.
  108. ```bash
  109. git clone --depth=1 https://gitee.com/romkatv/gitstatus.git ~/gitstatus
  110. echo 'source ~/gitstatus/gitstatus.prompt.sh' >> ~/.bashrc
  111. ```
  112. Alternatively, if you have Homebrew installed:
  113. ```zsh
  114. brew install romkatv/gitstatus/gitstatus
  115. echo "source $(brew --prefix)/opt/gitstatus/gitstatus.prompt.sh" >> ~/.bashrc
  116. ```
  117. (If you choose this option, replace `~/gitstatus` with `$(brew --prefix)/opt/gitstatus/gitstatus`
  118. in all code snippets below.)
  119. This will give you a basic yet functional prompt with git status in it. It's
  120. [over 10x faster](#benchmarks) than any alternative that can give you comparable prompt.
  121. ![Bash Prompt with GitStatus](
  122. https://raw.githubusercontent.com/romkatv/gitstatus/1ac366952366d89980b3f3484f270b4fa5ae4293/bash-prompt.png)
  123. In order to customize your prompt, set `PS1` at the end of `~/.bashrc` after sourcing
  124. `gitstatus.prompt.sh`. Insert `${GITSTATUS_PROMPT}` where you want git status to go. For example:
  125. ```bash
  126. source ~/gitstatus/gitstatus.prompt.sh
  127. PS1='\w ${GITSTATUS_PROMPT}\n\$ ' # directory followed by git status and $/# (normal/root)
  128. ```
  129. The expansion of `${GITSTATUS_PROMPT}` can contain the following bits:
  130. | segment | meaning |
  131. |-------------|-------------------------------------------------------|
  132. | `master` | current branch |
  133. | `#v1` | HEAD is tagged with `v1`; not shown when on a branch |
  134. | `@5fc6fca4` | current commit; not shown when on a branch or tag |
  135. | `⇣1` | local branch is behind the remote by 1 commit |
  136. | `⇡2` | local branch is ahead of the remote by 2 commits |
  137. | `⇠3` | local branch is behind the push remote by 3 commits |
  138. | `⇢4` | local branch is ahead of the push remote by 4 commits |
  139. | `*5` | there are 5 stashes |
  140. | `merge` | merge is in progress (could be some other action) |
  141. | `~6` | there are 6 merge conflicts |
  142. | `+7` | there are 7 staged changes |
  143. | `!8` | there are 8 unstaged changes |
  144. | `?9` | there are 9 untracked files |
  145. If you'd like to change the format of git status, or want to have greater control over the
  146. process of assembling `PS1`, you can copy and modify parts of
  147. [gitstatus.prompt.sh](gitstatus.prompt.sh) instead of sourcing the script. Your `~/.bashrc` might
  148. look something like this:
  149. ```bash
  150. source ~/gitstatus/gitstatus.plugin.sh
  151. function my_set_prompt() {
  152. PS1='\w'
  153. if gitstatus_query && [[ "$VCS_STATUS_RESULT" == ok-sync ]]; then
  154. if [[ -n "$VCS_STATUS_LOCAL_BRANCH" ]]; then
  155. PS1+=" ${VCS_STATUS_LOCAL_BRANCH//\\/\\\\}" # escape backslash
  156. else
  157. PS1+=" @${VCS_STATUS_COMMIT//\\/\\\\}" # escape backslash
  158. fi
  159. (( VCS_STATUS_HAS_STAGED" )) && PS1+='+'
  160. (( VCS_STATUS_HAS_UNSTAGED" )) && PS1+='!'
  161. (( VCS_STATUS_HAS_UNTRACKED" )) && PS1+='?'
  162. fi
  163. PS1+='\n\$ '
  164. shopt -u promptvars # disable expansion of '$(...)' and the like
  165. }
  166. gitstatus_stop && gitstatus_start
  167. PROMPT_COMMAND=my_set_prompt
  168. ```
  169. This snippet is sourcing `gitstatus.plugin.sh` rather than `gitstatus.prompt.sh`. The former
  170. defines low-level bindings that communicate with gitstatusd over pipes. The latter is a simple
  171. script that uses these bindings to assemble git prompt.
  172. Note: Bash bindings, unlike Zsh bindings, don't support asynchronous calls.
  173. ## Using from other shells
  174. If there are no gitstatusd bindings for your shell, you'll need to get your hands dirty.
  175. Use the existing bindings for inspiration; run `gitstatusd --help` or read the same thing in
  176. [options.cc](src/options.cc).
  177. ## How it works
  178. gitstatusd reads requests from stdin and prints responses to stdout. Requests contain an ID and
  179. a directory. Responses contain the same ID and machine-readable git status for the directory.
  180. gitstatusd keeps some state in memory for the directories it has seen in order to serve future
  181. requests faster.
  182. [Zsh bindings](gitstatus.plugin.zsh) and [Bash bindings](gitstatus.plugin.sh) start gitstatusd in
  183. the background and communicate with it via pipes. Themes such as
  184. [Powerlevel10k](https://github.com/romkatv/powerlevel10k) use these bindings to put git status in
  185. `PROMPT`.
  186. Note that gitstatus cannot be used as a drop-in replacement for `git status` command as it doesn't
  187. produce output in the same format. It does perform the same computation though.
  188. ## Benchmarks
  189. The following benchmark results were obtained on Intel i9-7900X running Ubuntu 18.04 in
  190. a clean [chromium](https://github.com/chromium/chromium) repository synced to `9394e49a`. The
  191. repository was checked out to an ext4 filesystem on M.2 SSD.
  192. Three functionally equivalent tools for computing git status were benchmarked:
  193. * `gitstatusd`
  194. * `git` with untracked cache enabled
  195. * `lg2` -- a demo/example executable from [libgit2](https://github.com/romkatv/libgit2) that
  196. implements a subset of `git` functionality on top of libgit2 API; for the purposes of this
  197. benchmark the subset is sufficient to generate the same data as the other tools
  198. Every tool was benchmark in cold and hot conditions. For `git` the first run in a repository was
  199. considered cold, with the following runs considered hot. `lg2` was patched to compute results twice
  200. in a single invocation without freeing the repository in between; the second run was considered hot.
  201. The same patching was not done for `git` because `git` cannot be easily modified to refresh inmemory
  202. index state between invocations; in fact, this limitation is one of the primary reasons developers
  203. use libgit2. `gitstatusd` was benchmarked similarly to `lg2` with two result computations in the
  204. same invocation.
  205. Two commands were benchmarked: `status` and `describe`.
  206. ### Status
  207. In this benchmark all tools were computing the equivalent of `git status`. Lower numbers are better.
  208. | Tool | Cold | Hot |
  209. |---------------|-----------:|------------:|
  210. | **gitstatus** | **291 ms** | **30.9 ms** |
  211. | git | 876 ms | 295 ms |
  212. | lg2 | 1730 ms | 1310 ms |
  213. gitstatusd is substantially faster than the alternatives, especially on hot runs. Note that hot runs
  214. are of primary importance to the main use case of gitstatus in interactive shells.
  215. The performance of `git status` fluctuated wildly in this benchmarks for reasons unknown to the
  216. author. Moreover, performance is sticky -- once `git status` settles around a number, it stays
  217. there for a long time. Numbers as diverse as 295, 352, 663 and 730 had been observed on hot runs on
  218. the same repository. The number in the table is the lowest (fastest or best) that `git status` had
  219. shown.
  220. ### Describe
  221. In this benchmark all tools were computing the equivalent of `git describe --tags --exact-match`
  222. to find tags that resolve to the same commit as `HEAD`. Lower numbers are better.
  223. | Tool | Cold | Hot |
  224. |---------------|------------:|--------------:|
  225. | **gitstatus** | **4.04 ms** | **0.0345 ms** |
  226. | git | 18.0 ms | 14.5 ms |
  227. | lg2 | 185 ms | 45.2 ms |
  228. gitstatusd is once again faster than the alternatives, more so on hot runs.
  229. ## Why fast
  230. Since gitstatusd doesn't have to print all staged/unstaged/untracked files but only report
  231. whether there are any, it can terminate repository scan early. It can also remember which files
  232. were dirty on the previous run and check them first on the next run to avoid the scan entirely if
  233. the files are still dirty. However, the benchmarks above were performed in a clean repository where
  234. these shortcuts do not trigger. All benchmarked tools had to do the same work -- check the status
  235. of every file in the index to see if it has changed, check every directory for newly created files,
  236. etc. And yet, gitstatusd came ahead by a large margin. This section describes what it does that
  237. makes it so fast.
  238. Most of the following comparisons are done against libgit2 rather than git because of the author's
  239. familiarity with the former but not the with latter. libgit2 has clean, well-documented APIs and an
  240. elegant implementation, which makes it so much easier to work with and to analyze performance
  241. bottlenecks.
  242. ### Summary for the impatient
  243. Under the benchmark conditions described above, the equivalent of libgit2's
  244. `git_diff_index_to_workdir` (the most expensive part of `status` command) is 46.3 times faster in
  245. gitstatusd. The speedup comes from the following sources.
  246. * gitstatusd uses more efficient data structures and algorithms and employs performance-conscious
  247. coding style throughout the codebase. This reduces CPU time in userspace by 32x compared to libgit2.
  248. * gitstatusd uses less expensive system calls and makes fewer of them. This reduces CPU time spent
  249. in kernel by 1.9x.
  250. * gitstatusd can utilize multiple cores to scan index and workdir in parallel with almost perfect
  251. scaling. This reduces total run time by 12.4x while having virtually no effect on total CPU time.
  252. ### Problem statement
  253. The most resource-intensive part of the `status` command is finding the difference between _index_
  254. and _workdir_ (`git_diff_index_to_workdir` in libgit2). Index is a list of all files in the git
  255. repository with their last modification times. This is an obvious simplification but it suffices for
  256. this exposition. On disk, index is stored sorted by file path. Here's an example of git index:
  257. | File | Last modification time |
  258. |-------------|-----------------------:|
  259. | Makefile | 2019-04-01T14:12:32Z |
  260. | src/hello.c | 2019-04-01T14:12:00Z |
  261. | src/hello.h | 2019-04-01T14:12:32Z |
  262. This list needs to be compared to the list of files in the working directory. If any of the files
  263. listed in the index are missing from the workdir or have different last modification time, they are
  264. "unstaged" in gitstatusd parlance. If you run `git status`, they'll be shown as "changes not staged
  265. for commit". Thus, any implementation of `status` command has to call `stat()` or one of its
  266. variants on every file in the index.
  267. In addition, all files in the working directory for which there is no entry in the index at all are
  268. "untracked". `git status` will show them as "untracked files". Finding untracked files requires some
  269. form of work directory traversal.
  270. ### Single-threaded scan
  271. Let's see how `git_diff_index_to_workdir` from libgit2 accomplishes these tasks. Here's its CPU
  272. profile from 200 hot runs over chromium repository.
  273. ![libgit2 CPU profile (hot)](
  274. https://raw.githubusercontent.com/romkatv/gitstatus/1ac366952366d89980b3f3484f270b4fa5ae4293/cpu-profile-libgit2.png)
  275. (The CPU profile was created with [gperftools](https://github.com/gperftools/gperftools) and
  276. rendered with [pprof](https://github.com/google/pprof)).
  277. We can see `__GI__lxstat` taking a lot of time. This is the `stat()` call for every file in the
  278. index. We can also identify `__opendir`, `__readdir` and `__GI___close_nocancel` -- glibc wrappers
  279. for reading the contents of a directory. This is for finding untracked files. Out of the total 232
  280. seconds, 111 seconds -- or 47.7% -- was spent on these calls. The rest is computation -- comparing
  281. strings, sorting arrays, etc.
  282. Now let's take a look at the CPU profile of gitstatusd on the same task.
  283. ![gitstatusd CPU profile (hot)](
  284. https://raw.githubusercontent.com/romkatv/gitstatus/1ac366952366d89980b3f3484f270b4fa5ae4293/cpu-profile-gitstatusd-hot.png)
  285. The first impression is that this profile looks pruned. This isn't an artifact. The profile was
  286. generated with the same tools and the same flags as the profile of libgit2.
  287. Since both profiles were generated from the same workload, absolute numbers can be compared. We can
  288. see that gitstatusd took 62 seconds in total compared to libgit2's 232 seconds. System calls at the
  289. core of the algorithm are cleary visible. `__GI___fxstatat` is a flavor of `stat()`, and the other
  290. three calls -- `__libc_openat64`, `__libc_close` and `__GI___fxstat` are responsible for opening
  291. directories and finding untracked files. Notice that there is almost nothing else in the profile
  292. apart from these calls. The rest of the code accounts for 3.77 seconds of CPU time -- 32 times less
  293. than in libgit2.
  294. So, one reason gitstatusd is fast is that it has efficient diffing code -- very little time is spent
  295. outside of kernel. However, if we look closely, we can notice that system calls in gitstatusd are
  296. _also_ faster than in libgit2. For example, libgit2 spent 72.07 seconds in `__GI__lxstat` while
  297. gitstatusd spent only 48.82 seconds in `__GI___fxstatat`. There are two reasons for this difference.
  298. First, libgit2 makes more `stat()` calls than is strictly required. It's not necessary to stat
  299. directories because index only has files. There are 25k directories in chromium repository (and 300k
  300. files) -- that's 25k `stat()` calls that could be avoided. The second reason is that libgit2 and
  301. gitstatusd use different flavors of `stat()`. libgit2 uses `lstat()`, which takes a path to the file
  302. as input. Its performance is linear in the number of subdirectories in the path because it needs to
  303. perform a lookup for every one of them and to check permissions. gitstatusd uses `fstatat()`, which
  304. takes a file descriptor to the parent directory and a name of the file. Just a single lookup, less
  305. CPU time.
  306. Similarly to `lstat()` vs `fstatat()`, it's faster to open files and directories with `openat()`
  307. from the parent directory file descriptor than with regular `open()` that accepts full file path.
  308. gitstatusd takes advantage of `openat()` to open directories as fast as possible. It opens about 90%
  309. of the directories (this depends on the actual directory structure of the repository) from the
  310. immediate parent -- the most efficient way -- and the remaining 10% it opens from the repository's
  311. root directory. The reason it's done this way is to keep the maximum number of simultaneously open
  312. file descriptors bounded. libgit2 can have O(repository depth) simultaneously open file descriptors,
  313. which may be OK for a single-threaded application but can balloon to a large number when scans are
  314. done by many threads simultaneously, like in gitstatusd.
  315. There is no equivalent to `__opendir` or `__readdir` in the gitstatusd profile because it uses the
  316. equivalent of [untracked cache](https://git-scm.com/docs/git-update-index#_untracked_cache) from
  317. git. On the first scan of the workdir gitstatusd lists all files just like libgit2. But, unlike
  318. libgit2, it remembers the last modification time of every directory along with the list of
  319. untracked files under it. On the next scan, gitstatusd can skip listing files in directories whose
  320. last modification time hasn't changed.
  321. To summarize, here's what gitstatusd was doing when the CPU profile was captured:
  322. 1. `__libc_openat64`: Open every directory for which there are files in the index.
  323. 2. `__GI___fxstat`: Check last modification time of the directory. Since it's the same as on the
  324. last scan, this directory has the same list of untracked files as before, which is empty (the
  325. repository is clean).
  326. 3. `__GI___fxstatat`: Check last modification time for every file in the index that belongs to this
  327. directory.
  328. 4. `__libc_close`: Close the file descriptor to the directory.
  329. Here's how the very first scan of a repository looks like in gitstatusd:
  330. ![gitstatusd CPU profile (cold)](
  331. https://raw.githubusercontent.com/romkatv/gitstatus/1ac366952366d89980b3f3484f270b4fa5ae4293/cpu-profile-gitstatusd-cold.png)
  332. (Some glibc functions are mislabel on this profile. `explicit_bzero` and `__nss_passwd_lookup` are
  333. in reality `strcmp` and `memcmp`.)
  334. This is a superset of the previous -- hot -- profile, with an extra `syscall` and string sorting for
  335. directory listing. gitstatusd uses `getdents64` Linux system call directly, bypassing the glibc
  336. wrapper that libgit2 uses. This is 23% faster. The details of this optimization can be found in a
  337. [separate document](docs/listdir.md).
  338. ### Multithreading
  339. The diffing algorithm in gitstatusd was designed from the ground up with the intention of using it
  340. concurrently from multiple threads. With a fast SSD, `status` is CPU bound, so taking advantage of
  341. all available CPU cores is an obvious way to yield results faster.
  342. gitstatusd exhibits almost perfect scaling from multithreading. Engaging all cores allows it to
  343. produce results 12.4 times faster than in single-threaded execution. This is on Intel i9-7900X with
  344. 10 cores (20 with hyperthreading) with single-core frequency of 4.3GHz and all-core frequency of
  345. 4.0GHz.
  346. Note: `git status` also uses all available cores in some parts of its algorithm while `lg2` does
  347. everything in a single thread.
  348. ### Postprocessing
  349. Once the difference between the index and the workdir is found, we have a list of _candidates_ --
  350. files that may be unstaged or untracked. To make the final judgement, these files need to be checked
  351. against `.gitignore` rules and a few other things.
  352. gitstatusd uses [patched libgit2](https://github.com/romkatv/libgit2) for this step. This fork
  353. adds several optimizations that make libgit2 faster. The patched libgit2 performs more than twice
  354. as fast in the benchmark as the original even without changes in the user code (that is, in the
  355. code that uses the libgit2 APIs). The fork also adds several API extensions, most notable of which
  356. is the support for multi-threaded scans. If `lg2 status` is modified to take advantage of these
  357. extensions, it outperforms the original libgit2 by a factor of 18. Lastly, the fork fixes a score of
  358. bugs, most of which become apparent only when using libgit2 from multiple threads.
  359. _WARNING: Changes to libgit2 are extensive but the testing they underwent isn't. It is
  360. **not recommended** to use the patched libgit2 in production._
  361. ## Requirements
  362. * To compile: binutils, cmake, gcc, g++, git and GNU make.
  363. * To run: Linux, macOS, FreeBSD, Android, WSL, Cygwin or MSYS2.
  364. ## Compiling
  365. There are prebuilt `gitstatusd` binaries in [releases](
  366. https://github.com/romkatv/gitstatus/releases). When using the official shell bindings
  367. provided by gitstatus, the right binary for your architecture gets downloaded automatically.
  368. If prebuilt binaries don't work for you, you'll need to get your hands dirty.
  369. ### Compiling for personal use
  370. ```zsh
  371. git clone --depth=1 https://github.com/romkatv/gitstatus.git
  372. cd gitstatus
  373. ./build -w -s -d docker
  374. ```
  375. Users in mainland China can use the official mirror on gitee.com for faster download.<br>
  376. 中国大陆用户可以使用 gitee.com 上的官方镜像加速下载.
  377. ```zsh
  378. git clone --depth=1 https://gitee.com/romkatv/gitstatus.git
  379. cd gitstatus
  380. ./build -w -s -d docker
  381. ```
  382. - If it says that `-d docker` is not supported on your OS, remove this flag.
  383. - If it says that `-s` is not supported on your OS, remove this flag.
  384. - If it tell you to install docker but you cannot or don't want to, remove `-d docker`.
  385. - If it says that some command is missing, install it.
  386. If everything goes well, the newly built binary will appear in `./usrbin`. It'll be picked up
  387. by shell bindings automatically.
  388. When you update shell bindings, they may refuse to work with the binary you've built earlier. In
  389. this case you'll need to rebuild.
  390. If you are using gitstatus through [Powerlevel10k](https://github.com/romkatv/powerlevel10k), the
  391. instructions are the same except that you don't need to clone gitstatus. Instead, change your
  392. current directory to `/path/to/powerlevel10k/gitstatus` (`/path/to/powerlevel10k` is the directory
  393. where you've installed Powerlevel10k) and run `./build -w -s -d docker` from there as described
  394. above.
  395. ### Compiling for distribution
  396. It's currently neither easy nor recommended to package and distribute gitstatus. There are no
  397. instructions you can follow that would allow you to easily update your package when new versions of
  398. gitstatus are released. This may change in the future but not soon.
  399. ## License
  400. GNU General Public License v3.0. See [LICENSE](LICENSE). Contributions are covered by the same
  401. license.