1 Commits

Author SHA1 Message Date
Ivan Malopinsky 32e798c218 Merge pull request #59 from imsky/safety-checks
add safety checks before running git-fresh
2015-12-03 20:28:20 -05:00
5 changed files with 43 additions and 237 deletions
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2017 Ivan Malopinsky
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+5 -20
View File
@@ -1,53 +1,38 @@
# git-fresh :lemon:
# git-fresh :lemon:
Keep your repo fresh with one command.
## Usage
```
Usage: git fresh [-fmrtRW] [-sl] [remote] [root]
Usage: git fresh [-fmrF] [-sl] [remote] [root]
By default, git-fresh will:
- update local root (master) to match remote root
- rebase against remote current branch
- stash changes
- prune remote branches
git-fresh will ignore any branches listed in a .freshignore file.
.freshignore should contain branch names you would like to ignore
on separate lines. The file can exist in the current Git repo
or in the home directory, i.e. ~/.freshignore.
-f: Delete stale local and remote branches
-m: Merge remote root into current branch
-r: Rebase current branch against remote root
-t: Remove local tags that do not exist on remote
-R: Reset local root to remote root
-W: Wipe workspace clean
-F: Reset local root to remote root, wipe workspace
-s: Apply stashed changes after run
-l: Only delete local stale branches
-v: Print git-fresh version and exit
remote: remote name, origin by default
root: root branch, master by default
```
## Installation
### Manual on Linux or MacOSX
### Manual
1. Clone or download
2. `cd git-fresh`
3. `sudo ./install`
### Manual on Windows
Copy the file [git-fresh](https://raw.githubusercontent.com/imsky/git-fresh/master/git-fresh) to `usr\bin` in your git installation directory.
This usually is `C:\Program Files\Git\usr\bin`.
### Package
* [Homebrew](http://brew.sh/): `brew install git-fresh`
* [bpkg](http://www.bpkg.io/): `bpkg install imsky/git-fresh`
## License
+36 -194
View File
@@ -7,29 +7,20 @@
usage () {
cat << EOD
Usage: git fresh [-fmrtRW] [-sl] [remote] [root]
Usage: git fresh [-fmrF] [-sl] [remote] [root]
By default, git-fresh will:
- update local root (master) to match remote root
- rebase against remote current branch
- stash changes
- prune remote branches
git-fresh will ignore any branches listed in a .freshignore file.
.freshignore should contain branch names you would like to ignore
on separate lines. The file can exist in the current Git repo
or in the home directory, i.e. ~/.freshignore.
-f: Delete stale local and remote branches
-m: Merge remote root into current branch
-r: Rebase current branch against remote root
-t: Remove local tags that do not exist on remote
-R: Reset local root to remote root
-W: Wipe workspace clean
-F: Reset local root to remote root, wipe workspace
-s: Apply stashed changes after run
-l: Only delete local stale branches
-v: Print git-fresh version and exit
remote: remote name, origin by default
root: root branch, master by default
EOD
@@ -37,22 +28,19 @@ EOD
exit 0
}
say () {
echo "[git-fresh] $@" 1>&2
}
die () {
say $@
error () {
echo "[git-fresh] error on line $1"
exit 1
}
error () {
die "Error on line $1: $(head -n $1 $0 | tail -1)"
die () {
echo "[git-fresh] $@" 1>&2
exit 1
}
trap 'error $LINENO' ERR
while getopts ":fmrtslRWTv" opt; do
while getopts ":fmrslF" opt; do
case $opt in
f)
FORCE_DELETE_STALE=true
@@ -63,26 +51,14 @@ while getopts ":fmrtslRWTv" opt; do
r)
REBASE=true
;;
t)
TAGS=true
;;
s)
APPLY_STASH=true
;;
l)
DELETE_ONLY_LOCAL=true
;;
R)
RESET_ROOT=true
;;
W)
WIPE_WORKSPACE=true
;;
T)
TEST=true
;;
v)
VERSION=1.11.0
F)
FORCE_LOCAL_RESET=true
;;
*)
usage
@@ -93,120 +69,33 @@ done
shift $((OPTIND-1))
# Are we in version mode?
[[ ! -e .git ]] && die "Not a git repository"
[[ $(ls -l .git/refs/heads | wc -l) -eq "1" ]] && die "No HEAD ref available"
if [[ ! -z $VERSION ]]; then
echo git-fresh $VERSION
exit 0
fi
# Are we in testing mode?
if [[ $TEST = true ]]; then
PATH=$(pwd):$PATH
TEST_DIR=/tmp/git-fresh-test
fail_test () {
echo 'Tests failed!'
rm -rf $TEST_DIR
exit 1
}
rm -rf $TEST_DIR; mkdir -p $TEST_DIR; cd $TEST_DIR
git init; touch test; git add test; git commit -am 'test'
git checkout -b test; rm test; git commit -am 'delete test'
git checkout master; git merge test; git checkout test
git-fresh -fr; git rev-parse --verify test && fail_test || true
git checkout -b test; git checkout master; git-fresh; git checkout -
git rev-parse --abbrev-ref HEAD | grep -q test || fail_test
rm -rf $TEST_DIR
echo 'Tests passed!'
exit 0
fi
# Are we inside a git repository?
INSIDE_GIT_REPO=$(git rev-parse --is-inside-work-tree 2> /dev/null)
if [[ -z "$INSIDE_GIT_REPO" ]]; then
die "Not a git repository"
fi
# Are we in a non-empty git repository?
TOP_LEVEL_DIRECTORY=$(git rev-parse --show-toplevel)
CURRENT=$(git rev-parse --abbrev-ref HEAD)
REMOTE=${1:-origin}
ROOT=${2:-master}
if [[ $(ls -1 "$TOP_LEVEL_DIRECTORY/.git/refs/heads" | wc -l | xargs) -eq "0" ]]; then
if git rev-parse --verify "$ROOT"; then
git rev-parse "$ROOT" > "$TOP_LEVEL_DIRECTORY/.git/refs/heads/$ROOT"
else
(git fsck --lost-found &> /dev/null; git checkout "$ROOT") || die "No HEAD ref available"
fi
fi
CURRENT=$(git rev-parse --abbrev-ref HEAD)
if [[ $(git remote -v | wc -l) -gt "0" ]]; then
REMOTES=true
fi
# Is this branch in .freshignore?
FRESH_IGNORE="$TOP_LEVEL_DIRECTORY/.freshignore"
if [[ ! -f $FRESH_IGNORE ]]; then
FRESH_IGNORE="~/.freshignore"
fi
if [[ -f $FRESH_IGNORE ]]; then
if [[ ! -z $(grep -Fx "$CURRENT" "$FRESH_IGNORE") ]]; then
die "Branch $CURRENT is ignored"
fi
fi
git remote update
git remote prune $REMOTE
STASH_STAMP=git-fresh-$(date +%s)
# Stash changed files
if ! git diff-files --quiet; then
git stash save $STASH_STAMP
fi
if [[ $REMOTES = true ]]; then
# Update remotes and prune stale remotes
git rebase $REMOTE $CURRENT
git remote prune $REMOTE
git remote update $REMOTE
git remote prune $REMOTE
fi
git checkout $ROOT > /dev/null 2>&1
# If we are not already on root branch, switch to root branch (master)
if [[ "$ROOT" != "$CURRENT" ]]; then
git checkout $ROOT > /dev/null 2>&1
fi
# Wipe workspace?
if [[ $WIPE_WORKSPACE = true ]]; then
if [[ "$FORCE_LOCAL_RESET" = true ]]; then
git clean -dfx
git reset --hard $REMOTE/$ROOT
else
git rebase -q $REMOTE/$ROOT
fi
if [[ $REMOTES = true ]]; then
# Reset root?
if [[ $RESET_ROOT = true ]]; then
git reset --hard $REMOTE/$ROOT
fi
git pull --quiet --ff-only $REMOTE $ROOT || say "Fast forward merge failed on $ROOT. You can reset local $ROOT by running git fresh -R."
fi
# Compute stale branches
SMART_STALE=$(git branch -a --merged | tr -d "\* " | grep -Ev ">|$ROOT" | cat)
LOCAL_STALE=$(grep -Ev "^remotes/" <<< "$SMART_STALE" | cat)
@@ -218,100 +107,53 @@ REMOTE_STALE=${REMOTE_STALE//remotes\/$REMOTE\/}
if [[ ! -z "${SMART_STALE// }" ]]; then
if [[ ! -z "${LOCAL_STALE// }" ]]; then
STALE_BRANCHES=true
if [[ -f "$FRESH_IGNORE" ]]; then
LOCAL_STALE=$(echo -n $LOCAL_STALE | tr " " "\n" | grep -Fxvf "$FRESH_IGNORE" | tr "\n" " ")
if [[ -z $LOCAL_STALE ]]; then
STALE_BRANCHES=false
fi
fi
if [[ "$FORCE_DELETE_STALE" = true ]]; then
echo -n $LOCAL_STALE | tr " " "\0" | xargs -0 git branch -d 2> /dev/null
echo -n $LOCAL_STALE | xargs git branch -d 2> /dev/null
else
if [[ $STALE_BRANCHES = true ]]; then
say "Local stale branches found:" $(echo -n $LOCAL_STALE | tr "\n" " ")
fi
echo "Local stale branches found:" $(echo -n $LOCAL_STALE | tr "\n" " ")
fi
fi
if [[ ! -z "${REMOTE_STALE// }" ]]; then
STALE_BRANCHES=true
if [[ -f "$FRESH_IGNORE" ]]; then
REMOTE_STALE=$(echo -n $REMOTE_STALE | tr " " "\n" | grep -Fxvf "$FRESH_IGNORE" | tr "\n" " ")
if [[ -z $REMOTE_STALE ]]; then
STALE_BRANCHES=false
fi
fi
if [[ "$FORCE_DELETE_STALE" = true ]]; then
if [[ "$DELETE_ONLY_LOCAL" != true ]]; then
echo -n $REMOTE_STALE | tr " " "\0" | xargs -0 git push $REMOTE --delete
echo -n $REMOTE_STALE | xargs git push $REMOTE --delete
fi
else
if [[ $STALE_BRANCHES = true ]]; then
say "Remote stale branches found:" $(echo -n $REMOTE_STALE | tr "\n" " ")
fi
echo "Remote stale branches found:" $(echo -n $REMOTE_STALE | tr "\n" " ")
fi
fi
if [[ "$FORCE_DELETE_STALE" != true && "$STALE_BRANCHES" = true ]]; then
say "Delete stale branches with: git fresh -f"
echo "Delete stale branches with: git fresh -f"
fi
fi
# Remove tracking information for missing upstreams
if [[ ! -z $(git branch -vv | grep -F "[$REMOTE/$CURRENT: gone]") ]]; then
git branch --unset-upstream $CURRENT
fi
# Rebase or merge remote root against local branch
if [[ ! -z $(git rev-parse --verify --quiet "$CURRENT") ]]; then
if [[ "$ROOT" != "$CURRENT" ]]; then
git checkout $CURRENT
fi
git checkout $CURRENT 2> /dev/null
if [ "$REBASE" = true ] && [ "$MERGE" = true ]; then
say "Rebase and merge enabled, skipping both"
echo "Rebase and merge enabled, skipping both"
else
if [[ "$REMOTES" = true ]]; then
if [[ "$REBASE" = true ]]; then
git rebase $ROOT
fi
if [[ "$REBASE" = true ]]; then
git rebase $REMOTE/$ROOT
fi
if [[ "$MERGE" = true ]]; then
git merge --no-edit $ROOT
fi
if [[ "$MERGE" = true ]]; then
git merge --no-edit $REMOTE/$ROOT
fi
fi
else
echo "$CURRENT branch was stale, staying on $ROOT"
fi
# Remove local tags that are missing on the remote
if [[ "$TAGS" = true ]]; then
REMOTE_TAGS=$(git ls-remote --tags $REMOTE | cut -f 2)
LOCAL_TAGS=$(git show-ref --tags | cut -d' ' -f 2)
for tag in $LOCAL_TAGS; do
if [[ -z $(grep $tag <<< "$REMOTE_TAGS" | cat) ]]; then
MISSING_TAG="${tag//refs\/tags\/}"
git tag -d $MISSING_TAG
fi
done
fi
# Restore stashed changes
if [[ ! -z $(git stash list | grep $STASH_STAMP | cat) ]]; then
if [[ "$APPLY_STASH" = true ]]; then
git stash pop
else
say "Stashed changes present, apply with: git stash pop"
echo "Stashed changes present, apply with: git stash pop"
fi
fi
if ! git gc --auto --force; then
git prune
rm -rf "$TOP_LEVEL_DIRECTORY/.git/gc.log"
fi
git gc --auto --prune=now
View File
+2 -2
View File
@@ -1,8 +1,8 @@
{
"name": "git-fresh",
"version": "1.11.0",
"version": "1.3.0",
"description": "Utility to keep Git repositories fresh",
"global": true,
"repo": "imsky/git-fresh",
"install": "sudo ./install.sh"
"install": "sudo ./install"
}