From 799d1c1046c581c3ddb55ff64f4c71d2608a64f1 Mon Sep 17 00:00:00 2001 From: David Kopp Date: Sun, 1 Jun 2025 20:56:54 +0200 Subject: [PATCH 1/2] Use git worktree --- README.md | 36 ++++++++++++++++++++++ configure-git.sh | 23 +++++++++++--- setup-interactive.sh | 71 ++++++++++++++++++++++++++++++++------------ setup-multi-repo.sh | 13 ++++---- setup-single-repo.sh | 15 ++++++---- 5 files changed, 124 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 3fbab58..6a49141 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 a Git worktree. That means we place the git repository in the local Termux storage without a worktree (only the `.git` folder) 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 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)" From 01cbbf4e77364288f9d75b350499fff82089f7fa Mon Sep 17 00:00:00 2001 From: David Kopp Date: Sun, 1 Jun 2025 22:16:45 +0200 Subject: [PATCH 2/2] Add changelog entry --- CHANGELOG.md | 5 +++++ README.md | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) 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 6a49141..56254a8 100644 --- a/README.md +++ b/README.md @@ -93,14 +93,14 @@ git clone https://github.com/davidkopp/termux-scripts.git cd termux-scripts ``` -### Setup git worktree +### 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 a Git worktree. That means we place the git repository in the local Termux storage without a worktree (only the `.git` folder) and place the worktree in the shared storage. +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: @@ -117,7 +117,7 @@ Here are the relevant commands that can be used to set it up manually: git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" ``` -- Add worktree in detached mode (we don't want to create a new branch): +- 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