diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fee4fd..fb82bcb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ All notable changes to this project will be documented in this file. +## 2025-06-01 + +Use [git-worktree](https://git-scm.com/docs/git-worktree) to be able to place the git repository in the local Termux storage and the worktree in the shared storage. This avoid filesystem issues using Git. +To be able to use this new feature I recommend to run the script `setup-interactive.sh" and clone the repository/repositories again. + ## 2024-05-12 Big refactoring of the setup scripts: now three different scripts are provided you can choose from. diff --git a/README.md b/README.md index 3fbab58..56254a8 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,42 @@ git clone https://github.com/davidkopp/termux-scripts.git cd termux-scripts ``` +### Setup git-worktree + +We need to use the shared storage so Android apps will be able to access the folder. However, using the shared storage has also some disadvantages. See the Termux Wiki page [Internal and external storage](https://wiki.termux.com/wiki/Internal_and_external_storage) for more information. +I personally had the issue that Git was only able to execute one command on a git repository located on the shared storage and the following commands failed with an error + +> Unable to read current working directory: No such file or directory + +A solution is to use [git-worktree](https://git-scm.com/docs/git-worktree). That means we place the git repository in the local Termux storage as a bare repository and place the worktree in the shared storage. + +The script `setup-interactive.sh` can set up everything for you. +Here are the relevant commands that can be used to set it up manually: + +- Clone repo as bare: + + ```sh + cd $HOME + GIT_REPO_URL= # fill in your repository URL + REPO_NAME= # repo name is optional, I use the suffix `.git` to know later that it is a bare git repository (e.g. "notes.git") + git clone --bare --depth=1 ${GIT_REPO_URL} ${REPO_NAME} + cd ${REPO_NAME} + # workaround: by default bare repos don't fetch remote branches + git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" + ``` + +- Add git-worktree in detached mode (we don't want to create a new branch): + + ```sh + # specify a path in shared storage where you want to store your worktree + PATH_TO_REPO=~/storage/shared/git/notes + git worktree add --detach ${PATH_TO_REPO} + cd ${PATH_TO_REPO} + git switch main + git fetch origin + git branch --set-upstream-to=origin/main + ``` + ### Setup sync _Note: During the setup some changes are made to your git configuration. If you want other options, modify the script 'configure-git.sh'._ diff --git a/configure-git.sh b/configure-git.sh index 47d101d..b2ccbc3 100755 --- a/configure-git.sh +++ b/configure-git.sh @@ -9,11 +9,26 @@ git config --global core.editor "nano" # To avoid conflicts between Linux and Windows, set git file mode setting to false: git config core.fileMode false -# Configure branch `main` for sync: -git config "branch.${GIT_BRANCH_NAME}.sync" true +# Configure branch for sync: +git config "branch.${BRANCH_NAME}.sync" true # Automatically add new (untracked) files and sync them: -git config "branch.${GIT_BRANCH_NAME}.syncNewFiles" true +git config "branch.${BRANCH_NAME}.syncNewFiles" true # Set commit message: -git config "branch.${GIT_BRANCH_NAME}.syncCommitMsg" "android on \$(printf '%(%Y-%m-%d %H:%M:%S)T\\n' -1)" +git config "branch.${BRANCH_NAME}.syncCommitMsg" "android on \$(printf '%(%Y-%m-%d %H:%M:%S)T\\n' -1)" + +# Switch to provided branch +if ! git switch "${BRANCH_NAME}"; then + echo "Switching to branch '${BRANCH_NAME}' failed! Check your configuration." + exit 1 +fi + +# Set upstream +git branch --set-upstream-to="${REMOTE_NAME}/${BRANCH_NAME}" + +# Finally try to fetch from remote +if ! git fetch "${REMOTE_NAME}"; then + echo "Fetching from remote '${REMOTE_NAME}' failed! Check your configuration." + exit 1 +fi diff --git a/setup-interactive.sh b/setup-interactive.sh index ce74120..5c318df 100755 --- a/setup-interactive.sh +++ b/setup-interactive.sh @@ -1,7 +1,8 @@ #!/data/data/com.termux/files/usr/bin/bash -# Default path used for cloning a new repository -BASE_PATH_FOR_REPO_CLONING="$HOME/storage/shared/git" +# Default paths used for cloning a new repository +BASE_PATH_GIT_BARE_REPOS="$HOME" +BASE_PATH_GIT_WORKTREE_MAIN="$HOME/storage/shared/git" MY_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" @@ -9,23 +10,55 @@ echo "Do you want to clone a new repository (1) or provide a path to an already read -p "Enter your choice (1 or 2): " choice case $choice in 1) - # Clone a new git repository - canonical_base_path=$(readlink -f "${BASE_PATH_FOR_REPO_CLONING}") - echo "Git clone URL (repo will be cloned to '${canonical_base_path}/REPO_NAME'):" + # Clone a new git repository (add bare repo in local storage and worktree in shared storage) + canonical_base_path_bare_repos=$(readlink -f "${BASE_PATH_GIT_BARE_REPOS}") + canonical_base_path_worktree=$(readlink -f "${BASE_PATH_GIT_WORKTREE_MAIN}") + echo "Git clone URL:" read GIT_REPO_URL echo "" - mkdir -p "${canonical_base_path}" - cd "${canonical_base_path}" || (echo "cd ${canonical_base_path} failed!" && exit 1) + REPO_NAME="$(basename "$GIT_REPO_URL" .git)" - GIT_REPO_PATH=$(readlink -f "${PWD}/${REPO_NAME}") - if [[ -d $GIT_REPO_PATH ]]; then - echo "Directory '${GIT_REPO_PATH}' already exists! Skip cloning of git repository ${GIT_REPO_URL}. Try to use existing directory instead." - else - if ! git clone "$GIT_REPO_URL"; then + BARE_REPO_NAME="${REPO_NAME}.git" + mkdir -p "${canonical_base_path_bare_repos}" + cd "${canonical_base_path_bare_repos}" || (echo "cd ${canonical_base_path_bare_repos} failed!" && exit 1) + GIT_BARE_REPO_PATH=$(readlink -f "${PWD}/${BARE_REPO_NAME}") + mkdir -p "${canonical_base_path_worktree}" + cd "${canonical_base_path_worktree}" || (echo "cd ${canonical_base_path_worktree} failed!" && exit 1) + GIT_WORKTREE_PATH=$(readlink -f "${PWD}/${REPO_NAME}") + + echo "The repo '${GIT_REPO_URL}' will be cloned as a bare repository to '${GIT_BARE_REPO_PATH}' and the worktree will be placed in '${GIT_WORKTREE_PATH}'." + if [[ -d $GIT_BARE_REPO_PATH ]]; then + echo "Directory '${GIT_BARE_REPO_PATH}' already exists! Cloning of the git repository will be skipped and the existing directory will be used instead." + fi + if [[ -d $GIT_WORKTREE_PATH ]]; then + echo "Directory '${GIT_WORKTREE_PATH}' already exists! Adding worktree will be skipped. This is probably not intended and the setup probably won't work!" + fi + read -r -p "Continue? [Y/n] " response + response=${response,,} + if [[ "$response" == "n" ]]; then + echo "Exiting." + exit 1 + fi + echo "" + + # clone the repository as a bare repo + if ! [[ -d $GIT_BARE_REPO_PATH ]]; then + cd "${canonical_base_path_bare_repos}" || (echo "cd ${canonical_base_path_bare_repos} failed!" && exit 1) + if ! git clone --bare "$GIT_REPO_URL" "$BARE_REPO_NAME"; then echo "Git clone of '$GIT_REPO_URL' failed!" exit 1 fi - echo "Git repository cloned to: ${GIT_REPO_PATH}" + cd "${GIT_BARE_REPO_PATH}" || (echo "cd ${GIT_BARE_REPO_PATH} failed!" && exit 1) + # workaround: by default bare repos don't fetch remote branches + git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" + fi + echo "" + # create main worktree in detached mode (we don't want to create a new branch) + if ! [[ -d $GIT_WORKTREE_PATH ]]; then + if ! git worktree add --detach "${GIT_WORKTREE_PATH}"; then + echo "Add worktree for bare repository '${GIT_BARE_REPO_PATH}' in '${GIT_WORKTREE_PATH} failed!" + exit 1 + fi fi ;; 2) @@ -37,7 +70,7 @@ case $choice in echo "Provided git repo path '${canonical_path_to_repo}' does not exist!" exit 1 fi - GIT_REPO_PATH=$canonical_path_to_repo + GIT_WORKTREE_PATH=$canonical_path_to_repo ;; *) echo "Invalid choice. Please enter 1 or 2." @@ -46,9 +79,9 @@ case $choice in esac # Ask for branch name, default is main -if [[ -z "${GIT_BRANCH_NAME}" ]]; then -echo -e "\nWhich branch do you want to use for syncing? (if none is provided, 'main' is used)" -read GIT_BRANCH_NAME +if [[ -z "${BRANCH_NAME}" ]]; then + echo -e "\nWhich branch do you want to use for syncing? (if none is provided, 'main' is used)" + read BRANCH_NAME fi # Single or multi repo setup? @@ -56,9 +89,9 @@ echo -e "Do you want to setup sync for only one repository or for multiple ones? read -p "Enter your choice (1 or 2): " choice case $choice in 1) - source "$MY_DIR/setup-single-repo.sh" "${GIT_REPO_PATH}" "${GIT_BRANCH_NAME}" + source "$MY_DIR/setup-single-repo.sh" "${GIT_WORKTREE_PATH}" "${BRANCH_NAME}" ;; 2) - source "$MY_DIR/setup-multi-repo.sh" "${GIT_REPO_PATH}" "${GIT_BRANCH_NAME}" + source "$MY_DIR/setup-multi-repo.sh" "${GIT_WORKTREE_PATH}" "${BRANCH_NAME}" ;; esac diff --git a/setup-multi-repo.sh b/setup-multi-repo.sh index 1ee2868..9c302e4 100755 --- a/setup-multi-repo.sh +++ b/setup-multi-repo.sh @@ -6,15 +6,15 @@ # $3: repo name (optional, default extracted repo name, used as a suffix for script names) GIT_REPO_PATH=$1 -GIT_BRANCH_NAME=$2 +BRANCH_NAME=$2 REPO_NAME=$3 if [[ -z "${GIT_REPO_PATH}" ]]; then echo -e "Path to local Git repository not provided!\nUsage: $(basename "$0") git-path\nAs an alternative you can also use the interactive setup script: setup-interactive.sh" exit 1 fi -if [[ -z "${GIT_BRANCH_NAME}" ]]; then - GIT_BRANCH_NAME=main +if [[ -z "${BRANCH_NAME}" ]]; then + BRANCH_NAME=main fi if [[ -z "${REPO_NAME}" ]]; then REPO_NAME=$(basename "$GIT_REPO_PATH") @@ -28,6 +28,7 @@ MY_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" cp "$MY_DIR/open-repo.sh" "$HOME/open-repo.sh" chmod +x "$HOME/open-repo.sh" +# Go to repo ... # shellcheck source=open-repo.sh if ! source "$HOME/open-repo.sh" "${GIT_REPO_PATH}" then @@ -35,12 +36,13 @@ then exit 1 fi -if [[ ! $(git branch --list "${GIT_BRANCH_NAME}") ]]; then - echo "Git branch '${GIT_BRANCH_NAME}' does not exist!" +if [[ ! $(git branch --list "${BRANCH_NAME}") ]]; then + echo "Git branch '${BRANCH_NAME}' does not exist!" exit 1 fi # Configure git repository +# shellcheck source=configure-git.sh source "$MY_DIR/configure-git.sh" # Setup scripts for Termux:Widget and Termux:Tasker @@ -66,4 +68,5 @@ chmod +x "$HOME"/.termux/tasker/*.sh cd "$MY_DIR" || exit 1 rm -r "$MY_DIR/temp" +echo "" echo "Setup auto-sync of '${REPO_NAME}' was successful! (multi-repo setup)" diff --git a/setup-single-repo.sh b/setup-single-repo.sh index 1bada93..47eb5fa 100755 --- a/setup-single-repo.sh +++ b/setup-single-repo.sh @@ -5,10 +5,10 @@ # $2: git branch name (optional, default "main") GIT_REPO_PATH=$1 -GIT_BRANCH_NAME=$2 +BRANCH_NAME=$2 -if [[ -z "${GIT_BRANCH_NAME}" ]]; then - GIT_BRANCH_NAME=main +if [[ -z "${BRANCH_NAME}" ]]; then + BRANCH_NAME=main fi ########################################### @@ -27,10 +27,11 @@ if [[ -e "$HOME/repo.conf" ]]; then exit 1 fi + # Go to repo ... # shellcheck source=open-repo.sh if ! source "$HOME/open-repo.sh" then - echo "Open repo with path defined in '"$HOME"/repo.conf' failed!" + echo "Open repo with path defined in '$HOME/repo.conf' failed!" exit 1 fi # Otherwise create new config file @@ -49,12 +50,13 @@ else exit 1 fi -if [[ ! $(git branch --list "${GIT_BRANCH_NAME}") ]]; then - echo "Git branch '${GIT_BRANCH_NAME}' does not exist!" +if [[ ! $(git branch --list "${BRANCH_NAME}") ]]; then + echo "Git branch '${BRANCH_NAME}' does not exist!" exit 1 fi # Configure git repository +# shellcheck source=configure-git.sh source "$MY_DIR/configure-git.sh" # Setup scripts for Termux:Widget @@ -71,4 +73,5 @@ chmod +x "$HOME"/.termux/tasker/*.sh REPO_NAME=$(basename "$GIT_REPO_PATH") +echo "" echo "Setup auto-sync of '${REPO_NAME}' was successful! (single-repo setup)"