fix(git-auto-fetch): background git-fetch-all and other fixes (#9468)

This commit is contained in:
Marc Cornellà 2020-11-26 22:27:07 +01:00 committed by GitHub
parent d88887195f
commit 05e2956dc6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 85 additions and 41 deletions

View file

@ -1,26 +1,29 @@
# Git auto-fetch
Automatically fetches all changes from all remotes while you are working in git-initialized directory.
Automatically fetches all changes from all remotes while you are working in a git-initialized directory.
#### Usage
Add `git-auto-fetch` to the plugins array in your zshrc file:
To use it, add `git-auto-fetch` to the plugins array in your zshrc file:
```shell
plugins=(... git-auto-fetch)
```
Every time you launch a command in your shell all remotes will be fetched in background.
By default autofetch will be triggered only if last fetch was done at least 60 seconds ago.
You can change fetch interval in your .zshrc:
```
GIT_AUTO_FETCH_INTERVAL=1200 #in seconds
```
Log of `git fetch --all` will be saved into `.git/FETCH_LOG`
## Usage
Every time the command prompt is shown all remotes will be fetched in the background. By default,
`git-auto-fetch` will be triggered only if the last auto-fetch was done at least 60 seconds ago.
You can change the fetch interval in your .zshrc:
#### Toggle auto fetch per folder
If you are using mobile connection or for any other reason you can disable git-auto-fetch for any folder:
```sh
GIT_AUTO_FETCH_INTERVAL=1200 # in seconds
```
A log of `git fetch --all` will be saved in `.git/FETCH_LOG`.
## Toggle auto-fetch per folder
If you are using a mobile connection or for any other reason you can disable git-auto-fetch
for any folder:
```shell
$ cd to/your/project
@ -29,3 +32,19 @@ disabled
$ git-auto-fetch
enabled
```
## Caveats
Automatically fetching all changes defeats the purpose of `git push --force-with-lease`,
and makes it behave like `git push --force` in some cases. For example:
Consider that you made some changes and possibly rebased some stuff, which means you'll
need to use `--force-with-lease` to overwrite the remote history of a branch. Between the
time when you make the changes (maybe do a `git log`) and the time when you `git push`,
it's possible that someone else updates the branch you're working on.
If `git-auto-fetch` triggers then, you'll have fetched the remote changes without knowing
it, and even though you're running the push with `--force-with-lease`, git will overwrite
the recent changes because you already have them in your local repository. The
[`git push --force-with-lease` docs](https://git-scm.com/docs/git-push) talk about possible
solutions to this problem.

View file

@ -1,36 +1,61 @@
GIT_AUTO_FETCH_INTERVAL=${GIT_AUTO_FETCH_INTERVAL:=60}
# Default auto-fetch interval: 60 seconds
: ${GIT_AUTO_FETCH_INTERVAL:=60}
# Necessary for the git-fetch-all function
zmodload zsh/datetime zsh/stat
function git-fetch-all {
(`command git rev-parse --is-inside-work-tree 2>/dev/null` &&
dir=`command git rev-parse --git-dir` &&
[[ ! -f $dir/NO_AUTO_FETCH ]] &&
(( `date +%s` - `date -r $dir/FETCH_LOG +%s 2>/dev/null || echo 0` > $GIT_AUTO_FETCH_INTERVAL )) &&
GIT_SSH_COMMAND="command ssh -o BatchMode=yes" \
command git fetch --all 2>/dev/null &>! $dir/FETCH_LOG &)
(
# Get git root directory
if ! gitdir="$(command git rev-parse --git-dir 2>/dev/null)"; then
return 0
fi
# Do nothing if auto-fetch disabled
if [[ -z "$gitdir" || -f "$gitdir/NO_AUTO_FETCH" ]]; then
return 0
fi
# Get time (seconds) when auto-fetch was last run
lastrun="$(zstat +mtime "$gitdir/FETCH_LOG" 2>/dev/null || echo 0)"
# Do nothing if not enough time has passed since last auto-fetch
if (( EPOCHSECONDS - lastrun < $GIT_AUTO_FETCH_INTERVAL )); then
return 0
fi
# Fetch all remotes (avoid ssh passphrase prompt)
GIT_SSH_COMMAND="command ssh -o BatchMode=yes" \
command git fetch --all 2>/dev/null &>! "$gitdir/FETCH_LOG"
) &|
}
function git-auto-fetch {
`command git rev-parse --is-inside-work-tree 2>/dev/null` || return
guard="`command git rev-parse --git-dir`/NO_AUTO_FETCH"
# Do nothing if not in a git repository
command git rev-parse --is-inside-work-tree &>/dev/null || return 0
(rm $guard 2>/dev/null &&
echo "${fg_bold[green]}enabled${reset_color}") ||
(touch $guard &&
echo "${fg_bold[red]}disabled${reset_color}")
# Remove or create guard file depending on its existence
local guard="$(command git rev-parse --git-dir)/NO_AUTO_FETCH"
if [[ -f "$guard" ]]; then
command rm "$guard" && echo "${fg_bold[green]}enabled${reset_color}"
else
command touch "$guard" && echo "${fg_bold[red]}disabled${reset_color}"
fi
}
# Override zle-line-init if it exists
if (( $+functions[zle-line-init] )); then
eval "override-git-auto-fetch-$(declare -f zle-line-init)"
function zle-line-init () {
git-fetch-all
override-git-auto-fetch-zle-line-init
}
else
function zle-line-init () {
git-fetch-all
}
fi
zle -N zle-line-init
# zle-line-init widget (don't redefine if already defined)
(( ! ${+functions[_git-auto-fetch_zle-line-init]} )) || return 0
case "$widgets[zle-line-init]" in
# Simply define the function if zle-line-init doesn't yet exist
builtin|"") function _git-auto-fetch_zle-line-init() {
git-fetch-all
} ;;
# Override the current zle-line-init widget, calling the old one
user:*) zle -N _git-auto-fetch_orig_zle-line-init "${widgets[zle-line-init]#user:}"
function _git-auto-fetch_zle-line-init() {
git-fetch-all
zle _git-auto-fetch_orig_zle-line-init -- "$@"
} ;;
esac
zle -N zle-line-init _git-auto-fetch_zle-line-init