Full Code of Vinzent03/obsidian-git for AI

master 2b95ce763984 cached
90 files
705.0 KB
167.8k tokens
645 symbols
1 requests
Download .txt
Showing preview only (737K chars total). Download the full file or copy to clipboard to get everything.
Repository: Vinzent03/obsidian-git
Branch: master
Commit: 2b95ce763984
Files: 90
Total size: 705.0 KB

Directory structure:
gitextract_b4m8u31b/

├── .editorconfig
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   └── bug_report.yml
│   └── workflows/
│       ├── releases.yml
│       └── test.yml
├── .gitignore
├── .prettierrc.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── docs/
│   ├── .gitignore
│   ├── Authentication.md
│   ├── Common issues.md
│   ├── Features.md
│   ├── Getting Started.md
│   ├── Installation.md
│   ├── Integration with other tools.md
│   ├── Line Authoring.md
│   ├── Start here.md
│   ├── Tips-and-Tricks.md
│   └── dev/
│       └── LineAuthorFeature.md
├── esbuild.config.mjs
├── eslint.config.mjs
├── manifest.json
├── package.json
├── polyfill_buffer.js
├── src/
│   ├── automaticsManager.ts
│   ├── commands.ts
│   ├── constants.ts
│   ├── editor/
│   │   ├── control.ts
│   │   ├── editorIntegration.ts
│   │   ├── eventsPerFilepath.ts
│   │   ├── lineAuthor/
│   │   │   ├── lineAuthorIntegration.ts
│   │   │   ├── lineAuthorProvider.ts
│   │   │   ├── model.ts
│   │   │   └── view/
│   │   │       ├── cache.ts
│   │   │       ├── contextMenu.ts
│   │   │       ├── gutter/
│   │   │       │   ├── coloring.ts
│   │   │       │   ├── commitChoice.ts
│   │   │       │   ├── gutter.ts
│   │   │       │   ├── gutterElementSearch.ts
│   │   │       │   ├── initial.ts
│   │   │       │   └── untrackedFile.ts
│   │   │       └── view.ts
│   │   └── signs/
│   │       ├── changesStatusBar.ts
│   │       ├── diff.ts
│   │       ├── gutter.ts
│   │       ├── hunkActions.ts
│   │       ├── hunkState.ts
│   │       ├── hunks.ts
│   │       ├── signsIntegration.ts
│   │       ├── signsProvider.ts
│   │       └── tooltip.ts
│   ├── externalLibTypes.d.ts
│   ├── gitManager/
│   │   ├── gitManager.ts
│   │   ├── isomorphicGit.ts
│   │   ├── myAdapter.ts
│   │   └── simpleGit.ts
│   ├── main.ts
│   ├── openInGitHub.ts
│   ├── pluginGlobalRef.ts
│   ├── promiseQueue.ts
│   ├── setting/
│   │   ├── localStorageSettings.ts
│   │   └── settings.ts
│   ├── statusBar.ts
│   ├── tools.ts
│   ├── types.ts
│   ├── ui/
│   │   ├── diff/
│   │   │   ├── diffView.ts
│   │   │   └── splitDiffView.ts
│   │   ├── history/
│   │   │   ├── components/
│   │   │   │   ├── logComponent.svelte
│   │   │   │   ├── logFileComponent.svelte
│   │   │   │   └── logTreeComponent.svelte
│   │   │   ├── historyView.svelte
│   │   │   └── historyView.ts
│   │   ├── modals/
│   │   │   ├── branchModal.ts
│   │   │   ├── changedFilesModal.ts
│   │   │   ├── customMessageModal.ts
│   │   │   ├── discardModal.ts
│   │   │   ├── generalModal.ts
│   │   │   └── ignoreModal.ts
│   │   ├── sourceControl/
│   │   │   ├── components/
│   │   │   │   ├── fileComponent.svelte
│   │   │   │   ├── pulledFileComponent.svelte
│   │   │   │   ├── stagedFileComponent.svelte
│   │   │   │   ├── tooManyFilesComponent.svelte
│   │   │   │   └── treeComponent.svelte
│   │   │   ├── sourceControl.svelte
│   │   │   └── sourceControl.ts
│   │   └── statusBar/
│   │       └── branchStatusBar.ts
│   └── utils.ts
├── styles.css
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
# top-most EditorConfig file
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
tab_width = 4

================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: Bug Report
description: File a bug report
title: "[Bug]: "
labels: "bug"
body:
- type: markdown
  attributes:
    value: "**Please make sure you are on the latest version.**"
- type: textarea
  id: what-happened
  attributes:
    label: Describe the bug
    placeholder: The following error occurs when running command X when I have X enabled. 
  validations:
    required: true
- type: textarea
  id: logs
  attributes:
    label: Relevant errors (if available) from notifications or console (`CTRL+SHIFT+I`)
    description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
    render: shell
- type: textarea
  id: reproduce
  attributes:
    label: Steps to reproduce
  validations:
    required: true
- type: textarea
  id: expected
  attributes:
    label: Expected Behavior
    description: A clear and concise description of what you expected to happen.
- type: textarea
  id: context
  attributes:
    label: Addition context
    description: Add any other context about the problem here.
- type: dropdown
  id: os
  attributes:
    label: Operating system
    description: Which OS are you using?
    options:
      - Windows
      - Linux
      - macOS
      - Android
      - iOS
  validations:
    required: true
- type: dropdown
  id: installation-method
  attributes:
    label: Installation Method
    description: Only necessary on Linux
    options:
      - Flatpak
      - AppImage
      - Snap
      - Other
  validations:
    required: false
- type: input
  id: version
  attributes: 
    label: Plugin version
  validations:
    required: true


================================================
FILE: .github/workflows/releases.yml
================================================
name: Build obsidian plugin

on:
    push:
        tags:
            - "*"

env:
    PLUGIN_NAME: obsidian-git

permissions:
    contents: write

jobs:
    build:
        runs-on: ubuntu-latest
        steps:
            - uses: actions/checkout@v4
            - uses: pnpm/action-setup@v2
              with:
                  version: latest
            - name: Build
              id: build
              run: |
                  pnpm install
                  pnpm run build --if-present
                  mkdir ${{ env.PLUGIN_NAME }}
                  cp main.js manifest.json styles.css ${{ env.PLUGIN_NAME }}
                  zip -r ${{ env.PLUGIN_NAME }}.zip ${{ env.PLUGIN_NAME }}
            - name: Create Release
              id: create_release
              uses: softprops/action-gh-release@v1
              with:
                  tag_name: ${{ github.ref }}
                  name: ${{ github.ref_name }}
                  generate_release_notes: true
                  draft: false
                  prerelease: false
            - name: Upload zip file
              id: upload-zip
              uses: actions/upload-release-asset@v1
              env:
                  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
              with:
                  upload_url: ${{ steps.create_release.outputs.upload_url }}
                  asset_path: ./${{ env.PLUGIN_NAME }}.zip
                  asset_name: ${{ env.PLUGIN_NAME }}-${{ github.ref_name }}.zip
                  asset_content_type: application/zip
            - name: Upload main.js
              id: upload-main
              uses: actions/upload-release-asset@v1
              env:
                  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
              with:
                  upload_url: ${{ steps.create_release.outputs.upload_url }}
                  asset_path: ./main.js
                  asset_name: main.js
                  asset_content_type: text/javascript
            - name: Upload manifest.json
              id: upload-manifest
              uses: actions/upload-release-asset@v1
              env:
                  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
              with:
                  upload_url: ${{ steps.create_release.outputs.upload_url }}
                  asset_path: ./manifest.json
                  asset_name: manifest.json
                  asset_content_type: application/json
            - name: Upload styles.css
              id: upload-css
              uses: actions/upload-release-asset@v1
              env:
                  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
              with:
                  upload_url: ${{ steps.create_release.outputs.upload_url }}
                  asset_path: ./styles.css
                  asset_name: styles.css
                  asset_content_type: text/css


================================================
FILE: .github/workflows/test.yml
================================================
name: test
on:
    push:
    pull_request:
jobs:
    svelte-check:
        runs-on: ubuntu-latest
        steps:
            - uses: actions/checkout@v4
            - uses: pnpm/action-setup@v2
              with:
                  version: latest
            - name: Install modules
              run: pnpm install
            - name: Run Svelte-Check
              run: pnpm run svelte
    lint:
        runs-on: ubuntu-latest
        steps:
            - uses: actions/checkout@v4
            - uses: pnpm/action-setup@v2
              with:
                  version: latest
            - name: Install modules
              run: pnpm install
            - name: Run ESLint
              run: pnpm run lint
    format:
        runs-on: ubuntu-latest
        steps:
            - uses: actions/checkout@v4
            - uses: pnpm/action-setup@v2
              with:
                  version: latest
            - name: Install modules
              run: pnpm install
            - name: Run Prettier
              run: pnpm run format
    compile:
        runs-on: ubuntu-latest
        steps:
            - uses: actions/checkout@v4
            - uses: pnpm/action-setup@v2
              with:
                  version: latest
            - name: Install modules
              run: pnpm install
            - name: Run tsc
              run: tsc --noEmit
    build:
        runs-on: ubuntu-latest
        steps:
            - uses: actions/checkout@v4
            - uses: pnpm/action-setup@v2
              with:
                  version: latest
            - name: Install modules
              run: pnpm install
            - name: Run build
              run: pnpm run build


================================================
FILE: .gitignore
================================================
# Intellij
*.iml
.idea

# npm
node_modules
main.js
yarn.lock

# build
*.js.map

.prettierignore
/data.json

.vscode

.DS_Store


================================================
FILE: .prettierrc.json
================================================
{
    "trailingComma": "es5",
    "tabWidth": 4,
    "semi": true,
    "plugins": ["prettier-plugin-svelte"],
    "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
}


================================================
FILE: CHANGELOG.md
================================================
# Changelog

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

## [2.38.0](https://github.com/Vinzent03/obsidian-git/compare/2.37.1...2.38.0) (2026-03-04)


### Features

