diff --git a/app/build.gradle b/app/build.gradle
index e4f2e03e1..e08af72ba 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -10,8 +10,8 @@ android {
defaultConfig {
minSdkVersion 21
targetSdkVersion 33
- versionCode 212
- versionName "1.8.24"
+ versionCode 214
+ versionName "1.8.25"
applicationId = "com.orgzlyrevived"
resValue "string", "application_id", "com.orgzlyrevived"
diff --git a/app/src/androidTest/java/com/orgzly/android/repos/GitRepoTest.kt b/app/src/androidTest/java/com/orgzly/android/repos/GitRepoTest.kt
new file mode 100644
index 000000000..3f4b1a260
--- /dev/null
+++ b/app/src/androidTest/java/com/orgzly/android/repos/GitRepoTest.kt
@@ -0,0 +1,152 @@
+package com.orgzly.android.repos
+
+import android.net.Uri
+import android.os.Build
+import androidx.core.net.toUri
+import com.orgzly.R
+import com.orgzly.android.OrgzlyTest
+import com.orgzly.android.db.entity.BookView
+import com.orgzly.android.db.entity.Repo
+import com.orgzly.android.git.GitFileSynchronizer
+import com.orgzly.android.git.GitPreferencesFromRepoPrefs
+import com.orgzly.android.prefs.AppPreferences
+import com.orgzly.android.prefs.RepoPreferences
+import com.orgzly.android.util.MiscUtils
+import org.eclipse.jgit.api.Git
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Assume
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.ExpectedException
+import java.io.File
+import java.io.IOException
+import java.nio.file.Path
+import kotlin.io.path.createTempDirectory
+
+class GitRepoTest : OrgzlyTest() {
+
+ private lateinit var bareRepoPath: Path
+ private lateinit var repoUri: Uri
+ private lateinit var gitPreferences: GitPreferencesFromRepoPrefs
+ private lateinit var workingtree: File
+ private lateinit var repoPreferences: RepoPreferences
+ private lateinit var repo: Repo
+ private lateinit var syncRepo: GitRepo
+ private lateinit var git: Git
+ private lateinit var synchronizer: GitFileSynchronizer
+
+ @Rule
+ @JvmField
+ var exceptionRule: ExpectedException = ExpectedException.none()
+
+ override fun setUp() {
+ super.setUp()
+ bareRepoPath = createTempDirectory()
+ Git.init().setBare(true).setDirectory(bareRepoPath.toFile()).call()
+ AppPreferences.gitIsEnabled(context, true)
+ repoUri = bareRepoPath.toFile().toUri()
+ repo = testUtils.setupRepo(RepoType.GIT, repoUri.toString())
+ repoPreferences = RepoPreferences(context, repo.id, repoUri)
+ gitPreferences = GitPreferencesFromRepoPrefs(repoPreferences)
+ workingtree = File(gitPreferences.repositoryFilepath())
+ workingtree.mkdirs()
+ git = GitRepo.ensureRepositoryExists(gitPreferences, true, null)
+ syncRepo = dataRepository.getRepoInstance(repo.id, RepoType.GIT, repo.url) as GitRepo
+ synchronizer = GitFileSynchronizer(git, gitPreferences)
+ }
+
+ override fun tearDown() {
+ super.tearDown()
+ testUtils.deleteRepo(repo.url)
+ workingtree.deleteRecursively()
+ bareRepoPath.toFile()?.deleteRecursively()
+ }
+
+ @Test
+ fun testSyncNewBookWithoutLinkAndOneRepo() {
+ testUtils.setupBook("book1", "book content")
+ testUtils.sync()
+ val bookView = dataRepository.getBooks()[0]
+ assertEquals(repoUri.toString(), bookView.linkRepo?.url)
+ assertEquals(1, syncRepo.books.size)
+ assertEquals(bookView.syncedTo.toString(), syncRepo.books[0].toString())
+ assertEquals(context.getString(R.string.sync_status_saved, repo.url), bookView.book.lastAction!!.message)
+ assertEquals("/book1.org", bookView.syncedTo!!.uri.toString())
+ }
+
+ @Test
+ fun testIgnoredFilesInRepoAreNotLoaded() {
+ Assume.assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+ // Create ignore file in working tree and commit
+ val ignoreFileContents = """
+ ignoredbook.org
+ ignored-*.org
+ """.trimIndent()
+ addAndCommitIgnoreFile(ignoreFileContents)
+ // Add multiple files to repo
+ for (fileName in arrayOf("ignoredbook.org", "ignored-3.org", "notignored.org")) {
+ val tmpFile = File.createTempFile("orgzlytest", null)
+ MiscUtils.writeStringToFile("book content", tmpFile)
+ synchronizer.addAndCommitNewFile(tmpFile, fileName)
+ tmpFile.delete()
+ }
+ testUtils.sync()
+ assertEquals(1, syncRepo.books.size)
+ assertEquals(1, dataRepository.getBooks().size)
+ assertEquals("notignored", dataRepository.getBooks()[0].book.name)
+ }
+
+ @Test
+ fun testUnIgnoredFilesInRepoAreLoaded() {
+ Assume.assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+ // Create ignore file in working tree and commit
+ val ignoreFileContents = """
+ *.org
+ !notignored.org
+ """.trimIndent()
+ addAndCommitIgnoreFile(ignoreFileContents)
+ // Add multiple files to repo
+ for (fileName in arrayOf("ignoredbook.org", "ignored-3.org", "notignored.org")) {
+ val tmpFile = File.createTempFile("orgzlytest", null)
+ MiscUtils.writeStringToFile("book content", tmpFile)
+ synchronizer.addAndCommitNewFile(tmpFile, fileName)
+ tmpFile.delete()
+ }
+ testUtils.sync()
+ assertEquals(1, syncRepo.books.size)
+ assertEquals(1, dataRepository.getBooks().size)
+ assertEquals("notignored", dataRepository.getBooks()[0].book.name)
+ }
+
+ @Test
+ fun testIgnoreRulePreventsLinkingBook() {
+ Assume.assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+ addAndCommitIgnoreFile("*.org")
+ testUtils.setupBook("booky", "")
+ exceptionRule.expect(IOException::class.java)
+ exceptionRule.expectMessage("matches a rule in .orgzlyignore")
+ testUtils.syncOrThrow()
+ }
+
+ @Test
+ fun testIgnoreRulePreventsRenamingBook() {
+ Assume.assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+ addAndCommitIgnoreFile("badname*")
+ testUtils.setupBook("goodname", "")
+ testUtils.sync()
+ var bookView: BookView? = dataRepository.getBookView("goodname")
+ dataRepository.renameBook(bookView!!, "badname")
+ bookView = dataRepository.getBooks()[0]
+ assertTrue(
+ bookView.book.lastAction.toString().contains("matches a rule in .orgzlyignore")
+ )
+ }
+
+ private fun addAndCommitIgnoreFile(contents: String) {
+ val tmpFile = File.createTempFile("orgzlytest", null)
+ MiscUtils.writeStringToFile(contents, tmpFile)
+ synchronizer.addAndCommitNewFile(tmpFile, RepoIgnoreNode.IGNORE_FILE)
+ tmpFile.delete()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/orgzly/android/repos/SyncRepo.java b/app/src/main/java/com/orgzly/android/repos/SyncRepo.java
index 192bbf470..ff09131d4 100644
--- a/app/src/main/java/com/orgzly/android/repos/SyncRepo.java
+++ b/app/src/main/java/com/orgzly/android/repos/SyncRepo.java
@@ -41,6 +41,14 @@ public interface SyncRepo {
*/
InputStream openRepoFileInputStream(String fileName) throws IOException;
+ /**
+ * Open a file in the repository for reading. Originally added for parsing the .orgzlyignore
+ * file.
+ * @param fileName The file to open
+ * @throws IOException
+ */
+ InputStream openRepoFileInputStream(String fileName) throws IOException;
+
/**
* Uploads book storing it under given filename under repo's url.
* @param file The contents of this file should be stored at the remote location/repo
diff --git a/app/src/main/java/com/orgzly/android/repos/WebdavRepo.kt b/app/src/main/java/com/orgzly/android/repos/WebdavRepo.kt
index 03f96d475..e807e8633 100644
--- a/app/src/main/java/com/orgzly/android/repos/WebdavRepo.kt
+++ b/app/src/main/java/com/orgzly/android/repos/WebdavRepo.kt
@@ -291,4 +291,4 @@ class WebdavRepo(
private fun Uri.toUrl(): String {
return this.toString().replace("^(?:web)?dav(s?://)".toRegex(), "http$1")
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/res/layout/dialog_whats_new.xml b/app/src/main/res/layout/dialog_whats_new.xml
index 28a7422d9..1cb332d59 100644
--- a/app/src/main/res/layout/dialog_whats_new.xml
+++ b/app/src/main/res/layout/dialog_whats_new.xml
@@ -20,6 +20,19 @@
android:layout_height="wrap_content" />
+
+
+
+
+
+