Skip to content

Use Git worktree #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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'._
Expand Down
23 changes: 19 additions & 4 deletions configure-git.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
71 changes: 52 additions & 19 deletions setup-interactive.sh
Original file line number Diff line number Diff line change
@@ -1,31 +1,64 @@
#!/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 )"

echo "Do you want to clone a new repository (1) or provide a path to an already existing git repository on your device (2)?"
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)
Expand All @@ -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."
Expand All @@ -46,19 +79,19 @@ 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?
echo -e "Do you want to setup sync for only one repository or for multiple ones?\nOne: (1)\nMultiple: (2)"
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
13 changes: 8 additions & 5 deletions setup-multi-repo.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -28,19 +28,21 @@ 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
echo "Open repo '${GIT_REPO_PATH}' failed!"
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
Expand All @@ -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)"
15 changes: 9 additions & 6 deletions setup-single-repo.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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

###########################################
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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)"