* Default {{hostname}} placeholder to OS hostname when not explicitly configured ([#1044](https://github.com/Vinzent03/obsidian-git/issues/1044)) ([1cc045d](https://github.com/Vinzent03/obsidian-git/commit/1cc045dbc2a93b33d6c0ef02e6752a889685efdd))


### Bug Fixes

* do not alter Obsidian's own process env vars ([b8bd1ec](https://github.com/Vinzent03/obsidian-git/commit/b8bd1ec521561f6888857852cb565f66cc6a2241)), closes [#1041](https://github.com/Vinzent03/obsidian-git/issues/1041)
* handle having a config value defined multiple times ([8e15ddc](https://github.com/Vinzent03/obsidian-git/commit/8e15ddc3bc2ce27b8c0afbc736ccc0258daffe3b)), closes [#1038](https://github.com/Vinzent03/obsidian-git/issues/1038)
* wrap buttons in source control view ([932b7a0](https://github.com/Vinzent03/obsidian-git/commit/932b7a0b617b4f09d70fa7f48072e13ecd5f7f05)), closes [#1011](https://github.com/Vinzent03/obsidian-git/issues/1011)

### [2.37.1](https://github.com/Vinzent03/obsidian-git/compare/2.37.0...2.37.1) (2026-02-15)

## [2.37.0](https://github.com/Vinzent03/obsidian-git/compare/2.36.1...2.37.0) (2026-02-13)


### Features

* add settings pane icon ([5ea08b8](https://github.com/Vinzent03/obsidian-git/commit/5ea08b874e2bfdc532e8e351921a4a3d97ff41bb))
* allow empty default manual commit message ([#1022](https://github.com/Vinzent03/obsidian-git/issues/1022)) ([20c942e](https://github.com/Vinzent03/obsidian-git/commit/20c942e4780e9ed272c92824f89b703dd59f53db))
* close hunk preview with escape key ([fd9bef8](https://github.com/Vinzent03/obsidian-git/commit/fd9bef871ceed52834cae41add670ed5d59420b5)), closes [#1027](https://github.com/Vinzent03/obsidian-git/issues/1027)
* respect push.autoSetupRemote config ([bde2b3d](https://github.com/Vinzent03/obsidian-git/commit/bde2b3df909e4557e15591d6def20a80e15cec40)), closes [#1020](https://github.com/Vinzent03/obsidian-git/issues/1020)
* set author info as html attribute in line author information ([fad0dae](https://github.com/Vinzent03/obsidian-git/commit/fad0dae28391cba62744103165ddf0a909fa1014)), closes [#1017](https://github.com/Vinzent03/obsidian-git/issues/1017)
* show commit summary as tooltip in line author information ([8746e4a](https://github.com/Vinzent03/obsidian-git/commit/8746e4a4f9591a0f83c42d71b1cdb44911364f80))


### Bug Fixes

* some ssh askpass fixes ([b89d095](https://github.com/Vinzent03/obsidian-git/commit/b89d095a2b8a2b6ef943772c0d54602be74e6229)), closes [#994](https://github.com/Vinzent03/obsidian-git/issues/994)

### [2.36.1](https://github.com/Vinzent03/obsidian-git/compare/2.36.0...2.36.1) (2026-01-11)


### Bug Fixes

* align font size in unified diff with Obsidian ([99b2522](https://github.com/Vinzent03/obsidian-git/commit/99b25224a0a38b2a9885ddada7bad6774e70cd7f)), closes [#1008](https://github.com/Vinzent03/obsidian-git/issues/1008)
* align signs properly with different line heights ([5bdf09c](https://github.com/Vinzent03/obsidian-git/commit/5bdf09c464d25e6dfb8b685f64c2a3c5af901f79))
* less gutter updates for same git result ([4da4c64](https://github.com/Vinzent03/obsidian-git/commit/4da4c647feded146f2e5896e20b50b2319415d0d))
* obscure password prompt in isomorphic-git auth ([#1007](https://github.com/Vinzent03/obsidian-git/issues/1007)) ([174cad8](https://github.com/Vinzent03/obsidian-git/commit/174cad88ab81e232e6b14abaebe4482afefa3846))

## [2.36.0](https://github.com/Vinzent03/obsidian-git/compare/2.35.2...2.36.0) (2026-01-04)


### Features

* buttons for hunk actions and display on click ([a3dcd02](https://github.com/Vinzent03/obsidian-git/commit/a3dcd02fba27afc895968af6df6e752c5f06842f))
* command to preview hunk ([7f26cfe](https://github.com/Vinzent03/obsidian-git/commit/7f26cfe131b6ec47c2c361d510855a19a412bcaf))
* display hunk changes inline ([f346cac](https://github.com/Vinzent03/obsidian-git/commit/f346cac94199b0a97fda8c8d7908765c075d6109))
* go to prev/next hunk commands ([48d6658](https://github.com/Vinzent03/obsidian-git/commit/48d66580564fd4105291cdae56180d7e005a080a))
* granular settings and change status bar ([29bd5ae](https://github.com/Vinzent03/obsidian-git/commit/29bd5ae0ff916b33b43bff028340ee476ad7b81c))
* hunk actions ([1c32ceb](https://github.com/Vinzent03/obsidian-git/commit/1c32ceb038f112c674ed102dc78bf36a2c7018cf))
* specify merge strategy in settings ([#934](https://github.com/Vinzent03/obsidian-git/issues/934)) ([d64e4d7](https://github.com/Vinzent03/obsidian-git/commit/d64e4d7dfa57a7f5fb2073f9ebaaa2643acefdf8))
* stage hunk ([5f0a5a2](https://github.com/Vinzent03/obsidian-git/commit/5f0a5a2344b2d390e0da300f50ed88d0221ec1f6))
* stage individual hunks from split diff view ([47e97c9](https://github.com/Vinzent03/obsidian-git/commit/47e97c9911655227de401d4da8eccbc160040f95))


### Bug Fixes

* detect another error message for offline mode ([387d4bd](https://github.com/Vinzent03/obsidian-git/commit/387d4bd895a5fd04df80cd0705705d93e7fce4bd)), closes [#990](https://github.com/Vinzent03/obsidian-git/issues/990)
* disable staged hunks as their computation is not feasible ([5ff0fed](https://github.com/Vinzent03/obsidian-git/commit/5ff0fede7f15d4ae3e7e9e0b2c445ec1cced0d59))
* don't discard ignored files ([5483881](https://github.com/Vinzent03/obsidian-git/commit/54838815eae722a386de17fff71a1434bc6d9f84)), closes [#1006](https://github.com/Vinzent03/obsidian-git/issues/1006)
* don't remove dom elements in line authoring ([4b29d76](https://github.com/Vinzent03/obsidian-git/commit/4b29d76df035be329c55f5718a0542a85bf2f941))
* escape file path for css selector ([c6fcbdf](https://github.com/Vinzent03/obsidian-git/commit/c6fcbdf7fa813bfcf3047fd41174d60ea3ffdbf1)), closes [#986](https://github.com/Vinzent03/obsidian-git/issues/986)
* fallback to debounced diff for slow diffs ([cfac162](https://github.com/Vinzent03/obsidian-git/commit/cfac162d52cddc5b3435ae95277ea1a7cce8ba9c))
* make diff buttons horizontal ([5d2627c](https://github.com/Vinzent03/obsidian-git/commit/5d2627c3b31ec233402c5cf58f3e86a292ee19a1))
* only show non 0 numbers in change status bar ([9ee0c06](https://github.com/Vinzent03/obsidian-git/commit/9ee0c06c0d5595f89fdc371942a1f60692d87403))
* properly disable signs via settings ([bda731b](https://github.com/Vinzent03/obsidian-git/commit/bda731b15f36f69219e54943be1caace6b5de47a))
* properly manage extensions ([067144c](https://github.com/Vinzent03/obsidian-git/commit/067144cd3c6fddd88eb3c478de7baf6a46efb459))
* properly show tooltip ([f5a48af](https://github.com/Vinzent03/obsidian-git/commit/f5a48afde5e6ce3a971f70a2b3866313045b3bd8))
* refresh cached file index version every 10 seconds ([4ba53a4](https://github.com/Vinzent03/obsidian-git/commit/4ba53a428bc61285f1bec37112f2996f234dcc12))
* reset changing an empty line ([9549bde](https://github.com/Vinzent03/obsidian-git/commit/9549bde6bd58f8186a558c5157a8f5ad3a9e4737))
* reset new empty line ([8cf494e](https://github.com/Vinzent03/obsidian-git/commit/8cf494e7375b62c4ec3e4cdefc8310c53dca0e19))
* select hunks and unstage no_nl_at_eof ([67ea27e](https://github.com/Vinzent03/obsidian-git/commit/67ea27e6925c901d6963ca27e7bf7a98a8bd29ed))
* show signs for new hunks on refresh ([491784d](https://github.com/Vinzent03/obsidian-git/commit/491784d381528c4f168da2399d7327c37e55479f))
* show tooltip for topdelete ([adae383](https://github.com/Vinzent03/obsidian-git/commit/adae383b0d661389d1e357a3e435d40f7025b939))
* small fixes ([b90eeb8](https://github.com/Vinzent03/obsidian-git/commit/b90eeb8229620f2d613b6816212709ce1b87a199))
* update settings description ([5e13e49](https://github.com/Vinzent03/obsidian-git/commit/5e13e492118c36beca22b4f138d7ec481a95f3ba))
* update styling ([ebd9734](https://github.com/Vinzent03/obsidian-git/commit/ebd973471ddfa7e267ad85c442a89f9ef2dd1eb1))
* use rem instead of px for dimensions ([60e11b7](https://github.com/Vinzent03/obsidian-git/commit/60e11b77ca2a49281d9c79ce45d7a858dfa7a905))

### [2.35.2](https://github.com/Vinzent03/obsidian-git/compare/2.35.1...2.35.2) (2025-11-05)


### Bug Fixes

* catch aborted clone depth modal ([d11579c](https://github.com/Vinzent03/obsidian-git/commit/d11579c96932f0b2c16c7306c5a05cfbae2509b1))
* only check files to be committed for big files ([91654ef](https://github.com/Vinzent03/obsidian-git/commit/91654ef1e0a29fe4b8e67612b25c314a2cb95eb9)), closes [#966](https://github.com/Vinzent03/obsidian-git/issues/966)
* prepend custom PATH instead of append ([b8da471](https://github.com/Vinzent03/obsidian-git/commit/b8da471eaf342eb3450cc13cc7a3c57887c7e7d3)), closes [#981](https://github.com/Vinzent03/obsidian-git/issues/981)
* save edits from split diff view for repo not in vault root ([71fe8dc](https://github.com/Vinzent03/obsidian-git/commit/71fe8dc1be54bfb2aa9dd3d1861b778c212a3a35)), closes [#985](https://github.com/Vinzent03/obsidian-git/issues/985)
* trim git object result ([0f3d368](https://github.com/Vinzent03/obsidian-git/commit/0f3d368fea440f4a703ea8db21798c2af6d64557))

### [2.35.1](https://github.com/Vinzent03/obsidian-git/compare/2.35.0...2.35.1) (2025-09-19)


### Bug Fixes

* correctly abort selection when no branch selected ([23c009a](https://github.com/Vinzent03/obsidian-git/commit/23c009a2e9d23f0be6c882b2b992248e24bcf2da))
* don't collapse changed files on stage and add bottom padding ([3832059](https://github.com/Vinzent03/obsidian-git/commit/38320597ec712804eecc04dfcd64ce774963c303))
* get last commit time on branch without commits ([73300a1](https://github.com/Vinzent03/obsidian-git/commit/73300a1bf7c6333ea389a6792f73ea412b7f751d))
* Prevent jitter of refresh icon in mobile ([#941](https://github.com/Vinzent03/obsidian-git/issues/941)) ([180a314](https://github.com/Vinzent03/obsidian-git/commit/180a314ca65bf666a8e15aaa07dc0d6b1e1ec838))
* refresh view when staging files from menu ([1bd92c3](https://github.com/Vinzent03/obsidian-git/commit/1bd92c3961ac311312ff20e9fa2e3067a95c0434))

## [2.35.0](https://github.com/Vinzent03/obsidian-git/compare/2.34.0...2.35.0) (2025-08-07)


### Features

* add hidden commit staged command ([234bf8f](https://github.com/Vinzent03/obsidian-git/commit/234bf8f97e767b2523c74dc336a2c690b9d231b9)), closes [#932](https://github.com/Vinzent03/obsidian-git/issues/932)
* auto commit only staged files ([e64ef60](https://github.com/Vinzent03/obsidian-git/commit/e64ef60406c52729f5105887f27e9db10a9e4cb2)), closes [#927](https://github.com/Vinzent03/obsidian-git/issues/927)
* pause automatics via command ([f6f650e](https://github.com/Vinzent03/obsidian-git/commit/f6f650e4e85337f8e1613255899f191c12989f38))


### Bug Fixes

* catch not existing tracking branch ([5833ed3](https://github.com/Vinzent03/obsidian-git/commit/5833ed39c4c14e6c88e58713368c6a558a71425a))
* correctly set non-existing remote branch as upstream branch ([8283e10](https://github.com/Vinzent03/obsidian-git/commit/8283e10a26492c6359e68219750be5951e8c090d)), closes [#599](https://github.com/Vinzent03/obsidian-git/issues/599)
* discard files correctly ([1c4e0c3](https://github.com/Vinzent03/obsidian-git/commit/1c4e0c3e37fbc466d33a2f0e8089a65f4de8b103))
* don't include all files in status on mobile ([8410e32](https://github.com/Vinzent03/obsidian-git/commit/8410e3219ae68b7fd9bea39274e0d81155a560f3))
* get old path of renamed file ([b31a855](https://github.com/Vinzent03/obsidian-git/commit/b31a855102cd8f81a5a36cae160bac77b96ddfd6)), closes [#944](https://github.com/Vinzent03/obsidian-git/issues/944)
* properly close toggle tree items ([69fdf79](https://github.com/Vinzent03/obsidian-git/commit/69fdf79c5b45a114043996b5fafd4839728f32e6)), closes [#950](https://github.com/Vinzent03/obsidian-git/issues/950)
* properly color hovered icon buttons ([197a4c7](https://github.com/Vinzent03/obsidian-git/commit/197a4c7f82121b75e47ab61cae10ca0fb59e1587))
* use correct remote for push on mobile ([7e54350](https://github.com/Vinzent03/obsidian-git/commit/7e5435000116eaefc16826e6747cb5004675efa1))

## [2.34.0](https://github.com/Vinzent03/obsidian-git/compare/2.33.0...2.34.0) (2025-06-14)


### Features

* commit message from script ([c05f847](https://github.com/Vinzent03/obsidian-git/commit/c05f847baac1cfa121755bd6a10cfc5316a8f680)), closes [#849](https://github.com/Vinzent03/obsidian-git/issues/849)
* make commit command and commit button in source control 'smart' ([f03d75e](https://github.com/Vinzent03/obsidian-git/commit/f03d75e5f9d239d89e7770dfe86c21e9aa7db632)), closes [#907](https://github.com/Vinzent03/obsidian-git/issues/907)


### Bug Fixes

* don't use spawnSync, but a non-blocking alternative ([ad03171](https://github.com/Vinzent03/obsidian-git/commit/ad03171300604b968340ca9212790da01b6ba1fd))

## [2.33.0](https://github.com/Vinzent03/obsidian-git/compare/2.32.1...2.33.0) (2025-04-29)


### Features

* add setting to hide error notifications ([54f0541](https://github.com/Vinzent03/obsidian-git/commit/54f0541296d910dba317f4f83cf0fbbc1702a596)), closes [#870](https://github.com/Vinzent03/obsidian-git/issues/870)
* collapse unchanged lines in split diff view ([#893](https://github.com/Vinzent03/obsidian-git/issues/893)) ([6377157](https://github.com/Vinzent03/obsidian-git/commit/637715747deb465ca0730d273002679b8bde5540))
* ctrl + enter to commit-and-sync ([e15cdc0](https://github.com/Vinzent03/obsidian-git/commit/e15cdc06eaa9a1b33f39535621fd6ee873cd27e0)), closes [#901](https://github.com/Vinzent03/obsidian-git/issues/901)


### Bug Fixes

* add obsidian_askpass.sh to .git/info/exclude ([655d171](https://github.com/Vinzent03/obsidian-git/commit/655d1718da42a7eacce8e9a8b066c57304c512bb)), closes [#903](https://github.com/Vinzent03/obsidian-git/issues/903)
* don't overwrite debug logging of other packages ([#905](https://github.com/Vinzent03/obsidian-git/issues/905)) ([c258fe2](https://github.com/Vinzent03/obsidian-git/commit/c258fe2bcb45be83833f07e5d8d2b788f5571992))
* duplicated status bar ([f653019](https://github.com/Vinzent03/obsidian-git/commit/f65301946ee7d3160f5b6b00ca2f0aec8e0ef6ed)), closes [#892](https://github.com/Vinzent03/obsidian-git/issues/892)
* handle "Add to .gitignore" edgecases and make the added path absolute ([#890](https://github.com/Vinzent03/obsidian-git/issues/890)) ([485e4cd](https://github.com/Vinzent03/obsidian-git/commit/485e4cda3d374cc5e0d5ea93b0496f6ef24d1aa3))
* improve settings ui ([#886](https://github.com/Vinzent03/obsidian-git/issues/886)) ([edbbfb6](https://github.com/Vinzent03/obsidian-git/commit/edbbfb610462a3dd85436b4d97e74adfc4bcee7d))
* improve the "is file openable in obsidian" check ([#884](https://github.com/Vinzent03/obsidian-git/issues/884)) ([f7b286c](https://github.com/Vinzent03/obsidian-git/commit/f7b286cf0fb146cdd59a39302796e13a744e9c77))
* line authoring crashes when dom Element cannot be removed. ([#891](https://github.com/Vinzent03/obsidian-git/issues/891)) ([2d2ebfd](https://github.com/Vinzent03/obsidian-git/commit/2d2ebfd9a91aae636215f03e0620bc14c357e333))
* make view header buttons sticky and contain scrollbar to body ([#864](https://github.com/Vinzent03/obsidian-git/issues/864)) ([953dbd4](https://github.com/Vinzent03/obsidian-git/commit/953dbd4ac350e7c119439b3ddbf8951377464369))
* obscure password for ssh passphrase prompt ([5dbd3cd](https://github.com/Vinzent03/obsidian-git/commit/5dbd3cd8dedb4ccd97326205392cbd63702152d5)), closes [#845](https://github.com/Vinzent03/obsidian-git/issues/845)

### [2.32.1](https://github.com/Vinzent03/obsidian-git/compare/2.32.0...2.32.1) (2025-03-20)


### Bug Fixes

* don't reschedule auto commit-and-sync too often ([7f506c7](https://github.com/Vinzent03/obsidian-git/commit/7f506c7d54cc83bebcf14e22207c53e57a55add0)), closes [#875](https://github.com/Vinzent03/obsidian-git/issues/875)
* don't reset auto commit-and-sync timer while still running ([bd5b942](https://github.com/Vinzent03/obsidian-git/commit/bd5b9423eb6f90dce2e078cffa7591a4634bfc87))
* properly hide md extension ([6494ccf](https://github.com/Vinzent03/obsidian-git/commit/6494ccf0375d3790202e803864d2f6cbd46d1269)), closes [#873](https://github.com/Vinzent03/obsidian-git/issues/873)
* **settings:** remove ability to enter non-number in number input boxes ([#880](https://github.com/Vinzent03/obsidian-git/issues/880)) ([18e966e](https://github.com/Vinzent03/obsidian-git/commit/18e966e3905814b4c7ba9918466529998a45e26e)), closes [#878](https://github.com/Vinzent03/obsidian-git/issues/878)

## [2.32.0](https://github.com/Vinzent03/obsidian-git/compare/2.31.1...2.32.0) (2025-03-05)


### Features

* append .git to https remote url if not present ([2aca7c8](https://github.com/Vinzent03/obsidian-git/commit/2aca7c85b40792f619c511206375f224ece5ab27)), closes [#867](https://github.com/Vinzent03/obsidian-git/issues/867)
* show a mini file menu for hidden files from source control ([d2991cc](https://github.com/Vinzent03/obsidian-git/commit/d2991ccedcd59c56e685c2c776159f236bad8331)), closes [#766](https://github.com/Vinzent03/obsidian-git/issues/766)


### Bug Fixes

* check file size for non Obsidian files as well ([0754fd8](https://github.com/Vinzent03/obsidian-git/commit/0754fd87ba5e947096466c3a63890da12e0159ca)), closes [#859](https://github.com/Vinzent03/obsidian-git/issues/859)
* refresh already opened views on open command ([fbb62ad](https://github.com/Vinzent03/obsidian-git/commit/fbb62ad0cece4dbf36518616312a29463c39227c)), closes [#838](https://github.com/Vinzent03/obsidian-git/issues/838)
* schedule new automatic after it finishes ([15416cb](https://github.com/Vinzent03/obsidian-git/commit/15416cb57ce126f8cd47cc8446bab77dd751815a)), closes [#851](https://github.com/Vinzent03/obsidian-git/issues/851)

### [2.31.1](https://github.com/Vinzent03/obsidian-git/compare/2.31.0...2.31.1) (2025-01-04)


### Bug Fixes

* Allow commiting of files large than 100mb if handled by LFS ([#822](https://github.com/Vinzent03/obsidian-git/issues/822)) ([e67928d](https://github.com/Vinzent03/obsidian-git/commit/e67928db625a57405f0fa1711e9e031f0e89a683))
* load more logs in history view on scroll ([a17cb55](https://github.com/Vinzent03/obsidian-git/commit/a17cb55bcffd7e0e6028c72b5b0704fb6fb3fe00)), closes [#807](https://github.com/Vinzent03/obsidian-git/issues/807)
* more efficient too big file check ([43c70d8](https://github.com/Vinzent03/obsidian-git/commit/43c70d8c85e9471b5d859ad7026ef6e44161118c)), closes [#822](https://github.com/Vinzent03/obsidian-git/issues/822)

## [2.31.0](https://github.com/denolehov/obsidian-git/compare/2.30.1...2.31.0) (2024-12-31)


### Features

* run raw git commands ([d05b99c](https://github.com/denolehov/obsidian-git/commit/d05b99ca827b630e1c96fdb2a4dc19469b0b9b81))
* split diff view ([8c8551f](https://github.com/denolehov/obsidian-git/commit/8c8551f05a2cdbc3f389097e870718ecaf04ff0a))


### Bug Fixes

* don't open diff for binary files, just open them instead ([4c102fb](https://github.com/denolehov/obsidian-git/commit/4c102fb881764ba30a048b765926eda577c9f5f7)), closes [#801](https://github.com/denolehov/obsidian-git/issues/801)
* important fixes for diff view ([e5509f9](https://github.com/denolehov/obsidian-git/commit/e5509f981a51638c910f5cc960737b25d77a36ab))
* reset commit message to template ([16f0bd9](https://github.com/denolehov/obsidian-git/commit/16f0bd9fcf7a70687b529476bb4d9d5929375dbf)), closes [#828](https://github.com/denolehov/obsidian-git/issues/828)

### [2.30.1](https://github.com/denolehov/obsidian-git/compare/2.30.0...2.30.1) (2024-11-28)


### Bug Fixes

* use custom git path ([5346fc8](https://github.com/denolehov/obsidian-git/commit/5346fc8995d11ef46de3277f724bca30aa208a49)), closes [#820](https://github.com/denolehov/obsidian-git/issues/820)

## [2.30.0](https://github.com/denolehov/obsidian-git/compare/2.29.0...2.30.0) (2024-11-25)


### Features

* if Git is not found in PATH on windows try default location ([4301e31](https://github.com/denolehov/obsidian-git/commit/4301e31b2b66e111826e900e2d0942e0de871c74)), closes [#489](https://github.com/denolehov/obsidian-git/issues/489)


### Bug Fixes

* set SSH_AKSPASS instead of GIT_ASKPASS and better error handling ([044cd6a](https://github.com/denolehov/obsidian-git/commit/044cd6a7ace2e2f18f146b4439344190943342ae))

## [2.29.0](https://github.com/denolehov/obsidian-git/compare/2.28.2...2.29.0) (2024-11-24)


### Features

* provide a GIT_ASKPASS handler via Obsidian modal ([1a788b0](https://github.com/denolehov/obsidian-git/commit/1a788b01af7cab7ad6d5a850f68a905ec680a2d2))


### Bug Fixes

* Clarification which information was not set ([#802](https://github.com/denolehov/obsidian-git/issues/802)) ([45ba119](https://github.com/denolehov/obsidian-git/commit/45ba119fff28547772c59856f2abf8700e544149))
* create new upstream branch ([c5a95c1](https://github.com/denolehov/obsidian-git/commit/c5a95c1ac9d9a0788d3ae053e83a11dbc30e590d)), closes [#808](https://github.com/denolehov/obsidian-git/issues/808)
* get unset config values ([e229c22](https://github.com/denolehov/obsidian-git/commit/e229c227acd21d0a1674218b2a778726c3849908))
* Make the spelling of .gitignore consistent ([#805](https://github.com/denolehov/obsidian-git/issues/805)) ([8366927](https://github.com/denolehov/obsidian-git/commit/836692735d1cadb0023cd503573a58a14c76299e))
* properly disable auto pull ([f8464e6](https://github.com/denolehov/obsidian-git/commit/f8464e665850c0eac301a881b40b0c74809d4478)), closes [#816](https://github.com/denolehov/obsidian-git/issues/816)

### [2.28.2](https://github.com/denolehov/obsidian-git/compare/2.28.1...2.28.2) (2024-10-30)


### Bug Fixes

* duplicated history view ([ec0668d](https://github.com/denolehov/obsidian-git/commit/ec0668d6a69e9907aa8174976e3030f8c289be90)), closes [#800](https://github.com/denolehov/obsidian-git/issues/800)

### [2.28.1](https://github.com/denolehov/obsidian-git/compare/2.28.0...2.28.1) (2024-10-27)


### Bug Fixes

* open general modal ([2a0bbd1](https://github.com/denolehov/obsidian-git/commit/2a0bbd13aaff41bf08386133441b4681983538a2)), closes [#798](https://github.com/denolehov/obsidian-git/issues/798)

## [2.28.0](https://github.com/denolehov/obsidian-git/compare/2.27.0...2.28.0) (2024-10-26)


### Features

* pull on commit-and-sync even if no commit happened ([263e675](https://github.com/denolehov/obsidian-git/commit/263e675ead38fba8a7d5a5f3349830477817ca25)), closes [#787](https://github.com/denolehov/obsidian-git/issues/787)
* reload settings on file change ([d453c6f](https://github.com/denolehov/obsidian-git/commit/d453c6fe85143b6afcd672e3fd65ef7851dd9ae3)), closes [#779](https://github.com/denolehov/obsidian-git/issues/779)


### Bug Fixes

* recognize more errors as network issues ([4aceed3](https://github.com/denolehov/obsidian-git/commit/4aceed30c5b349431f7a5ed21c4d3b4bdabb88e7)), closes [#735](https://github.com/denolehov/obsidian-git/issues/735)
* refresh status after opening source control view ([666b8a8](https://github.com/denolehov/obsidian-git/commit/666b8a8f263ce49a3d02dbfb040aa774cce95db2))
* rework errors ([ee3eff4](https://github.com/denolehov/obsidian-git/commit/ee3eff46e0d98999e6ab312971392adc008d4f6e))
* strip files list after 500 entries in source control view ([fe1aedb](https://github.com/denolehov/obsidian-git/commit/fe1aedb0a12d4e0b5d4a81821a8d08c1417dd6b8))
* typo in settings ([1e6c3dd](https://github.com/denolehov/obsidian-git/commit/1e6c3dddae7f5cd3b8393f36817fba94ed3cd12d))

## [2.27.0](https://github.com/denolehov/obsidian-git/compare/2.26.0...2.27.0) (2024-09-18)


### Features

* rename backup to commit-and-sync and better settings page ([cd9ffc2](https://github.com/denolehov/obsidian-git/commit/cd9ffc2ebe964dc59c8ce5d114f444222eb1d068))


### Bug Fixes

* discard deleted files ([42bf536](https://github.com/denolehov/obsidian-git/commit/42bf536b7973ef35a251a88fa61127b9a3b9972d))
* discard not tracked directory ([183929b](https://github.com/denolehov/obsidian-git/commit/183929b0cf3da47670faf930d3b6700df65db5c4))
* don't refresh views if git client is not ready ([7887c7f](https://github.com/denolehov/obsidian-git/commit/7887c7f4518fd2f1f83810ad3fc13cb53af4fe19))
* refresh data on view loading ([73d2c29](https://github.com/denolehov/obsidian-git/commit/73d2c299298d3b11dd5a0e976b1187d46d17041e))
* show better diff view for non existing file ([07d9fce](https://github.com/denolehov/obsidian-git/commit/07d9fce47306a3315128327bec6e50ac351d9bff))
* trigger vault backup after edit on file rename ([d1ad3d4](https://github.com/denolehov/obsidian-git/commit/d1ad3d49a116db03c926df26044e181f9a53a3a0)), closes [#765](https://github.com/denolehov/obsidian-git/issues/765)

## [2.26.0](https://github.com/denolehov/obsidian-git/compare/2.25.0...2.26.0) (2024-09-01)


### Features

* open source control view with ribbon button ([dea4d6f](https://github.com/denolehov/obsidian-git/commit/dea4d6f915492ecc3d43b259fbe8764b9c6210a4))


### Bug Fixes

* open diffs in new split with middle click ([65ef5ba](https://github.com/denolehov/obsidian-git/commit/65ef5ba2fa783717baaea24d05c6dcc8e760596d))
* remove root folding line in git views ([b2df0ed](https://github.com/denolehov/obsidian-git/commit/b2df0ed27b2af948df06dcc45021005b8c54e363))

## [2.25.0](https://github.com/denolehov/obsidian-git/compare/2.24.3...2.25.0) (2024-07-23)


### Features

* add context menu to git views ([115c4ba](https://github.com/denolehov/obsidian-git/commit/115c4baccaaf0e905fa8eefee8cb5f35abfff88f)), closes [#615](https://github.com/denolehov/obsidian-git/issues/615)


### Bug Fixes

* catch sidebar leaf being null ([86065c9](https://github.com/denolehov/obsidian-git/commit/86065c987bb478cbf65b4baca1745d7162041b5d))
* don't require .git suffix to open file on github ([9b264bf](https://github.com/denolehov/obsidian-git/commit/9b264bffb49a36d86f8af4c17435de2e6ca6c580)), closes [#753](https://github.com/denolehov/obsidian-git/issues/753)
* open file on github from submodule ([4981f8b](https://github.com/denolehov/obsidian-git/commit/4981f8bcc2eacda44fcebf8a95f458acd514febc)), closes [#592](https://github.com/denolehov/obsidian-git/issues/592)
* use active color for buttons in file component ([c28d44b](https://github.com/denolehov/obsidian-git/commit/c28d44b1c6f358cdb0113ebcc4b1634a161dfdf2))

### [2.24.3](https://github.com/denolehov/obsidian-git/compare/2.24.2...2.24.3) (2024-06-22)


### Bug Fixes

* Adjust git.cwd to use a relative path to git root ([#733](https://github.com/denolehov/obsidian-git/issues/733)) ([ed31553](https://github.com/denolehov/obsidian-git/commit/ed31553a8cf25548ab722c4edf40d8d0a20df4e8))
* limit amount of files to list in commit msg ([a0416ed](https://github.com/denolehov/obsidian-git/commit/a0416edf5f3ac5d9adc9fc37cf9a9c932583ced4))
* support vault in subdirectory of git repo ([#722](https://github.com/denolehov/obsidian-git/issues/722)) ([171693f](https://github.com/denolehov/obsidian-git/commit/171693f7fda542f4ba0452a54979d92c8ecfcdb6))

### [2.24.2](https://github.com/denolehov/obsidian-git/compare/2.24.1...2.24.2) (2024-05-09)


### Bug Fixes

* ask for upstream branch in backup ([d1143f7](https://github.com/denolehov/obsidian-git/commit/d1143f7d643581ddf674b492921bf0aab9044643))
* hide line authoring on small width window([#684](https://github.com/denolehov/obsidian-git/issues/684)) ([6a89424](https://github.com/denolehov/obsidian-git/commit/6a89424231ab45ca7741a6d9b96693f63ca40e6e))

### [2.24.1](https://github.com/denolehov/obsidian-git/compare/2.24.0...2.24.1) (2024-03-12)


### Bug Fixes

* disable line authoring on mobile ([ac28656](https://github.com/denolehov/obsidian-git/commit/ac2865676135a22a81f8d1a440825e7583aa73ec))

## [2.24.0](https://github.com/denolehov/obsidian-git/compare/2.23.2...2.24.0) (2024-03-04)


### Features

* show date and author in history view ([a6e33d3](https://github.com/denolehov/obsidian-git/commit/a6e33d30d1556c485cc2ac6467972aa471b94758)), closes [#691](https://github.com/denolehov/obsidian-git/issues/691)


### Bug Fixes

* update submodules without outer remote repo ([675cef5](https://github.com/denolehov/obsidian-git/commit/675cef52d4e0ebbd2d11ff2322aa21649574bf9d)), closes [#701](https://github.com/denolehov/obsidian-git/issues/701)

### [2.23.2](https://github.com/denolehov/obsidian-git/compare/2.23.1...2.23.2) (2024-01-31)


### Bug Fixes

* many issues with list changed files ([8b3fc8b](https://github.com/denolehov/obsidian-git/commit/8b3fc8bd69b76193086d5b3a814546a9e3cf51ea)), closes [#655](https://github.com/denolehov/obsidian-git/issues/655)

### [2.23.1](https://github.com/denolehov/obsidian-git/compare/2.23.0...2.23.1) (2024-01-29)


### Bug Fixes

* commit in source control view ([90985b1](https://github.com/denolehov/obsidian-git/commit/90985b1b7733eb39247b3aaa71ddc2482012f272)), closes [#686](https://github.com/denolehov/obsidian-git/issues/686)

## [2.23.0](https://github.com/denolehov/obsidian-git/compare/2.22.2...2.23.0) (2024-01-28)


### Features

* add commit amend command ([8f10261](https://github.com/denolehov/obsidian-git/commit/8f10261b08a498b0fc8f989209c1e0b048a27c35)), closes [#648](https://github.com/denolehov/obsidian-git/issues/648)
* add setting to disable 'No changes...' popups ([#676](https://github.com/denolehov/obsidian-git/issues/676)) ([bfd6de9](https://github.com/denolehov/obsidian-git/commit/bfd6de9092aaa18d7624b374d10873a179f12351))


### Bug Fixes

* fold only one item ([cd1d932](https://github.com/denolehov/obsidian-git/commit/cd1d93226a4e2f5ebfaa89ada97851c60f35a4fd)), closes [#680](https://github.com/denolehov/obsidian-git/issues/680)

### [2.22.2](https://github.com/denolehov/obsidian-git/compare/2.22.1...2.22.2) (2024-01-26)

### [2.22.1](https://github.com/denolehov/obsidian-git/compare/2.22.0...2.22.1) (2024-01-08)


### Bug Fixes

* allow different ssh remote user than git ([5b6400c](https://github.com/denolehov/obsidian-git/commit/5b6400cc85c827bc13e11ffa1bc0cbb3bd6cfd26)), closes [#664](https://github.com/denolehov/obsidian-git/issues/664)
* create new remote ([1a4cca8](https://github.com/denolehov/obsidian-git/commit/1a4cca8baf20de91ce3ee825740a85f1d33c1744)), closes [#599](https://github.com/denolehov/obsidian-git/issues/599)
* grammar improvement in settings ([#635](https://github.com/denolehov/obsidian-git/issues/635)) ([1d81577](https://github.com/denolehov/obsidian-git/commit/1d81577877ccb548b06fb91036a246aa442a41ae))
* tooltip direction ([#600](https://github.com/denolehov/obsidian-git/issues/600)) ([a913303](https://github.com/denolehov/obsidian-git/commit/a91330381e83cfc2ece14186325b129d7fc9b6bf))
* update settings grammar ([#656](https://github.com/denolehov/obsidian-git/issues/656)) ([d9e8be1](https://github.com/denolehov/obsidian-git/commit/d9e8be14b5dbb64a9b78b2d3fe56c474bd57596f))

## [2.22.0](https://github.com/denolehov/obsidian-git/compare/2.21.0...2.22.0) (2023-08-30)


### Features

* highlight opened diff view file ([5708c63](https://github.com/denolehov/obsidian-git/commit/5708c63ad7cad72c3939a4d554a5b98bc04783ed)), closes [#545](https://github.com/denolehov/obsidian-git/issues/545)


### Bug Fixes

* ui alignment ([a9adfff](https://github.com/denolehov/obsidian-git/commit/a9adfff996d570f8e893a2e2786059d0fa2e1cb9))

## [2.21.0](https://github.com/denolehov/obsidian-git/compare/2.20.7...2.21.0) (2023-08-22)


### Features

* add fetch command ([222245b](https://github.com/denolehov/obsidian-git/commit/222245b7c750bd4d2740aa25913d74d538e5035d))
* mark push button when push is ready ([32936eb](https://github.com/denolehov/obsidian-git/commit/32936eb76dff45c2333660791faa0bc8f86e1154)), closes [#557](https://github.com/denolehov/obsidian-git/issues/557)


### Bug Fixes

* clarify backup after file change setting ([30d12ca](https://github.com/denolehov/obsidian-git/commit/30d12ca872c523cc3d6ee2987c2299270f50a578)), closes [#575](https://github.com/denolehov/obsidian-git/issues/575)
* show file name in diff view on mobile ([20e0aba](https://github.com/denolehov/obsidian-git/commit/20e0aba46fa3454f8dabc60dd9d3c579efc322ee)), closes [#564](https://github.com/denolehov/obsidian-git/issues/564)

### [2.20.7](https://github.com/denolehov/obsidian-git/compare/2.20.6...2.20.7) (2023-07-31)


### Bug Fixes

* properly collapse icon in tree views ([919d7f8](https://github.com/denolehov/obsidian-git/commit/919d7f8f65174e76a4f13a992b3f37931eaf7262))
* refresh status bar after push ([ed31df8](https://github.com/denolehov/obsidian-git/commit/ed31df88effc5e686cb2e81e0379df93627bdd9b)), closes [#566](https://github.com/denolehov/obsidian-git/issues/566)

### [2.20.6](https://github.com/denolehov/obsidian-git/compare/2.20.5...2.20.6) (2023-07-16)


### Bug Fixes

* allow empty commit in history view ([2571473](https://github.com/denolehov/obsidian-git/commit/257147311cf65a2b5dedf957f4c71ad9624ce7be))

### [2.20.5](https://github.com/denolehov/obsidian-git/compare/2.20.4...2.20.5) (2023-06-29)


### Bug Fixes

* disallow clone in vault root on desktop ([c073d19](https://github.com/denolehov/obsidian-git/commit/c073d19283c01af4fdc79002d1913b1d5672a5fd)), closes [#540](https://github.com/denolehov/obsidian-git/issues/540)
* textarea for commit message in settings ([ea4a7a1](https://github.com/denolehov/obsidian-git/commit/ea4a7a105a8a954e705b372dac127fa3a83fddc1))

### [2.20.4](https://github.com/denolehov/obsidian-git/compare/2.20.3...2.20.4) (2023-06-21)


### Bug Fixes

* make  `{{files}}` variable visible in settings ([#536](https://github.com/denolehov/obsidian-git/issues/536)) ([07abcce](https://github.com/denolehov/obsidian-git/commit/07abcce878a66e686e7f0221c68df746f69b590b))
* missing git file status 113 ([#537](https://github.com/denolehov/obsidian-git/issues/537)) ([ba2b40c](https://github.com/denolehov/obsidian-git/commit/ba2b40cbc5687f92ab9ca6a65110d5d6ec39c2ca))

### [2.20.3](https://github.com/denolehov/obsidian-git/compare/2.20.2...2.20.3) (2023-06-04)


### Bug Fixes

* show correct empty diff ([c8bbe7c](https://github.com/denolehov/obsidian-git/commit/c8bbe7c5d2b7beb49ab5fa55922f289c2bcdbed1)), closes [#327](https://github.com/denolehov/obsidian-git/issues/327)

### [2.20.2](https://github.com/denolehov/obsidian-git/compare/2.20.1...2.20.2) (2023-06-02)


### Bug Fixes

* hide line authoring settings on mobile ([c135c0b](https://github.com/denolehov/obsidian-git/commit/c135c0b49cd0cc8033ea40f64e6f922702375aa0))
* properly resolve merge conflict ([80c0b65](https://github.com/denolehov/obsidian-git/commit/80c0b65f8d0d8dc8dbddff61118bd79733ffce94)), closes [#502](https://github.com/denolehov/obsidian-git/issues/502)

### [2.20.1](https://github.com/denolehov/obsidian-git/compare/2.20.0...2.20.1) (2023-05-31)


### Bug Fixes

* use queue for actions in source control view ([eb20dd4](https://github.com/denolehov/obsidian-git/commit/eb20dd4c93cb6013e9aef1e47f817586261507d5)), closes [#517](https://github.com/denolehov/obsidian-git/issues/517)

## [2.20.0](https://github.com/denolehov/obsidian-git/compare/2.19.1...2.20.0) (2023-05-17)


### Features

* Line Authoring ([aa8dd1b](https://github.com/denolehov/obsidian-git/commit/aa8dd1b3cf0fc440c4d7177831795f3fc5b0076c)), closes [#321](https://github.com/denolehov/obsidian-git/issues/321)


### Bug Fixes

* use proper tree structure on Obsidian 1.3.1 ([c124943](https://github.com/denolehov/obsidian-git/commit/c124943d5f1ba388f700524e517a43cf9682abc8)), closes [#512](https://github.com/denolehov/obsidian-git/issues/512)

### [2.19.1](https://github.com/denolehov/obsidian-git/compare/2.19.0...2.19.1) (2023-04-04)


### Bug Fixes

* handle missing tracking branch ([#483](https://github.com/denolehov/obsidian-git/issues/483)) ([703fc18](https://github.com/denolehov/obsidian-git/commit/703fc18e7dccec89c810e6529a869ec5c271c21e))

## [2.19.0](https://github.com/denolehov/obsidian-git/compare/2.18.0...2.19.0) (2023-03-22)


### Features
* new History view
* show last commit time in status bar ([b6d93a1](https://github.com/denolehov/obsidian-git/commit/b6d93a1b8574b1d9dc56c3be9bf8403d95fcef26)), closes [#334](https://github.com/denolehov/obsidian-git/issues/334)


### Bug Fixes

* catch error in diffView ([cbff377](https://github.com/denolehov/obsidian-git/commit/cbff37701fd8aa8b9e1257d09fb8ca9fb655b35b))
* catch huge auto intervals ([35bca00](https://github.com/denolehov/obsidian-git/commit/35bca003c98637f65295fe7d5a8bc4ae1fd68b07)), closes [#153](https://github.com/denolehov/obsidian-git/issues/153)

## [2.18.0](https://github.com/denolehov/obsidian-git/compare/2.17.4...2.18.0) (2023-03-20)


### Features

* add setting to hide file menu actions ([a59d38a](https://github.com/denolehov/obsidian-git/commit/a59d38a9f00042a1c27dc64426cd93e595f8eb6b)), closes [#456](https://github.com/denolehov/obsidian-git/issues/456)
* show last commit time in status bar ([4525fef](https://github.com/denolehov/obsidian-git/commit/4525fef302c23b54c233186bdbf898615fc1b314)), closes [#334](https://github.com/denolehov/obsidian-git/issues/334)


### Bug Fixes

* catch huge auto intervals ([b96efc5](https://github.com/denolehov/obsidian-git/commit/b96efc5e06654f144d5837e784da297d79496c51)), closes [#153](https://github.com/denolehov/obsidian-git/issues/153)
* minor source control view improvements ([fd7792c](https://github.com/denolehov/obsidian-git/commit/fd7792c80403d884d09c31bb09a76799bbd0dff0))
* typo in settings ([4014057](https://github.com/denolehov/obsidian-git/commit/4014057879d24cb176a2ee1baac868fab05bc856)), closes [#468](https://github.com/denolehov/obsidian-git/issues/468)

### [2.17.4](https://github.com/denolehov/obsidian-git/compare/2.17.3...2.17.4) (2023-03-07)


### Bug Fixes

* add additional author check ([58ce847](https://github.com/denolehov/obsidian-git/commit/58ce84749936c78a2789f3eae1e2de3877350b96))

### [2.17.3](https://github.com/denolehov/obsidian-git/compare/2.17.2...2.17.3) (2023-03-07)


### Bug Fixes

* better error message for missing author ([2e9e3b1](https://github.com/denolehov/obsidian-git/commit/2e9e3b135de411f764ba6eef5b0aaf4d21216b55))
* don't checkout when nothing changed after merge ([f807d8a](https://github.com/denolehov/obsidian-git/commit/f807d8a19712bc8f697e37ba1a571b47be77c064))
* show diff with custom base path ([fdde0bf](https://github.com/denolehov/obsidian-git/commit/fdde0bf83b4fedf430fe829724992207f1393d48))
* use correct git path on clone on mobile ([686c323](https://github.com/denolehov/obsidian-git/commit/686c3230daff6a7fa1d51cf9270295ad975e2599))

### [2.17.2](https://github.com/denolehov/obsidian-git/compare/2.17.1...2.17.2) (2023-03-06)


### Bug Fixes

* use correct git dir on mobile ([fd456e5](https://github.com/denolehov/obsidian-git/commit/fd456e5f505ba1bedc0ab85fdc62ee9aa91c18e5))

### [2.17.1](https://github.com/denolehov/obsidian-git/compare/2.17.0...2.17.1) (2023-03-05)


### Bug Fixes

* show missing repo message ([70a6464](https://github.com/denolehov/obsidian-git/commit/70a64640f3b21fe61bbdaf0b6215d2878df732be))

## [2.17.0](https://github.com/denolehov/obsidian-git/compare/2.16.0...2.17.0) (2023-02-25)


### Features

* include old file name in log ([fa34fb5](https://github.com/denolehov/obsidian-git/commit/fa34fb5c87c9d9d6b294ef3fe28f5c7538df21ac))
* specify depth on clone ([cf81f0c](https://github.com/denolehov/obsidian-git/commit/cf81f0c1ea72931b2274265e32ab4db2d11d0c82)), closes [#307](https://github.com/denolehov/obsidian-git/issues/307)


### Bug Fixes

* correct git dir for clone on mobile ([0b06487](https://github.com/denolehov/obsidian-git/commit/0b0648716f790e2676509b77dee444a72ef06814))
* handle github link errors ([#445](https://github.com/denolehov/obsidian-git/issues/445)) ([fd294cc](https://github.com/denolehov/obsidian-git/commit/fd294ccf50237c24000559d0d99cff4758e43b1a))

## [2.16.0](https://github.com/denolehov/obsidian-git/compare/2.15.0...2.16.0) (2023-01-16)


### Features

* additional environment variables ([f9b1bca](https://github.com/denolehov/obsidian-git/commit/f9b1bca38c6db23f05abfb211933f7ce4f69db7f)), closes [#414](https://github.com/denolehov/obsidian-git/issues/414)
* custom GIT_DIR ([978453e](https://github.com/denolehov/obsidian-git/commit/978453ebb1cf0df1c70bd169665709cd512264dd))

## [2.15.0](https://github.com/denolehov/obsidian-git/compare/2.14.0...2.15.0) (2023-01-06)


### Features

* improve discard modal ([872fc18](https://github.com/denolehov/obsidian-git/commit/872fc182743df108a42ae244699bb2d2b03d7c69))

## [2.14.0](https://github.com/denolehov/obsidian-git/compare/2.13.0...2.14.0) (2022-12-14)


### Features

* add instructions to conflict  file ([50291d3](https://github.com/denolehov/obsidian-git/commit/50291d3b182ba4789dac25164ca66f511ba1ab67)), closes [#402](https://github.com/denolehov/obsidian-git/issues/402)


### Bug Fixes

* close empty leaf of deleted conflict file ([cd6027d](https://github.com/denolehov/obsidian-git/commit/cd6027dcb8f3f9c353c9e9f9592b057c06fceb70)), closes [#401](https://github.com/denolehov/obsidian-git/issues/401)

## [2.13.0](https://github.com/denolehov/obsidian-git/compare/2.12.1...2.13.0) (2022-12-07)


### Features

* add file name to diff view tab name ([8520c2b](https://github.com/denolehov/obsidian-git/commit/8520c2beed20f9fe20e6af830c34f59b1678b36a))


### Bug Fixes

* move commit msg setting to correct heading ([88eabc9](https://github.com/denolehov/obsidian-git/commit/88eabc930ca98ea205d366e874a245af964efabd))
* use correct path for diff view via command ([1150351](https://github.com/denolehov/obsidian-git/commit/1150351e6bdf066e59cd7579e9efc087d4d5a595)), closes [#397](https://github.com/denolehov/obsidian-git/issues/397)

### [2.12.1](https://github.com/denolehov/obsidian-git/compare/2.12.0...2.12.1) (2022-11-27)


### Bug Fixes

* use correct git implementation ([2def322](https://github.com/denolehov/obsidian-git/commit/2def32278a6dadab4777aae38a57821f5d044406)), closes [#387](https://github.com/denolehov/obsidian-git/issues/387) [#386](https://github.com/denolehov/obsidian-git/issues/386)

## [2.12.0](https://github.com/denolehov/obsidian-git/compare/2.11.0...2.12.0) (2022-11-27)


### Features

* set last auto backup to last commit ([d8cfbf2](https://github.com/denolehov/obsidian-git/commit/d8cfbf2efe31770f6fd7ac6399bb7563f3caa831)), closes [#73](https://github.com/denolehov/obsidian-git/issues/73)

## [2.11.0](https://github.com/denolehov/obsidian-git/compare/2.10.2...2.11.0) (2022-11-26)


### Features

* add backup button to source control view ([477b166](https://github.com/denolehov/obsidian-git/commit/477b16644dddd13dce1bde1600aed996b2b8f377)), closes [#374](https://github.com/denolehov/obsidian-git/issues/374)


### Bug Fixes

* hide 'finished pull'  notice when hiding notifications ([8ba0e75](https://github.com/denolehov/obsidian-git/commit/8ba0e7526001eaefed71b2978e9f1ab76ab18136)), closes [#292](https://github.com/denolehov/obsidian-git/issues/292)

### [2.10.2](https://github.com/denolehov/obsidian-git/compare/2.10.1...2.10.2) (2022-11-17)


### Bug Fixes

* focus diff view via command ([e56641c](https://github.com/denolehov/obsidian-git/commit/e56641c25f9672fcffeb0801f09ca7eadf99ede0)), closes [#377](https://github.com/denolehov/obsidian-git/issues/377)

### [2.10.1](https://github.com/denolehov/obsidian-git/compare/2.10.0...2.10.1) (2022-11-13)


### Bug Fixes

* add remote on mobile ([c529a37](https://github.com/denolehov/obsidian-git/commit/c529a377195fea76028b424d5973aae80498670e)), closes [#375](https://github.com/denolehov/obsidian-git/issues/375)

## [2.10.0](https://github.com/denolehov/obsidian-git/compare/2.9.4...2.10.0) (2022-11-08)


### Features

* log git commands ([a63bb8a](https://github.com/denolehov/obsidian-git/commit/a63bb8a0063b69cc020a0fd0017b42d7ee31ed1e))


### Bug Fixes

* reorder settings item ([8d5b596](https://github.com/denolehov/obsidian-git/commit/8d5b59658500329b9f52a68127d619c3f5016906))

### [2.9.4](https://github.com/denolehov/obsidian-git/compare/2.9.3...2.9.4) (2022-11-04)


### Bug Fixes

* unset config on empty value ([d0f927e](https://github.com/denolehov/obsidian-git/commit/d0f927ecec9aeeae4ee86873511a208bf943e29c))

### [2.9.3](https://github.com/denolehov/obsidian-git/compare/2.9.2...2.9.3) (2022-11-03)

### [2.9.2](https://github.com/denolehov/obsidian-git/compare/2.9.1...2.9.2) (2022-11-02)


### Bug Fixes

* detect network unreachable ([76b894c](https://github.com/denolehov/obsidian-git/commit/76b894c21085ff99d2f0bbaf1c4f46351e3f19f1)), closes [#211](https://github.com/denolehov/obsidian-git/issues/211)
* hide notification on mobile ([7d62527](https://github.com/denolehov/obsidian-git/commit/7d6252795ca62f4176fee90d674041659a0a1d9f)), closes [#292](https://github.com/denolehov/obsidian-git/issues/292)

### [2.9.1](https://github.com/denolehov/obsidian-git/compare/2.9.0...2.9.1) (2022-11-02)


### Bug Fixes

* set path env var ([8a2ae4d](https://github.com/denolehov/obsidian-git/commit/8a2ae4dfe2ebb0023a351c251845d29a311a9560))

## [2.9.0](https://github.com/denolehov/obsidian-git/compare/2.8.0...2.9.0) (2022-11-01)


### Features

* custom PATH env paths ([2c42609](https://github.com/denolehov/obsidian-git/commit/2c4260942a738421bf517f1b0d063b536345f8bf))


### Bug Fixes

* store username in localstorage ([f3668ac](https://github.com/denolehov/obsidian-git/commit/f3668ac23f13d263e50b7d6b716d91150a11b6c7))

## [2.8.0](https://github.com/denolehov/obsidian-git/compare/2.7.0...2.8.0) (2022-10-18)


### Features

* new discard icon ([730e9a6](https://github.com/denolehov/obsidian-git/commit/730e9a6405b4018dc987b29c0a156feb01b583f2))


### Bug Fixes

* align buttons ([a09bc4a](https://github.com/denolehov/obsidian-git/commit/a09bc4ac2b5165b11a740230db55a4ef05e3c219))
* center buttons in discard modal ([79a1e86](https://github.com/denolehov/obsidian-git/commit/79a1e86ce5ba7e039393c49414b0e408e940aaa5))
* create .gitignore if not exists ([ac8e3ee](https://github.com/denolehov/obsidian-git/commit/ac8e3ee380340fbeedf0dac8e80a4c28aeadffa8))
* full directory path on hover ([0f2c9d5](https://github.com/denolehov/obsidian-git/commit/0f2c9d56b1733450283af487c740d01908201284))

## [2.7.0](https://github.com/denolehov/obsidian-git/compare/2.6.0...2.7.0) (2022-10-18)


### Features

* discard all changes ([3461a30](https://github.com/denolehov/obsidian-git/commit/3461a300ee563a316faf5d198473f2ccc323b1e8))
* discard directories ([149805f](https://github.com/denolehov/obsidian-git/commit/149805f24e310e2b225be904c75094b90d38dd33))
* stage/unstage button on category ([3373e6d](https://github.com/denolehov/obsidian-git/commit/3373e6d0ee4f2a4d84e7b3513fd7712046b2e889))


### Bug Fixes

* correct height for textarea ([b44c900](https://github.com/denolehov/obsidian-git/commit/b44c9008db9b12b1e4f23ef5fc87151618953231))
* jittering of refresh button ([dbf36b2](https://github.com/denolehov/obsidian-git/commit/dbf36b2a63617b4d937f75326cd007ea08cfb622))
* sum folder paths in n depth ([e690164](https://github.com/denolehov/obsidian-git/commit/e690164c0ef6bc1749008357299f92b9a244d960))
* unstage all on mobile ([4507fdb](https://github.com/denolehov/obsidian-git/commit/4507fdb9c6f2f5c890198a18980586d892786d0e))
* unstage dir ([3d421b7](https://github.com/denolehov/obsidian-git/commit/3d421b70e0611b5dbbd91c23668e8b98df57116a))
* unstage folder on desktop ([56afe51](https://github.com/denolehov/obsidian-git/commit/56afe510ade79fab52a8a1aa2a9c15739d16a904))

## [2.6.0](https://github.com/denolehov/obsidian-git/compare/2.5.1...2.6.0) (2022-10-13)


### Features

* combine multiple empty directory into one in git view ([4e45e6a](https://github.com/denolehov/obsidian-git/commit/4e45e6accc468402033c5b56d6fb56ec5b461c1e))
* redesign source control view ([06f3c22](https://github.com/denolehov/obsidian-git/commit/06f3c229cfd199c716401ad1f4e524e2e23bc4f7))
* stage/unstage directory ([61b3eb3](https://github.com/denolehov/obsidian-git/commit/61b3eb3ac38553ef4cda0512be19b1f4480d613c))

### [2.5.1](https://github.com/denolehov/obsidian-git/compare/2.5.0...2.5.1) (2022-09-29)


### Bug Fixes

* push with file named like branch ([2664bfe](https://github.com/denolehov/obsidian-git/commit/2664bfe633e9ea0c76123ed7b0d4ac56aaf05b10)), closes [#171](https://github.com/denolehov/obsidian-git/issues/171)

## [2.5.0](https://github.com/denolehov/obsidian-git/compare/2.4.1...2.5.0) (2022-09-28)


### Features

* improve source control view style ([d5647a8](https://github.com/denolehov/obsidian-git/commit/d5647a8e2e49c8a77f28854bb4c276a17f390d55))


### Bug Fixes

* reveal source control view ([c88a1b4](https://github.com/denolehov/obsidian-git/commit/c88a1b43633e9964e3a9f60e94c0dc7f8307edc1))

### [2.4.1](https://github.com/denolehov/obsidian-git/compare/2.4.0...2.4.1) (2022-09-22)


### Bug Fixes

* keep git view on unload ([8b846da](https://github.com/denolehov/obsidian-git/commit/8b846da0010a852b5422d64034c1e4b309fa7f35)), closes [#321](https://github.com/denolehov/obsidian-git/issues/321)

## [2.4.0](https://github.com/denolehov/obsidian-git/compare/2.3.0...2.4.0) (2022-09-22)


### Features

* prefill edit remote modal ([223193c](https://github.com/denolehov/obsidian-git/commit/223193c51b362788a0682dc598c7d0eefa9ccdf0))


### Bug Fixes

* middle click to open file/diff in new tab ([ddb1164](https://github.com/denolehov/obsidian-git/commit/ddb1164b10f5e0d373daaa4cd8ec4d60119cc544))

## [2.3.0](https://github.com/denolehov/obsidian-git/compare/2.2.1...2.3.0) (2022-09-21)


### Features

* branch management ([caaacd1](https://github.com/denolehov/obsidian-git/commit/caaacd11c8634e86b01dc19dfb57b546adedf7e6)), closes [#132](https://github.com/denolehov/obsidian-git/issues/132) [#220](https://github.com/denolehov/obsidian-git/issues/220)


### Bug Fixes

* backup with reset sync method ([41a00ff](https://github.com/denolehov/obsidian-git/commit/41a00ff8b17212a23c515d0f19be69a0b8d2f1c1)), closes [#319](https://github.com/denolehov/obsidian-git/issues/319)

### [2.2.1](https://github.com/denolehov/obsidian-git/compare/2.2.0...2.2.1) (2022-09-20)


### Bug Fixes

* localstorage migration ([1d9391a](https://github.com/denolehov/obsidian-git/commit/1d9391a970624f03fcc60fb68f3bd8ee450af24b))

## [2.2.0](https://github.com/denolehov/obsidian-git/compare/2.1.2...2.2.0) (2022-09-20)


### Features

* diff view on mobile ([86b4d5a](https://github.com/denolehov/obsidian-git/commit/86b4d5ad4be23b420fe0efd0f3dfd989047be23a)), closes [#302](https://github.com/denolehov/obsidian-git/issues/302)


### Bug Fixes

* respect obsidian default hotkey for open file ([b8631f4](https://github.com/denolehov/obsidian-git/commit/b8631f4ff0b9a0bdeacc829248195085f4512f1d)), closes [#306](https://github.com/denolehov/obsidian-git/issues/306)
* save localstorage per vault ([a3c4e4f](https://github.com/denolehov/obsidian-git/commit/a3c4e4f8916b78160de074e72444c5ccd91c32b2))

### [2.1.2](https://github.com/denolehov/obsidian-git/compare/2.1.1...2.1.2) (2022-09-19)


### Bug Fixes

* respect obsidian default hotkey for open diff ([271ec02](https://github.com/denolehov/obsidian-git/commit/271ec022d4caa91ebf0f4c1d82755bb879525ef6)), closes [#306](https://github.com/denolehov/obsidian-git/issues/306)
* scroll line number in diff view ([1a01e30](https://github.com/denolehov/obsidian-git/commit/1a01e30357894980d8c5ffc77d2828af57174af7)), closes [#318](https://github.com/denolehov/obsidian-git/issues/318)

### [2.1.1](https://github.com/denolehov/obsidian-git/compare/2.1.0...2.1.1) (2022-09-15)


### Bug Fixes

* open diff in new leaf ([6914830](https://github.com/denolehov/obsidian-git/commit/6914830ce03651b7f9604ba7be81581636a34f5b)), closes [#306](https://github.com/denolehov/obsidian-git/issues/306)
* retry auth with different credentials ([f8da5f4](https://github.com/denolehov/obsidian-git/commit/f8da5f455a15f27e93e8b317af3bf9dba7dc3d57)), closes [#296](https://github.com/denolehov/obsidian-git/issues/296)

## [2.1.0](https://github.com/denolehov/obsidian-git/compare/2.0.3...2.1.0) (2022-09-08)


### Features

* disable plugin per device ([82b2c1a](https://github.com/denolehov/obsidian-git/commit/82b2c1ad82196927eda16b965094f025a1ed2960)), closes [#301](https://github.com/denolehov/obsidian-git/issues/301)
* specify source control refresh timer ([a1ecb1b](https://github.com/denolehov/obsidian-git/commit/a1ecb1b39954422de150169a71d0b9da8ee84167)), closes [#199](https://github.com/denolehov/obsidian-git/issues/199)

### [2.0.3](https://github.com/denolehov/obsidian-git/compare/2.0.2...2.0.3) (2022-09-06)


### Bug Fixes

* don't show mobile notice on new installation ([218f002](https://github.com/denolehov/obsidian-git/commit/218f002f433ec3a69f92ebfdf1876c50cd99e85c))

### [2.0.2](https://github.com/denolehov/obsidian-git/compare/2.0.1...2.0.2) (2022-09-06)


### Bug Fixes

* don't show mobile notice on mobile ([c93ddfa](https://github.com/denolehov/obsidian-git/commit/c93ddfaee5d7621248f2ed1183b8819a7d216706))

### [2.0.1](https://github.com/denolehov/obsidian-git/compare/2.0.0...2.0.1) (2022-09-06)

## [2.0.0](https://github.com/denolehov/obsidian-git/compare/1.31.0...2.0.0) (2022-09-06)


### ⚠ BREAKING CHANGES

* mobile support

### Features

* mobile support ([9ffda76](https://github.com/denolehov/obsidian-git/commit/9ffda762dbc0cba380942acdeabcb66adce8253d)), closes [#57](https://github.com/denolehov/obsidian-git/issues/57)


### Bug Fixes

* password field description ([9dc5f7c](https://github.com/denolehov/obsidian-git/commit/9dc5f7c7bab3a3b1b24d42ae2fadb10e48cbc292)), closes [#293](https://github.com/denolehov/obsidian-git/issues/293)

## [1.31.0](https://github.com/denolehov/obsidian-git/compare/1.30.0...1.31.0) (2022-08-28)


### Features

* command to backup and close Obsidian ([c144d80](https://github.com/denolehov/obsidian-git/commit/c144d80e719fae5d3aba6f0ff5535172993a2c69)), closes [#13](https://github.com/denolehov/obsidian-git/issues/13)


### Bug Fixes

* **mobile** don't show push notice on empty push ([9986667](https://github.com/denolehov/obsidian-git/commit/998666778ed07f380b6a4057afc33ca637c472c7))
* set upstream branch for existing remote branch ([c454b9d](https://github.com/denolehov/obsidian-git/commit/c454b9d7ae3323e5c9f4e758e139e97eb85ec40e)), closes [#224](https://github.com/denolehov//github.com/denolehov/obsidian-git/issues/224/issues/issuecomment-1229136511)

## [1.30.0](https://github.com/denolehov/obsidian-git/compare/1.29.2...1.30.0) (2022-08-24)


### Features

* store git binary path in localstorage ([bd8bafc](https://github.com/denolehov/obsidian-git/commit/bd8bafc904e0f14501a9985c0ae490bea98297be)), closes [#283](https://github.com/denolehov/obsidian-git/issues/283)


### Bug Fixes

* **mobile:** clone and delete local config dir ([9b0bc8a](https://github.com/denolehov/obsidian-git/commit/9b0bc8afb2f21440382f40a442dfc7b1bd369cca))
* **mobile:** readdir with empty base path ([1c38b91](https://github.com/denolehov/obsidian-git/commit/1c38b913d15ed6a2410eeb9b0da7ae9675ef3ab3))
* respect custom base path in open in github ([c4e8acf](https://github.com/denolehov/obsidian-git/commit/c4e8acf62586b477434bd0c7e55c3ea0c0c99e8e)), closes [#284](https://github.com/denolehov/obsidian-git/issues/284)
* too many changes to display ([c4bf4eb](https://github.com/denolehov/obsidian-git/commit/c4bf4eb8bd7d3e9110b354910eed9e29bafbafa6))

### [1.29.2](https://github.com/denolehov/obsidian-git/compare/1.29.1...1.29.2) (2022-08-22)


### Bug Fixes

* catch ssh network failure ([62e4a6a](https://github.com/denolehov/obsidian-git/commit/62e4a6a255e2ceedd38e4a016fa92f407e052485)), closes [#211](https://github.com/denolehov/obsidian-git/issues/211)
* diff of new file ([92d24bf](https://github.com/denolehov/obsidian-git/commit/92d24bf8f25749f48ea8646088adec55a1ae2c25)), closes [#277](https://github.com/denolehov/obsidian-git/issues/277)
* **mobile:** set correct base path after clone ([3a69b79](https://github.com/denolehov/obsidian-git/commit/3a69b79583d3e76b22b36ef5c414fc4b98d1fdb7)), closes [#282](https://github.com/denolehov/obsidian-git/issues/282)
* require upstream branch for pull ([3fac8ad](https://github.com/denolehov/obsidian-git/commit/3fac8ad86f8885ca322865b6e27f4a43b804a6ce)), closes [#261](https://github.com/denolehov/obsidian-git/issues/261)

### [1.29.1](https://github.com/denolehov/obsidian-git/compare/1.29.0...1.29.1) (2022-08-19)


### Bug Fixes

* export mock IsomorphicGit.ts ([aa5fa37](https://github.com/denolehov/obsidian-git/commit/aa5fa37579903243f6623fa99592203c76cd5478)), closes [#281](https://github.com/denolehov/obsidian-git/issues/281)

## [1.29.0](https://github.com/denolehov/obsidian-git/compare/1.28.0...1.29.0) (2022-08-19)


### Features

* add delete repo command ([26cdfb8](https://github.com/denolehov/obsidian-git/commit/26cdfb8629f2909e019fecebecd6ff745ad0b932))
* add to .gitignore command ([c824903](https://github.com/denolehov/obsidian-git/commit/c824903ea8572619b147b405dee76e51b4970f9c))
* edit .gitignore ([1cad1b7](https://github.com/denolehov/obsidian-git/commit/1cad1b72649c4ad7da931a32bb891176e2f96b3d))
* commit only staged files ([f6f4a97](https://github.com/denolehov/obsidian-git/commit/f6f4a97c36acda5950bb156f1732ab0ece89a63e))
* fix clone overwrite ([d853a4e](https://github.com/denolehov/obsidian-git/commit/d853a4ea00f636bcf98a3e5c31ad360923f30219))
* hide settings when git is not ready ([4c40556](https://github.com/denolehov/obsidian-git/commit/4c40556653132767d1dd424fa37c75ccf7cafe86))
* set author to config ([f40920d](https://github.com/denolehov/obsidian-git/commit/f40920d9970dcdf6146b9b108b76cad88d166fdc))
* stage and unstage to context menu ([081ad1d](https://github.com/denolehov/obsidian-git/commit/081ad1dda58f6ae8a3458bf8568de5165824410d))


### Bug Fixes

* abort edit remotes on no url ([e617278](https://github.com/denolehov/obsidian-git/commit/e617278e68019583b39ac961de27fe84d46f572a))
* require valid repo for list changed files ([fe300c7](https://github.com/denolehov/obsidian-git/commit/fe300c767d4dac81ce9968e29106eaaf6aeb3ea2))
* restart notice after clone ([140bed5](https://github.com/denolehov/obsidian-git/commit/140bed5cde1772b8c59a72db9ffa89a6eac9151e))
* set base path after clone ([0327090](https://github.com/denolehov/obsidian-git/commit/032709096b6afd8411868135596d5b9ef6c19fbd))
* stage individual file ([76e317b](https://github.com/denolehov/obsidian-git/commit/76e317b5320b3a7e9ab303b402518f3791333a8d))

## [1.28.0](https://github.com/denolehov/obsidian-git/compare/1.27.1...1.28.0) (2022-07-25)


### Features

* stage and unstage current file ([f014e52](https://github.com/denolehov/obsidian-git/commit/f014e52e11cbb345a313914a9e71e0807a0d4197)), closes [#265](https://github.com/denolehov/obsidian-git/issues/265)


### Bug Fixes

* register event listener after initial load ([d32d0f4](https://github.com/denolehov/obsidian-git/commit/d32d0f4bc26db390da8008e8af878eef97ba98f4))

### [1.27.1](https://github.com/denolehov/obsidian-git/compare/1.27.0...1.27.1) (2022-07-20)


### Bug Fixes

* check for too big files in source control view ([2275d4f](https://github.com/denolehov/obsidian-git/commit/2275d4f716c00a305bf4371e9cb1b934669b2272))

## [1.27.0](https://github.com/denolehov/obsidian-git/compare/1.26.4...1.27.0) (2022-07-20)


### Features

* check for too big files ([f0f3942](https://github.com/denolehov/obsidian-git/commit/f0f394246fb001dcd4b5b42f17d764f7a95a4486)), closes [#248](https://github.com/denolehov/obsidian-git/issues/248) [#189](https://github.com/denolehov/obsidian-git/issues/189)

### [1.26.4](https://github.com/denolehov/obsidian-git/compare/1.26.3...1.26.4) (2022-07-20)


### Bug Fixes

* Version History Diff for empty base path ([3b9b699](https://github.com/denolehov/obsidian-git/commit/3b9b69938d97c1a257755a4896857fe8ee8db557)), closes [#263](https://github.com/denolehov/obsidian-git/issues/263)

### [1.26.3](https://github.com/denolehov/obsidian-git/compare/1.26.2...1.26.3) (2022-07-17)


### Bug Fixes

* commit only staged files, again ([ba35555](https://github.com/denolehov/obsidian-git/commit/ba35555eebbac5f4a69aaa7da6847928e0ddd017)), closes [#253](https://github.com/denolehov/obsidian-git/issues/253)

### [1.26.2](https://github.com/denolehov/obsidian-git/compare/1.26.1...1.26.2) (2022-07-16)


### Bug Fixes

* clarification about disabling notifications ([#249](https://github.com/denolehov/obsidian-git/issues/249)) ([f90b284](https://github.com/denolehov/obsidian-git/commit/f90b284f1f4140eab6aec1b77353cb52e661a8e3))
* commit only staged files ([f71fdf5](https://github.com/denolehov/obsidian-git/commit/f71fdf58271c1490887f057c6ecc5e6d3689dbd4)), closes [#253](https://github.com/denolehov/obsidian-git/issues/253)
* open diff view in correct pane ([96d2913](https://github.com/denolehov/obsidian-git/commit/96d2913c67bf8348953440954d5e41986c6b121b)), closes [#252](https://github.com/denolehov/obsidian-git/issues/252)
* open files from source control view ([0d5ec26](https://github.com/denolehov/obsidian-git/commit/0d5ec262187281517c4a63a78c417c8d9940750f)), closes [#258](https://github.com/denolehov/obsidian-git/issues/258)

### [1.26.1](https://github.com/denolehov/obsidian-git/compare/1.26.0...1.26.1) (2022-06-09)


### Bug Fixes

* open file with custom base path ([8a11666](https://github.com/denolehov/obsidian-git/commit/8a11666e6d430ed3fba952a584b9f8af6cc462fe))
* use correct path with custom base path ([0d86e68](https://github.com/denolehov/obsidian-git/commit/0d86e6872fa16c809f7bf71f05e344acaf31008d))

## [1.26.0](https://github.com/denolehov/obsidian-git/compare/1.25.3...1.26.0) (2022-06-09)


### Features

* different intervals for commit and push ([59367aa](https://github.com/denolehov/obsidian-git/commit/59367aa3d0fde912cf393f5e48989758f44b82e0)), closes [#106](https://github.com/denolehov/obsidian-git/issues/106)
* show changes files count in status bar ([d091694](https://github.com/denolehov/obsidian-git/commit/d0916947546aad84f506f39f9f754a6b3c33f42c)), closes [#243](https://github.com/denolehov/obsidian-git/issues/243)


### Bug Fixes

* handle merge conflict better ([101aff9](https://github.com/denolehov/obsidian-git/commit/101aff991ecaecd57f004dd7bfd1811866b755e5))

### [1.25.3](https://github.com/denolehov/obsidian-git/compare/1.25.2...1.25.3) (2022-05-14)


### Bug Fixes

* show renamed files ([b76c783](https://github.com/denolehov/obsidian-git/commit/b76c78332978dbbf7045f94295ed3228de7132a9)), closes [#226](https://github.com/denolehov/obsidian-git/issues/226)

### [1.25.2](https://github.com/denolehov/obsidian-git/compare/1.25.1...1.25.2) (2022-05-07)


### Bug Fixes

* improve base path description ([8ee3a63](https://github.com/denolehov/obsidian-git/commit/8ee3a63fff91d4c9fd61cb9da73cc738026b8af1))

### [1.25.1](https://github.com/denolehov/obsidian-git/compare/1.25.0...1.25.1) (2022-04-22)


### Bug Fixes

* recursive submodules ([#217](https://github.com/denolehov/obsidian-git/issues/217)) ([98f566f](https://github.com/denolehov/obsidian-git/commit/98f566ffa29bb99dde44615f52fd352c099bd7f4))

## [1.25.0](https://github.com/denolehov/obsidian-git/compare/1.24.1...1.25.0) (2022-04-07)


### Features

* custom git repository root ([#209](https://github.com/denolehov/obsidian-git/issues/209)) ([4157e42](https://github.com/denolehov/obsidian-git/commit/4157e42fa6cbd0f69d8ed03169c5bc836229d6d4))
* offline mode ([6989ba4](https://github.com/denolehov/obsidian-git/commit/6989ba4fd7ce44bfac5c6f7479cf41ef8fcb5de3)), closes [#211](https://github.com/denolehov/obsidian-git/issues/211)


### Bug Fixes

* refresh source control view less frequently ([b90b1a5](https://github.com/denolehov/obsidian-git/commit/b90b1a5fa596142f42698727dd76cadd97e9bdc6))

### [1.24.1](https://github.com/denolehov/obsidian-git/compare/1.24.0...1.24.1) (2022-03-23)


### Bug Fixes

* :adhesive_bandage: More specific CSS selectors for the diff-view ([c0c9a38](https://github.com/denolehov/obsidian-git/commit/c0c9a381f2c4c0527674e4e215e2418c71d68b73))
* refresh source control view on first open ([6e75300](https://github.com/denolehov/obsidian-git/commit/6e75300424eb8d78f1a4c79caf830ce5d5fd1727))

## [1.24.0](https://github.com/denolehov/obsidian-git/compare/1.23.0...1.24.0) (2022-03-18)


### Features

* add show, diff, log as api ([b3a72a4](https://github.com/denolehov/obsidian-git/commit/b3a72a46dfb917b28ca9af7848994668d1846b64))

## [1.23.0](https://github.com/denolehov/obsidian-git/compare/1.22.0...1.23.0) (2022-03-18)


### Features

* reworked diff view handling ([be4856b](https://github.com/denolehov/obsidian-git/commit/be4856b0f3a6ecc7f4416f98d9eb05a992b61443)), closes [#202](https://github.com/denolehov/obsidian-git/issues/202) [#203](https://github.com/denolehov/obsidian-git/issues/203)


### Bug Fixes

* expand selection width on stagedFileComponent too ([daf8ac7](https://github.com/denolehov/obsidian-git/commit/daf8ac7e4279d6334fd36b4706c85c77d2e8dbbe))
* highlight staged file on hover ([ef0d3e6](https://github.com/denolehov/obsidian-git/commit/ef0d3e6640712669e8d303d7cf1bff7dbdedbc7d))
* refresh source control view on exception ([c1eee7b](https://github.com/denolehov/obsidian-git/commit/c1eee7b0d378677dff4da75fc3945b88c3ede7d3)), closes [#183](https://github.com/denolehov/obsidian-git/issues/183)

## [1.22.0](https://github.com/denolehov/obsidian-git/compare/1.21.2...1.22.0) (2022-03-02)


### Features

* separate commit message for auto backup ([b59db5d](https://github.com/denolehov/obsidian-git/commit/b59db5dc816479fd6dad5d798d0979c49c8b8ccf)), closes [#197](https://github.com/denolehov/obsidian-git/issues/197)


### Bug Fixes

* automatically refresh source control ([9c2b063](https://github.com/denolehov/obsidian-git/commit/9c2b063584366226da876c9e5e6509868b6a01cd)), closes [#199](https://github.com/denolehov/obsidian-git/issues/199)
* correct pull changes count ([6aead15](https://github.com/denolehov/obsidian-git/commit/6aead155571eda2499ef1ab6377236152124df96)), closes [#198](https://github.com/denolehov/obsidian-git/issues/198)

### [1.21.2](https://github.com/denolehov/obsidian-git/compare/1.21.1...1.21.2) (2022-03-01)


### Bug Fixes

* catch git error on commit ([fe78ae3](https://github.com/denolehov/obsidian-git/commit/fe78ae364e5296a378a3d0844a3daa53b3d024c7))
* stage files without glob pattern ([99b1f6c](https://github.com/denolehov/obsidian-git/commit/99b1f6c3d61e4b2fca274531fa98359af0a8c64e)), closes [#196](https://github.com/denolehov/obsidian-git/issues/196)

### [1.21.1](https://github.com/denolehov/obsidian-git/compare/1.21.0...1.21.1) (2022-02-19)


### Bug Fixes

* better automatic backup/pull description ([10c3072](https://github.com/denolehov/obsidian-git/commit/10c307228da5c79cf62acfa2d6c90d2f519855a8)), closes [#181](https://github.com/denolehov/obsidian-git/issues/181)
* catch more git errors ([153fd82](https://github.com/denolehov/obsidian-git/commit/153fd82d7467d6c58905fa77c4376b2e79594810))
* stage filenames with leading '-' ([c06296e](https://github.com/denolehov/obsidian-git/commit/c06296e364962474299687e941fcdab8e03c9061)), closes [#184](https://github.com/denolehov/obsidian-git/issues/184)

### [1.21.0](https://github.com/denolehov/obsidian-git/compare/1.20.1...1.20.2) (2022-02-02)


### Bug Fixes

* stage files in vault below git root ([9d3c662](https://github.com/denolehov/obsidian-git/commit/9d3c6620f32b392935a689a9ff645aa664f49478)), closes [#172](https://github.com/denolehov/obsidian-git/issues/172)

### Features

* new sync method ([f1d6b33](https://github.com/denolehov/obsidian-git/commit/f1d6b334972b76271a15883c31784812f24d6878))

### [1.20.1](https://github.com/denolehov/obsidian-git/compare/1.20.0...1.20.1) (2022-01-29)


### Bug Fixes

* show correct debug console hotkey ([087582e](https://github.com/denolehov/obsidian-git/commit/087582e429d96345c1f1ee17e0d6a1eeb71d9489)), closes [#175](https://github.com/denolehov/obsidian-git/issues/175)

## [1.20.0](https://github.com/denolehov/obsidian-git/compare/1.19.0...1.20.0) (2022-01-08)


### Features

* :sparkles: Add "Open File in GitHub" Command, fix [#149](https://github.com/denolehov/obsidian-git/issues/149) ([2c216d0](https://github.com/denolehov/obsidian-git/commit/2c216d033fe0a82a68cf4951d72b5af4e9d10c87))
* :sparkles: Add Command to open file history on GitHub ([e7dd288](https://github.com/denolehov/obsidian-git/commit/e7dd288ba85a87e783d18c2b51e9027ec20f94fa))
* :sparkles: Add Diff View ([78cd43f](https://github.com/denolehov/obsidian-git/commit/78cd43fadece2b2d6bed80582bba18d842632e1a)), closes [#158](https://github.com/denolehov/obsidian-git/issues/158)
* :sparkles: Add Folder view to Sidebar ([919dc44](https://github.com/denolehov/obsidian-git/commit/919dc4435f08e8b3217ee66237a0671687bdb5a1)), closes [#134](https://github.com/denolehov/obsidian-git/issues/134)
* :sparkles: Allow multiline commit messages, fix [#157](https://github.com/denolehov/obsidian-git/issues/157) ([80ea17e](https://github.com/denolehov/obsidian-git/commit/80ea17e34f07f43bfe2aef1b5c520160a0e71e10))
* Add toggle in Settings to choose default layout ([38c7240](https://github.com/denolehov/obsidian-git/commit/38c7240918d1e463044431d976b9025eb1fdc318))


### Bug Fixes

* :bug: Fix RegEx for openInGitHub ([ca59a2d](https://github.com/denolehov/obsidian-git/commit/ca59a2db581643cfd77f3663850fe1243efe4260))
* :children_crossing: Show diff on double click ([407dcc0](https://github.com/denolehov/obsidian-git/commit/407dcc05d6e9679c7487c1d2dfa78f580c16b5da))
* catch diff for deleted file ([710cd2c](https://github.com/denolehov/obsidian-git/commit/710cd2cc6e69c1561277c762518ba0ba903e91f3))
* different tree data structure ([0fd2f95](https://github.com/denolehov/obsidian-git/commit/0fd2f954b66f0336475f8babb1904130711cbc50))
* many minor fixes ([7d29bef](https://github.com/denolehov/obsidian-git/commit/7d29bef4ed7793b399a704f73a3ab458e043e595))
* refresh source control view on change ([45e54e2](https://github.com/denolehov/obsidian-git/commit/45e54e21d097492d35084e4b7c52e1f7df5c59b1))
* remove tree structure from settings ([5af00ae](https://github.com/denolehov/obsidian-git/commit/5af00ae593d573016694da3bc9bbb218c8baa978))

## [1.19.0](https://github.com/denolehov/obsidian-git/compare/1.18.1...1.19.0) (2021-12-22)


### Features

* add rebase option for pull ([b04e444](https://github.com/denolehov/obsidian-git/commit/b04e444e99ca31d1abb1e4bfdd81cbdaca88caec)), closes [#155](https://github.com/denolehov/obsidian-git/issues/155)

### [1.18.1](https://github.com/denolehov/obsidian-git/compare/1.18.0...1.18.1) (2021-12-09)


### Bug Fixes

* use more specific css class ([471b257](https://github.com/denolehov/obsidian-git/commit/471b257671b861f69747882fcd67be22f7dca287))

## [1.18.0](https://github.com/denolehov/obsidian-git/compare/1.17.0...1.18.0) (2021-12-09)


### Features

* add commands for push and commit ([82dd037](https://github.com/denolehov/obsidian-git/commit/82dd037189a4dbe1b8ef7cad13d0c11b0817af2d)), closes [#122](https://github.com/denolehov/obsidian-git/issues/122)
* use icons for status bar ([96dcbc4](https://github.com/denolehov/obsidian-git/commit/96dcbc443369803a6f11d69ca80f34176025864a)), closes [#147](https://github.com/denolehov/obsidian-git/issues/147)


### Bug Fixes

* show error notices for a longer time ([d455489](https://github.com/denolehov/obsidian-git/commit/d45548993bcb95924a28f723d616e6c2f8c7c293)), closes [#148](https://github.com/denolehov/obsidian-git/issues/148)

## [1.17.0](https://github.com/denolehov/obsidian-git/compare/1.16.2...1.17.0) (2021-12-08)


### Features

* add hostname commit message placeholder ([32d8382](https://github.com/denolehov/obsidian-git/commit/32d8382c804b1e86effb409246dad06cad78506d)), closes [#146](https://github.com/denolehov/obsidian-git/issues/146)


### Bug Fixes

* clear autobackup/pull correctly ([1c5eeab](https://github.com/denolehov/obsidian-git/commit/1c5eeab098609ab5925a2ddda3aeef76db2660b3))
* don't start autobackup with 0 interval time ([a36c741](https://github.com/denolehov/obsidian-git/commit/a36c741cb1a5e557615768af3656dc76d6391ed0))

### [1.16.2](https://github.com/denolehov/obsidian-git/compare/1.16.1...1.16.2) (2021-11-29)


### Bug Fixes

* don't use new auto backup after change by default ([cc95a96](https://github.com/denolehov/obsidian-git/commit/cc95a96613386c30c379457d7d33198808403c63))

### [1.16.1](https://github.com/denolehov/obsidian-git/compare/1.16.0...1.16.1) (2021-11-28)


### Bug Fixes

* proper utf-8 encoding ([1bc7d28](https://github.com/denolehov/obsidian-git/commit/1bc7d2844ec3b3a954abe3c11766fd1e2d1c1b2a)), closes [#121](https://github.com/denolehov/obsidian-git/issues/121)

## [1.16.0](https://github.com/denolehov/obsidian-git/compare/1.15.1...1.16.0) (2021-11-28)


### Features

* add auto backup after last change ([192a947](https://github.com/denolehov/obsidian-git/commit/192a9474af7fbd607d451c8b95afaa46c84b7a9d)), closes [#140](https://github.com/denolehov/obsidian-git/issues/140)

### [1.15.1](https://github.com/denolehov/obsidian-git/compare/1.15.0...1.15.1) (2021-11-25)


### Bug Fixes

* use custom git binary path for git check ([7188753](https://github.com/denolehov/obsidian-git/commit/718875300f6f9d22e8773a5336bd70b095f63845))

## [1.15.0](https://github.com/denolehov/obsidian-git/compare/1.14.3...1.15.0) (2021-11-11)


### Features

* add custom commit message to auto backup ([b3d8077](https://github.com/denolehov/obsidian-git/commit/b3d8077bab29edf2602cd57a392969abf89e4241)), closes [#135](https://github.com/denolehov/obsidian-git/issues/135)

### [1.14.3](https://github.com/denolehov/obsidian-git/compare/1.14.2...1.14.3) (2021-11-03)

### Bug Fixes

* open file from Git view when no other file is opened

### [1.14.2](https://github.com/denolehov/obsidian-git/compare/1.14.1...1.14.2) (2021-11-01)


### Bug Fixes

* replace '?' by 'U' for untracked files ([64cf162](https://github.com/denolehov/obsidian-git/commit/64cf1623e50513f0f46141f6860650d0a865238c))
* wrap tooltip for long paths ([1fc4c1f](https://github.com/denolehov/obsidian-git/commit/1fc4c1fd7afbdbb08d7e3a061dd5d602e6f195a3))

### [1.14.1](https://github.com/denolehov/obsidian-git/compare/1.14.0...1.14.1) (2021-11-01)


### Bug Fixes

* list files in commit body ([f52a18b](https://github.com/denolehov/obsidian-git/commit/f52a18b3a3b6b05d643541baf2f74c32bb3e88d4)), closes [#131](https://github.com/denolehov/obsidian-git/issues/131)

## [1.14.0](https://github.com/denolehov/obsidian-git/compare/1.13.1...1.14.0) (2021-10-31)


### Features

* New Git view in the sidebar to stage and commit individual files. Thanks to @phibr0 for making the UI


### [1.13.1](https://github.com/denolehov/obsidian-git/compare/1.13.0...1.13.1) (2021-09-30)


### Bug Fixes

* changed files path was wrong with whitespaces ([043f02f](https://github.com/denolehov/obsidian-git/commit/043f02f89daea8051612b5f7816564ba7f7657e8)), closes [#119](https://github.com/denolehov/obsidian-git/issues/119)

## [1.13.0](https://github.com/denolehov/obsidian-git/compare/1.12.0...1.13.0) (2021-09-21)


### Features

* support cloning remote repos ([ab5ece7](https://github.com/denolehov/obsidian-git/commit/ab5ece75ceba3af5845770dc029732d5657720a3))

## [1.12.0](https://github.com/denolehov/obsidian-git/compare/1.11.0...1.12.0) (2021-09-18)


### Features

* support custom git binary path ([7793035](https://github.com/denolehov/obsidian-git/commit/77930351622a86ef3babdb4d60acbc8ff334cc84)), closes [#113](https://github.com/denolehov/obsidian-git/issues/113)

## [1.11.0](https://github.com/denolehov/obsidian-git/compare/1.10.2...1.11.0) (2021-09-15)


### Features

* add remote editing ([f70363b](https://github.com/denolehov/obsidian-git/commit/f70363b522c2e144260411d01e26108f7dedb735))
* support initalizing a new repo ([0fd2062](https://github.com/denolehov/obsidian-git/commit/0fd20627c3c289a05e0aba179b36badfe11d2414))
* support selecting upstream branch ([013878e](https://github.com/denolehov/obsidian-git/commit/013878e378bdbc6bab23c94615fba0c2bb72e1dc))

### [1.10.2](https://github.com/denolehov/obsidian-git/compare/1.10.1...1.10.2) (2021-09-05)


### Bug Fixes

* plugin status bar now displays time from last update (push or pull) ([b835fc3](https://github.com/denolehov/obsidian-git/commit/b835fc3548884dca4084ec37a296ebebf9c9dab7))

### [1.10.1](https://github.com/denolehov/obsidian-git/compare/1.10.0...1.10.1) (2021-08-19)


### Bug Fixes

* checkRequirements cant find user.name/email ([1994a44](https://github.com/denolehov/obsidian-git/commit/1994a44c5ecec121965505e2627d26460425e4dd))
* rename commands to be more consistend ([5e07e80](https://github.com/denolehov/obsidian-git/commit/5e07e80a13640b6ba587185880ba01befbd563ac))

## [1.10.0](https://github.com/denolehov/obsidian-git/compare/1.9.3...1.10.0) (2021-08-11)


### Features

* add submodules support ([2a4ce6d](https://github.com/denolehov/obsidian-git/commit/2a4ce6d47696cd6667b639c8479b37f61346e9be)), closes [#93](https://github.com/denolehov/obsidian-git/issues/93)


### Bug Fixes

* Changed the branchLocal command to branch with no-color ([dbd93cf](https://github.com/denolehov/obsidian-git/commit/dbd93cfe5f127874a514837577b42b34a07bcf3e))

### [1.9.3](https://github.com/denolehov/obsidian-git/compare/1.9.2...1.9.3) (2021-07-13)


### Bug Fixes

* storing lastAutos in a file caused many problems ([2812d94](https://github.com/denolehov/obsidian-git/commit/2812d948f0c6b0534c507425249c93397f71e973)), closes [#90](https://github.com/denolehov/obsidian-git/issues/90) [#78](https://github.com/denolehov/obsidian-git/issues/78)

### [1.9.2](https://github.com/denolehov/obsidian-git/compare/1.9.1...1.9.2) (2021-05-12)


### Bug Fixes

* plugin started wrong when normally enabled ([dc9c4b1](https://github.com/denolehov/obsidian-git/commit/dc9c4b13387067793e315a9aca24c05c75fb6d38))
* storing of last auto backup/pull caused merge conflicts ([cf6f279](https://github.com/denolehov/obsidian-git/commit/cf6f27900d05eb3ffe74222950cd00270879fd6c)), closes [#74](https://github.com/denolehov/obsidian-git/issues/74)

### [1.9.1](https://github.com/denolehov/obsidian-git/compare/1.9.0...1.9.1) (2021-05-07)


### Bug Fixes

* init slowed Obsidian startup time down ([e3f559c](https://github.com/denolehov/obsidian-git/commit/e3f559c14b54ef97eb8d07397d8b92250eeb3d62)), closes [#72](https://github.com/denolehov/obsidian-git/issues/72)

## [1.9.0](https://github.com/denolehov/obsidian-git/compare/1.8.1...1.9.0) (2021-05-02)


### Features

* add env var OBSIDIAN_GIT for scripting ([2b76097](https://github.com/denolehov/obsidian-git/commit/2b7609774cfd8689297c23ea672264cea6255409))
* add option to disable status bar ([0ab55d3](https://github.com/denolehov/obsidian-git/commit/0ab55d3f0805a031e9e3ec5b5cfa21d8c5026330)), closes [#70](https://github.com/denolehov/obsidian-git/issues/70)
* auto pull/backup outlives session ([7ec00e7](https://github.com/denolehov/obsidian-git/commit/7ec00e7cfd113aeb827f282d765ca061d85235a6)), closes [#68](https://github.com/denolehov/obsidian-git/issues/68)

### [1.8.1](https://github.com/denolehov/obsidian-git/compare/1.8.0...1.8.1) (2021-04-12)


### Bug Fixes

* add promise queue ([f95d71a](https://github.com/denolehov/obsidian-git/commit/f95d71a5475107dbf1bbacfb3bdb4e74fd190d15)), closes [#61](https://github.com/denolehov/obsidian-git/issues/61)

## [1.8.0](https://github.com/denolehov/obsidian-git/compare/1.7.0...1.8.0) (2021-03-31)


### Features

* open not supported files in changed files modal in default app ([93930e0](https://github.com/denolehov/obsidian-git/commit/93930e079384d0ae2ed165e94241dc1d0acee82a))

## [1.7.0](https://github.com/denolehov/obsidian-git/compare/1.6.1...1.7.0) (2021-03-24)


### Features

* add git initialization and conflict files status to statusbar ([ba0ef11](https://github.com/denolehov/obsidian-git/commit/ba0ef11a5abcc8ff11d9e33ca8157a283d06920b))
* auto pull on specified interval ([2aa7fb8](https://github.com/denolehov/obsidian-git/commit/2aa7fb866e41c1f7170b723a35d9acd2942921b0)), closes [#59](https://github.com/denolehov/obsidian-git/issues/59)
* conflict files support ([358dc6e](https://github.com/denolehov/obsidian-git/commit/358dc6e492e6ef8156687535d14a9070ebadfb30)), closes [#38](https://github.com/denolehov/obsidian-git/issues/38)
* list changed files ([5e28b94](https://github.com/denolehov/obsidian-git/commit/5e28b9449f3f7f978fe825fb102b61fb27d191e4))


### Bug Fixes

* conflict files pane was opened on pull error ([8d43e7b](https://github.com/denolehov/obsidian-git/commit/8d43e7b32e7b5082c3518537ce32c0627b35dfb2))

### [1.6.1](https://github.com/denolehov/obsidian-git/compare/1.6.0...1.6.1) (2021-03-17)


### Bug Fixes

* disable check for root git repository ([49a68e0](https://github.com/denolehov/obsidian-git/commit/49a68e0396b46c09a49f03898b804f97d1a709b3)), closes [#55](https://github.com/denolehov/obsidian-git/issues/55) [#11](https://github.com/denolehov/obsidian-git/issues/11)

## [1.6.0](https://github.com/denolehov/obsidian-git/compare/1.5.0...1.6.0) (2021-03-15)


### Features

* commit changes with specified message ([e992199](https://github.com/denolehov/obsidian-git/commit/e9921994e135ac01f5eda8f23d7c4db312cedd05)), closes [#26](https://github.com/denolehov/obsidian-git/issues/26)
* list filenames affected by commit in the commit body ([0ce9ac3](https://github.com/denolehov/obsidian-git/commit/0ce9ac310c402a3a7a679fc30e591a045d3a4fb2)), closes [#3](https://github.com/denolehov/obsidian-git/issues/3)
* pull before push ([30d8798](https://github.com/denolehov/obsidian-git/commit/30d8798d433f080404bd22c8a33a1ea49b37648f)), closes [#43](https://github.com/denolehov/obsidian-git/issues/43)


### Bug Fixes

* does not push when no changes detected ([d016dee](https://github.com/denolehov/obsidian-git/commit/d016dee92db4af02446b112de580b5197a3303f3)), fixes [#33](https://github.com/denolehov/obsidian-git/issues/33)
* git repository check ([98fa9f7](https://github.com/denolehov/obsidian-git/commit/98fa9f758f9b08546c0c9319a14fd25b85af4503))
* initialization procedure ([1d71418](https://github.com/denolehov/obsidian-git/commit/1d714181d8967fa6089cd380b879ce652332a3fa)), fixes [#27](https://github.com/denolehov/obsidian-git/issues/27)
* lastUpdate gets changed when no changes are detected ([71d2a59](https://github.com/denolehov/obsidian-git/commit/71d2a59f1d5ea7f7fd08e77b1802a47d0aae3f46))
* needed tracking branch to commit ([619c5d1](https://github.com/denolehov/obsidian-git/commit/619c5d182e95c5f1ca946c56d8c002e6b3f09daf))

## [1.5.0](https://github.com/denolehov/obsidian-git/compare/v1.2.0...v1.5.0) (2020-12-08)


### Features

* add {{files}} template placeholder ([64adf0f](https://github.com/denolehov/obsidian-git/commit/64adf0f464cfdad544fec225e52798ccbb565d4d))
* add option to toggle pushing to remote


### Bug Fixes

* change "auto push" setting to "disable push" to resolve issues with obsidian settings not loading correctly ([e00014c](https://github.com/denolehov/obsidian-git/commit/e00014cb269efa6391ebeb1d1e0026d209635bfe))
* correctly update `.lastUpdate` timestamp during push/pull ([4b61297](https://github.com/denolehov/obsidian-git/commit/4b61297be84fa7940e2909ddfdd2ef1d8608e20d))
* fix plugin getting stuck at "checking repo status.." message ([4875519](https://github.com/denolehov/obsidian-git/commit/4875519f9986946f0628a343c8ffd94686b86fa4))
* fix status bar messages race conditions ([f3f0a63](https://github.com/denolehov/obsidian-git/commit/f3f0a63132e0cd38c27d0e14c08a8b7c59134a83))

## [1.4.0](https://github.com/denolehov/obsidian-git/compare/v1.3.0...v1.4.0) (2020-11-01)


### Features

* display messages in status bar (including error ones) ([e1e0fcc](https://github.com/denolehov/obsidian-git/commit/e1e0fcc26d5736637239316d5881a696f78eca30))

## [1.3.0](https://github.com/denolehov/obsidian-git/compare/v1.2.0...v1.3.0) (2020-10-31)


### Features

* add `{{numFiles}}` placeholder ([fbc6ce8](https://github.com/denolehov/obsidian-git/commit/fbc6ce85d4f6f2b183c7a41f9cbd8f2814027e92))
* add more granular customization of `{{date}}` commit message placeholder ([7063f5a](https://github.com/denolehov/obsidian-git/commit/7063f5a902c3141671ddbf3c82c2076e07cc872b))

## [1.2.0](https://github.com/denolehov/obsidian-git/compare/v1.1.0...v1.2.0) (2020-10-31)


### Features

* `master` branch is no longer hardcoded ([dc8f3bd](https://github.com/denolehov/obsidian-git/commit/dc8f3bda9751a358fdd64771eec0c6b25bb07f6d))
* allow specifying `{{date}}` placeholder in commit message ([43c5f6e](https://github.com/denolehov/obsidian-git/commit/43c5f6e509d1284411ff26332b7820710fd51c2f))
* rename "Autosave" to "Vault backup interval" ([26cd1e3](https://github.com/denolehov/obsidian-git/commit/26cd1e371ad5b7076ac1da7575983ba4f6791713))


### Bug Fixes

* fix `undefined` backup settings and rearrange settings a bit ([68f8b84](https://github.com/denolehov/obsidian-git/commit/68f8b8438c9aba3c314ee2baa857bfd1efd587d2))
* register interval functions so Obsidian properly unloads them ([717a538](https://github.com/denolehov/obsidian-git/commit/717a53811ef55907ca804ead83d7db6a4747199f))
* save settings on plugin unload ([67cd7a3](https://github.com/denolehov/obsidian-git/commit/67cd7a3f9303505b86b6399694bf1d8e4c8bff4e))

## [1.1.0](https://github.com/denolehov/obsidian-git/compare/v1.0.0...v1.1.0) (2020-10-29)


### Features

* Add "Disable notifications" setting + some minor fixes ([ec240a7](https://github.com/denolehov/obsidian-git/commit/ec240a7122656e551b93a79ad5af9b7be138b2ec))
* Add an option to automatically fetch updates from remote repository when Obsidian starts ([aa59d29](https://github.com/denolehov/obsidian-git/commit/aa59d29fb23ac5b42d8c6a644fdc413a04931966))
* Add status bar that shows status updates ([80dbf0f](https://github.com/denolehov/obsidian-git/commit/80dbf0f647fe27237bd86174feebe7987a90be63))

## [1.0.0](https://github.com/denolehov/obsidian-git/compare/v0.0.6...v1.0.0) (2020-10-27)


### Bug Fixes

* update some Notice messages ([a97c44e](https://github.com/denolehov/obsidian-git/commit/a97c44e2f5a1581e5bb8ea432deca108df8c7fde))

### [0.0.6](https://github.com/denolehov/obsidian-git/compare/v0.0.5...v0.0.6) (2020-10-27)


### Features

* Add autosave feature ([6f0d6bc](https://github.com/denolehov/obsidian-git/commit/6f0d6bc0b8b84fe6e14fcf1c85e6a6213c9da578))

### [0.0.5](https://github.com/denolehov/obsidian-git/compare/v0.0.4...v0.0.5) (2020-10-27)


### Features

* Add an ability to specify custom commit message (specified in plugin settings) ([ca67112](https://github.com/denolehov/obsidian-git/commit/ca671124c5b2dc5127b76f48ab94e63d1e2b3626))

### [0.0.4](https://github.com/denolehov/obsidian-git/compare/v0.0.3...v0.0.4) (2020-10-27)


### Features

* Improve UX a bit by showing notification of what's happening when user presses hotkey ([c562e74](https://github.com/denolehov/obsidian-git/commit/c562e746d7538923a378104d0204dad1f3f2aa61))

### [0.0.3](https://github.com/denolehov/obsidian-git/compare/v0.0.2...v0.0.3) (2020-10-27)


### Features

* add an ability to push changes to a remote repository ([f229516](https://github.com/denolehov/obsidian-git/commit/f2295165fbd77dd9ed6e4cdd2f6d085b3ee78bfe))

### [0.0.2](https://github.com/denolehov/obsidian-git/compare/v0.0.1...v0.0.2) (2020-10-27)


### Features

* Add an ability to pull changes from remote repository. ([88da6e5](https://github.com/denolehov/obsidian-git/commit/88da6e5bc01ef5066ab994e69640e0e101ed6b8f))

### 0.0.1 (2020-10-27)


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2020 Vinzent03, Denis Olehov

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# Obsidian Git Plugin

A powerful community plugin for [Obsidian.md](Obsidian.md) that brings Git integration right into your vault. Automatically commit, pull, push, and see your changes — all within Obsidian.

## 📚 Documentation

All setup instructions (including mobile), common issues, tips, and advanced configuration can be found in the 📖 [full documentation](https://publish.obsidian.md/git-doc).

> Mobile users: The plugin is **highly unstable ⚠️ !** Please check the dedicated [Mobile](#-mobile-support-%EF%B8%8F--experimental) section below.

## Key Features

- 🔁 **Automatic commit-and-sync** (commit, pull, and push) on a schedule.
- 📥 **Auto-pull on Obsidian startup**
- 📂 **Submodule support** for managing multiple repositories (desktop only and opt-in)
- 🔧 **Source Control View** to stage/unstage, commit and diff files - Open it with the `Open source control view` command.
- 📜 **History View** for browsing commit logs and changed files - Open it with the `Open history view` command.
- 🔍 **Diff View** for viewing changes in a file - Open it with the `Open diff view` command.
- 📝 **Signs in the editor** to indicate added, modified, and deleted lines/hunks (desktop only).
- GitHub integration to open files and history in your browser

> For detailed file history, consider pairing this plugin with the [Version History Diff](obsidian://show-plugin?id=obsidian-version-history-diff) plugin.

## UI Previews

### 🔧 Source Control View

Manage your file changes directly inside Obsidian like stage/unstage individual files and commit them.

![Source Control View](https://raw.githubusercontent.com/Vinzent03/obsidian-git/master/images/source-view.png)

### 📜 History View

Show the commit history of your repository. The commit message, author, date, and changed files can be shown. Author and date are disabled by default as shown in the screenshot, but can be enabled in the settings.

![History View](https://raw.githubusercontent.com/Vinzent03/obsidian-git/master/images/history-view.png)

### 🔍 Diff View 

Compare versions with a clear and concise diff viewer.
Open it from the source control view or via the `Open diff view` command.

![Diff View](https://raw.githubusercontent.com/Vinzent03/obsidian-git/master/images/diff-view.png)

### 📝 Signs in the Editor

View line-by-line changes directly in the editor with added, modified, and deleted line/hunk indicators. You can stage and reset changes right from the signs. There also commands to navigate between hunks and stage/reset hunks under the cursor. Needs to be enabled in the plugin settings.

![Signs](https://raw.githubusercontent.com/Vinzent03/obsidian-git/master/images/signs.png)

## Available Commands
> Not exhaustive - these are just some of the most common commands. For a full list, see the Command Palette in Obsidian.

- 🔄 Changes
  - `List changed files`: Lists all changes in a modal
  - `Open diff view`: Open diff view for the current file
  - `Stage current file`
  - `Unstage current file`
  - `Discard all changes`: Discard all changes in the repository
- ✅ Commit
  - `Commit`: If files are staged only commits those, otherwise commits only files that have been staged
  - `Commit with specific message`: Same as above, but with a custom message
  - `Commit all changes`: Commits all changes without pushing
  - `Commit all changes with specific message`: Same as above, but with a custom message
- 🔀 Commit-and-sync
  - `Commit-and-sync`: With default settings, this will commit all changes, pull, and push
  - `Commit-and-sync with specific message`: Same as above, but with a custom message
  - `Commit-and-sync and close`: Same as `Commit-and-sync`, but if running on desktop, will close the Obsidian window. Will not exit Obsidian app on mobile.
- 🌐 Remote
  - `Push`, `Pull`
  - `Edit remotes`: Add new remotes or edit existing remotes
  - `Remove remote`
  - `Clone an existing remote repo`: Opens dialog that will prompt for URL and authentication to clone a remote repo
  - `Open file on GitHub`: Open the file view of the current file on GitHub in a browser window. Note: only works on desktop
  - `Open file history on GitHub`: Open the file history of the current file on GitHub in a browser window. Note: only works on desktop
- 🏠 Manage local repository
  - `Initialize a new repo`
  - `Create new branch`
  - `Delete branch`
  - `CAUTION: Delete repository`
- 🧪 Miscellaneous
  - `Open source control view`: Opens side pane displaying [Source control view](#sidebar-view)
  - `Open history view`: Opens side pane displaying [History view](#history-view)
  - `Edit .gitignore`
  - `Add file to .gitignore`: Add current file to `.gitignore`

## 💻 Desktop Notes

### 🔐 Authentication

Some Git services may require further setup for HTTPS/SSH authentication. Refer to the [Authentication Guide](https://publish.obsidian.md/git-doc/Authentication)

### Obsidian on Linux

- ⚠️  Snap is not supported due to its sandboxing restrictions.
- ⚠️  Flatpak is not recommended, because it doesn't have access to all system files. They are actively fixing many issues, but there are still issues. Especially with more advanced setups.
- ✅ Please use AppImage or a full access installation of your system's package manager instead ([Linux installation guide](https://publish.obsidian.md/git-doc/Installation#Linux))

## 📱 Mobile Support (⚠️  Experimental)

The Git implementation on mobile is **very unstable**! I would not recommend using this plugin on mobile, but try other syncing services.

One such alternative is [GitSync](https://github.com/ViscousPot/GitSync), which is available on both Android and iOS. It is not associated with this plugin, but it may be a better option for mobile users. A tutorial for setting it up can be found [here](https://viscouspotenti.al/posts/gitsync-all-devices-tutorial).

> 🧪 The Git plugin works on mobile thanks to [isomorphic-git](https://isomorphic-git.org/), a JavaScript-based re-implementation of Git - but it comes with serious limitations and issues. It is not possible for an Obsidian plugin to use a native Git installation on Android or iOS.

### ❌ Mobile Feature Limitations

- No **SSH authentication** ([isomorphic-git issue](https://github.com/isomorphic-git/isomorphic-git/issues/231))
- Limited repo size, because of memory restrictions
- No rebase merge strategy
- No submodules support

### ⚠️ Performance Caveats

> [!caution]
> Depending on your device and available free RAM, Obsidian may
>
> - crash on clone/pull
> - create buffer overflow errors
> - run indefinitely.
>
> It's caused by the underlying git implementation on mobile, which is not efficient. I don't know how to fix this. If that's the case for you, I have to admit this plugin won't work for you. So commenting on any issue or creating a new one won't help. I am sorry.

### Tips for Mobile Use:

If you have a large repo/vault I recommend to stage individual files and only commit staged files.

## 🙋 Contact & Credits

- The Line Authoring feature was developed by [GollyTicker](https://github.com/GollyTicker), so any questions may be best answered by her.
- This plugin was initial developed by [denolehov](https://github.com/denolehov). Since March 2021, it's me [Vinzent03](https://github.com/Vinzent03) who is developing this plugin. That's why the GitHub repository got moved to my account in July 2024.
- If you have any kind of feedback or questions, feel free to reach out via GitHub issues.

## ☕ Support

If you find this plugin useful and would like to support its development, you can support me on Ko-fi.

[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/F1F195IQ5)


================================================
FILE: docs/.gitignore
================================================
.obsidian

================================================
FILE: docs/Authentication.md
================================================
---
aliases:
  - "04 Authentication"
---
# macOS

## HTTPS

Run the following to use the macOS keychain to store your credentials.

```bash
git config --global credential.helper osxkeychain
```

You have to do one authentication action (clone/pull/push) after setting the helper in the terminal. After that you should be able to clone/pull/push in Obsidian without any issues.

## SSH

Remember you still have to setup ssh correctly, like adding your SSH key to the `ssh-agent`. GitHub provides a great documentation on how to [generate a new SSH key](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent?platform=mac#generating-a-new-ssh-key) and then on how to [add the SSH key to your ssh-agent](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent?platform=mac#adding-your-ssh-key-to-the-ssh-agent).

# Windows

## HTTPS

Ensure you are using Git 2.29 or higher and you are using Git Credential Manager as a credential helper. 
You can verify this by executing the following snippet in a terminal, preferably in the directory where your vault/repository is located. It should output `manager`.

```bash
git config credential.helper
```

If this doesn't output `manager`, please run `git config set credential.helper manager`
Just execute any authentication command like push/pull/clone and a pop window should come up, allowing your to sign in.

Alternatively, you can also leave that setting empty and always provide the username and password manually via the prompted modal in Obsidian. All available credential helpers are listed [here](https://git-scm.com/doc/credential-helpers).,

## SSH
Remember you still have to setup ssh correctly, like adding your SSH key to the `ssh-agent`. GitHub provides a great documentation on how to [generate a new SSH key](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent?platform=windows#generating-a-new-ssh-key) and then on how to [add the SSH key to your ssh-agent](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent?platform=windows#adding-your-ssh-key-to-the-ssh-agent).

# Linux

## HTTPS

### Storing

To securely store the username and password permanently without having to reenter it all the time you can use Git's [Credential Helper](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage). `libsecret` stores the password in a secure place. On GNOME it's backed up by [GNOME Keyring](https://wiki.gnome.org/Projects/GnomeKeyring/) and on KDE by [KDE Wallet](https://wiki.archlinux.org/title/KDE_Wallet).
To set `libsecret` as your credential helper execute the following in the terminal from the directory of your vault/repository. You can also add the `--global` flag to set that setting for all other repositories on your device, too.

```bash
git config credential.helper libsecret
```

You have to do one authentication action (clone/pull/push) after setting the helper in the terminal. After that you should be able to clone/pull/push in Obsidian without any issues.

In case you get the message `git: 'credential-libsecret' is not a git command`, libsecret is not installed on your system. You may have to install it by yourself.
Here is an example for Ubuntu.

```bash
sudo apt install libsecret-1-0 libsecret-1-dev make gcc

sudo make --directory=/usr/share/doc/git/contrib/credential/libsecret

# NOTE: This changes your global config, in case you don't want that you can omit the `--global` and execute it in your existing git repository.
git config --global credential.helper \
   /usr/share/doc/git/contrib/credential/libsecret/git-credential-libsecret

```

### SSH_PASS Tools
When Git is not connected to any terminal, so  you can't enter your username/password in the terminal, it relies on the `GIT_ASKPASS`/`SSH_ASKPASS` environment variable to provide an interface to the user to enter those values.

#### Native SSH_ASKPASS
In case you don't want to store it permanently you can install `ksshaskpass` (it's preinstalled on KDE systems) and set it as binary to ask for the password.

To use `ksshaskpass` in Obsidian as the tool for `SSH_ASKPASS` add the following line to the "Additional Environment Variables" in the plugin's settings in the "Advanced" section.

```
SSH_ASKPASS=ksshaskpass
```

You should get a new window to enter your username/password when using a Git action needing authentication now.

#### SSH_PASS integrated in Obsidian
The plugin now automatically provides an integrated script for the `SSH_ASKPASS` environment variable, if no other program is set, that opens a modal in Obsidian whenever Git asks for username or password.

## SSH
With one of the above [[#SSH_PASS Tools]]  installed to enter your passphrase, you can use ssh with a passphrase. Remember you still have to setup ssh correctly, like adding your SSH key to the `ssh-agent`. GitHub provides a great documentation on how to [generate a new SSH key](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent?platform=linux#generating-a-new-ssh-key) and then on how to [add the SSH key to your ssh-agent](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent?platform=linuxu#adding-your-ssh-key-to-the-ssh-agent).


================================================
FILE: docs/Common issues.md
================================================
## xcrun: error: invalid developer path

This is an error occurring only on macOS. It's easy to fix though. Just run the following snippet in the terminal. `xcode-select --install` See #64 as an example.

## Error: spansSync git ENOENT/ Cannot run Git command

This occurs, when the plugin can't find the Git executable. It takes it from the PATH. Head over to [[Installation]] to see if everything is properly installed for your platform.
If you think everything is correctly set up and the error still occurs try the following:

In case you know where Git is installed, you can set the path under "Custom Git binary path" in the settings. If you don't know where Git is installed, you can try to find it by running the following in the terminal:

### Windows

Run `where git` in the terminal. It should return the path to the Git executable. If it fails, Git is not properly installed.

### Linux/MacOS

Run `which git` in the terminal. It should return the path to the Git executable. If it fails, Git is not properly installed.

## Infinite pulling/pushing with no error

That's most time caused by authentication problems. Head over to [[Authentication]]

## Bad owner or permissions on /home/\<user>/.ssh/config

Run `chmod 600 ~/.ssh/config` in the terminal.


## Files in `.gitignore` aren't ignored

Since the plugin uses the native git installation, I can assure you that if the `.gitignore` file is properly written and git is correctly used, everything should work.

It's important to note that once a file is committed (or staged) changing the `.gitignore` doesn't help. You have to delete the file from your repo manually to ignore the file properly:
1. Run `git rm --cached <file>` in your terminal. The file will stay on your file system. It's just deleted in your repo.
2. The file should be listed as deleted in `git status`
3. Commit the deletion
4. Now any changes to the file are properly ignored.

## Cannot run gpg

```
Error: error: cannot run gpg: No such file or directory
error: gpg failed to sign the data
fatal: failed to write commit object
```

See [[Integration with other tools#GPG Signing]] on how to solve this.

## This repository is configured for Git LFS but 'git-lfs' was not found on your path.

See [[Integration with other tools#Git Large File Storage]] on how to solve this.

================================================
FILE: docs/Features.md
================================================
## Source Control View

Open it using the "Open source control view" command. It lists all current changes like when you run `git status`. It provides the following features

- Stage/Unstage individual files
- Discard any changes to a specific file
- Open the diff view for changed files
- Stage/Unstage all files
- Push/Pull
- Commit or [[Start here#commit-and-sync|commit-and-sync]]
- Switch between list and tree view using the button at the top

## History View

Open it using the "Open history view" command. It behaves like `git log` resulting in a list of the last commits. Each commit entry can be expanded to see the changed files in that commit. By clicking on a file, you can even see the diff.

## Line Authoring

For each line, view the last time, it was modified: [[Line Authoring|Line Authoring]]. Technically known as `git-blame`.

## Automatic commit-and-sync

See [[Start here#commit-and-sync|commit-and-sync]] for an explanation of the term. The goal of automatic commit-and-sync is that you can focus on taking notes and not care about saving your work, as this plugin will take care of it.
There are multiple ways to trigger an automatic commit-and-sync. The default is a basic interval to run commit-and-sync every X minutes. Use the "Auto commit-and-sync interval" setting for that. The interval works across Obsidian sessions to ensure opening Obsidian only for short times doesn't prevent running commit-and-sync. For example, if you set a 15 minutes interval, you don't have to keep Obsidian open for 15 minutes. If you close Obsidian before the interval end, the commit-and-sync will automatically run the next time you start Obsidian.

Another method is to enable "Auto commit-and-sync after stopping file edits". This waits X minutes after your latest change for the commit-and-sync. This is useful if you don't want to get interrupted by a commit while typing. 

The last mode is the "Auto commit-and-sync after latest commit" setting. This sets the last commit-and-sync timestamp to the latest commit. By default, the plugin only compares with it's own latest run of commit-and-sync. So if you manually commit and want the commit-and-sync timer to reset, enable this setting.

## Commit message

The plugin uses [momentjs](https://momentjs.com/) for formatting the date, so read through their documentation on how to construct your date placeholder.

## Submodules Support

Since version 1.10.0 submodules are supported. While adding/cloning new submodules is still not supported (might come later), updating existing submodules on the known "Commit-and-sync" and "Pull" commands is supported. This works even recursively. "Commit-and-sync" will cause adding, commit and push (if turned on) all changes in all submodules. This feature needs to be turned on in the settings.

Additional **requirements**:

- Checked out branch (not just a commit as it is when running `git submodule update --init`)
- Tracking branch is set up, so that `git push` works
- Tracking branch needs to be fetched, so that a `git diff` with the branch works


================================================
FILE: docs/Getting Started.md
================================================
# Desktop
You can either start by cloning an existing remote repository as described [[#For existing remote repository|here]] or start with initializing a new repository locally and optionally push that to a remote repository as described [[#Create new local repository|here]].

## Create new local repository

1. Follow the [[Installation]] instructions for your operating system
2. Call the `Initialize a new repo` command
3. Create your first commit by creating some files and calling the `Commit all changes with specific message` command
4. If you want to Setup to push it to a remote repository like to GitHub:
	1. Setup [[Authentication]]
	2. Ensure that the remote repository is empty. Otherwise delete the repository and instead proceed to clone the remote repository as described in the [[#For existing remote repository|next section]].
	3. Call the `Push` command. It should ask you for a name and URL of the remote repository. Just enter `origin` for the remote name and copy the URL to push to somewhere from your remote git service.

## For existing remote repository

To clone, you have to use a remote URL. This can be one of two protocols: either `https` or `ssh`. This depends on your chosen [[Authentication]] method.
`https`: `https://github.com/<username>/<repo>.git`
`ssh`: `git@github.com:<username>/<repo>.git`

1. Follow the [[Installation]] instructions for your operating system
2. Setup [[Authentication]]
3. Git can only clone a remote repo in a new folder. Thus you have two options
    - Use the "Clone an exising remote repository" command to clone your repo into a subfolder of your vault. You then have again two choices
        - Move all your files from the new folder (including `.git` !) into your vault root.
        - Open your new subfolder as a new vault. You may have to install the plugin again.
    - Run `git clone <your-remote-url>` in the command line wherever you want your vault to be located.
4. Read on how to best configure your [[Tips-and-Tricks#Gitignore|.gitignore]]


> [!info] iCloud and Git
> When syncing your vault with iCloud and using Git on your desktop device the whole `.git` directory gets synced to your mobile device as well. This may slow down the Obsidian startup time. 
> - One solution is to put the git repository above your Obsidian vault. So that your vault is a sub directory of your git repository.
> - Another solution is to move the `.git` directory to another location and create a `.git` file in your vault with only the following line: `gitdir: <path-to-your-actual-git-direcotry>`

# Mobile
The Git implementation on mobile is **very unstable**! I would not recommend using this plugin on mobile, but try other syncing services.

One such alternative is [GitSync](https://github.com/ViscousPot/GitSync), which is available on both Android and iOS. It is not associated with this plugin, but it may be a better option for mobile users. A tutorial for setting it up can be found [here](https://viscouspotenti.al/posts/gitsync-all-devices-tutorial).
Another alternative for iOS is [Working Copy](https://workingcopy.app/).

## Restrictions

I am using [isomorphic-git](https://isomorphic-git.org/), which is a re-implementation of Git in JavaScript, because you cannot use native Git on Android or iOS.

-   SSH authentication is not supported ([isomorphic-git issue](https://github.com/isomorphic-git/isomorphic-git/issues/231))
-   Repo size is limited, because of memory restrictions
-   Rebase merge strategy is not supported
-   Submodules are not supported

## Performance on mobile

> [!danger] Warning
> Depending on your device and available free RAM, Obsidian may
> - crash on clone/pull
> - create buffer overflow errors
> - run indefinitely.
>   
> It's caused by the underlying git implementation on mobile, which is not efficient. I don't know how to fix this. If that's the case for you, I have to admit this plugin won't work for you. So commenting on any issue or creating a new one won't help. I am sorry.

## Start with existing remote repository

### Clone via plugin

Follow these instructions for setting up an Obsidian Vault on a mobile device that is already backed up in a remote git repository.

The instructions assume you are using [GitHub](https://github.com), but can be extrapolated to other providers.

1. Make sure any outstanding changes on all devices are pushed and reconciled with the remote repo.
2. Install Obsidian for Android or iOS.
3. Create a new vault (or point Obsidian to an empty directory). Do NOT select `Store in iCloud` if you are on iOS.
4. If your repo is hosted on GitHub, [authentication must be done with a personal access token](https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/). Detailed instruction for that process can be found [here](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token).
    - Minimal permissions required are
        - "Read access to metadata"
        - "Read and Write access to contents and commit status"
5. In Obsidian settings, enable community plugins. Browse plugins to install Git.
6. Enable Git (on the same screen)
7. Go to Options for the Git plugin (bottom of main settings page, under Community Plugins section)
8. Under the "Authentication/Commit Author" section, fill in the username on your git server and your password/personal access token.
9. Don't touch any settings under "Advanced"
10. Exit plugin settings, open command palette, choose "Git: Clone existing remote repo".
11. Fill in repo URL in the text field and press the repo URL button below it. The repo URL is NOT the URL in the browser. You have to append `.git`. - `https://github.com/<username>/<repo>.git`
    - E.g. `https://github.com/denolehov/obsidian-git.git`
12. Follow instructions to determine the folder to place repo in and whether an `.obsidian` directory already exits.
13. Clone should start. Popup notifications (if not disabled) will display the progress. Do not exit until a popup appears requesting that you "Restart Obsidian".

### Clone via Working Copy on iOS

Depending on the size of your repository and your device, Obsidian may crash during clone via the plugin. Alternatively, the initial clone can be done via [Working Copy](https://workingcopy.app/). None that this a paid app. The usual commit-and-sync can then be done via the plugin. The following guide assumes you don't commit your `.obsidian` directory.

1. Make sure any outstanding changes on all devices are pushed and reconciled with the remote repo.
2. Install Obsidian for Android or iOS.
3. Create a new vault (or point Obsidian to an empty directory). Do NOT select `Store in iCloud` if you are on iOS.
4. If your repo is hosted on GitHub, [authentication must be done with a personal access token](https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/). Detailed instruction for that process can be found [here](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token).
    - Minimal permissions required are
        - "Read access to metadata"
        - "Read and Write access to contents and commit status"
5. Swipe up and away Obsidian to fully close it. Open Working Copy app.
6. Clone the repo using Working Copy. Instead of logging in to GitHub through the Working Copy interface, enter the clone URL directly. Then enter your username, and for the password your Personal Access Token.
7. Open Files app.
8. Copy the repo from Working Copy. Delete the vault from Obsidian and paste the repo there (repo has the same name as the vault).
9. Open Obsidian.
10. All your cloned files should be visible.
11. Install and enable the Git plugin.
12. Add your name/email to the "Authentication/Commit Author" section in the plugin settings.
13. Use the command palette to call the "Pull" command.

## Start with new repo

Similar steps as [existing repo](#existing-repo), except use the `Initialize a new repo` command, followed by `Edit remotes` to add the remote repo to track. This remote repo will need to exist and be empty. Also make sure to read on how to best configure your [[Tips-and-Tricks#Gitignore|.gitignore]].


================================================
FILE: docs/Installation.md
================================================
---
aliases:
  - 02 Installation
---

> [!important]
> Although the plugin itself is desktop platform independent, an incorrect installation of Obsidian or Git may break the plugin.

## Plugin installation

### From within Obsidian
Go to "Settings" -> "Community plugins" -> "Browse", search for "Git", install and enable it.

### Manual
1. Download `obsidian-git-<latest-version>.zip` from the [latest release](https://github.com/Vinzent03/obsidian-git/releases/latest)
2. Unpack the zip in `<vault>/.obsidian/plugins/obsidian-git`
3. Restart Obsidian
4. Go to settings and disable restricted mode
5. Enable `Git`

# Windows

Installing [GitHub Desktop](https://github.com/apps/desktop) is **not** enough! You need to install regular Git as well.
## Git installation

> [!info] 
> Ensure you are using Git 2.29 or higher. 

Install Git from the official [website](https://git-scm.com/download/win) with all default settings.
Make sure you have `3rd-party software` access enabled.

![[third-party-windows-git.png]]

Enable Git Credential Manager. You can verify this for existing installations by executing the following. It should ouput `manager`.

```bash
git config credential.helper
```

![[credential-manager-windows-git.png]]


# Linux

## Obsidian installation

Known **supported** Obsidian installation methods:
- AppImage

Known **not fully supported** package managers
- Snap (Snap puts Obsidian in a kind of sandbox, so that Obsidian can't access Git)
- [Flatpak](https://flathub.org/apps/details/md.obsidian.Obsidian) can access Git, but not all system files, so it's not recommended.

If you installed Obsidian a while ago via **Flatpak**, and it doesn't work, please run the following snippet.

```
$ flatpak update md.obsidian.Obsidian
$ flatpak override --reset md.obsidian.Obsidian
$ flatpak run md.obsidian.Obsidian
```
[Source of this snippet](https://github.com/flathub/md.obsidian.Obsidian/issues/5#issuecomment-736974662)

# MacOS

## Git Installation

In order to install `git` on your Mac Computer please follow a suitable route explained in the [Official Git documentation](https://git-scm.com/install/mac)

## Keychain

Run the following to use the macOS keychain to store your credentials.

```zsh
git config --global credential.helper osxkeychain
```

>[!info]
> You have to complete a **single authenticated action** (either clone, pull or push) after setting the helper in the terminal. Once done, you should be able to sync Obsidian without any issues.

================================================
FILE: docs/Integration with other tools.md
================================================
Most issues with the integration of other installable tools are that their installation path is not added to the `PATH` environment variable. The `PATH` environment variable contains the directories where to search for executable programs. You probably don't have issues with executing your tools from the terminal, because you edited the `PATH` in your  `.bashrc`,`.zshrc`, but those files only apply to your shell and not to desktop applications like Obsidian. So some installation directories are missing in the `PATH` and the plugin can't find them.

# Git Large File Storage
Git Large File Storage is supported, but may need a bit configuration for the plugin to find the `git-lfs` executable.

## MacOS

1. Make sure to install [git-lfs](https://git-lfs.com/) using `brew install git-lfs`.
	- This will install `git-lfs` to `/opt/homebrew/bin/`, which is probably not in your `PATH` environment variable when using Obsidian.
2. To make `/opt/homebrew/bin/` available in Obsidian, add `/opt/homebrew/bin/` to the "Additional PATH environment variables paths" setting under "Advanced".
3. Restart Obsidian.

## Linux
1. Make sure to install [git-lfs](https://git-lfs.com/).
	- The place where `git-lfs` is installed to varies by package manager and distribution. Usually there is no need to manually add it to your `PATH`, but if the plugin can't find `git-lfs`follow the next steps.
2. Run `which git-lfs` in your terminal to get the installation path. It should output something of the form `<some-path>/git-lfs.
2. Add the `<some-path>` part of the previous step to the "Additional PATH environment variables paths" setting under "Advanced".
3. Restart Obsidian.

## Windows
There is no need to change anything for the plugin, because git-lfs is installed with Git for Windows and should be available if Git is available as well.

# GPG Signing

GitHub provides a great [documentation about GPG](https://docs.github.com/en/authentication/managing-commit-signature-verification/generating-a-new-gpg-key), which should work with Obsidian as well.
One issue you might encounter though is the following:
```
Error: error: cannot run gpg: No such file or directory
error: gpg failed to sign the data
fatal: failed to write commit object
```

This means there is no `gpg`  binary in your PATH, which you may have only properly configured for your shell. But since Obsidian is started in a different way, these PATH modifications don't affect Obsidian. To get the binary path of your `gpg` installation, run `which gpg` on Linux and Mac-OS and `where gpg` on Windows. A common location may be `/usr/local/bin/gpg`.

- You can either add that to the "Additional PATH environment variables" plugin setting to provide the gpg binary to your  plugin installation only.
- Or set it in your Git config via `git config --global gpg.program <your previous output>` to set the gpg binary globally for all git repositories.

Please create an issue if you encounter any issues and the documentation needs to be improved.

================================================
FILE: docs/Line Authoring.md
================================================
# Quick User Guide

A quick showcase of all functionality. This feature is based on [git-blame](https://git-scm.com/docs/git-blame).

ℹ️ The line author view only works in Live-Preview and Source mode - not in Reading mode.

ℹ️ Currently, only Obsidian on desktop is supported.

ℹ️ The recently released Obsidian v1.0 is fully supported. The images and GIFs in this document are however not yet updated.

## Activate

![](assets/line-author-activate.png)

It can also be activated via Command Palette `Git: Toggle line author information`.

## Default line author information

![](assets/line-author-default.png)

Shows the initials of the author as well as the authoring date in `YYYY-MM-DD` format.

The `*` indicates, that the author and committer (or their timestamps) are different - i.e., due to a rebase.

## Commit hash and full name

![](assets/line-author-commit-hash-full-name.png)

via config

![](assets/line-author-commit-hash-full-name-config.png)

## Natural language dates

![](assets/line-author-natural-language-dates.png)

## Custom date formats

![](assets/line-author-custom-dates.png)

via config

![](assets/line-author-custom-dates-config.png)

## Commit time in local/author/UTC time-zone

**UTC+0000/Z**

The simplest option to start with is showing the time in `UTC+00:00/Z` time-zone.
This is independent of both your local and the author's time-zone.
It is shown with a suffix `Z` to avoid confusion with local time.

![](assets/line-author-tz-utc0000.png)

This is the time displayed in the guter is the same for all users.

**My local (default)**

By default, the times are shown in your local time-zone - i.e., `What was the clock-time at my wall showing, when the commit was made?` This depends on your local time-zone. For instance, this is the view for a user in the `UTC+01:00` time-zone.

![](assets/line-author-tz-viewer-plus0100.png)

Note, how the displayed time is `1h` ahead of the above `UTC+0000` time.

**Author's local**

Alternatively, it can show it in the author's time-zone with explicit `UTC` offset - i.e., `What was clock-time at the author's wall and their explicit UTC offset, when the commit was made?`

This is independent of your local time-zone and the same time is displayed for all users.

![](assets/line-author-tz-author-local.png)

**Configuration**

![](assets/line-author-tz-config.png)

## Age-based gutter colors

The line gutter color is based on the age of the commit. It adapts to the dark/light mode automatically.

![](assets/line-author-dark-light.gif)

Red-ish means newer and blue-ish means older. All commits at and above a certain maximum coloring
age (configurable; default `1 year`) get the same strongest blue-ish color.

The colors are configurable and the defaults are chosen to be accessible.

![](assets/line-author-color-config.png)

## Adjust text color CSS based on theme

By default, the gutter text color uses `var(--text-muted)` which
is whatever is defined by your theme. You can however, change it to a different CSS
color or variable.

![](assets/line-author-text-color.png)

Example:
| `var(--text-muted)` | `var(--text-normal)` |
|----------------------------------------------|-----------------------------------------------|
| ![](assets/line-author-text-color-muted.png) | ![](assets/line-author-text-color-normal.png) |

## Copy commit hash

![](assets/line-author-copy-commit-hash.png)

## Quick configure gutter

![](assets/line-author-quick-configure-gutter.gif)

## New/uncommitted lines and files show `+++`

![](assets/line-author-untracked.png)

## Follow lines across cut-copy-paste-ing within same commit / all commits

By default, each line shows the last commit, where it was changed.
This means, that cut-copy-paste-ing lines will show the new commit,
even though it was not originally written in that commit.

![](assets/line-author-follow-no-follow.png)

However, if for instance following is set to `all commits`, then this is the result:

![](assets/line-author-follow-all-commits.png)

Configuration:

![](assets/line-author-follow-config.png)

## Soft and unintrusive ansynchronous view updates

Since computing the line author information takes time (due to a `git blame` shell invocation)
the result appears delayed. To minimize distraction and improve user experience,
the view is updated in a soft and unintrusive manner.

When opening a file, a placeholder is shown meanwhile:

![](assets/line-author-soft-unintrusive-ux.gif)

While editing, a placeholder is shown as well until the file is saved and the line author information is computed.

![](assets/line-author-soft-unintrusive-ux-editing.gif)

## Multi-line block support

The markdown rendering of multiple lines as a combined block is also supported.
In this case the newest of all lines is shown in the gutter.

![](assets/line-author-multi-line-newest.gif)

## Ignore whitespace and newlines

This can be activated in the settings.

| **Original**                                         | **Changed with preserved whitespace**                   | **Changed with ignored whitespace**                   |
| ---------------------------------------------------- | ------------------------------------------------------- | ----------------------------------------------------- |
| ![](assets/line-author-ignore-whitespace-before.png) | ![](assets/line-author-ignore-whitespace-preserved.png) | ![](assets/line-author-ignore-whitespace-ignored.png) |

Note, how ignoring the whitespace does not mark the indented
lines as changes, as only additional whitespace was added.

## Submodules support

Line author information is fully supported in submodules.


================================================
FILE: docs/Start here.md
================================================
---
aliases:
    - "01 Start here"
---

# Git plugin Documentation

## Topics

-   [[Installation|Installation]]
-   [[Getting Started|Getting Started]]
-   [[Authentication|Authentication]]
-   [[Integration with other tools]]
-   [[Features|Features]]
-   [[Tips-and-Tricks|Tips-and-Tricks]]
-   [[Common issues|Common Issues]]
-   [[Line Authoring|Line Authoring]]

> [!warning] Obsidian installation on Linux
> Please don't use Flatpak or Snap to install Obsidian on Linux. Learn more [[Installation#Linux|here]]

![[Getting Started#Performance on mobile]]

## What is Git?

Git is a version control system. It allows you to keep track of changes to your notes and revert back to previous versions. It also allows you to collaborate with other people on the same files. You can read more about Git [here](https://git-scm.com/book/en/v2/Getting-Started-About-Version-Control).

> [!info] Git/GitHub is not a syncing service!
> Git is not meant to share your changes live to the cloud or another person. Meaning it should not be used to work with someone live on the same note. However, it's perfect for async collaboration.

You build your history by batching multiple changes into commits. These can then be reverted or checked out. You can view the difference between version of a note via the [Version History Diff](obsidian://show-plugin?id=obsidian-version-history-diff) plugin.
Git itself only manages a local repository. It becomes really handy in conjunction with an online remote repository. You can push and pull your commits to/from a remote repository to share or backup your vault. The most popular provider is [GitHub](https://github.com).

Git is primarily used by developers and thus the command line is sometimes needed. Obsidian-Git is a plugin for Obsidian that allows you to use Git from within Obsidian without always having to use the command line or leaving Obsidian.

## Terminology and concepts

### Backup - no longer in use

For simplification, the term "Backup" refers to staging everything -> committing -> pulling -> pushing.

### Sync

Syncing is the process of pulling and pushing changes to and from a remote repository. This is done to keep your local repository up to date with the remote repository on e.g. GitHub.

### Commit-and-sync

Commit-and-sync is the process of staging everything -> committing -> pulling -> pushing. Ideally this is a single action that you do regularly to keep your local and remote repository in sync. It's recommended you set it up from the plugin's settings to be run automatically every X minutes. You can also disable the pulling or pushing part from the "Commit-and-sync" section in the plugin's settings. This reduces the "commit-and-sync" action to either a "commit and pull", "commit and push" or just commit action.


================================================
FILE: docs/Tips-and-Tricks.md
================================================
# Tips and Tricks

## Gitignore

To exclude cache files from the repository, create `.gitignore` file in the root of your vault and add the lines in the snippet below.
There's also the `Edit .gitignore` command that will open the file in a modal.

```
# to exclude Obsidian's settings (including plugin and hotkey configurations)
.obsidian/

# to only exclude plugin configuration. Might be useful to prevent some plugin from exposing sensitive data
.obsidian/plugins

# OR only to exclude workspace cache
.obsidian/workspace.json

# to exclude workspace cache specific to mobile devices
.obsidian/workspace-mobile.json

# Add below lines to exclude OS settings and caches
.trash/
.DS_Store
```


## Usage with Obsidian Sync

A common use case for using git and Obsidian Sync is to use Obsidian Sync to actually sync between all your devices and Git as a form of backup and version history.

### Use Git plugin only on one device

In case you are syncing your enabled plugins and their settings, the Git plugin is enabled and running even though the `.git` directory doesn't exist or you don't want to run automatics on that device. To fix this, you can enable the "Disable on this device" option under "Advanced" in the plugin settings. That setting is not synced to other devices.

### Use Git plugin, but not to pull your files

Another use case might be that you don't want to update your files on pull, because Obsidian Sync already updated your files. You can still commit/push/commit-and-sync. To accomplish this use "Other sync service" as "Merge strategy" under "Pull". This only updates the HEAD to the latest commit on pull, but doesn't change your files at all.


================================================
FILE: docs/dev/LineAuthorFeature.md
================================================
# Line Authoring Feature - Developer Documentation

-   This feature was developed by [GollyTicker](https://github.com/GollyTicker).
-   [Feature documentation for users](https://publish.obsidian.md/git-doc/Line+Authoring)

## Architecture

To understand how this feature integrates with the [Codemirror 6 editor](https://codemirror.net/) used in the Obsidian editors, it is adviseable to read the following sections of the [Codemirror Guide](https://codemirror.net/docs/guide/):

-   Architecture Overview > (everything)
-   Data Model
    -   Configuration
    -   Facets
    -   Transactions
-   View > (intro)
-   Extending Codemirror
    -   State Fields

Furthermore, the following concepts are necessary:

-   [EditorState](https://codemirror.net/docs/ref/#state.EditorState)
-   [State Field](https://codemirror.net/docs/ref/#state.StateField)
-   [Transaction](https://codemirror.net/docs/ref/#state.Transaction)
-   [Creating a transaction](https://codemirror.net/docs/ref/#state.EditorState.update)
-   [Annotation within a transaction](https://codemirror.net/docs/ref/#state.Annotation)
-   [ChangeSet](https://codemirror.net/docs/ref/#state.ChangeSet) (for the unsaved changes gutter update)
-   [Exmaple: Document Changes](https://codemirror.net/examples/change/)
-   [Example: Configuratoin and Extension](https://codemirror.net/examples/config/)

Given changes/updates of the file or file-view within Obsidian, we want to re-compute the line authoring (via [git-blame](https://git-scm.com/docs/git-blame)) and show it in the line gutters left to the editors.

When doing this, we need to integrate with the declarative modeling of Codemirror - and have its views automatically updated, when we change its associated data.

We achieve the goal via the following steps:

1. Every new editor pane in Obsidian subscribes itself
   by its filepath ([LineAuthoringSubcriber](/src/lineAuthor/control.ts))
   and listens in an internal publish-subscriber-model
   ([eventsPerFilepath.ts](/src/lineAuthor/eventsPerFilepath.ts))
   for updates on that filepath.
2. Any changed file in the Obsidian Vault or anytime when a new
   file is opened, [lineAuthorProvider](/src/lineAuthor/lineAuthoProvider.ts)
   initiates the asynchronous computation of the
   [LineAuthoring](/src/lineAuthor/model.ts)
   via [simpleGit.ts](/src/simpleGit.ts) -
   which parses the output of `git-blame`.
3. Once the `LineAuthoring` is computed, the publish-subscriber-model is notified
   of the new value for the corresponding filepath.
4. The notified `LineAuthoringSubcriber` creates a new transaction
   (via [newComputationResultAsTransaction](/src/lineAuthor/model.ts))
   containing the `LineAuthoring`.
5. The `LineAuthoringSubscriber` [dispatches the transaction
   on the current EditorView](https://codemirror.net/docs/ref/#view.EditorView.dispatch).
6. The [StateField's update](https://codemirror.net/docs/ref/#state.StateField^define^config.update)
   method is called by Codemirror due to the dispatched transaction.
   The [lineAuthorState](/src/lineAuthor/model.ts) updates itself with the
   newest `LineAuthoring`, if it one was provided in the transaction.
7. The [lineAuthorGutter](/src/lineAuthor/view/view.ts) is automatically re-rendered,
   due to the dispatch and the changes of the state-fields. The re-rendering
   now accesses the newest state-field values - resulting in a new DOM.

## Development

You can use this test-vault https://github.com/GollyTicker/obsidian-git-test-vault-online.

Once the watchmode npm is started, one can simply open the `test-vault` in Obsidian to
test the plugin. The Git plugin files are symbolic links to the
automatically re-compiled files at repository root level.

One can additionally use the
[docker-setup from this branch for a reproduceable developer setup](https://github.com/GollyTicker/obsidian-git/tree/docker-setup).

## Edge cases and error cases

These cases should be tested, when changes to this feature have been made.

-   running outside of a git repository
-   opening an untracked file
-   opening and closing obsidian windows of panes/notes
-   notes with a starting "--" in their filename
-   special characters in filenames
-   unicode filenames
-   empty file
-   file with populated last line
-   multi-line block with differeing line commits
-   examples for moving/copy-following
-   submodules
-   vault root != repository root
-   error in git blame result
-   open multiple files simultanously
-   open same file multiple times - and edit
-   open same files in multiple windows - and edit
-   open empty tracked file and make edits. quick update should respond sensibly
-   open file in a large, complex real-world vault with unknown characteristics
    (the private vault of the developer GollyTicker suffices) and repeatedly press Enter in a tracked file.
    -   We expect no errors, but after adding the unsaved changed gutter update feature,
        an early bu was present, where errors would occur during rendering and the view would become messed up.
-   UI should render correctly regardless of whether line numbers are shown as well or not.
    -   [[see obsidan forum discussion](https://forum.obsidian.md/t/added-editor-gutter-overlaps-and-obscures-editor-content/45217)
-   indentation changes and changes after last line (without trailing newline) with 'Ignored whitespace' enabled/disabled
-   [Unsaved Changes Gutter Update Scenario](#unsaved-changes-gutter-update-scenario)
-   commit file in a different time-zone than the current Obsidian user
    -   check that time-zone "local" formatting is correct
    -   time-zone "UTC" should always show the same result regardless of the local time-zone
-   line authoring id correctly uses submodule HEAD revision rather than super-project.

    -   There was a bug with the old super-project identifier. It did not fully work with submodules as the following scenario lead to a different displayed line authoring, than the true one.

    1. remember the lineAuthoringId A for a file in a submodule in the vault.

        - it uses the HEAD of the git super-project rather than of the submodule the file is contained in.

    2. add a few lines in the file. The plugin will correctly detect the changed file-contents
       hash, which will trigger re-computation and re-render.
    3. commit the changes in the submodule - without making a corresponding commit in the super-project.
    4. Close the file and re-open it in Obsidian.

        - In the submodule, the HEAD has changed - but not in the super-project.
        - Since the file path and file contents are same after committing, they haven't changed.
        - The current cache key doesn't detect this change and hence the view isn't updated.
        - Reloading Obsidian entirely will evict the cache - and the line authoring will be shown correctly again.

### Unsaved Changes Gutter Update Scenario

This scenario contains two main cases to test:

#### 1. Untracked file

1. Open an untracked file. It should show +++ everywhere.
2. Make insertions, deletions and in-line changes. It should always show +++.

#### 2. Tracked file

1. Open a tracked file with different line author dates and colors
2. Make insertions, deletions and in-line changes.

-   It should first show % until the changes are saved and the line authoring is computed.
-   The % should preserving the color of the changed line and insertions/deletions should shift the
    line authoring for subsequent lines accordingly

3. Make multi-line insertions, deletions and in-line changes (e.g. via cut-copy-pasting of blocks of text).

-   Hint: Use Ctrl+Z as well.
-   The behavior should be same as above.

4. Make changes at the intersection of unsaved and saved changes. The result should be consistent with above.

## Potential Future Improvements

-   show commit info when click/hover on gutter
-   show / highlight diff when hover/click on gutter
-   small tooltip widget when hovering/right-clicking on line author gutter with author/hash, etc.
-   show deleted lines
-   interpret new 'newline' at end of line as non-change to make gutter change marking more intuitive.
    -   [one option is to add a setting which switches between compatibility-mode and comfort-mode](https://github.com/denolehov/obsidian-git/pull/288)
-   distinguish untracked and changed line (e.g. "~" and "+")
-   use addMomentFormat in settings.ts when configuring the line author date format.
-   main.ts: refreshUpdatedHead(): Detect, if the head has changed from outside of Git (e.g. script) and run this callback then.
-   Avoid "Uncaught illegal access error" when closing a separate Obsidian window.
    It doesn't seem to have any impact on UX yet though...
-   Unique initials option: [work in progress branch](https://github.com/GollyTicker/obsidian-git/tree/line-author-unique-initials)


================================================
FILE: esbuild.config.mjs
================================================
import esbuild from "esbuild";
import esbuildSvelte from "esbuild-svelte";
import process from "process";
import { sveltePreprocess } from "svelte-preprocess";

const banner = `/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source visit the plugins github repository (https://github.com/denolehov/obsidian-git)
*/
`;

const prod = process.argv[2] === "production";

const context = await esbuild.context({
    banner: {
        js: banner,
    },
    entryPoints: ["src/main.ts"],
    bundle: true,
    external: [
        "obsidian",
        "electron",
        "child_process",
        "fs",
        "os",
        "path",
        "moment",
        "node:events",
        "@codemirror/autocomplete",
        "@codemirror/collab",
        "@codemirror/commands",
        "@codemirror/language",
        "@codemirror/lint",
        "@codemirror/search",
        "@codemirror/state",
        "@codemirror/view",
        "@lezer/common",
        "@lezer/highlight",
        "@lezer/lr",
    ],
    format: "cjs",
    target: "es2018",
    logLevel: "info",
    sourcemap: prod ? false : "inline",
    treeShaking: true,
    platform: "browser",
    minify: prod,
    conditions: [prod ? "production" : "development"], // https://www.npmjs.com/package/esm-env
    plugins: [
        esbuildSvelte({
            compilerOptions: {
                css: "injected",
                dev: !prod,
            },
            filterWarnings: (warning) => {
                if (warning.code.startsWith("a11y-")) return false;
                return true;
            },
            preprocess: sveltePreprocess(),
        }),
    ],
    inject: ["polyfill_buffer.js"],
    outfile: "main.js",
});

if (prod) {
    await context.rebuild();
    process.exit(0);
} else {
    await context.watch();
}


================================================
FILE: eslint.config.mjs
================================================
import svelteParser from "svelte-eslint-parser";
import tsParser from "@typescript-eslint/parser";
import eslint from "@eslint/js";
import tseslint from "typescript-eslint";
import eslintPluginSvelte from "eslint-plugin-svelte";
import { defineConfig } from "eslint/config";

export default defineConfig(
    {
        ignores: ["**/node_modules/", "**/main.js"],
    },
    eslint.configs.recommended,
    ...tseslint.configs.recommendedTypeChecked,
    ...eslintPluginSvelte.configs["flat/prettier"],
    {
        languageOptions: {
            parserOptions: {
                projectService: true,
                tsconfigRootDir: import.meta.dirname,
            },
        },
        rules: {
            "@typescript-eslint/no-unused-vars": [
                "error",
                {
                    args: "all",
                    argsIgnorePattern: "^_",
                    caughtErrors: "all",
                    caughtErrorsIgnorePattern: "^_",
                    destructuredArrayIgnorePattern: "^_",
                    varsIgnorePattern: "^_",
                    ignoreRestSiblings: true,
                },
            ],
        },
    },
    {
        files: ["**/*.svelte"],
        languageOptions: {
            parser: svelteParser,
            parserOptions: {
                extraFileExtensions: [".svelte"],
                parser: tsParser,
            },
        },
        rules: {
            "no-undef": "off",
        },
    }
);


================================================
FILE: manifest.json
================================================
{
    "author": "Vinzent",
    "authorUrl": "https://github.com/Vinzent03",
    "id": "obsidian-git",
    "name": "Git",
    "description": "Integrate Git version control with automatic backup and other advanced features.",
    "isDesktopOnly": false,
    "fundingUrl": "https://ko-fi.com/vinzent",
    "version": "2.38.0"
}


================================================
FILE: package.json
================================================
{
    "name": "obsidian-git",
    "version": "2.38.0",
    "description": "Integrate Git version control with automatic backup and other advanced features in Obsidian.md",
    "main": "main.js",
    "scripts": {
        "dev": "node esbuild.config.mjs dev",
        "build": "node esbuild.config.mjs production",
        "release": "standard-version",
        "lint": "eslint src",
        "format": "prettier --check src",
        "tsc": "tsc --noEmit",
        "svelte": "svelte-check",
        "all": "pnpm run tsc && pnpm run svelte && pnpm run format && pnpm run lint"
    },
    "keywords": [],
    "author": "Vinzent03",
    "license": "MIT",
    "standard-version": {
        "t": ""
    },
    "engines": {
        "node": ">=18",
        "pnpm": ">=9"
    },
    "devDependencies": {
        "@eslint/js": "^9.39.2",
        "@types/debug": "^4.1.12",
        "@types/deep-equal": "^1.0.4",
        "@types/diff": "^5.2.3",
        "@types/diff3": "^0.0.2",
        "@types/node": "^22.19.10",
        "@typescript-eslint/parser": "8.47.0",
        "esbuild": "^0.24.2",
        "esbuild-svelte": "^0.8.2",
        "eslint": "^9.39.2",
        "eslint-plugin-svelte": "^2.46.1",
        "obsidian": "^1.11.4",
        "prettier": "3.3.2",
        "prettier-plugin-svelte": "^3.4.1",
        "scss": "^0.2.4",
        "standard-version": "^9.5.0",
        "svelte-check": "^4.3.6",
        "svelte-eslint-parser": "^0.43.0",
        "svelte-preprocess": "^6.0.3",
        "tslib": "^2.8.1",
        "typescript": "5.8.3",
        "typescript-eslint": "^8.54.0"
    },
    "dependencies": {
        "@codemirror/commands": "^6.10.2",
        "@codemirror/merge": "^6.12.0",
        "@codemirror/search": "^6.6.0",
        "@codemirror/state": "^6.5.4",
        "@codemirror/view": "^6.39.13",
        "buffer": "^6.0.3",
        "codemirror": "^6.0.2",
        "css-color-converter": "^2.0.0",
        "debug": "^4.4.3",
        "deep-equal": "^2.2.3",
        "diff": "^8.0.3",
        "diff2html": "^3.4.56",
        "diff3": "^0.0.4",
        "isomorphic-git": "^1.36.3",
        "js-sha256": "^0.9.0",
        "simple-git": "github:Vinzent03/git-js#release",
        "supports-color": "^9.4.0",
        "svelte": "^5.50.0"
    },
    "moduleFileExtensions": [
        "js",
        "ts",
        "svelte"
    ]
}


================================================
FILE: polyfill_buffer.js
================================================
import { Platform } from 'obsidian';
let buffer;
if (Platform.isMobileApp) {
    buffer = require('buffer/index.js').Buffer
} else {
    buffer = global.Buffer
}

export const Buffer = buffer;


================================================
FILE: src/automaticsManager.ts
================================================
import { debounce } from "obsidian";
import type ObsidianGit from "./main";

export default class AutomaticsManager {
    private timeoutIDCommitAndSync?: number;
    private timeoutIDPush?: number;
    private timeoutIDPull?: number;

    constructor(private readonly plugin: ObsidianGit) {}

    private saveLastAuto(date: Date, mode: "backup" | "pull" | "push") {
        if (mode === "backup") {
            this.plugin.localStorage.setLastAutoBackup(date.toString());
        } else if (mode === "pull") {
            this.plugin.localStorage.setLastAutoPull(date.toString());
        } else if (mode === "push") {
            this.plugin.localStorage.setLastAutoPush(date.toString());
        }
    }

    private loadLastAuto(): { backup: Date; pull: Date; push: Date } {
        return {
            backup: new Date(
                this.plugin.localStorage.getLastAutoBackup() ?? ""
            ),
            pull: new Date(this.plugin.localStorage.getLastAutoPull() ?? ""),
            push: new Date(this.plugin.localStorage.getLastAutoPush() ?? ""),
        };
    }

    async init() {
        await this.setUpAutoCommitAndSync();
        const lastAutos = this.loadLastAuto();

        if (
            this.plugin.settings.differentIntervalCommitAndPush &&
            this.plugin.settings.autoPushInterval > 0
        ) {
            const diff = this.diff(
                this.plugin.settings.autoPushInterval,
                lastAutos.push
            );
            this.startAutoPush(diff);
        }
        if (this.plugin.settings.autoPullInterval > 0) {
            const diff = this.diff(
                this.plugin.settings.autoPullInterval,
                lastAutos.pull
            );
            this.startAutoPull(diff);
        }
    }

    unload() {
        this.clearAutoPull();
        this.clearAutoPush();
        this.clearAutoCommitAndSync();
    }

    /**
     * Clears all timers and sets all timers to their current settings.
     *
     * This does not calculate any differences to last autos or commits.
     * Should only be used when settings are changed.
     */
    reload(...type: ("commit" | "push" | "pull")[]) {
        if (this.plugin.localStorage.getPausedAutomatics()) return;

        if (type.contains("commit")) {
            this.clearAutoCommitAndSync();
            if (this.plugin.settings.autoSaveInterval > 0) {
                this.startAutoCommitAndSync(
                    this.plugin.settings.autoSaveInterval
                );
            }
        }
        if (type.contains("push")) {
            this.clearAutoPush();
            if (
                this.plugin.settings.differentIntervalCommitAndPush &&
                this.plugin.settings.autoPushInterval > 0
            ) {
                this.startAutoPush(this.plugin.settings.autoPushInterval);
            }
        }
        if (type.contains("pull")) {
            this.clearAutoPull();
            if (this.plugin.settings.autoPullInterval > 0) {
                this.startAutoPull(this.plugin.settings.autoPullInterval);
            }
        }
    }

    /**
     * Starts the auto commit-and-sync with the correct remaining time.
     *
     * Additionally, if `setLastSaveToLastCommit` is enabled, the last auto commit-and-sync
     * is set to the last commit time.
     */
    private async setUpAutoCommitAndSync() {
        if (this.plugin.settings.setLastSaveToLastCommit) {
            this.clearAutoCommitAndSync();
            const lastCommitDate =
                await this.plugin.gitManager.getLastCommitTime();
            if (lastCommitDate) {
                this.saveLastAuto(lastCommitDate, "backup");
            }
        }

        if (!this.timeoutIDCommitAndSync && !this.plugin.autoCommitDebouncer) {
            const lastAutos = this.loadLastAuto();

            if (this.plugin.settings.autoSaveInterval > 0) {
                const diff = this.diff(
                    this.plugin.settings.autoSaveInterval,
                    lastAutos.backup
                );
                this.startAutoCommitAndSync(diff);
            }
        }
    }

    private startAutoCommitAndSync(minutes?: number) {
        let time = (minutes ?? this.plugin.settings.autoSaveInterval) * 60000;
        if (this.plugin.settings.autoBackupAfterFileChange) {
            if (minutes === 0) {
                this.doAutoCommitAndSync();
            } else {
                this.plugin.autoCommitDebouncer = debounce(
                    () => this.doAutoCommitAndSync(),
                    time,
                    true
                );
            }
        } else {
            // max timeout in js
            if (time > 2147483647) time = 2147483647;
            this.timeoutIDCommitAndSync = window.setTimeout(
                () => this.doAutoCommitAndSync(),
                time
            );
        }
    }

    // This is used for both auto commit-and-sync and commit only
    private doAutoCommitAndSync(): void {
        this.plugin.promiseQueue.addTask(
            async () => {
                // Re-check if the auto commit should run now or be postponed,
                // because the last commit time has changed
                if (this.plugin.settings.setLastSaveToLastCommit) {
                    const lastCommitDate =
                        await this.plugin.gitManager.getLastCommitTime();
                    if (lastCommitDate) {
                        this.saveLastAuto(lastCommitDate, "backup");
                        const diff = this.diff(
                            this.plugin.settings.autoSaveInterval,
                            lastCommitDate
                        );
                        if (diff > 0) {
                            this.startAutoCommitAndSync(diff);
                            // Return false to mark the next iteration
                            // already being scheduled
                            return false;
                        }
                    }
                }
                const onlyStaged = this.plugin.settings.autoCommitOnlyStaged;
                if (this.plugin.settings.differentIntervalCommitAndPush) {
                    await this.plugin.commit({ fromAuto: true, onlyStaged });
                } else {
                    await this.plugin.commitAndSync({
                        fromAutoBackup: true,
                        onlyStaged,
                    });
                }
                return true;
            },
            (schedule) => {
                // Don't schedule if the next iteration is already scheduled
                if (schedule !== false) {
                    this.saveLastAuto(new Date(), "backup");
                    this.startAutoCommitAndSync();
                }
            }
        );
    }

    private startAutoPull(minutes?: number) {
        let time = (minutes ?? this.plugin.settings.autoPullInterval) * 60000;
        // max timeout in js
        if (time > 2147483647) time = 2147483647;

        this.timeoutIDPull = window.setTimeout(() => this.doAutoPull(), time);
    }

    private doAutoPull(): void {
        this.plugin.promiseQueue.addTask(
            () => this.plugin.pullChangesFromRemote(),
            () => {
                this.saveLastAuto(new Date(), "pull");
                this.startAutoPull();
            }
        );
    }

    private startAutoPush(minutes?: number) {
        let time = (minutes ?? this.plugin.settings.autoPushInterval) * 60000;
        // max timeout in js
        if (time > 2147483647) time = 2147483647;

        this.timeoutIDPush = window.setTimeout(() => this.doAutoPush(), time);
    }

    private doAutoPush(): void {
        this.plugin.promiseQueue.addTask(
            () => this.plugin.push(),
            () => {
                this.saveLastAuto(new Date(), "push");
                this.startAutoPush();
            }
        );
    }

    private clearAutoCommitAndSync(): boolean {
        let wasActive = false;
        if (this.timeoutIDCommitAndSync) {
            window.clearTimeout(this.timeoutIDCommitAndSync);
            this.timeoutIDCommitAndSync = undefined;
            wasActive = true;
        }
        if (this.plugin.autoCommitDebouncer) {
            this.plugin.autoCommitDebouncer?.cancel();
            this.plugin.autoCommitDebouncer = undefined;
            wasActive = true;
        }
        return wasActive;
    }

    private clearAutoPull(): boolean {
        if (this.timeoutIDPull) {
            window.clearTimeout(this.timeoutIDPull);
            this.timeoutIDPull = undefined;
            return true;
        }
        return false;
    }

    private clearAutoPush(): boolean {
        if (this.timeoutIDPush) {
            window.clearTimeout(this.timeoutIDPush);
            this.timeoutIDPush = undefined;
            return true;
        }
        return false;
    }

    /**
     * Calculates the minutes until the next auto action. >= 0
     *
     * This is done by the difference between the setting and the time since the last auto action, but at least 0.
     */
    private diff(setting: number, lastAuto: Date) {
        const now = new Date();
        const diff =
            setting -
            Math.round((now.getTime() - lastAuto.getTime()) / 1000 / 60);
        return Math.max(0, diff);
    }
}


================================================
FILE: src/commands.ts
================================================
import { Notice, Platform, TFolder, WorkspaceLeaf } from "obsidian";
import { HISTORY_VIEW_CONFIG, SOURCE_CONTROL_VIEW_CONFIG } from "./constants";
import { SimpleGit } from "./gitManager/simpleGit";
import ObsidianGit from "./main";
import { openHistoryInGitHub, openLineInGitHub } from "./openInGitHub";
import { ChangedFilesModal } from "./ui/modals/changedFilesModal";
import { GeneralModal } from "./ui/modals/generalModal";
import { IgnoreModal } from "./ui/modals/ignoreModal";
import { assertNever } from "./utils";
import { togglePreviewHunk } from "./editor/signs/tooltip";

export function addCommmands(plugin: ObsidianGit) {
    const app = plugin.app;

    plugin.addCommand({
        id: "edit-gitignore",
        name: "Edit .gitignore",
        callback: async () => {
            const path = plugin.gitManager.getRelativeVaultPath(".gitignore");
            if (!(await app.vault.adapter.exists(path))) {
                await app.vault.adapter.write(path, "");
            }
            const content = await app.vault.adapter.read(path);
            const modal = new IgnoreModal(app, content);
            const res = await modal.openAndGetReslt();
            if (res !== undefined) {
                await app.vault.adapter.write(path, res);
                await plugin.refresh();
            }
        },
    });
    plugin.addCommand({
        id: "open-git-view",
        name: "Open source control view",
        callback: async () => {
            const leafs = app.workspace.getLeavesOfType(
                SOURCE_CONTROL_VIEW_CONFIG.type
            );
            let leaf: WorkspaceLeaf;
            if (leafs.length === 0) {
                leaf =
                    app.workspace.getRightLeaf(false) ??
                    app.workspace.getLeaf();
                await leaf.setViewState({
                    type: SOURCE_CONTROL_VIEW_CONFIG.type,
                });
            } else {
                leaf = leafs.first()!;
            }
            await app.workspace.revealLeaf(leaf);

            // Is not needed for the first open, but allows to refresh the view
            // per hotkey even if already opened
            app.workspace.trigger("obsidian-git:refresh");
        },
    });
    plugin.addCommand({
        id: "open-history-view",
        name: "Open history view",
        callback: async () => {
            const leafs = app.workspace.getLeavesOfType(
                HISTORY_VIEW_CONFIG.type
            );
            let leaf: WorkspaceLeaf;
            if (leafs.length === 0) {
                leaf =
                    app.workspace.getRightLeaf(false) ??
                    app.workspace.getLeaf();
                await leaf.setViewState({
                    type: HISTORY_VIEW_CONFIG.type,
                });
            } else {
                leaf = leafs.first()!;
            }
            await app.workspace.revealLeaf(leaf);

            // Is not needed for the first open, but allows to refresh the view
            // per hotkey even if already opened
            app.workspace.trigger("obsidian-git:refresh");
        },
    });

    plugin.addCommand({
        id: "open-diff-view",
        name: "Open diff view",
        checkCallback: (checking) => {
            const file = app.workspace.getActiveFile();
            if (checking) {
                return file !== null;
            } else {
                const filePath = plugin.gitManager.getRelativeRepoPath(
                    file!.path,
                    true
                );
                plugin.tools.openDiff({
                    aFile: filePath,
                    aRef: "",
                });
            }
        },
    });

    plugin.addCommand({
        id: "view-file-on-github",
        name: "Open file on GitHub",
        editorCallback: (editor, { file }) => {
            if (file) return openLineInGitHub(editor, file, plugin.gitManager);
        },
    });

    plugin.addCommand({
        id: "view-history-on-github",
        name: "Open file history on GitHub",
        editorCallback: (_, { file }) => {
            if (file) return openHistoryInGitHub(file, plugin.gitManager);
        },
    });

    plugin.addCommand({
        id: "pull",
        name: "Pull",
        callback: () =>
            plugin.promiseQueue.addTask(() => plugin.pullChangesFromRemote()),
    });

    plugin.addCommand({
        id: "fetch",
        name: "Fetch",
        callback: () => plugin.promiseQueue.addTask(() => plugin.fetch()),
    });

    plugin.addCommand({
        id: "switch-to-remote-branch",
        name: "Switch to remote branch",
        callback: () =>
            plugin.promiseQueue.addTask(() => plugin.switchRemoteBranch()),
    });

    plugin.addCommand({
        id: "add-to-gitignore",
        name: "Add file to .gitignore",
        checkCallback: (checking) => {
            const file = app.workspace.getActiveFile();
            if (checking) {
                return file !== null;
            } else {
                plugin
                    .addFileToGitignore(file!.path, file instanceof TFolder)
                    .catch((e) => plugin.displayError(e));
            }
        },
    });

    plugin.addCommand({
        id: "push",
        name: "Commit-and-sync",
        callback: () =>
            plugin.promiseQueue.addTask(() =>
                plugin.commitAndSync({ fromAutoBackup: false })
            ),
    });

    plugin.addCommand({
        id: "backup-and-close",
        name: "Commit-and-sync and then close Obsidian",
        callback: () =>
            plugin.promiseQueue.addTask(async () => {
                await plugin.commitAndSync({ fromAutoBackup: false });
                window.close();
            }),
    });

    plugin.addCommand({
        id: "commit-push-specified-message",
        name: "Commit-and-sync with specific message",
        callback: () =>
            plugin.promiseQueue.addTask(() =>
                plugin.commitAndSync({
                    fromAutoBackup: false,
                    requestCustomMessage: true,
                })
            ),
    });

    plugin.addCommand({
        id: "commit",
        name: "Commit all changes",
        callback: () =>
            plugin.promiseQueue.addTask(() =>
                plugin.commit({ fromAuto: false })
            ),
    });

    plugin.addCommand({
        id: "commit-specified-message",
        name: "Commit all changes with specific message",
        callback: () =>
            plugin.promiseQueue.addTask(() =>
                plugin.commit({
                    fromAuto: false,
                    requestCustomMessage: true,
                })
            ),
    });

    plugin.addCommand({
        id: "commit-smart",
        name: "Commit",
        callback: () =>
            plugin.promiseQueue.addTask(async () => {
                const status = await plugin.updateCachedStatus();
                const onlyStaged = status.staged.length > 0;
                return plugin.commit({
                    fromAuto: false,
                    requestCustomMessage: false,
                    onlyStaged: onlyStaged,
                });
            }),
    });

    plugin.addCommand({
        id: "commit-staged",
        name: "Commit staged",
        checkCallback: function (checking) {
            // Don't show this command in command palette, because the
            // commit-smart command is more useful. Still provide this command
            // for hotkeys and automation.
            if (checking) return false;

            plugin.promiseQueue.addTask(async () => {
                return plugin.commit({
                    fromAuto: false,
                    requestCustomMessage: false,
                });
            });
        },
    });

    if (Platform.isDesktopApp) {
        plugin.addCommand({
            id: "commit-amend-staged-specified-message",
            name: "Amend staged",
            callback: () =>
                plugin.promiseQueue.addTask(() =>
                    plugin.commit({
                        fromAuto: false,
                        requestCustomMessage: true,
                        onlyStaged: true,
                        amend: true,
                    })
                ),
        });
    }

    plugin.addCommand({
        id: "commit-smart-specified-message",
        name: "Commit with specific message",
        callback: () =>
            plugin.promiseQueue.addTask(async () => {
                const status = await plugin.updateCachedStatus();
                const onlyStaged = status.staged.length > 0;
                return plugin.commit({
                    fromAuto: false,
                    requestCustomMessage: true,
                    onlyStaged: onlyStaged,
                });
            }),
    });

    plugin.addCommand({
        id: "commit-staged-specified-message",
        name: "Commit staged with specific message",
        checkCallback: function (checking) {
            // Same reason as for commit-staged
            if (checking) return false;
            return plugin.promiseQueue.addTask(() =>
                plugin.commit({
                    fromAuto: false,
                    requestCustomMessage: true,
                    onlyStaged: true,
                })
            );
        },
    });

    plugin.addCommand({
        id: "push2",
        name: "Push",
        callback: () => plugin.promiseQueue.addTask(() => plugin.push()),
    });

    plugin.addCommand({
        id: "stage-current-file",
        name: "Stage current file",
        checkCallback: (checking) => {
            const file = app.workspace.getActiveFile();
            if (checking) {
                return file !== null;
            } else {
                plugin.promiseQueue.addTask(() => plugin.stageFile(file!));
            }
        },
    });

    plugin.addCommand({
        id: "unstage-current-file",
        name: "Unstage current file",
        checkCallback: (checking) => {
            const file = app.workspace.getActiveFile();
            if (checking) {
                return file !== null;
            } else {
                plugin.promiseQueue.addTask(() => plugin.unstageFile(file!));
            }
        },
    });

    plugin.addCommand({
        id: "edit-remotes",
        name: "Edit remotes",
        callback: () =>
            plugin.editRemotes().catch((e) => plugin.displayError(e)),
    });

    plugin.addCommand({
        id: "remove-remote",
        name: "Remove remote",
        callback: () =>
            plugin.removeRemote().catch((e) => plugin.displayError(e)),
    });

    plugin.addCommand({
        id: "set-upstream-branch",
        name: "Set upstream branch",
        callback: () =>
            plugin.setUpstreamBranch().catch((e) => plugin.displayError(e)),
    });

    plugin.addCommand({
        id: "delete-repo",
        name: "CAUTION: Delete repository",
        callback: async () => {
            const repoExists = await app.vault.adapter.exists(
                `${plugin.settings.basePath}/.git`
            );
            if (repoExists) {
                const modal = new GeneralModal(plugin, {
                    options: ["NO", "YES"],
                    placeholder:
                        "Do you really want to delete the repository (.git directory)? plugin action cannot be undone.",
                    onlySelection: true,
                });
                const shouldDelete = (await modal.openAndGetResult()) === "YES";
                if (shouldDelete) {
                    await app.vault.adapter.rmdir(
                        `${plugin.settings.basePath}/.git`,
                        true
                    );
                    new Notice(
                        "Successfully deleted repository. Reloading plugin..."
                    );
                    plugin.unloadPlugin();
                    await plugin.init({ fromReload: true });
                }
            } else {
                new Notice("No repository found");
            }
        },
    });

    plugin.addCommand({
        id: "init-repo",
        name: "Initialize a new repo",
        callback: () =>
            plugin.createNewRepo().catch((e) => plugin.displayError(e)),
    });

    plugin.addCommand({
        id: "clone-repo",
        name: "Clone an existing remote repo",
        callback: () =>
            plugin.cloneNewRepo().catch((e) => plugin.displayError(e)),
    });

    plugin.addCommand({
        id: "list-changed-files",
        name: "List changed files",
        callback: async () => {
            if (!(await plugin.isAllInitialized())) return;

            try {
                const status = await plugin.updateCachedStatus();
                if (status.changed.length + status.staged.length > 500) {
                    plugin.displayError("Too many changes to display");
                    return;
                }

                new ChangedFilesModal(plugin, status.all).open();
            } catch (e) {
                plugin.displayError(e);
            }
        },
    });

    plugin.addCommand({
        id: "switch-branch",
        name: "Switch branch",
        callback: () => {
            plugin.switchBranch().catch((e) => plugin.displayError(e));
        },
    });

    plugin.addCommand({
        id: "create-branch",
        name: "Create new branch",
        callback: () => {
            plugin.createBranch().catch((e) => plugin.displayError(e));
        },
    });

    plugin.addCommand({
        id: "delete-branch",
        name: "Delete branch",
        callback: () => {
            plugin.deleteBranch().catch((e) => plugin.displayError(e));
        },
    });

    plugin.addCommand({
        id: "discard-all",
        name: "CAUTION: Discard all changes",
        callback: async () => {
            const res = await plugin.discardAll();
            switch (res) {
                case "discard":
                    new Notice("Discarded all changes in tracked files.");
                    break;
                case "delete":
                    new Notice("Discarded all files.");
                    break;
                case false:
                    break;
                default:
                    assertNever(res);
            }
        },
    });

    plugin.addCommand({
        id: "pause-automatic-routines",
        name: "Pause/Resume automatic routines",
        callback: () => {
            const pause = !plugin.localStorage.getPausedAutomatics();
            plugin.localStorage.setPausedAutomatics(pause);
            if (pause) {
                plugin.automaticsManager.unload();
                new Notice(`Paused automatic routines.`);
            } else {
                plugin.automaticsManager.reload("commit", "push", "pull");
                new Notice(`Resumed automatic routines.`);
            }
        },
    });

    plugin.addCommand({
        id: "raw-command",
        name: "Raw command",
        checkCallback: (checking) => {
            const gitManager = plugin.gitManager;
            if (checking) {
                // only available on desktop
                return gitManager instanceof SimpleGit;
            } else {
                plugin.tools
                    .runRawCommand()
                    .catch((e) => plugin.displayError(e));
            }
        },
    });

    plugin.addCommand({
        id: "toggle-line-author-info",
        name: "Toggle line author information",
        callback: () =>
            plugin.settingsTab?.configureLineAuthorShowStatus(
                !plugin.settings.lineAuthor.show
            ),
    });

    plugin.addCommand({
        id: "reset-hunk",
        name: "Reset hunk",
        editorCheckCallback(checking, _, __) {
            if (checking) {
                return (
                    plugin.settings.hunks.hunkCommands &&
                    plugin.hunkActions.editor !== undefined
                );
            }

            plugin.hunkActions.resetHunk();
        },
    });

    plugin.addCommand({
        id: "stage-hunk",
        name: "Stage hunk",
        editorCheckCallback: (checking, _, __) => {
            if (checking) {
                return (
                    plugin.settings.hunks.hunkCommands &&
                    plugin.hunkActions.editor !== undefined
                );
            }
            plugin.promiseQueue.addTask(() => plugin.hunkActions.stageHunk());
        },
    });

    plugin.addCommand({
        id: "preview-hunk",
        name: "Preview hunk",
        editorCheckCallback: (checking, _, __) => {
            if (checking) {
                return (
                    plugin.settings.hunks.hunkCommands &&
                    plugin.hunkActions.editor !== undefined
                );
            }
            const editor = plugin.hunkActions.editor!.editor;
            togglePreviewHunk(editor);
        },
    });

    plugin.addCommand({
        id: "next-hunk",
        name: "Go to next hunk",
        editorCheckCallback: (checking, _, __) => {
            if (checking) {
                return (
                    plugin.settings.hunks.hunkCommands &&
                    plugin.hunkActions.editor !== undefined
                );
            }
            plugin.hunkActions.goToHunk("next");
        },
    });

    plugin.addCommand({
        id: "prev-hunk",
        name: "Go to previous hunk",
        editorCheckCallback: (checking, _, __) => {
            if (checking) {
                return (
                    plugin.settings.hunks.hunkCommands &&
                    plugin.hunkActions.editor !== undefined
                );
            }
            plugin.hunkActions.goToHunk("prev");
        },
    });
}


================================================
FILE: src/constants.ts
================================================
import { Platform } from "obsidian";
import type { ObsidianGitSettings } from "./types";
export const DATE_FORMAT = "YYYY-MM-DD";
export const DATE_TIME_FORMAT_MINUTES = `${DATE_FORMAT} HH:mm`;
export const DATE_TIME_FORMAT_SECONDS = `${DATE_FORMAT} HH:mm:ss`;

export const GIT_LINE_AUTHORING_MOVEMENT_DETECTION_MINIMAL_LENGTH = 40;

export const CONFLICT_OUTPUT_FILE = "conflict-files-obsidian-git.md";

export const DEFAULT_SETTINGS: ObsidianGitSettings = {
    commitMessage: "vault backup: {{date}}",
    autoCommitMessage: "vault backup: {{date}}",
    commitMessageScript: "",
    commitDateFormat: DATE_TIME_FORMAT_SECONDS,
    autoSaveInterval: 0,
    autoPushInterval: 0,
    autoPullInterval: 0,
    autoPullOnBoot: false,
    autoCommitOnlyStaged: false,
    disablePush: false,
    pullBeforePush: true,
    disablePopups: false,
    showErrorNotices: true,
    disablePopupsForNoChanges: false,
    listChangedFilesInMessageBody: false,
    showStatusBar: true,
    updateSubmodules: false,
    syncMethod: "merge",
    mergeStrategy: "none",
    customMessageOnAutoBackup: false,
    autoBackupAfterFileChange: false,
    treeStructure: false,
    refreshSourceControl: Platform.isDesktopApp,
    basePath: "",
    differentIntervalCommitAndPush: false,
    changedFilesInStatusBar: false,
    showedMobileNotice: false,
    refreshSourceControlTimer: 7000,
    showBranchStatusBar: true,
    setLastSaveToLastCommit: false,
    submoduleRecurseCheckout: false,
    gitDir: "",
    showFileMenu: true,
    authorInHistoryView: "hide",
    dateInHistoryView: false,
    diffStyle: "split",
    hunks: {
        showSigns: false,
        hunkCommands: false,
        statusBar: "disabled",
    },
    lineAuthor: {
        show: false,
        followMovement: "inactive",
        authorDisplay: "initials",
        showCommitHash: false,
        dateTimeFormatOptions: "date",
        dateTimeFormatCustomString: DATE_TIME_FORMAT_MINUTES,
        dateTimeTimezone: "viewer-local",
        coloringMaxAge: "1y",
        // colors were picked via:
        // https://color.adobe.com/de/create/color-accessibility
        colorNew: { r: 255, g: 150, b: 150 },
        colorOld: { r: 120, g: 160, b: 255 },
        textColorCss: "var(--text-muted)", //  more pronounced than line numbers, but less than the content text
        ignoreWhitespace: false,
        gutterSpacingFallbackLength: 5,
    },
};

export const SOURCE_CONTROL_VIEW_CONFIG = {
    type: "git-view",
    name: "Source Control",
    icon: "git-pull-request",
};

export const HISTORY_VIEW_CONFIG = {
    type: "git-history-view",
    name: "History",
    icon: "history",
};

export const SPLIT_DIFF_VIEW_CONFIG = {
    type: "split-diff-view",
    name: "Diff view",
    icon: "diff",
};
export const DIFF_VIEW_CONFIG = {
    type: "diff-view",
    name: "Diff View",
    icon: "git-pull-request",
};

export const DEFAULT_WIN_GIT_PATH = "C:\\Program Files\\Git\\cmd\\git.exe";
export const ASK_PASS_INPUT_FILE = ".git_credentials_input";
export const ASK_PASS_SCRIPT_FILE = "obsidian_askpass.sh";

export const ASK_PASS_SCRIPT = `#!/bin/sh

PROMPT="$1"
TEMP_FILE="$OBSIDIAN_GIT_CREDENTIALS_INPUT"

cleanup() {
    rm -f "$TEMP_FILE" "$TEMP_FILE.response"
}
trap cleanup EXIT

echo "$PROMPT" > "$TEMP_FILE"

while [ ! -e "$TEMP_FILE.response" ]; do
    if [ ! -e "$TEMP_FILE" ]; then
        echo "Trigger file got removed: Abort" >&2
        exit 1
    fi
    sleep 0.1
done

RESPONSE=$(cat "$TEMP_FILE.response")

echo "$RESPONSE"
`;

/**
 * Copied from https://github.com/sindresorhus/binary-extensions/blob/main/binary-extensions.json
 */
export const BINARY_EXTENSIONS = [
    "3dm",
    "3ds",
    "3g2",
    "3gp",
    "7z",
    "a",
    "aac",
    "adp",
    "afdesign",
    "afphoto",
    "afpub",
    "ai",
    "aif",
    "aiff",
    "alz",
    "ape",
    "apk",
    "appimage",
    "ar",
    "arj",
    "asf",
    "au",
    "avi",
    "bak",
    "baml",
    "bh",
    "bin",
    "bk",
    "bmp",
    "btif",
    "bz2",
    "bzip2",
    "cab",
    "caf",
    "cgm",
    "class",
    "cmx",
    "cpio",
    "cr2",
    "cur",
    "dat",
    "dcm",
    "deb",
    "dex",
    "djvu",
    "dll",
    "dmg",
    "dng",
    "doc",
    "docm",
    "docx",
    "dot",
    "dotm",
    "dra",
    "DS_Store",
    "dsk",
    "dts",
    "dtshd",
    "dvb",
    "dwg",
    "dxf",
    "ecelp4800",
    "ecelp7470",
    "ecelp9600",
    "egg",
    "eol",
    "eot",
    "epub",
    "exe",
    "f4v",
    "fbs",
    "fh",
    "fla",
    "flac",
    "flatpak",
    "fli",
    "flv",
    "fpx",
    "fst",
    "fvt",
    "g3",
    "gh",
    "gif",
    "graffle",
    "gz",
    "gzip",
    "h261",
    "h263",
    "h264",
    "icns",
    "ico",
    "ief",
    "img",
    "ipa",
    "iso",
    "jar",
    "jpeg",
    "jpg",
    "jpgv",
    "jpm",
    "jxr",
    "key",
    "ktx",
    "lha",
    "lib",
    "lvp",
    "lz",
    "lzh",
    "lzma",
    "lzo",
    "m3u",
    "m4a",
    "m4v",
    "mar",
    "mdi",
    "mht",
    "mid",
    "midi",
    "mj2",
    "mka",
    "mkv",
    "mmr",
    "mng",
    "mobi",
    "mov",
    "movie",
    "mp3",
    "mp4",
    "mp4a",
    "mpeg",
    "mpg",
    "mpga",
    "mxu",
    "nef",
    "npx",
    "numbers",
    "nupkg",
    "o",
    "odp",
    "ods",
    "odt",
    "oga",
    "ogg",
    "ogv",
    "otf",
    "ott",
    "pages",
    "pbm",
    "pcx",
    "pdb",
    "pdf",
    "pea",
    "pgm",
    "pic",
    "png",
    "pnm",
    "pot",
    "potm",
    "potx",
    "ppa",
    "ppam",
    "ppm",
    "pps",
    "ppsm",
    "ppsx",
    "ppt",
    "pptm",
    "pptx",
    "psd",
    "pya",
    "pyc",
    "pyo",
    "pyv",
    "qt",
    "rar",
    "ras",
    "raw",
    "resources",
    "rgb",
    "rip",
    "rlc",
    "rmf",
    "rmvb",
    "rpm",
    "rtf",
    "rz",
    "s3m",
    "s7z",
    "scpt",
    "sgi",
    "shar",
    "snap",
    "sil",
    "sketch",
    "slk",
    "smv",
    "snk",
    "so",
    "stl",
    "suo",
    "sub",
    "swf",
    "tar",
    "tbz",
    "tbz2",
    "tga",
    "tgz",
    "thmx",
    "tif",
    "tiff",
    "tlz",
    "ttc",
    "ttf",
    "txz",
    "udf",
    "uvh",
    "uvi",
    "uvm",
    "uvp",
    "uvs",
    "uvu",
    "viv",
    "vob",
    "war",
    "wav",
    "wax",
    "wbmp",
    "wdp",
    "weba",
    "webm",
    "webp",
    "whl",
    "wim",
    "wm",
    "wma",
    "wmv",
    "wmx",
    "woff",
    "woff2",
    "wrm",
    "wvx",
    "xbm",
    "xif",
    "xla",
    "xlam",
    "xls",
    "xlsb",
    "xlsm",
    "xlsx",
    "xlt",
    "xltm",
    "xltx",
    "xm",
    "xmind",
    "xpi",
    "xpm",
    "xwd",
    "xz",
    "z",
    "zip",
    "zipx",
];


================================================
FILE: src/editor/control.ts
================================================
import type { EditorState } from "@codemirror/state";
import { StateField } from "@codemirror/state";
import type { EditorView } from "@codemirror/view";
import { editorEditorField, editorInfoField } from "obsidian";
import { eventsPerFilePathSingleton } from "./eventsPerFilepath";
import type { LineAuthoring, LineAuthoringId } from "./lineAuthor/model";
import { newComputationResultAsTransaction } from "./lineAuthor/model";
import {
    hunksState,
    newGitCompareResultAsTransaction,
    type GitCompareResult,
} from "./signs/hunkState";

/*
================== CONTROL ======================
Contains classes and function responsible for updating the model
given the changes in the Obsidian UI.
*/

/**
 * Subscribes to changes in the files on a specific filepath.
 * It knows its corresponding editor and initiates editor view changes.
 */
export class FileSubscriber {
    private lastSeenPath: string; // remember path to detect and adapt to renames

    constructor(private state: EditorState) {
        this.subscribeMe();
    }

    public notifyLineAuthoring(id: LineAuthoringId, la: LineAuthoring) {
        if (this.view === undefined) {
            console.warn(
                `Git: View is not defined for editor cache key. Unforeseen situation. id: ${id}`
            );
            return;
        }

        // using "this.state" directly here leads to some problems when closing panes. Hence, "this.view.state"
        const state = this.view.state;
        const transaction = newComputationResultAsTransaction(id, la, state);
        this.view.dispatch(transaction);
    }

    public notifyGitCompare(data: GitCompareResult) {
        if (this.view === undefined) {
            console.warn(
                `Git: View is not defined for editor cache key. Unforeseen situation. id: `
            );
            //TODO removed it above in the error message
            return;
        }

        // Prevent updates to stale subscribers
        if (this.removeIfStale()) {
            return;
        }

        // using "this.state" directly here leads to some problems when closing panes. Hence, "this.view.state"
        const state = this.view.state;
        const hunkState = state.field(hunksState);
        if (
            !hunkState ||
            hunkState.compareText != data.compareText ||
            hunkState.compareTextHead != data.compareTextHead
        ) {
            const transaction = newGitCompareResultAsTransaction(data, state);

            this.view.dispatch(transaction);
        }
    }

    public updateToNewState(state: EditorState) {
        this.state = state;

        // If no filepath was previously available subscribe now
        if (!this.lastSeenPath && this.filepath) {
            this.subscribeMe();
            // the update of the view by starting a new computation is done by
            // listening to rename events in the line authoring controller.
        }

        return this;
    }

    public removeIfStale(): boolean {
        // If a new `subscribeNewEditor` field has been created, then this instance is stale.
        // This happens when in the same leaf and `EditorView` a new file is opened
        if (
            this.view?.state.field(subscribeNewEditor, false) != this ||
            // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
            (this.view as any).destroyed
        ) {
            this.unsubscribeMe(this.lastSeenPath);
            return true;
        }
        return false;
    }

    // When a file is renamed, the editor's filepath changes.
    // So we resubscribe all editors to the new filepath.
    public changeToNewFilepath(filepath: string) {
        this.unsubscribeMe(this.lastSeenPath);
        this.subscribeMe(filepath);
        // the update of the view by starting a new computation is done by
        // listening to rename events in the line authoring controller.
    }

    private subscribeMe(filepath?: string) {
        filepath ??= this.filepath;
        if (filepath === undefined) return; // happens on the very first editor after start.

        eventsPerFilePathSingleton.ifFilepathDefinedTransformSubscribers(
            filepath,
            (subs) => subs.add(this)
        );
        this.lastSeenPath = filepath;
    }

    private unsubscribeMe(oldFilepath: string) {
        eventsPerFilePathSingleton.ifFilepathDefinedTransformSubscribers(
            oldFilepath,
            (subs) => subs.delete(this)
        );
    }

    private get filepath(): string | undefined {
        return this.state.field(editorInfoField)?.file?.path;
    }

    private get view(): EditorView | undefined {
        return this.state.field(editorEditorField);
    }
}

export type FileSubscribers = Set<FileSubscriber>;

/**
 * The Codemirror {@link Extension} used to make each editor subscribe itself to this pub-sub.
 */
export const subscribeNewEditor: StateField<FileSubscriber> =
    StateField.define<FileSubscriber>({
        create: (state) => new FileSubscriber(state),
        update: (v, transaction) => v.updateToNewState(transaction.state),
        compare: (a, b) => a === b,
    });


================================================
FILE: src/editor/editorIntegration.ts
================================================
import type ObsidianGit from "src/main";
import { LineAuthoringFeature } from "./lineAuthor/lineAuthorIntegration";
import { SignsFeature } from "./signs/signsIntegration";
import { subscribeNewEditor } from "./control";

export class EditorIntegration {
    constructor(private plg: ObsidianGit) {}

    lineAuthoringFeature: LineAuthoringFeature = new LineAuthoringFeature(
        this.plg
    );
    signsFeature: SignsFeature = new SignsFeature(this.plg);

    onUnloadPlugin() {
        this.lineAuthoringFeature.deactivateFeature();
        this.signsFeature.deactivateFeature();
    }

    onLoadPlugin() {
        this.plg.registerEditorExtension(subscribeNewEditor);
        this.lineAuthoringFeature.onLoadPlugin();
        this.signsFeature.onLoadPlugin();
    }

    onReady() {
        this.lineAuthoringFeature.conditionallyActivateBySettings();
        this.signsFeature.conditionallyActivateBySettings();
    }

    activateLineAuthoring() {
        this.lineAuthoringFeature.activateFeature();
    }
    deactiveLineAuthoring() {
        this.lineAuthoringFeature.deactivateFeature();
    }

    refreshSignsSettings() {
        const hunkSettings = this.plg.settings.hunks;
        if (
            hunkSettings.showSigns ||
            hunkSettings.statusBar != "disabled" ||
            hunkSettings.hunkCommands
        ) {
            this.signsFeature.deactivateFeature();
            this.signsFeature.activateFeature();
        } else {
            this.signsFeature.deactivateFeature();
        }
    }
}


================================================
FILE: src/editor/eventsPerFilepath.ts
================================================
import type { FileSubscriber, FileSubscribers } from "./control";

const SECONDS = 1000;
const REMOVE_STALES_FREQUENCY = 60 * SECONDS;

/**
 * * stores the subscribers/editors interested in changed per filepath
 * * We need this pub-sub design, because a filepath may be opened in multiple editors
 *   and each editor should be updated asynchronously and independently.
 * * Subscribers can be cleared when the feature is deactivated
 */
class EventsPerFilePath {
    private eventsPerFilepath: Map<string, FileSubscribers> = new Map();
    private removeStalesSubscribersTimer: number;

    constructor() {
        this.startRemoveStalesSubscribersInterval();
    }

    /**
     * Run the {@link handler} on the subscribers to {@link filepath}.
     */
    public ifFilepathDefinedTransformSubscribers<T>(
        filepath: string | undefined,
        handler: (lass: FileSubscribers) => T
    ): T | undefined {
        if (!filepath) return;

        this.ensureInitialized(filepath);

        return handler(this.eventsPerFilepath.get(filepath)!);
    }

    public forEachSubscriber(handler: (las: FileSubscriber) => void): void {
        this.eventsPerFilepath.forEach((subs) => subs.forEach(handler));
    }

    private ensureInitialized(filepath: string) {
        if (!this.eventsPerFilepath.get(filepath))
            this.eventsPerFilepath.set(filepath, new Set());
    }

    private startRemoveStalesSubscribersInterval() {
        this.removeStalesSubscribersTimer = window.setInterval(
            () => this?.forEachSubscriber((las) => las?.removeIfStale()),
            REMOVE_STALES_FREQUENCY
        );
    }

    public clear() {
        window.clearInterval(this.removeStalesSubscribersTimer);
        this.eventsPerFilepath.clear();
    }
}

export const eventsPerFilePathSingleton = new EventsPerFilePath();


================================================
FILE: src/editor/lineAuthor/lineAuthorIntegration.ts
================================================
import type { Extension } from "@codemirror/state";
import type { EventRef, TAbstractFile, WorkspaceLeaf } from "obsidian";
import { MarkdownView, Platform, TFile } from "obsidian";
import { SimpleGit } from "src/gitManager/simpleGit";
import {
    LineAuthorProvider,
    enabledLineAuthorInfoExtensions,
} from "./lineAuthorProvider";
import type { LineAuthorSettings } from "./model";
import { provideSettingsAccess } from "./model";
import { handleContextMenu } from "./view/contextMenu";
import { setTextColorCssBasedOnSetting } from "./view/gutter/coloring";
import { prepareGutterSearchForContextMenuHandling } from "./view/gutter/gutterElementSearch";
import type ObsidianGit from "src/main";

/**
 * Manages the interaction between Obsidian (file-open event, modification event, etc.)
 * and the line authoring feature. It also manages the (de-) activation of the
 * line authoring functionality.
 */
export class LineAuthoringFeature {
    private lineAuthorInfoProvide
Download .txt
gitextract_b4m8u31b/

├── .editorconfig
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   └── bug_report.yml
│   └── workflows/
│       ├── releases.yml
│       └── test.yml
├── .gitignore
├── .prettierrc.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── docs/
│   ├── .gitignore
│   ├── Authentication.md
│   ├── Common issues.md
│   ├── Features.md
│   ├── Getting Started.md
│   ├── Installation.md
│   ├── Integration with other tools.md
│   ├── Line Authoring.md
│   ├── Start here.md
│   ├── Tips-and-Tricks.md
│   └── dev/
│       └── LineAuthorFeature.md
├── esbuild.config.mjs
├── eslint.config.mjs
├── manifest.json
├── package.json
├── polyfill_buffer.js
├── src/
│   ├── automaticsManager.ts
│   ├── commands.ts
│   ├── constants.ts
│   ├── editor/
│   │   ├── control.ts
│   │   ├── editorIntegration.ts
│   │   ├── eventsPerFilepath.ts
│   │   ├── lineAuthor/
│   │   │   ├── lineAuthorIntegration.ts
│   │   │   ├── lineAuthorProvider.ts
│   │   │   ├── model.ts
│   │   │   └── view/
│   │   │       ├── cache.ts
│   │   │       ├── contextMenu.ts
│   │   │       ├── gutter/
│   │   │       │   ├── coloring.ts
│   │   │       │   ├── commitChoice.ts
│   │   │       │   ├── gutter.ts
│   │   │       │   ├── gutterElementSearch.ts
│   │   │       │   ├── initial.ts
│   │   │       │   └── untrackedFile.ts
│   │   │       └── view.ts
│   │   └── signs/
│   │       ├── changesStatusBar.ts
│   │       ├── diff.ts
│   │       ├── gutter.ts
│   │       ├── hunkActions.ts
│   │       ├── hunkState.ts
│   │       ├── hunks.ts
│   │       ├── signsIntegration.ts
│   │       ├── signsProvider.ts
│   │       └── tooltip.ts
│   ├── externalLibTypes.d.ts
│   ├── gitManager/
│   │   ├── gitManager.ts
│   │   ├── isomorphicGit.ts
│   │   ├── myAdapter.ts
│   │   └── simpleGit.ts
│   ├── main.ts
│   ├── openInGitHub.ts
│   ├── pluginGlobalRef.ts
│   ├── promiseQueue.ts
│   ├── setting/
│   │   ├── localStorageSettings.ts
│   │   └── settings.ts
│   ├── statusBar.ts
│   ├── tools.ts
│   ├── types.ts
│   ├── ui/
│   │   ├── diff/
│   │   │   ├── diffView.ts
│   │   │   └── splitDiffView.ts
│   │   ├── history/
│   │   │   ├── components/
│   │   │   │   ├── logComponent.svelte
│   │   │   │   ├── logFileComponent.svelte
│   │   │   │   └── logTreeComponent.svelte
│   │   │   ├── historyView.svelte
│   │   │   └── historyView.ts
│   │   ├── modals/
│   │   │   ├── branchModal.ts
│   │   │   ├── changedFilesModal.ts
│   │   │   ├── customMessageModal.ts
│   │   │   ├── discardModal.ts
│   │   │   ├── generalModal.ts
│   │   │   └── ignoreModal.ts
│   │   ├── sourceControl/
│   │   │   ├── components/
│   │   │   │   ├── fileComponent.svelte
│   │   │   │   ├── pulledFileComponent.svelte
│   │   │   │   ├── stagedFileComponent.svelte
│   │   │   │   ├── tooManyFilesComponent.svelte
│   │   │   │   └── treeComponent.svelte
│   │   │   ├── sourceControl.svelte
│   │   │   └── sourceControl.ts
│   │   └── statusBar/
│   │       └── branchStatusBar.ts
│   └── utils.ts
├── styles.css
└── tsconfig.json
Download .txt
SYMBOL INDEX (645 symbols across 52 files)

FILE: src/automaticsManager.ts
  class AutomaticsManager (line 4) | class AutomaticsManager {
    method constructor (line 9) | constructor(private readonly plugin: ObsidianGit) {}
    method saveLastAuto (line 11) | private saveLastAuto(date: Date, mode: "backup" | "pull" | "push") {
    method loadLastAuto (line 21) | private loadLastAuto(): { backup: Date; pull: Date; push: Date } {
    method init (line 31) | async init() {
    method unload (line 54) | unload() {
    method reload (line 66) | reload(...type: ("commit" | "push" | "pull")[]) {
    method setUpAutoCommitAndSync (line 100) | private async setUpAutoCommitAndSync() {
    method startAutoCommitAndSync (line 123) | private startAutoCommitAndSync(minutes?: number) {
    method doAutoCommitAndSync (line 146) | private doAutoCommitAndSync(): void {
    method startAutoPull (line 189) | private startAutoPull(minutes?: number) {
    method doAutoPull (line 197) | private doAutoPull(): void {
    method startAutoPush (line 207) | private startAutoPush(minutes?: number) {
    method doAutoPush (line 215) | private doAutoPush(): void {
    method clearAutoCommitAndSync (line 225) | private clearAutoCommitAndSync(): boolean {
    method clearAutoPull (line 240) | private clearAutoPull(): boolean {
    method clearAutoPush (line 249) | private clearAutoPush(): boolean {
    method diff (line 263) | private diff(setting: number, lastAuto: Date) {

FILE: src/commands.ts
  function addCommmands (line 12) | function addCommmands(plugin: ObsidianGit) {

FILE: src/constants.ts
  constant DATE_FORMAT (line 3) | const DATE_FORMAT = "YYYY-MM-DD";
  constant DATE_TIME_FORMAT_MINUTES (line 4) | const DATE_TIME_FORMAT_MINUTES = `${DATE_FORMAT} HH:mm`;
  constant DATE_TIME_FORMAT_SECONDS (line 5) | const DATE_TIME_FORMAT_SECONDS = `${DATE_FORMAT} HH:mm:ss`;
  constant GIT_LINE_AUTHORING_MOVEMENT_DETECTION_MINIMAL_LENGTH (line 7) | const GIT_LINE_AUTHORING_MOVEMENT_DETECTION_MINIMAL_LENGTH = 40;
  constant CONFLICT_OUTPUT_FILE (line 9) | const CONFLICT_OUTPUT_FILE = "conflict-files-obsidian-git.md";
  constant DEFAULT_SETTINGS (line 11) | const DEFAULT_SETTINGS: ObsidianGitSettings = {
  constant SOURCE_CONTROL_VIEW_CONFIG (line 72) | const SOURCE_CONTROL_VIEW_CONFIG = {
  constant HISTORY_VIEW_CONFIG (line 78) | const HISTORY_VIEW_CONFIG = {
  constant SPLIT_DIFF_VIEW_CONFIG (line 84) | const SPLIT_DIFF_VIEW_CONFIG = {
  constant DIFF_VIEW_CONFIG (line 89) | const DIFF_VIEW_CONFIG = {
  constant DEFAULT_WIN_GIT_PATH (line 95) | const DEFAULT_WIN_GIT_PATH = "C:\\Program Files\\Git\\cmd\\git.exe";
  constant ASK_PASS_INPUT_FILE (line 96) | const ASK_PASS_INPUT_FILE = ".git_credentials_input";
  constant ASK_PASS_SCRIPT_FILE (line 97) | const ASK_PASS_SCRIPT_FILE = "obsidian_askpass.sh";
  constant ASK_PASS_SCRIPT (line 99) | const ASK_PASS_SCRIPT = `#!/bin/sh
  constant BINARY_EXTENSIONS (line 127) | const BINARY_EXTENSIONS = [

FILE: src/editor/control.ts
  class FileSubscriber (line 24) | class FileSubscriber {
    method constructor (line 27) | constructor(private state: EditorState) {
    method notifyLineAuthoring (line 31) | public notifyLineAuthoring(id: LineAuthoringId, la: LineAuthoring) {
    method notifyGitCompare (line 45) | public notifyGitCompare(data: GitCompareResult) {
    method updateToNewState (line 73) | public updateToNewState(state: EditorState) {
    method removeIfStale (line 86) | public removeIfStale(): boolean {
    method changeToNewFilepath (line 102) | public changeToNewFilepath(filepath: string) {
    method subscribeMe (line 109) | private subscribeMe(filepath?: string) {
    method unsubscribeMe (line 120) | private unsubscribeMe(oldFilepath: string) {
    method filepath (line 127) | private get filepath(): string | undefined {
    method view (line 131) | private get view(): EditorView | undefined {
  type FileSubscribers (line 136) | type FileSubscribers = Set<FileSubscriber>;

FILE: src/editor/editorIntegration.ts
  class EditorIntegration (line 6) | class EditorIntegration {
    method constructor (line 7) | constructor(private plg: ObsidianGit) {}
    method onUnloadPlugin (line 14) | onUnloadPlugin() {
    method onLoadPlugin (line 19) | onLoadPlugin() {
    method onReady (line 25) | onReady() {
    method activateLineAuthoring (line 30) | activateLineAuthoring() {
    method deactiveLineAuthoring (line 33) | deactiveLineAuthoring() {
    method refreshSignsSettings (line 37) | refreshSignsSettings() {

FILE: src/editor/eventsPerFilepath.ts
  constant SECONDS (line 3) | const SECONDS = 1000;
  constant REMOVE_STALES_FREQUENCY (line 4) | const REMOVE_STALES_FREQUENCY = 60 * SECONDS;
  class EventsPerFilePath (line 12) | class EventsPerFilePath {
    method constructor (line 16) | constructor() {
    method ifFilepathDefinedTransformSubscribers (line 23) | public ifFilepathDefinedTransformSubscribers<T>(
    method forEachSubscriber (line 34) | public forEachSubscriber(handler: (las: FileSubscriber) => void): void {
    method ensureInitialized (line 38) | private ensureInitialized(filepath: string) {
    method startRemoveStalesSubscribersInterval (line 43) | private startRemoveStalesSubscribersInterval() {
    method clear (line 50) | public clear() {

FILE: src/editor/lineAuthor/lineAuthorIntegration.ts
  class LineAuthoringFeature (line 21) | class LineAuthoringFeature {
    method constructor (line 32) | constructor(private plg: ObsidianGit) {}
    method onLoadPlugin (line 36) | public onLoadPlugin() {
    method conditionallyActivateBySettings (line 47) | public conditionallyActivateBySettings() {
    method activateFeature (line 53) | public activateFeature() {
    method deactivateFeature (line 76) | public deactivateFeature() {
    method isAvailableOnCurrentPlatform (line 87) | public isAvailableOnCurrentPlatform(): {
    method refreshLineAuthorViews (line 102) | public refreshLineAuthorViews() {
    method activateCodeMirrorExtensions (line 111) | private activateCodeMirrorExtensions() {
    method deactivateCodeMirrorExtensions (line 121) | private deactivateCodeMirrorExtensions() {
    method createEventHandlers (line 132) | private createEventHandlers() {
    method destroyEventHandlers (line 152) | private destroyEventHandlers() {
    method createFileOpenEvent (line 184) | private createFileOpenEvent(): EventRef {
    method createWorkspaceLeafChangeEvent (line 194) | private createWorkspaceLeafChangeEvent(): EventRef {
    method createFileRenameEvent (line 201) | private createFileRenameEvent(): EventRef {
    method createVaultFileModificationHandler (line 210) | private createVaultFileModificationHandler() {
    method createHeadChangeEvent (line 219) | private createHeadChangeEvent(): EventRef {
    method createCssRefreshHandler (line 225) | private createCssRefreshHandler(): EventRef {
    method createGutterContextMenuHandler (line 231) | private createGutterContextMenuHandler() {

FILE: src/editor/lineAuthor/lineAuthorProvider.ts
  class LineAuthorProvider (line 22) | class LineAuthorProvider {
    method constructor (line 30) | constructor(private plugin: ObsidianGit) {}
    method trackChanged (line 32) | public async trackChanged(file: TFile) {
    method trackChangedHelper (line 40) | private async trackChangedHelper(file: TFile) {
    method destroy (line 53) | public destroy() {
    method computeLineAuthorInfo (line 58) | private async computeLineAuthorInfo(filepath: string) {
    method notifyComputationResultToSubscribers (line 90) | private notifyComputationResultToSubscribers(

FILE: src/editor/lineAuthor/model.ts
  type LineAuthoring (line 17) | type LineAuthoring = Blame | "untracked";
  type LineAuthoringId (line 44) | type LineAuthoringId = string;
  function lineAuthoringId (line 46) | function lineAuthoringId(
  type LineAuthoringWithChanges (line 59) | type LineAuthoringWithChanges = {
  function newComputationResultAsTransaction (line 78) | function newComputationResultAsTransaction(
  function laStateDigest (line 134) | function laStateDigest(
  type LineAuthorSettings (line 150) | type LineAuthorSettings = {
  type LineAuthorFollowMovement (line 168) | type LineAuthorFollowMovement =
  type LineAuthorDisplay (line 173) | type LineAuthorDisplay =
  type LineAuthorDateTimeFormatOptions (line 180) | type LineAuthorDateTimeFormatOptions =
  type LineAuthorTimezoneOption (line 187) | type LineAuthorTimezoneOption =
  function provideSettingsAccess (line 209) | function provideSettingsAccess(
  function maxAgeInDaysFromSettings (line 217) | function maxAgeInDaysFromSettings(settings: LineAuthorSettings) {
  function enrichUnsavedChanges (line 239) | function enrichUnsavedChanges(

FILE: src/editor/lineAuthor/view/cache.ts
  function clearViewCache (line 21) | function clearViewCache() {
  type LongestGutterCache (line 38) | type LongestGutterCache = {
  function conditionallyUpdateLongestRenderedGutter (line 53) | function conditionallyUpdateLongestRenderedGutter(
  constant ADAPTIVE_INITIAL_COLORING_AGE_CACHE_SIZE (line 85) | const ADAPTIVE_INITIAL_COLORING_AGE_CACHE_SIZE = 15;
  function recordRenderedAgeInDays (line 88) | function recordRenderedAgeInDays(age: number) {
  function computeAdaptiveInitialColoringAgeInDays (line 93) | function computeAdaptiveInitialColoringAgeInDays(): number | undefined {

FILE: src/editor/lineAuthor/view/contextMenu.ts
  type ContextMenuConfigurableSettingsKeys (line 9) | type ContextMenuConfigurableSettingsKeys =
  type CtxMenuCommitInfo (line 14) | type CtxMenuCommitInfo = Pick<BlameCommit, "hash" | "isZeroCommit"> & {
  constant COMMIT_ATTR (line 17) | const COMMIT_ATTR = "data-commit";
  function handleContextMenu (line 19) | function handleContextMenu(
  function addCopyHashMenuItem (line 43) | function addCopyHashMenuItem(commit: CtxMenuCommitInfo, menu: Menu) {
  function addConfigurableLineAuthorSettings (line 53) | function addConfigurableLineAuthorSettings(
  function enrichCommitInfoForContextMenu (line 100) | function enrichCommitInfoForContextMenu(
  function getCommitInfo (line 115) | function getCommitInfo(elt: HTMLElement): CtxMenuCommitInfo | undefined {

FILE: src/editor/lineAuthor/view/gutter/coloring.ts
  function previewColor (line 10) | function previewColor(
  function coloringBasedOnCommitAge (line 34) | function coloringBasedOnCommitAge(
  function lin (line 71) | function lin(z0: number, z1: number, x: number): number {
  function isDarkMode (line 75) | function isDarkMode() {
  function setTextColorCssBasedOnSetting (line 84) | function setTextColorCssBasedOnSetting(settings: LineAuthorSettings) {

FILE: src/editor/lineAuthor/view/gutter/commitChoice.ts
  function chooseNewestCommit (line 8) | function chooseNewestCommit(
  function isNewerThan (line 31) | function isNewerThan(left: BlameCommit, right: BlameCommit): boolean {

FILE: src/editor/lineAuthor/view/gutter/gutter.ts
  constant VALUE_NOT_FOUND_FALLBACK (line 31) | const VALUE_NOT_FOUND_FALLBACK = "-";
  constant NEW_CHANGE_CHARACTER (line 33) | const NEW_CHANGE_CHARACTER = "+";
  constant NEW_CHANGE_NUMBER_OF_CHARACTERS (line 34) | const NEW_CHANGE_NUMBER_OF_CHARACTERS = 3;
  constant DIFFERING_AUTHOR_COMMITTER_MARKER (line 36) | const DIFFERING_AUTHOR_COMMITTER_MARKER = "*";
  constant NON_WHITESPACE_REGEXP (line 38) | const NON_WHITESPACE_REGEXP = /\S/g;
  constant UNINTRUSIVE_CHARACTER_FOR_WAITING_RENDERING (line 39) | const UNINTRUSIVE_CHARACTER_FOR_WAITING_RENDERING = "%";
  class TextGutter (line 44) | class TextGutter extends GutterMarker {
    method constructor (line 45) | constructor(public text: string) {
    method eq (line 49) | eq(other: TextGutter): boolean {
    method toDOM (line 53) | toDOM() {
    method destroy (line 57) | destroy(dom: HTMLElement): void {
  class LineAuthoringGutter (line 68) | class LineAuthoringGutter extends GutterMarker {
    method constructor (line 78) | constructor(
    method eq (line 90) | public eq(other: GutterMarker): boolean {
    method toDOM (line 113) | public toDOM() {
    method destroy (line 119) | public destroy(dom: HTMLElement): void {
    method computeDom (line 133) | private computeDom() {
    method createHtmlNode (line 166) | private createHtmlNode(
    method renderNonZeroCommit (line 205) | private renderNonZeroCommit(commit: BlameCommit) {
    method renderHash (line 237) | private renderHash(nonZeroCommit: BlameCommit) {
    method renderAuthorName (line 241) | private renderAuthorName(
    method renderAuthoringDate (line 274) | private renderAuthoringDate(
    method adaptTextForFakeCommit (line 342) | private adaptTextForFakeCommit(
  function lineAuthoringGutterMarker (line 399) | function lineAuthoringGutterMarker(

FILE: src/editor/lineAuthor/view/gutter/gutterElementSearch.ts
  function prepareGutterSearchForContextMenuHandling (line 14) | function prepareGutterSearchForContextMenuHandling() {
  function findGutterElementUnderMouse (line 24) | function findGutterElementUnderMouse(): HTMLElement | undefined {
  function contains (line 30) | function contains(elt: HTMLElement, pt: { x: number; y: number }): boole...

FILE: src/editor/lineAuthor/view/gutter/initial.ts
  function initialSpacingGutter (line 24) | function initialSpacingGutter() {
  function initialLineAuthoringGutter (line 36) | function initialLineAuthoringGutter(settings: LineAuthorSettings) {
  function adaptiveInitialColoredWaitingLineAuthoring (line 59) | function adaptiveInitialColoredWaitingLineAuthoring(

FILE: src/editor/lineAuthor/view/gutter/untrackedFile.ts
  function newUntrackedFileGutter (line 9) | function newUntrackedFileGutter(

FILE: src/editor/lineAuthor/view/view.ts
  constant UNDISPLAYED (line 35) | const UNDISPLAYED = new TextGutter("");
  method markers (line 44) | markers(view) {
  method lineMarkerChange (line 49) | lineMarkerChange(update) {
  function lineAuthoringGutterMarkersRangeSet (line 75) | function lineAuthoringGutterMarkersRangeSet(
  function computeLineAuthoringGutterMarkersRangeSet (line 115) | function computeLineAuthoringGutterMarkersRangeSet(
  function computeLineMappingForUnsavedChanges (line 223) | function computeLineMappingForUnsavedChanges(
  function temporaryWorkaroundGutterSpacingForRenderedLineAuthoring (line 268) | function temporaryWorkaroundGutterSpacingForRenderedLineAuthoring(

FILE: src/editor/signs/changesStatusBar.ts
  class ChangesStatusBar (line 5) | class ChangesStatusBar {
    method constructor (line 6) | constructor(
    method display (line 28) | display(hunks: Hunk[], file: TFile | null): void {
    method remove (line 64) | remove() {

FILE: src/editor/signs/diff.ts
  type RawHunk (line 32) | type RawHunk = {
  function rawHunksToHunks (line 127) | function rawHunksToHunks(
  function rawHunkFromChunk (line 159) | function rawHunkFromChunk(
  function diffViaCMMerge (line 197) | function diffViaCMMerge(
  function computeHunks (line 222) | function computeHunks(

FILE: src/editor/signs/gutter.ts
  class GitGutterMarker (line 12) | class GitGutterMarker extends GutterMarker {
    method constructor (line 13) | constructor(
    method toDOM (line 20) | toDOM(_: EditorView) {
  function getMarkers (line 70) | function getMarkers(

FILE: src/editor/signs/hunkActions.ts
  class HunkActions (line 8) | class HunkActions {
    method constructor (line 9) | constructor(private readonly plugin: ObsidianGit) {}
    method editor (line 11) | get editor(): { obEditor: Editor; editor: EditorView } | undefined {
    method gitManager (line 22) | private get gitManager(): SimpleGit {
    method resetHunk (line 26) | resetHunk(pos?: number): void {
    method stageHunk (line 61) | async stageHunk(pos?: number): Promise<void> {
    method goToHunk (line 89) | goToHunk(direction: "first" | "last" | "next" | "prev"): void {

FILE: src/editor/signs/hunkState.ts
  function lineFromPos (line 24) | function lineFromPos(doc: Text, pos: number): number {
  method hasHunksData (line 34) | static hasHunksData(state: EditorState): boolean {
  method getHunks (line 39) | static getHunks(state: EditorState, staged: boolean): Hunk[] {
  method getHunkAtPos (line 45) | static getHunkAtPos(
  method getCursorHunk (line 58) | static getCursorHunk(
  method getHunk (line 68) | static getHunk(
  function applyHunkComputation (line 206) | function applyHunkComputation(
  function computeHunksTimed (line 277) | function computeHunksTimed(
  function scheduleHunkComputation (line 296) | function scheduleHunkComputation(
  type ComputedHunksData (line 338) | type ComputedHunksData = {
  type HunksData (line 344) | type HunksData = {
  type GitCompareResult (line 352) | type GitCompareResult = {
  function newGitCompareResultAsTransaction (line 357) | function newGitCompareResultAsTransaction(

FILE: src/editor/signs/hunks.ts
  type HunkType (line 10) | type HunkType = "add" | "change" | "delete";
  type HunkNode (line 12) | interface HunkNode {
  type Hunk (line 19) | interface Hunk {
  type SignType (line 27) | type SignType = HunkType | "topdelete" | "changedelete" | "untracked";
  type Sign (line 29) | interface Sign {
  type StatusObj (line 36) | interface StatusObj {
  method createHunk (line 43) | static createHunk(
  method createPartialHunk (line 60) | static createPartialHunk(
  method patchLines (line 116) | patchLines(hunk: Hunk, stripCr: boolean = false): string[] {
  method parseDiffLine (line 132) | static parseDiffLine(line: string): Hunk {
  method changeEnd (line 152) | private static changeEnd(hunk: Hunk): number {
  method calcSigns (line 166) | static calcSigns(
  method createPatch (line 252) | static createPatch(
  method getSummary (line 316) | getSummary(hunks: Hunk[]): StatusObj {
  method findHunk (line 337) | static findHunk(
  method findNearestHunk (line 357) | static findNearestHunk(
  method compareHeads (line 402) | compareHeads(a?: Hunk[], b?: Hunk[]): boolean {
  method compare (line 416) | private static compare(a: Hunk, b: Hunk): boolean {
  method filterCommon (line 434) | private static filterCommon(a?: Hunk[], b?: Hunk[]): Hunk[] | undefined {
  method computeStagedHunks (line 517) | static computeStagedHunks(

FILE: src/editor/signs/signsIntegration.ts
  class SignsFeature (line 19) | class SignsFeature {
    method constructor (line 29) | constructor(private plg: ObsidianGit) {}
    method onLoadPlugin (line 33) | public onLoadPlugin() {
    method conditionallyActivateBySettings (line 37) | public conditionallyActivateBySettings() {
    method activateFeature (line 47) | public activateFeature() {
    method deactivateFeature (line 74) | public deactivateFeature() {
    method isAvailableOnCurrentPlatform (line 85) | public isAvailableOnCurrentPlatform(): {
    method refresh (line 100) | public refresh() {
    method activateCodeMirrorExtensions (line 108) | private activateCodeMirrorExtensions() {
    method deactivateCodeMirrorExtensions (line 121) | private deactivateCodeMirrorExtensions() {
    method createEventHandlers (line 132) | private createEventHandlers() {
    method destroyEventHandlers (line 145) | private destroyEventHandlers() {
    method createWorkspaceLeafChangeEvent (line 170) | private createWorkspaceLeafChangeEvent(): EventRef {
    method createFileRenameEvent (line 177) | private createFileRenameEvent(): EventRef {
    method createPluginRefreshedEvent (line 194) | private createPluginRefreshedEvent(): EventRef {
    method createIntervalRefreshEvent (line 200) | private createIntervalRefreshEvent(): number {

FILE: src/editor/signs/signsProvider.ts
  class SignsProvider (line 18) | class SignsProvider {
    method constructor (line 19) | constructor(private plugin: ObsidianGit) {}
    method trackChanged (line 21) | public async trackChanged(file: TFile) {
    method trackChangedHelper (line 29) | private async trackChangedHelper(file: TFile) {
    method destroy (line 42) | public destroy() {}
    method computeSigns (line 44) | private async computeSigns(filepath: string) {
    method notifySignComputationResultToSubscribers (line 67) | private notifySignComputationResultToSubscribers(

FILE: src/editor/signs/tooltip.ts
  function togglePreviewHunk (line 20) | function togglePreviewHunk(editor: EditorView, pos?: number) {
  method update (line 41) | update(value, transaction) {
  method update (line 63) | update(value, transaction) {
  function getTooltips (line 92) | function getTooltips(state: EditorState): Tooltip[] {
  function createTooltip (line 118) | function createTooltip(

FILE: src/externalLibTypes.d.ts
  class Color (line 4) | class Color {

FILE: src/gitManager/gitManager.ts
  method constructor (line 17) | constructor(plugin: ObsidianGit) {
  method getRelativeVaultPath (line 137) | getRelativeVaultPath(path: string): string {
  method getRelativeRepoPath (line 148) | getRelativeRepoPath(
  method unload (line 163) | unload(): void {}
  method _getTreeStructure (line 165) | private _getTreeStructure<T = DiffFile | FileStatusResult>(
  method simplify (line 214) | private simplify<T>(tree: TreeItem<T>[]): TreeItem<T>[] {
  method getTreeStructure (line 261) | getTreeStructure<T = DiffFile | FileStatusResult>(
  method formatCommitMessage (line 270) | async formatCommitMessage(template: string): Promise<string> {

FILE: src/gitManager/isomorphicGit.ts
  class IsomorphicGit (line 30) | class IsomorphicGit extends GitManager {
    method constructor (line 57) | constructor(plugin: ObsidianGit) {
    method getRepo (line 61) | getRepo(): {
    method wrapFS (line 140) | async wrapFS<T>(call: Promise<T>): Promise<T> {
    method status (line 151) | async status(opts?: { path?: string }): Promise<Status> {
    method commitAll (line 197) | async commitAll({
    method commit (line 217) | async commit({
    method stage (line 250) | async stage(filepath: string, relativeToVault: boolean): Promise<void> {
    method stageAll (line 275) | async stageAll({
    method unstage (line 320) | async unstage(filepath: string, relativeToVault: boolean): Promise<voi...
    method unstageAll (line 333) | async unstageAll({
    method discard (line 361) | async discard(filepath: string): Promise<void> {
    method discardAll (line 377) | async discardAll({
    method getUntrackedPaths (line 418) | async getUntrackedPaths(opts: {
    method getProgressText (line 446) | getProgressText(action: string, event: GitProgressEvent): string {
    method resolveRef (line 460) | resolveRef(ref: string): Promise<string> {
    method pull (line 464) | async pull(): Promise<FileStatusResult[]> {
    method push (line 560) | async push(): Promise<number> {
    method getUnpushedCommits (line 598) | async getUnpushedCommits(): Promise<number> {
    method canPush (line 618) | async canPush(): Promise<boolean> {
    method checkRequirements (line 629) | async checkRequirements(): Promise<"valid" | "missing-repo"> {
    method branchInfo (line 637) | async branchInfo(): Promise<BranchInfo & { remote: string }> {
    method getCurrentRemote (line 666) | async getCurrentRemote(): Promise<string> {
    method checkout (line 674) | async checkout(branch: string, remote?: string): Promise<void> {
    method createBranch (line 690) | async createBranch(branch: string): Promise<void> {
    method deleteBranch (line 701) | async deleteBranch(branch: string): Promise<void> {
    method branchIsMerged (line 712) | branchIsMerged(_: string): Promise<boolean> {
    method init (line 716) | async init(): Promise<void> {
    method clone (line 725) | async clone(url: string, dir: string, depth?: number): Promise<void> {
    method setConfig (line 750) | async setConfig(
    method getConfig (line 768) | async getConfig(path: string): Promise<string> {
    method fetch (line 782) | async fetch(remote?: string): Promise<void> {
    method setRemote (line 806) | async setRemote(name: string, url: string): Promise<void> {
    method getRemoteBranches (line 822) | async getRemoteBranches(remote: string): Promise<string[]> {
    method getRemotes (line 837) | async getRemotes(): Promise<string[]> {
    method removeRemote (line 843) | async removeRemote(remoteName: string): Promise<void> {
    method getRemoteUrl (line 849) | async getRemoteUrl(remote: string): Promise<string | undefined> {
    method log (line 855) | async log(
    method updateBasePath (line 902) | updateBasePath(basePath: string): Promise<void> {
    method updateUpstreamBranch (line 907) | async updateUpstreamBranch(remoteBranch: string): Promise<void> {
    method updateGitPath (line 925) | updateGitPath(_: string): Promise<void> {
    method getFileChangesCount (line 930) | async getFileChangesCount(
    method walkDifference (line 942) | async walkDifference({
    method getStagedFiles (line 1001) | async getStagedFiles(
    method getUnstagedFiles (line 1016) | async getUnstagedFiles(base = "."): Promise<UnstagedFile[]> {
    method getDiffString (line 1129) | async getDiffString(
    method getLastCommitTime (line 1227) | async getLastCommitTime(): Promise<Date | undefined> {
    method getFileStatusResult (line 1235) | private getFileStatusResult(
    method checkAuthorInfo (line 1252) | private async checkAuthorInfo(): Promise<void> {
    method showNotice (line 1262) | private showNotice(message: string, infinity = true): Notice | undefin...
  function fromValue (line 1278) | function fromValue(value: any) {
  function asyncIteratorToArrayBuffer (line 1303) | async function asyncIteratorToArrayBuffer(

FILE: src/gitManager/myAdapter.ts
  class MyAdapter (line 10) | class MyAdapter {
    method constructor (line 19) | constructor(
    method readFile (line 38) | async readFile(path: string, opts: any) {
    method writeFile (line 68) | async writeFile(path: string, data: string | ArrayBuffer) {
    method readdir (line 93) | async readdir(path: string) {
    method mkdir (line 107) | async mkdir(path: string) {
    method rmdir (line 110) | async rmdir(path: string, opts: any) {
    method stat (line 114) | async stat(path: string) {
    method unlink (line 180) | async unlink(path: string) {
    method lstat (line 183) | async lstat(path: string) {
    method readlink (line 186) | async readlink(path: string) {
    method symlink (line 189) | async symlink(path: string) {
    method saveAndClear (line 193) | async saveAndClear(): Promise<void> {
    method clearIndex (line 209) | clearIndex() {
    method gitDir (line 215) | private get gitDir(): string {
    method maybeLog (line 219) | private maybeLog(_: string) {

FILE: src/gitManager/simpleGit.ts
  class SimpleGit (line 32) | class SimpleGit extends GitManager {
    method constructor (line 37) | constructor(plugin: ObsidianGit) {
    method setGitInstance (line 41) | async setGitInstance(ignoreError = false): Promise<void> {
    method getRelativeVaultPath (line 150) | getRelativeVaultPath(filePath: string): string {
    method getRelativeRepoPath (line 166) | getRelativeRepoPath(
    method absPluginConfigPath (line 184) | private get absPluginConfigPath(): string {
    method relPluginConfigPath (line 195) | private get relPluginConfigPath(): string {
    method askpass (line 198) | async askpass(): Promise<void> {
    method addAskPassScriptToExclude (line 280) | async addAskPassScriptToExclude(): Promise<void> {
    method unload (line 325) | unload(): void {
    method status (line 329) | async status(opts?: { path?: string }): Promise<Status> {
    method submoduleAwareHeadRevisonInContainingDirectory (line 359) | async submoduleAwareHeadRevisonInContainingDirectory(
    method getSubmodulePaths (line 374) | async getSubmodulePaths(): Promise<string[]> {
    method formatPath (line 421) | formatPath(path: { from?: string; path: string }): {
    method blame (line 446) | async blame(
    method isTracked (line 483) | async isTracked(path: string): Promise<boolean> {
    method commitAll (line 492) | async commitAll({ message }: { message: string }): Promise<number> {
    method commit (line 517) | async commit({
    method stage (line 538) | async stage(path: string, relativeToVault: boolean): Promise<void> {
    method stageAll (line 547) | async stageAll({ dir }: { dir?: string }): Promise<void> {
    method unstageAll (line 553) | async unstageAll({ dir }: { dir?: string }): Promise<void> {
    method unstage (line 559) | async unstage(path: string, relativeToVault: boolean): Promise<void> {
    method discard (line 568) | async discard(filepath: string): Promise<void> {
    method applyPatch (line 576) | async applyPatch(patch: string): Promise<void> {
    method getUntrackedPaths (line 587) | async getUntrackedPaths(opts: { path?: string }): Promise<string[]> {
    method hashObject (line 603) | async hashObject(filepath: string): Promise<string> {
    method discardAll (line 619) | async discardAll({ dir }: { dir?: string }): Promise<void> {
    method pull (line 623) | async pull(): Promise<FileStatusResult[] | undefined> {
    method push (line 721) | async push(): Promise<number | undefined | null> {
    method getUnpushedCommits (line 761) | async getUnpushedCommits(): Promise<number> {
    method canPush (line 785) | async canPush(): Promise<boolean> {
    method checkRequirements (line 803) | async checkRequirements(): Promise<
    method branchInfo (line 815) | async branchInfo(): Promise<BranchInfo> {
    method getRemoteUrl (line 826) | async getRemoteUrl(remote: string): Promise<string | undefined> {
    method log (line 840) | async log(
    method show (line 893) | async show(
    method checkout (line 903) | async checkout(branch: string, remote?: string): Promise<void> {
    method createBranch (line 923) | async createBranch(branch: string): Promise<void> {
    method deleteBranch (line 927) | async deleteBranch(branch: string, force: boolean): Promise<void> {
    method branchIsMerged (line 931) | async branchIsMerged(branch: string): Promise<boolean> {
    method init (line 936) | async init(): Promise<void> {
    method clone (line 940) | async clone(url: string, dir: string, depth?: number): Promise<void> {
    method setConfig (line 954) | async setConfig(path: string, value: string | undefined): Promise<void> {
    method getConfig (line 962) | async getConfig(
    method fetch (line 973) | async fetch(remote?: string): Promise<void> {
    method setRemote (line 977) | async setRemote(name: string, url: string): Promise<void> {
    method getRemoteBranches (line 985) | async getRemoteBranches(remote: string): Promise<string[]> {
    method getRemotes (line 995) | async getRemotes() {
    method removeRemote (line 1004) | async removeRemote(remoteName: string) {
    method updateUpstreamBranch (line 1011) | async updateUpstreamBranch(remoteBranch: string) {
    method updateGitPath (line 1033) | updateGitPath(_: string): Promise<void> {
    method updateBasePath (line 1037) | updateBasePath(_: string): Promise<void> {
    method getDiffString (line 1041) | async getDiffString(
    method diff (line 1052) | async diff(
    method rawCommand (line 1060) | async rawCommand(command: string): Promise<string> {
    method getSubmoduleOfFile (line 1066) | async getSubmoduleOfFile(
    method getLastCommitTime (line 1117) | async getLastCommitTime(): Promise<Date | undefined> {
    method isGitInstalled (line 1134) | private async isGitInstalled(): Promise<boolean> {
    method convertErrors (line 1163) | private convertErrors(error: unknown): never {
    method isFileTrackedByLFS (line 1186) | async isFileTrackedByLFS(filePath: string): Promise<boolean> {
  function parseBlame (line 1213) | function parseBlame(blameOutputUnnormalized: string): Blame {
  function parseLineInfoInto (line 1255) | function parseLineInfoInto(lineInfo: string[], line: number, result: Bla...
  function parseHeaderInto (line 1272) | function parseHeaderInto(header: string[], out: Blame, line: number) {
  function finalizeBlameCommitInfo (line 1326) | function finalizeBlameCommitInfo(commit: BlameCommit) {
  function isUndefinedOrEmptyObject (line 1344) | function isUndefinedOrEmptyObject(obj: object | undefined | null): boole...
  function startsWithNonWhitespace (line 1348) | function startsWithNonWhitespace(str: string): boolean {
  function removeEmailBrackets (line 1352) | function removeEmailBrackets(gitEmail: string) {

FILE: src/main.ts
  class ObsidianGit (line 66) | class ObsidianGit extends Plugin {
    method setPluginState (line 99) | setPluginState(state: Partial<PluginState>): void {
    method updateCachedStatus (line 104) | async updateCachedStatus(): Promise<Status> {
    method refresh (line 122) | async refresh() {
    method refreshUpdatedHead (line 147) | refreshUpdatedHead() {}
    method onload (line 149) | async onload() {
    method onExternalSettingsChange (line 177) | onExternalSettingsChange() {
    method reloadSettings (line 184) | async reloadSettings(): Promise<void> {
    method registerStuff (line 222) | registerStuff(): void {
    method setRefreshDebouncer (line 325) | setRefreshDebouncer(): void {
    method addFileToGitignore (line 338) | async addFileToGitignore(
    method handleFileMenu (line 358) | handleFileMenu(
    method migrateSettings (line 473) | async migrateSettings(): Promise<void> {
    method unloadPlugin (line 497) | unloadPlugin() {
    method onunload (line 517) | onunload() {
    method loadSettings (line 523) | async loadSettings() {
    method saveSettings (line 533) | async saveSettings() {
    method useSimpleGit (line 538) | get useSimpleGit(): boolean {
    method init (line 542) | async init({ fromReload = false }): Promise<void> {
    method createNewRepo (line 637) | async createNewRepo() {
    method cloneNewRepo (line 647) | async cloneNewRepo() {
    method isAllInitialized (line 753) | async isAllInitialized(): Promise<boolean> {
    method pullChangesFromRemote (line 761) | async pullChangesFromRemote(): Promise<void> {
    method commitAndSync (line 788) | async commitAndSync({
    method commit (line 840) | async commit({
    method push (line 1055) | async push(): Promise<boolean> {
    method pull (line 1119) | async pull(): Promise<false | number> {
    method fetch (line 1144) | async fetch(): Promise<void> {
    method mayDeleteConflictFile (line 1159) | async mayDeleteConflictFile(): Promise<void> {
    method stageFile (line 1174) | async stageFile(file: TFile): Promise<boolean> {
    method unstageFile (line 1185) | async unstageFile(file: TFile): Promise<boolean> {
    method switchBranch (line 1196) | async switchBranch(): Promise<string | undefined> {
    method switchRemoteBranch (line 1214) | async switchRemoteBranch(): Promise<string | undefined> {
    method createBranch (line 1229) | async createBranch(): Promise<string | undefined> {
    method deleteBranch (line 1243) | async deleteBranch(): Promise<string | undefined> {
    method remotesAreSet (line 1283) | async remotesAreSet(): Promise<boolean> {
    method setUpstreamBranch (line 1301) | async setUpstreamBranch(): Promise<boolean> {
    method discardAll (line 1316) | async discardAll(path?: string): Promise<DiscardResult> {
    method handleConflict (line 1384) | async handleConflict(conflicted?: string[]): Promise<void> {
    method editRemotes (line 1422) | async editRemotes(): Promise<string | undefined> {
    method selectRemoteBranch (line 1453) | async selectRemoteBranch(): Promise<string | undefined> {
    method removeRemote (line 1491) | async removeRemote() {
    method onActiveLeafChange (line 1507) | onActiveLeafChange(leaf: WorkspaceLeaf | null): void {
    method handleNoNetworkError (line 1558) | handleNoNetworkError(_: NoNetworkError): void {
    method displayMessage (line 1574) | displayMessage(message: string, timeout: number = 4 * 1000): void {
    method displayError (line 1589) | displayError(data: unknown, timeout: number = 10 * 1000): void {
    method log (line 1609) | log(...data: unknown[]) {

FILE: src/openInGitHub.ts
  function openLineInGitHub (line 6) | async function openLineInGitHub(
  function openHistoryInGitHub (line 36) | async function openHistoryInGitHub(file: TFile, manager: GitManager) {
  function getData (line 55) | async function getData(

FILE: src/promiseQueue.ts
  class PromiseQueue (line 3) | class PromiseQueue {
    method constructor (line 9) | constructor(private readonly plugin: ObsidianGit) {}
    method addTask (line 17) | addTask<T>(
    method handleTask (line 27) | private handleTask(): void {
    method clear (line 46) | clear(): void {

FILE: src/setting/localStorageSettings.ts
  class LocalStorageSettings (line 3) | class LocalStorageSettings {
    method constructor (line 6) | constructor(private readonly plugin: ObsidianGit) {
    method migrate (line 11) | migrate(): void {
    method getPassword (line 36) | getPassword(): string | null {
    method setPassword (line 40) | setPassword(value: string): void {
    method getUsername (line 44) | getUsername(): string | null {
    method setUsername (line 48) | setUsername(value: string): void {
    method getHostname (line 52) | getHostname(): string | null {
    method setHostname (line 56) | setHostname(value: string): void {
    method getConflict (line 60) | getConflict(): boolean {
    method setConflict (line 64) | setConflict(value: boolean): void {
    method getLastAutoPull (line 68) | getLastAutoPull(): string | null {
    method setLastAutoPull (line 72) | setLastAutoPull(value: string): void {
    method getLastAutoBackup (line 76) | getLastAutoBackup(): string | null {
    method setLastAutoBackup (line 80) | setLastAutoBackup(value: string): void {
    method getLastAutoPush (line 84) | getLastAutoPush(): string | null {
    method setLastAutoPush (line 88) | setLastAutoPush(value: string): void {
    method getGitPath (line 92) | getGitPath(): string | null {
    method setGitPath (line 96) | setGitPath(value: string): void {
    method getPATHPaths (line 100) | getPATHPaths(): string[] {
    method setPATHPaths (line 107) | setPATHPaths(value: string[]): void {
    method getEnvVars (line 114) | getEnvVars(): string[] {
    method setEnvVars (line 120) | setEnvVars(value: string[]): void {
    method getPluginDisabled (line 127) | getPluginDisabled(): boolean {
    method setPluginDisabled (line 133) | setPluginDisabled(value: boolean): void {
    method getPausedAutomatics (line 144) | getPausedAutomatics(): boolean {
    method setPausedAutomatics (line 151) | setPausedAutomatics(value: boolean): void {

FILE: src/setting/settings.ts
  constant FORMAT_STRING_REFERENCE_URL (line 34) | const FORMAT_STRING_REFERENCE_URL =
  constant LINE_AUTHOR_FEATURE_WIKI_LINK (line 36) | const LINE_AUTHOR_FEATURE_WIKI_LINK =
  class ObsidianGitSettingsTab (line 39) | class ObsidianGitSettingsTab extends PluginSettingTab {
    method constructor (line 41) | constructor(
    method settings (line 50) | private get settings() {
    method display (line 54) | display(): void {
    method mayDisableSetting (line 1017) | mayDisableSetting(setting: Setting, disable: boolean) {
    method configureLineAuthorShowStatus (line 1024) | public configureLineAuthorShowStatus(show: boolean) {
    method lineAuthorSettingHandler (line 1036) | public async lineAuthorSettingHandler<
    method beforeSaveSettings (line 1049) | public beforeSaveSettings() {
    method addLineAuthorInfoSettings (line 1060) | private addLineAuthorInfoSettings() {
    method createColorSetting (line 1302) | private createColorSetting(which: "oldest" | "newest") {
    method refreshColorSettingsName (line 1332) | private refreshColorSettingsName(which: "oldest" | "newest") {
    method refreshColorSettingsDesc (line 1343) | private refreshColorSettingsDesc(which: "oldest" | "newest", rgb?: RGB) {
    method colorSettingPreviewDescHtml (line 1354) | private colorSettingPreviewDescHtml(
    method previewCustomDateTimeDescriptionHtml (line 1375) | private previewCustomDateTimeDescriptionHtml(
    method previewOldestAgeDescriptionHtml (line 1382) | private previewOldestAgeDescriptionHtml(coloringMaxAge: string) {
    method setNonDefaultValue (line 1397) | private setNonDefaultValue({
    method refreshDisplayWithDelay (line 1426) | private refreshDisplayWithDelay(timeout = 80): void {
  function pickColor (line 1431) | function pickColor(
  function parseColoringMaxAgeDuration (line 1438) | function parseColoringMaxAgeDuration(

FILE: src/statusBar.ts
  type StatusBarMessage (line 5) | interface StatusBarMessage {
  class StatusBar (line 10) | class StatusBar {
    method constructor (line 22) | constructor(
    method displayMessage (line 35) | public displayMessage(message: string, timeout: number) {
    method display (line 43) | public display() {
    method displayState (line 62) | private displayState() {
    method displayFromNow (line 145) | private displayFromNow(): void {
    method refreshCommitTimestamp (line 179) | private async refreshCommitTimestamp() {
    method remove (line 186) | public remove() {

FILE: src/tools.ts
  class Tools (line 13) | class Tools {
    method constructor (line 14) | constructor(private readonly plugin: ObsidianGit) {}
    method hasTooBigFiles (line 16) | async hasTooBigFiles(
    method writeAndOpenFile (line 78) | async writeAndOpenFile(text?: string) {
    method openDiff (line 103) | openDiff({
    method runRawCommand (line 143) | async runRawCommand() {

FILE: src/types.ts
  type ObsidianGitSettings (line 3) | interface ObsidianGitSettings {
  function mergeSettingsByPriority (line 79) | function mergeSettingsByPriority(
  type SyncMethod (line 87) | type SyncMethod = "rebase" | "merge" | "reset";
  type MergeStrategy (line 89) | type MergeStrategy = "none" | "ours" | "theirs";
  type ShowAuthorInHistoryView (line 91) | type ShowAuthorInHistoryView = "full" | "initials" | "hide";
  type Author (line 93) | interface Author {
  type Status (line 98) | interface Status {
  type GitTimestamp (line 109) | interface GitTimestamp {
  type UserEmail (line 121) | interface UserEmail {
  type BlameCommit (line 126) | interface BlameCommit {
  type Blame (line 139) | interface Blame {
  type FileStatusResult (line 211) | interface FileStatusResult {
  type PluginState (line 225) | interface PluginState {
  type CurrentGitAction (line 230) | enum CurrentGitAction {
  type LogEntry (line 239) | interface LogEntry {
  type DiffEntry (line 252) | interface DiffEntry {
  type DiffFile (line 257) | interface DiffFile {
  type WalkDifference (line 267) | interface WalkDifference {
  type UnstagedFile (line 272) | type UnstagedFile = WalkDifference;
  type BranchInfo (line 274) | interface BranchInfo {
  type TreeItem (line 280) | interface TreeItem<T = DiffFile | FileStatusResult> {
  type RootTreeItem (line 288) | type RootTreeItem<T> = TreeItem<T> & { children: TreeItem<T>[] };
  type StatusRootTreeItem (line 290) | type StatusRootTreeItem = RootTreeItem<FileStatusResult>;
  type HistoryRootTreeItem (line 292) | type HistoryRootTreeItem = RootTreeItem<DiffFile>;
  type DiffViewState (line 294) | type DiffViewState = {
  type FileType (line 320) | enum FileType {
  class NoNetworkError (line 326) | class NoNetworkError extends Error {
    method constructor (line 327) | constructor(public readonly originalError: string) {
  type App (line 333) | interface App {
  type View (line 340) | interface View {
  type ViewRegistry (line 344) | interface ViewRegistry {
  type Workspace (line 352) | interface Workspace {

FILE: src/ui/diff/diffView.ts
  class DiffView (line 9) | class DiffView extends ItemView {
    method constructor (line 16) | constructor(
    method getViewType (line 32) | getViewType(): string {
    method getDisplayText (line 36) | getDisplayText(): string {
    method getIcon (line 46) | getIcon(): string {
    method setState (line 50) | async setState(state: DiffViewState, _: ViewStateResult): Promise<void> {
    method getState (line 61) | getState(): Record<string, unknown> {
    method onClose (line 65) | onClose(): Promise<void> {
    method onOpen (line 71) | async onOpen(): Promise<void> {
    method refresh (line 76) | async refresh(): Promise<void> {

FILE: src/ui/diff/splitDiffView.ts
  class SplitDiffView (line 24) | class SplitDiffView extends ItemView {
    method constructor (line 37) | constructor(
    method getViewType (line 125) | getViewType(): string {
    method getDisplayText (line 129) | getDisplayText(): string {
    method getIcon (line 147) | getIcon(): string {
    method setState (line 151) | async setState(state: DiffViewState, _: ViewStateResult): Promise<void> {
    method getState (line 163) | getState(): Record<string, unknown> {
    method onClose (line 167) | onClose(): Promise<void> {
    method onOpen (line 172) | async onOpen(): Promise<void> {
    method gitShow (line 177) | async gitShow(commitHash: string, file: string): Promise<string> {
    method bShouldBeEditable (line 204) | async bShouldBeEditable(): Promise<boolean> {
    method updateModifiableEditor (line 214) | async updateModifiableEditor() {
    method updateRefEditors (line 241) | async updateRefEditors() {
    method renderButtons (line 278) | renderButtons(): HTMLElement {
    method createMergeView (line 364) | async createMergeView() {

FILE: src/ui/history/historyView.ts
  class HistoryView (line 8) | class HistoryView extends ItemView implements HoverParent {
    method constructor (line 13) | constructor(leaf: WorkspaceLeaf, plugin: ObsidianGit) {
    method getViewType (line 19) | getViewType(): string {
    method getDisplayText (line 23) | getDisplayText(): string {
    method getIcon (line 27) | getIcon(): string {
    method onClose (line 31) | onClose(): Promise<void> {
    method reload (line 39) | reload(): void {
    method onOpen (line 53) | onOpen(): Promise<void> {

FILE: src/ui/modals/branchModal.ts
  class BranchModal (line 4) | class BranchModal extends FuzzySuggestModal<string> {
    method constructor (line 9) | constructor(
    method getItems (line 17) | getItems(): string[] {
    method getItemText (line 20) | getItemText(item: string): string {
    method onChooseItem (line 23) | onChooseItem(item: string, _: MouseEvent | KeyboardEvent): void {
    method openAndGetReslt (line 27) | openAndGetReslt(): Promise<string> {
    method onClose (line 34) | onClose() {

FILE: src/ui/modals/changedFilesModal.ts
  class ChangedFilesModal (line 5) | class ChangedFilesModal extends FuzzySuggestModal<FileStatusResult> {
    method constructor (line 9) | constructor(plugin: ObsidianGit, changedFiles: FileStatusResult[]) {
    method getItems (line 18) | getItems(): FileStatusResult[] {
    method getItemText (line 22) | getItemText(item: FileStatusResult): string {
    method onChooseItem (line 37) | onChooseItem(item: FileStatusResult, _: MouseEvent | KeyboardEvent): v...

FILE: src/ui/modals/customMessageModal.ts
  class CustomMessageModal (line 4) | class CustomMessageModal extends SuggestModal<string> {
    method constructor (line 8) | constructor(private readonly plugin: ObsidianGit) {
    method openAndGetResult (line 15) | openAndGetResult(): Promise<string> {
    method onClose (line 22) | onClose() {
    method getSuggestions (line 29) | getSuggestions(query: string): string[] {
    method renderSuggestion (line 35) | renderSuggestion(value: string, el: HTMLElement): void {
    method onChooseSuggestion (line 39) | onChooseSuggestion(value: string, __: MouseEvent | KeyboardEvent) {

FILE: src/ui/modals/discardModal.ts
  type DiscardResult (line 5) | type DiscardResult = false | "delete" | "discard";
  class DiscardModal (line 7) | class DiscardModal extends Modal {
    method constructor (line 11) | constructor({
    method openAndGetResult (line 35) | openAndGetResult(): Promise<DiscardResult> {
    method onOpen (line 42) | onOpen() {
    method onClose (line 115) | onClose() {

FILE: src/ui/modals/generalModal.ts
  type OptionalGeneralModalConfig (line 4) | interface OptionalGeneralModalConfig {
  type GeneralModalConfig (line 12) | interface GeneralModalConfig {
  class GeneralModal (line 30) | class GeneralModal extends SuggestModal<string> {
    method constructor (line 36) | constructor(plugin: ObsidianGit, config: OptionalGeneralModalConfig) {
    method openAndGetResult (line 65) | openAndGetResult(): Promise<string | undefined> {
    method onClose (line 76) | onClose() {
    method getSuggestions (line 82) | getSuggestions(query: string): string[] {
    method renderSuggestion (line 92) | renderSuggestion(value: string, el: HTMLElement): void {
    method onChooseSuggestion (line 100) | onChooseSuggestion(value: string, _: MouseEvent | KeyboardEvent) {

FILE: src/ui/modals/ignoreModal.ts
  class IgnoreModal (line 4) | class IgnoreModal extends Modal {
    method constructor (line 8) | constructor(
    method openAndGetReslt (line 15) | openAndGetReslt(): Promise<string> {
    method onOpen (line 22) | onOpen() {
    method onClose (line 42) | onClose() {

FILE: src/ui/sourceControl/sourceControl.ts
  class GitView (line 8) | class GitView extends ItemView implements HoverParent {
    method constructor (line 13) | constructor(leaf: WorkspaceLeaf, plugin: ObsidianGit) {
    method getViewType (line 19) | getViewType(): string {
    method getDisplayText (line 23) | getDisplayText(): string {
    method getIcon (line 27) | getIcon(): string {
    method onClose (line 31) | onClose(): Promise<void> {
    method reload (line 39) | reload(): void {
    method onOpen (line 53) | onOpen(): Promise<void> {

FILE: src/ui/statusBar/branchStatusBar.ts
  class BranchStatusBar (line 3) | class BranchStatusBar {
    method constructor (line 4) | constructor(
    method display (line 14) | async display() {
    method remove (line 27) | remove() {

FILE: src/utils.ts
  function assertNever (line 8) | function assertNever(x: never): never {
  function plural (line 13) | function plural(
  function getNewLeaf (line 36) | function getNewLeaf(
  function mayTriggerFileMenu (line 52) | function mayTriggerFileMenu(
  function impossibleBranch (line 86) | function impossibleBranch(x: never): never {
  function rgbToString (line 91) | function rgbToString(rgb: RGB): string {
  function convertToRgb (line 95) | function convertToRgb(str: string): RGB | undefined {
  function momentToEpochSeconds (line 104) | function momentToEpochSeconds(instant: moment.Moment): number {
  function median (line 108) | function median(array: number[]): number | undefined {
  function strictDeepEqual (line 113) | function strictDeepEqual<T>(a: T, b: T): boolean {
  function arrayProxyWithNewLength (line 117) | function arrayProxyWithNewLength<T>(array: T[], length: number): T[] {
  function resizeToLength (line 128) | function resizeToLength(
  function prefixOfLengthAsWhitespace (line 143) | function prefixOfLengthAsWhitespace(
  function between (line 159) | function between(l: number, x: number, r: number) {
  function splitRemoteBranch (line 162) | function splitRemoteBranch(
  function getDisplayPath (line 169) | function getDisplayPath(path: string): string {
  function formatMinutes (line 174) | function formatMinutes(minutes: number): string {
  function getExtensionFromPath (line 179) | function getExtensionFromPath(path: string): string {
  function fileIsBinary (line 187) | function fileIsBinary(path: string): boolean {
  function formatRemoteUrl (line 196) | function formatRemoteUrl(url: string): string {
  function fileOpenableInObsidian (line 208) | function fileOpenableInObsidian(
  function convertPathToAbsoluteGitignoreRule (line 228) | function convertPathToAbsoluteGitignoreRule({
  function hoverPreview (line 263) | function hoverPreview<YourView extends ItemView>(
  function spawnAsync (line 280) | function spawnAsync(
Condensed preview — 90 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (753K chars).
[
  {
    "path": ".editorconfig",
    "chars": 157,
    "preview": "# top-most EditorConfig file\nroot = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\nindent_style "
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "chars": 1643,
    "preview": "name: Bug Report\ndescription: File a bug report\ntitle: \"[Bug]: \"\nlabels: \"bug\"\nbody:\n- type: markdown\n  attributes:\n    "
  },
  {
    "path": ".github/workflows/releases.yml",
    "chars": 2822,
    "preview": "name: Build obsidian plugin\n\non:\n    push:\n        tags:\n            - \"*\"\n\nenv:\n    PLUGIN_NAME: obsidian-git\n\npermissi"
  },
  {
    "path": ".github/workflows/test.yml",
    "chars": 1686,
    "preview": "name: test\non:\n    push:\n    pull_request:\njobs:\n    svelte-check:\n        runs-on: ubuntu-latest\n        steps:\n       "
  },
  {
    "path": ".gitignore",
    "chars": 127,
    "preview": "# Intellij\n*.iml\n.idea\n\n# npm\nnode_modules\nmain.js\nyarn.lock\n\n# build\n*.js.map\n\n.prettierignore\n/data.json\n\n.vscode\n\n.DS"
  },
  {
    "path": ".prettierrc.json",
    "chars": 190,
    "preview": "{\n    \"trailingComma\": \"es5\",\n    \"tabWidth\": 4,\n    \"semi\": true,\n    \"plugins\": [\"prettier-plugin-svelte\"],\n    \"overr"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 87522,
    "preview": "# Changelog\n\nAll notable changes to this project will be documented in this file. See [standard-version](https://github."
  },
  {
    "path": "LICENSE",
    "chars": 1080,
    "preview": "MIT License\n\nCopyright (c) 2020 Vinzent03, Denis Olehov\n\nPermission is hereby granted, free of charge, to any person obt"
  },
  {
    "path": "README.md",
    "chars": 7645,
    "preview": "# Obsidian Git Plugin\n\nA powerful community plugin for [Obsidian.md](Obsidian.md) that brings Git integration right into"
  },
  {
    "path": "docs/.gitignore",
    "chars": 9,
    "preview": ".obsidian"
  },
  {
    "path": "docs/Authentication.md",
    "chars": 5532,
    "preview": "---\naliases:\n  - \"04 Authentication\"\n---\n# macOS\n\n## HTTPS\n\nRun the following to use the macOS keychain to store your cr"
  },
  {
    "path": "docs/Common issues.md",
    "chars": 2317,
    "preview": "## xcrun: error: invalid developer path\n\nThis is an error occurring only on macOS. It's easy to fix though. Just run the"
  },
  {
    "path": "docs/Features.md",
    "chars": 3065,
    "preview": "## Source Control View\n\nOpen it using the \"Open source control view\" command. It lists all current changes like when you"
  },
  {
    "path": "docs/Getting Started.md",
    "chars": 8242,
    "preview": "# Desktop\nYou can either start by cloning an existing remote repository as described [[#For existing remote repository|h"
  },
  {
    "path": "docs/Installation.md",
    "chars": 2484,
    "preview": "---\naliases:\n  - 02 Installation\n---\n\n> [!important]\n> Although the plugin itself is desktop platform independent, an in"
  },
  {
    "path": "docs/Integration with other tools.md",
    "chars": 3009,
    "preview": "Most issues with the integration of other installable tools are that their installation path is not added to the `PATH` "
  },
  {
    "path": "docs/Line Authoring.md",
    "chars": 5633,
    "preview": "# Quick User Guide\n\nA quick showcase of all functionality. This feature is based on [git-blame](https://git-scm.com/docs"
  },
  {
    "path": "docs/Start here.md",
    "chars": 2793,
    "preview": "---\naliases:\n    - \"01 Start here\"\n---\n\n# Git plugin Documentation\n\n## Topics\n\n-   [[Installation|Installation]]\n-   [[G"
  },
  {
    "path": "docs/Tips-and-Tricks.md",
    "chars": 1674,
    "preview": "# Tips and Tricks\n\n## Gitignore\n\nTo exclude cache files from the repository, create `.gitignore` file in the root of you"
  },
  {
    "path": "docs/dev/LineAuthorFeature.md",
    "chars": 8858,
    "preview": "# Line Authoring Feature - Developer Documentation\n\n-   This feature was developed by [GollyTicker](https://github.com/G"
  },
  {
    "path": "esbuild.config.mjs",
    "chars": 1809,
    "preview": "import esbuild from \"esbuild\";\nimport esbuildSvelte from \"esbuild-svelte\";\nimport process from \"process\";\nimport { svelt"
  },
  {
    "path": "eslint.config.mjs",
    "chars": 1473,
    "preview": "import svelteParser from \"svelte-eslint-parser\";\nimport tsParser from \"@typescript-eslint/parser\";\nimport eslint from \"@"
  },
  {
    "path": "manifest.json",
    "chars": 325,
    "preview": "{\n    \"author\": \"Vinzent\",\n    \"authorUrl\": \"https://github.com/Vinzent03\",\n    \"id\": \"obsidian-git\",\n    \"name\": \"Git\","
  },
  {
    "path": "package.json",
    "chars": 2325,
    "preview": "{\n    \"name\": \"obsidian-git\",\n    \"version\": \"2.38.0\",\n    \"description\": \"Integrate Git version control with automatic "
  },
  {
    "path": "polyfill_buffer.js",
    "chars": 193,
    "preview": "import { Platform } from 'obsidian';\nlet buffer;\nif (Platform.isMobileApp) {\n    buffer = require('buffer/index.js').Buf"
  },
  {
    "path": "src/automaticsManager.ts",
    "chars": 9340,
    "preview": "import { debounce } from \"obsidian\";\nimport type ObsidianGit from \"./main\";\n\nexport default class AutomaticsManager {\n  "
  },
  {
    "path": "src/commands.ts",
    "chars": 17789,
    "preview": "import { Notice, Platform, TFolder, WorkspaceLeaf } from \"obsidian\";\nimport { HISTORY_VIEW_CONFIG, SOURCE_CONTROL_VIEW_C"
  },
  {
    "path": "src/constants.ts",
    "chars": 6633,
    "preview": "import { Platform } from \"obsidian\";\nimport type { ObsidianGitSettings } from \"./types\";\nexport const DATE_FORMAT = \"YYY"
  },
  {
    "path": "src/editor/control.ts",
    "chars": 5191,
    "preview": "import type { EditorState } from \"@codemirror/state\";\nimport { StateField } from \"@codemirror/state\";\nimport type { Edit"
  },
  {
    "path": "src/editor/editorIntegration.ts",
    "chars": 1532,
    "preview": "import type ObsidianGit from \"src/main\";\nimport { LineAuthoringFeature } from \"./lineAuthor/lineAuthorIntegration\";\nimpo"
  },
  {
    "path": "src/editor/eventsPerFilepath.ts",
    "chars": 1834,
    "preview": "import type { FileSubscriber, FileSubscribers } from \"./control\";\n\nconst SECONDS = 1000;\nconst REMOVE_STALES_FREQUENCY ="
  },
  {
    "path": "src/editor/lineAuthor/lineAuthorIntegration.ts",
    "chars": 8264,
    "preview": "import type { Extension } from \"@codemirror/state\";\nimport type { EventRef, TAbstractFile, WorkspaceLeaf } from \"obsidia"
  },
  {
    "path": "src/editor/lineAuthor/lineAuthorProvider.ts",
    "chars": 3638,
    "preview": "import type { Extension } from \"@codemirror/state\";\nimport { Prec } from \"@codemirror/state\";\nimport type { TFile } from"
  },
  {
    "path": "src/editor/lineAuthor/model.ts",
    "chars": 10962,
    "preview": "import type { EditorState, Transaction } from \"@codemirror/state\";\nimport { StateEffect, StateField } from \"@codemirror/"
  },
  {
    "path": "src/editor/lineAuthor/view/cache.ts",
    "chars": 3998,
    "preview": "import type { RangeSet } from \"@codemirror/state\";\nimport type { GutterMarker } from \"@codemirror/view\";\nimport { latest"
  },
  {
    "path": "src/editor/lineAuthor/view/contextMenu.ts",
    "chars": 3986,
    "preview": "import type { Editor, MarkdownView, Menu } from \"obsidian\";\nimport { DEFAULT_SETTINGS } from \"src/constants\";\nimport typ"
  },
  {
    "path": "src/editor/lineAuthor/view/gutter/coloring.ts",
    "chars": 3048,
    "preview": "import type { App } from \"obsidian\";\nimport type { LineAuthorSettings } from \"src/editor/lineAuthor/model\";\nimport { max"
  },
  {
    "path": "src/editor/lineAuthor/view/gutter/commitChoice.ts",
    "chars": 1030,
    "preview": "import type { LineAuthoring } from \"src/editor/lineAuthor/model\";\nimport type { BlameCommit } from \"src/types\";\n\n/**\n * "
  },
  {
    "path": "src/editor/lineAuthor/view/gutter/gutter.ts",
    "chars": 14457,
    "preview": "import { GutterMarker } from \"@codemirror/view\";\nimport { sha256 } from \"js-sha256\";\nimport { moment, setTooltip } from "
  },
  {
    "path": "src/editor/lineAuthor/view/gutter/gutterElementSearch.ts",
    "chars": 1269,
    "preview": "import { attachedGutterElements } from \"src/editor/lineAuthor/view/cache\";\n\nconst mouseXY = { x: -10, y: -10 };\n\n// todo"
  },
  {
    "path": "src/editor/lineAuthor/view/gutter/initial.ts",
    "chars": 3214,
    "preview": "import { moment } from \"obsidian\";\nimport { DEFAULT_SETTINGS } from \"src/constants\";\nimport type {\n    LineAuthoring,\n  "
  },
  {
    "path": "src/editor/lineAuthor/view/gutter/untrackedFile.ts",
    "chars": 622,
    "preview": "import { zeroCommit } from \"src/gitManager/simpleGit\";\nimport type { LineAuthorSettings } from \"src/editor/lineAuthor/mo"
  },
  {
    "path": "src/editor/lineAuthor/view/view.ts",
    "chars": 9475,
    "preview": "import type { Extension, Range, Text } from \"@codemirror/state\";\nimport { RangeSet } from \"@codemirror/state\";\nimport ty"
  },
  {
    "path": "src/editor/signs/changesStatusBar.ts",
    "chars": 2139,
    "preview": "import type ObsidianGit from \"src/main\";\nimport type { Hunk } from \"./hunks\";\nimport { MarkdownView, TFile } from \"obsid"
  },
  {
    "path": "src/editor/signs/diff.ts",
    "chars": 8870,
    "preview": "import { Hunks, type Hunk } from \"../signs/hunks\";\nimport { Chunk } from \"@codemirror/merge\";\nimport { ChangeDesc, Text "
  },
  {
    "path": "src/editor/signs/gutter.ts",
    "chars": 3630,
    "preview": "import { RangeSet, StateField, Transaction } from \"@codemirror/state\";\nimport { EditorView, gutter, GutterMarker } from "
  },
  {
    "path": "src/editor/signs/hunkActions.ts",
    "chars": 3807,
    "preview": "import { editorInfoField, type Editor } from \"obsidian\";\nimport { HunksStateHelper } from \"./hunkState\";\nimport type { E"
  },
  {
    "path": "src/editor/signs/hunkState.ts",
    "chars": 11142,
    "preview": "import {\n    ChangeDesc,\n    EditorState,\n    StateEffect,\n    StateField,\n    Text,\n    Transaction,\n} from \"@codemirro"
  },
  {
    "path": "src/editor/signs/hunks.ts",
    "chars": 16961,
    "preview": "/**\n * This file contains code translated from Lua to TypeScript.\n * Original Source: https://github.com/lewis6991/gitsi"
  },
  {
    "path": "src/editor/signs/signsIntegration.ts",
    "chars": 7309,
    "preview": "import type { Extension } from \"@codemirror/state\";\nimport type { EventRef, WorkspaceLeaf } from \"obsidian\";\nimport { Ma"
  },
  {
    "path": "src/editor/signs/signsProvider.ts",
    "chars": 2692,
    "preview": "import type { Extension } from \"@codemirror/state\";\nimport type { TFile } from \"obsidian\";\nimport { eventsPerFilePathSin"
  },
  {
    "path": "src/editor/signs/tooltip.ts",
    "chars": 6239,
    "preview": "import { EditorState, StateEffect, StateField } from \"@codemirror/state\";\nimport {\n    EditorView,\n    showTooltip,\n    "
  },
  {
    "path": "src/externalLibTypes.d.ts",
    "chars": 396,
    "preview": "declare module \"css-color-converter\" {\n    /* The following list of type definitions is incomplete! */\n\n    class Color "
  },
  {
    "path": "src/gitManager/gitManager.ts",
    "chars": 11340,
    "preview": "import { hostname as osHostname } from \"os\";\nimport { type App, moment, Platform } from \"obsidian\";\nimport type Obsidian"
  },
  {
    "path": "src/gitManager/isomorphicGit.ts",
    "chars": 43758,
    "preview": "import { createPatch } from \"diff\";\nimport type {\n    AuthCallback,\n    AuthFailureCallback,\n    GitHttpRequest,\n    Git"
  },
  {
    "path": "src/gitManager/myAdapter.ts",
    "chars": 7842,
    "preview": "/* eslint-disable @typescript-eslint/require-await */\n/* eslint-disable @typescript-eslint/only-throw-error */\n/* eslint"
  },
  {
    "path": "src/gitManager/simpleGit.ts",
    "chars": 48206,
    "preview": "import debug from \"debug\";\nimport * as fsPromises from \"fs/promises\";\nimport type { FileSystemAdapter } from \"obsidian\";"
  },
  {
    "path": "src/main.ts",
    "chars": 57543,
    "preview": "import { Errors } from \"isomorphic-git\";\nimport type { Debouncer, Menu, TAbstractFile, WorkspaceLeaf } from \"obsidian\";\n"
  },
  {
    "path": "src/openInGitHub.ts",
    "chars": 4647,
    "preview": "import type { Editor, TFile } from \"obsidian\";\nimport { Notice } from \"obsidian\";\nimport type { GitManager } from \"./git"
  },
  {
    "path": "src/pluginGlobalRef.ts",
    "chars": 258,
    "preview": "import type ObsidianGit from \"src/main\";\n\n/**\n * Store the reference to the {@link ObsidianGit} plugin globally, so that"
  },
  {
    "path": "src/promiseQueue.ts",
    "chars": 1326,
    "preview": "import type ObsidianGit from \"./main\";\n\nexport class PromiseQueue {\n    private tasks: {\n        task: () => Promise<unk"
  },
  {
    "path": "src/setting/localStorageSettings.ts",
    "chars": 4394,
    "preview": "import type { App } from \"obsidian\";\nimport type ObsidianGit from \"../main\";\nexport class LocalStorageSettings {\n    pri"
  },
  {
    "path": "src/setting/settings.ts",
    "chars": 62506,
    "preview": "import type { App, RGB, TextComponent } from \"obsidian\";\nimport {\n    moment,\n    Notice,\n    Platform,\n    PluginSettin"
  },
  {
    "path": "src/statusBar.ts",
    "chars": 6861,
    "preview": "import { setIcon, moment } from \"obsidian\";\nimport type ObsidianGit from \"./main\";\nimport { CurrentGitAction } from \"./t"
  },
  {
    "path": "src/tools.ts",
    "chars": 5443,
    "preview": "import { Notice, Platform, TFile } from \"obsidian\";\nimport {\n    CONFLICT_OUTPUT_FILE,\n    DIFF_VIEW_CONFIG,\n    SPLIT_D"
  },
  {
    "path": "src/types.ts",
    "chars": 12295,
    "preview": "import type { LineAuthorSettings } from \"src/editor/lineAuthor/model\";\n\nexport interface ObsidianGitSettings {\n    commi"
  },
  {
    "path": "src/ui/diff/diffView.ts",
    "chars": 4685,
    "preview": "import { html } from \"diff2html\";\nimport type { EventRef, ViewStateResult, WorkspaceLeaf } from \"obsidian\";\nimport { Ite"
  },
  {
    "path": "src/ui/diff/splitDiffView.ts",
    "chars": 16909,
    "preview": "import type { Debouncer, ViewStateResult, WorkspaceLeaf } from \"obsidian\";\nimport { debounce, ItemView, Platform, setIco"
  },
  {
    "path": "src/ui/history/components/logComponent.svelte",
    "chars": 4319,
    "preview": "<script lang=\"ts\">\n    import { moment } from \"obsidian\";\n    import type ObsidianGit from \"src/main\";\n    import type {"
  },
  {
    "path": "src/ui/history/components/logFileComponent.svelte",
    "chars": 3220,
    "preview": "<script lang=\"ts\">\n    import { setIcon, TFile } from \"obsidian\";\n    import type { DiffFile } from \"src/types\";\n    imp"
  },
  {
    "path": "src/ui/history/components/logTreeComponent.svelte",
    "chars": 3721,
    "preview": "<!-- tslint:disable ts(2345)  -->\n<script lang=\"ts\">\n    import LogTreeComponent from \"./logTreeComponent.svelte\";\n    i"
  },
  {
    "path": "src/ui/history/historyView.svelte",
    "chars": 4548,
    "preview": "<script lang=\"ts\">\n    import { setIcon } from \"obsidian\";\n    import { SimpleGit } from \"src/gitManager/simpleGit\";\n   "
  },
  {
    "path": "src/ui/history/historyView.ts",
    "chars": 1567,
    "preview": "import type { HoverParent, HoverPopover, WorkspaceLeaf } from \"obsidian\";\nimport { ItemView } from \"obsidian\";\nimport { "
  },
  {
    "path": "src/ui/modals/branchModal.ts",
    "chars": 1053,
    "preview": "import { FuzzySuggestModal } from \"obsidian\";\nimport type ObsidianGit from \"src/main\";\n\nexport class BranchModal extends"
  },
  {
    "path": "src/ui/modals/changedFilesModal.ts",
    "chars": 1508,
    "preview": "import { FuzzySuggestModal } from \"obsidian\";\nimport type ObsidianGit from \"src/main\";\nimport type { FileStatusResult } "
  },
  {
    "path": "src/ui/modals/customMessageModal.ts",
    "chars": 1317,
    "preview": "import { moment, SuggestModal } from \"obsidian\";\nimport type ObsidianGit from \"src/main\";\n\nexport class CustomMessageMod"
  },
  {
    "path": "src/ui/modals/discardModal.ts",
    "chars": 3998,
    "preview": "import type { App } from \"obsidian\";\nimport { Modal } from \"obsidian\";\nimport { plural } from \"src/utils\";\n\nexport type "
  },
  {
    "path": "src/ui/modals/generalModal.ts",
    "chars": 3579,
    "preview": "import { SuggestModal } from \"obsidian\";\nimport type ObsidianGit from \"src/main\";\n\nexport interface OptionalGeneralModal"
  },
  {
    "path": "src/ui/modals/ignoreModal.ts",
    "chars": 1210,
    "preview": "import type { App } from \"obsidian\";\nimport { Modal } from \"obsidian\";\n\nexport class IgnoreModal extends Modal {\n    res"
  },
  {
    "path": "src/ui/sourceControl/components/fileComponent.svelte",
    "chars": 6073,
    "preview": "<script lang=\"ts\">\n    import { setIcon, TFile } from \"obsidian\";\n    import { hoverPreview } from \"src/utils\";\n    impo"
  },
  {
    "path": "src/ui/sourceControl/components/pulledFileComponent.svelte",
    "chars": 2311,
    "preview": "<script lang=\"ts\">\n    import { TFile } from \"obsidian\";\n    import { hoverPreview } from \"src/utils\";\n    import type {"
  },
  {
    "path": "src/ui/sourceControl/components/stagedFileComponent.svelte",
    "chars": 4144,
    "preview": "<script lang=\"ts\">\n    import { setIcon, TFile } from \"obsidian\";\n    import { hoverPreview } from \"src/utils\";\n    impo"
  },
  {
    "path": "src/ui/sourceControl/components/tooManyFilesComponent.svelte",
    "chars": 695,
    "preview": "<script lang=\"ts\">\n    interface Props {\n        files: unknown[];\n    }\n\n    let { files }: Props = $props();\n</script>"
  },
  {
    "path": "src/ui/sourceControl/components/treeComponent.svelte",
    "chars": 10366,
    "preview": "<!-- tslint:disable ts(2345)  -->\n<script lang=\"ts\">\n    import TreeComponent from \"./treeComponent.svelte\";\n\n    import"
  },
  {
    "path": "src/ui/sourceControl/sourceControl.svelte",
    "chars": 26904,
    "preview": "<script lang=\"ts\">\n    import { Platform, Scope, setIcon } from \"obsidian\";\n    import { SOURCE_CONTROL_VIEW_CONFIG } fr"
  },
  {
    "path": "src/ui/sourceControl/sourceControl.ts",
    "chars": 1605,
    "preview": "import type { HoverParent, HoverPopover, WorkspaceLeaf } from \"obsidian\";\nimport { ItemView } from \"obsidian\";\nimport { "
  },
  {
    "path": "src/ui/statusBar/branchStatusBar.ts",
    "chars": 832,
    "preview": "import type ObsidianGit from \"src/main\";\n\nexport class BranchStatusBar {\n    constructor(\n        private statusBarEl: H"
  },
  {
    "path": "src/utils.ts",
    "chars": 9772,
    "preview": "import * as cssColorConverter from \"css-color-converter\";\nimport { spawn, type SpawnOptionsWithoutStdio } from \"child_pr"
  },
  {
    "path": "styles.css",
    "chars": 18355,
    "preview": "@keyframes loading {\n    0% {\n        transform: rotate(0deg);\n    }\n\n    100% {\n        transform: rotate(360deg);\n    "
  },
  {
    "path": "tsconfig.json",
    "chars": 709,
    "preview": "{\n    \"compilerOptions\": {\n        \"baseUrl\": \".\",\n        \"inlineSourceMap\": true,\n        \"inlineSources\": true,\n     "
  }
]

About this extraction

This page contains the full source code of the Vinzent03/obsidian-git GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 90 files (705.0 KB), approximately 167.8k tokens, and a symbol index with 645 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!