From e476562a46e9c2f9cda2ad50eaf1a4dcf00c0e80 Mon Sep 17 00:00:00 2001 From: Robin Winslow Date: Fri, 28 Oct 2016 22:08:59 +0100 Subject: [PATCH 1/6] git-force-clone All files for git-force-clone, a command for overwriting local repos when closing repositories. --- Commands.md | 16 +++++ bin/git-force-clone | 109 ++++++++++++++++++++++++++++++++++ etc/git-extras-completion.zsh | 2 +- man/git-force-clone.1 | 57 ++++++++++++++++++ man/git-force-clone.html | 0 man/git-force-clone.md | 56 +++++++++++++++++ 6 files changed, 239 insertions(+), 1 deletion(-) create mode 100755 bin/git-force-clone create mode 100644 man/git-force-clone.1 create mode 100644 man/git-force-clone.html create mode 100644 man/git-force-clone.md diff --git a/Commands.md b/Commands.md index 9f58bc5..3eed497 100644 --- a/Commands.md +++ b/Commands.md @@ -19,6 +19,7 @@ - [`git extras`](#git-extras) - [`git feature|refactor|bug|chore`](#git-featurerefactorbugchore) - [`git fork`](#git-fork) + - [`git force-clone`](#git-force-clone) - [`git fresh-branch`](#git-fresh-branch) - [`git gh-pages`](#git-gh-pages) - [`git graft`](#git-graft) @@ -344,6 +345,21 @@ upstream git@github.com:LearnBoost/expect.js (fetch) upstream git@github.com:LearnBoost/expect.js (push) ``` +## git force-clone + +If the clone target directory exists, reset its contents to a clone of the +remote. + +``` bash +$ git force-clone [-b {branch_name}] {remote_url} {destination_path} +$ git force-clone -b master https://github.com/tj/git-extras ./target-directory +``` + +**CAUTION**: If the repository exists, this will destroy *all* local changes +to the repository - changed files will be reset and local branches will be +removed. + +[More information](man/git-force-clone.md). ## git release diff --git a/bin/git-force-clone b/bin/git-force-clone new file mode 100755 index 0000000..58536f6 --- /dev/null +++ b/bin/git-force-clone @@ -0,0 +1,109 @@ +#! /usr/bin/env bash + +set -euo pipefail + +_usage() { + echo " +Usage: + git-force-clone -b branch remote_url destination_path + +Example: + git-force-clone -b master git@github.com:me/repo.git ./repo_dir + +Provides the basic functionality of `git clone`, but if the destination +repository already exists it will force-reset it to resemble a clone of the +remote. + +Because it doesn't actually delete the directory, it is usually significantly +faster than the alternative of deleting the directory and cloning the +repository from scratch. + +**CAUTION**: If the repository exists, this will destroy *all* local work: +changed files will be reset, local branches and other remotes will be removed. + +OPTIONS: + -b, --branch The branch to pull from the remote (default: master) + -h, --help Display this help message +" +} + +_check() { + if [ -z "$1" ]; then + echo "Error: Missing ${2}" + _usage + exit 1 + fi +} + +main() { + while [[ -n "${1:-}" ]] && [[ "${1:0:1}" == "-" ]]; do + case $1 in + -b | --branch ) + branch=${2:-} + ;; + -h | --help ) + _usage + exit 0 + ;; + * ) + if [[ "${1:0:1}" == '-' ]]; then + echo "Error: Invalid option: $1" >>/dev/stderr + _usage + exit 1 + fi + ;; + esac + shift + done + + remote_url=${1:-} + destination_path=${2:-} + + _check "${remote_url}" "remote_url" + _check "${destination_path}" "destination_path" + + if [ -d "${destination_path}/.git" ]; then + ( + cd ${destination_path} + + # Delete all remotes + for remote in `git remote`; do + git remote rm ${remote} + done + + # Add origin + git remote add origin ${remote_url} + git fetch origin + + # Set default branch + if [ -z "${branch:-}" ]; then + branch=`git remote show origin | grep -oP '(?<=HEAD branch: )[^ ]+$'` + git remote set-head origin ${branch} + else + git remote set-head origin -a + fi + + # Make sure current branch is clean + git clean -fd + git reset --hard HEAD + + # Get on the desired branch + git checkout ${branch} + git reset --hard origin/${branch} + + # Delete all other branches + branches=`git branch | grep -v \* | xargs` + if [ -n "${branches}" ]; then + git branch -D ${branches} + fi + ) + elif [ -n "${branch:-}" ]; then + git clone -b ${branch} ${remote_url} ${destination_path} + else + git clone ${remote_url} ${destination_path} + fi +} + +main "$@" + +exit 0 diff --git a/etc/git-extras-completion.zsh b/etc/git-extras-completion.zsh index 9465cab..03d760d 100644 --- a/etc/git-extras-completion.zsh +++ b/etc/git-extras-completion.zsh @@ -259,7 +259,6 @@ _git-feature() { esac } - _git-graft() { _arguments \ ':src-branch-name:__gitex_branch_names' \ @@ -373,6 +372,7 @@ zstyle ':completion:*:*:git:*' user-commands \ effort:'show effort statistics on file(s)' \ extras:'awesome git utilities' \ feature:'create/merge feature branch' \ + force-clone:'overwrite local repositories with clone' \ fork:'fork a repo on github' \ fresh-branch:'create fresh branches' \ gh-pages:'create the github pages branch' \ diff --git a/man/git-force-clone.1 b/man/git-force-clone.1 new file mode 100644 index 0000000..ce94f88 --- /dev/null +++ b/man/git-force-clone.1 @@ -0,0 +1,57 @@ +.\" generated with Ronn/v0.7.3 +.\" http://github.com/rtomayko/ronn/tree/0.7.3 +. +.TH "GIT\-FORCE\-CLONE" "1" "2016-10-28" "" "Git Extras" +. +.SH "NAME" +\fBgit\-force\-clone\fR \- overwrite local repositories with clone +. +.SH "SYNOPSIS" +\fBforce\-clone \-\-help\fR \fBforce\-clone {remote_url} {destination_path}\fR \fBforce\-clone \-\-branch {branch_name} {remote_url} {destination_path}\fR +. +.SH "DESCRIPTION" +Provides the basic functionality of \fBgit clone\fR, but if the destination repository already exists it will force\-reset it to resemble a clone of the remote\. +. +.P +Because it doesn\'t actually delete the directory, it is usually significantly faster than the alternative of deleting the directory and cloning the repository from scratch\. +. +.P +\fBCAUTION\fR: If the repository exists, this will destroy \fIall\fR local work: changed files will be reset, local branches and other remotes will be removed\. +. +.SH "PROCESS" +If \fBtarget\-directory\fR doesn\'t exist then this will simply be passed through to \fBgit clone\fR, which will clone remote\. +. +.P +If \fBtarget\-directory\fR \fIdoes\fR exist then this will: +. +.IP "\(bu" 4 +Remove all remotes +. +.IP "\(bu" 4 +Set the origin remote to \fB{remote_url}\fR and fetch the remote +. +.IP "\(bu" 4 +Discover the default branch, if no branch was specified +. +.IP "\(bu" 4 +Check out the selected branch +. +.IP "\(bu" 4 +Delete all other local branches +. +.IP "" 0 +. +.SH "OPTIONS" +\fB{remote_url}\fR \- The URL for a git remote repository of which to make a clone\. \fB{destination_path}\fR \- A path to the directory to clone into\. \fB\-\-branch {branch_name}\fR \- After cloning, checkout this branch\. +. +.SH "EXAMPLES" +\fBgit\-force\-clone \-b master git@github\.com:me/repo\.git \./repo_dir\fR +. +.SH "AUTHOR" +Written by Robin Winslow \fIrobin@robinwinslow\.co\.uk\fR\. +. +.SH "REPORTING BUGS" +\fIhttps://github\.com/tj/git\-extras/issues\fR +. +.SH "SEE ALSO" +\fIhttps://github\.com/tj/git\-extras\fR diff --git a/man/git-force-clone.html b/man/git-force-clone.html new file mode 100644 index 0000000..e69de29 diff --git a/man/git-force-clone.md b/man/git-force-clone.md new file mode 100644 index 0000000..76719d5 --- /dev/null +++ b/man/git-force-clone.md @@ -0,0 +1,56 @@ +git-force-clone(1) -- overwrite local repositories with clone +=== + +## SYNOPSIS + +`force-clone --help` +`force-clone {remote_url} {destination_path}` +`force-clone --branch {branch_name} {remote_url} {destination_path}` + +## DESCRIPTION + +Provides the basic functionality of `git clone`, but if the destination +repository already exists it will force-reset it to resemble a clone of the +remote. + +Because it doesn't actually delete the directory, it is usually significantly +faster than the alternative of deleting the directory and cloning the +repository from scratch. + +**CAUTION**: If the repository exists, this will destroy *all* local work: +changed files will be reset, local branches and other remotes will be removed. + +## PROCESS + +If `target-directory` doesn't exist then this will simply be passed through to +`git clone`, which will clone remote. + +If `target-directory` *does* exist then this will: + +- Remove all remotes +- Set the origin remote to `{remote_url}` and fetch the remote +- Discover the default branch, if no branch was specified +- Check out the selected branch +- Delete all other local branches + +## OPTIONS + +`{remote_url}` - The URL for a git remote repository of which to make a clone. +`{destination_path}` - A path to the directory to clone into. +`--branch {branch_name}` - After cloning, checkout this branch. + +## EXAMPLES + +`git-force-clone -b master git@github.com:me/repo.git ./repo_dir` + +## AUTHOR + +Written by Robin Winslow . + +## REPORTING BUGS + + + +## SEE ALSO + + From ad5365fb53eb5c218cd926e09b5d32d574d67db7 Mon Sep 17 00:00:00 2001 From: Robin Winslow Date: Sat, 29 Oct 2016 16:16:04 +0100 Subject: [PATCH 2/6] Remove unnecessary check --- bin/git-force-clone | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/bin/git-force-clone b/bin/git-force-clone index 58536f6..828dea6 100755 --- a/bin/git-force-clone +++ b/bin/git-force-clone @@ -46,11 +46,9 @@ main() { exit 0 ;; * ) - if [[ "${1:0:1}" == '-' ]]; then - echo "Error: Invalid option: $1" >>/dev/stderr - _usage - exit 1 - fi + echo "Error: Invalid option: $1" >>/dev/stderr + _usage + exit 1 ;; esac shift From 21371249771ca34e3925fd4f418a878da424f2ea Mon Sep 17 00:00:00 2001 From: Robin Winslow Date: Sat, 29 Oct 2016 16:16:18 +0100 Subject: [PATCH 3/6] Use LC_ALL=C for locale consistency --- bin/git-force-clone | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/git-force-clone b/bin/git-force-clone index 828dea6..7096df6 100755 --- a/bin/git-force-clone +++ b/bin/git-force-clone @@ -75,7 +75,7 @@ main() { # Set default branch if [ -z "${branch:-}" ]; then - branch=`git remote show origin | grep -oP '(?<=HEAD branch: )[^ ]+$'` + branch=`LC_ALL=C git remote show origin | grep -oP '(?<=HEAD branch: )[^ ]+$'` git remote set-head origin ${branch} else git remote set-head origin -a From 6e87dc3e55812e24fb5889c2a7672ff849550918 Mon Sep 17 00:00:00 2001 From: Robin Winslow Date: Sat, 29 Oct 2016 16:18:09 +0100 Subject: [PATCH 4/6] Fix usage --- bin/git-force-clone | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/git-force-clone b/bin/git-force-clone index 7096df6..056d74e 100755 --- a/bin/git-force-clone +++ b/bin/git-force-clone @@ -10,7 +10,7 @@ Usage: Example: git-force-clone -b master git@github.com:me/repo.git ./repo_dir -Provides the basic functionality of `git clone`, but if the destination +Provides the basic functionality of 'git clone', but if the destination repository already exists it will force-reset it to resemble a clone of the remote. From 5550cfa4dcd51f0d54293608b585473e8517214a Mon Sep 17 00:00:00 2001 From: Robin Winslow Date: Sat, 29 Oct 2016 16:23:28 +0100 Subject: [PATCH 5/6] Fix branch option parsing Needs an extra `shift`, to remove the second option as well. --- bin/git-force-clone | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/git-force-clone b/bin/git-force-clone index 056d74e..4b3ec0b 100755 --- a/bin/git-force-clone +++ b/bin/git-force-clone @@ -40,6 +40,7 @@ main() { case $1 in -b | --branch ) branch=${2:-} + shift ;; -h | --help ) _usage From 71a12810e07a1eb76d5284d14124fdcca4124a20 Mon Sep 17 00:00:00 2001 From: Robin Winslow Date: Sat, 29 Oct 2016 17:24:18 +0100 Subject: [PATCH 6/6] Be explicit about only resetting git directories This expects a target git directory. If it's not a git directory, (e.g. a simple directory) then it will pass through to `git clone` which will raise an error. Here I'm clarifying that this tool will reset local *git directories* specifically. --- Commands.md | 4 +- bin/git-force-clone | 2 +- man/git-force-clone.1 | 10 +-- man/git-force-clone.html | 144 +++++++++++++++++++++++++++++++++++++++ man/git-force-clone.md | 10 +-- 5 files changed, 157 insertions(+), 13 deletions(-) diff --git a/Commands.md b/Commands.md index 3eed497..c6a1be7 100644 --- a/Commands.md +++ b/Commands.md @@ -347,8 +347,8 @@ upstream git@github.com:LearnBoost/expect.js (push) ## git force-clone -If the clone target directory exists, reset its contents to a clone of the -remote. +If the clone target directory exists and is a git repository, reset its +contents to a clone of the remote. ``` bash $ git force-clone [-b {branch_name}] {remote_url} {destination_path} diff --git a/bin/git-force-clone b/bin/git-force-clone index 4b3ec0b..0f75685 100755 --- a/bin/git-force-clone +++ b/bin/git-force-clone @@ -10,7 +10,7 @@ Usage: Example: git-force-clone -b master git@github.com:me/repo.git ./repo_dir -Provides the basic functionality of 'git clone', but if the destination +Provides the basic functionality of 'git clone', but if the destination git repository already exists it will force-reset it to resemble a clone of the remote. diff --git a/man/git-force-clone.1 b/man/git-force-clone.1 index ce94f88..1f57ccf 100644 --- a/man/git-force-clone.1 +++ b/man/git-force-clone.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "GIT\-FORCE\-CLONE" "1" "2016-10-28" "" "Git Extras" +.TH "GIT\-FORCE\-CLONE" "1" "2016-10-29" "" "Git Extras" . .SH "NAME" \fBgit\-force\-clone\fR \- overwrite local repositories with clone @@ -10,7 +10,7 @@ \fBforce\-clone \-\-help\fR \fBforce\-clone {remote_url} {destination_path}\fR \fBforce\-clone \-\-branch {branch_name} {remote_url} {destination_path}\fR . .SH "DESCRIPTION" -Provides the basic functionality of \fBgit clone\fR, but if the destination repository already exists it will force\-reset it to resemble a clone of the remote\. +Provides the basic functionality of \fBgit clone\fR, but if the destination git repository already exists it will force\-reset it to resemble a clone of the remote\. . .P Because it doesn\'t actually delete the directory, it is usually significantly faster than the alternative of deleting the directory and cloning the repository from scratch\. @@ -19,10 +19,10 @@ Because it doesn\'t actually delete the directory, it is usually significantly f \fBCAUTION\fR: If the repository exists, this will destroy \fIall\fR local work: changed files will be reset, local branches and other remotes will be removed\. . .SH "PROCESS" -If \fBtarget\-directory\fR doesn\'t exist then this will simply be passed through to \fBgit clone\fR, which will clone remote\. +If \fBtarget\-directory\fR doesn\'t exist or isn\'t a git repository then the arguments will simply be passed through to \fBgit clone\fR\. . .P -If \fBtarget\-directory\fR \fIdoes\fR exist then this will: +If \fBtarget\-directory\fR exists and is a git repository then this will: . .IP "\(bu" 4 Remove all remotes @@ -42,7 +42,7 @@ Delete all other local branches .IP "" 0 . .SH "OPTIONS" -\fB{remote_url}\fR \- The URL for a git remote repository of which to make a clone\. \fB{destination_path}\fR \- A path to the directory to clone into\. \fB\-\-branch {branch_name}\fR \- After cloning, checkout this branch\. +\fB{remote_url}\fR \- The URL for a git remote repository of which to make a clone\. \fB{destination_path}\fR \- A path to the local git repository location to clone into\. \fB\-\-branch {branch_name}\fR \- After cloning, checkout this branch\. . .SH "EXAMPLES" \fBgit\-force\-clone \-b master git@github\.com:me/repo\.git \./repo_dir\fR diff --git a/man/git-force-clone.html b/man/git-force-clone.html index e69de29..068b506 100644 --- a/man/git-force-clone.html +++ b/man/git-force-clone.html @@ -0,0 +1,144 @@ + + + + + + git-force-clone(1) - overwrite local repositories with clone + + + + +
+ + + +
    +
  1. git-force-clone(1)
  2. +
  3. Git Extras
  4. +
  5. git-force-clone(1)
  6. +
+ +

NAME

+

+ git-force-clone - overwrite local repositories with clone +

+ +

SYNOPSIS

+ +

force-clone --help +force-clone {remote_url} {destination_path} +force-clone --branch {branch_name} {remote_url} {destination_path}

+ +

DESCRIPTION

+ +

Provides the basic functionality of git clone, but if the destination git +repository already exists it will force-reset it to resemble a clone of the +remote.

+ +

Because it doesn't actually delete the directory, it is usually significantly +faster than the alternative of deleting the directory and cloning the +repository from scratch.

+ +

CAUTION: If the repository exists, this will destroy all local work: +changed files will be reset, local branches and other remotes will be removed.

+ +

PROCESS

+ +

If target-directory doesn't exist or isn't a git repository then the +arguments will simply be passed through to git clone.

+ +

If target-directory exists and is a git repository then this will:

+ +
    +
  • Remove all remotes
  • +
  • Set the origin remote to {remote_url} and fetch the remote
  • +
  • Discover the default branch, if no branch was specified
  • +
  • Check out the selected branch
  • +
  • Delete all other local branches
  • +
+ + +

OPTIONS

+ +

{remote_url} - The URL for a git remote repository of which to make a clone. +{destination_path} - A path to the local git repository location to clone into. +--branch {branch_name} - After cloning, checkout this branch.

+ +

EXAMPLES

+ +

git-force-clone -b master git@github.com:me/repo.git ./repo_dir

+ +

AUTHOR

+ +

Written by Robin Winslow robin@robinwinslow.co.uk.

+ +

REPORTING BUGS

+ +

https://github.com/tj/git-extras/issues

+ +

SEE ALSO

+ +

https://github.com/tj/git-extras

+ + +
    +
  1. +
  2. 2016-10-29
  3. +
  4. git-force-clone(1)
  5. +
+ +
+ + diff --git a/man/git-force-clone.md b/man/git-force-clone.md index 76719d5..b5040c9 100644 --- a/man/git-force-clone.md +++ b/man/git-force-clone.md @@ -9,7 +9,7 @@ git-force-clone(1) -- overwrite local repositories with clone ## DESCRIPTION -Provides the basic functionality of `git clone`, but if the destination +Provides the basic functionality of `git clone`, but if the destination git repository already exists it will force-reset it to resemble a clone of the remote. @@ -22,10 +22,10 @@ changed files will be reset, local branches and other remotes will be removed. ## PROCESS -If `target-directory` doesn't exist then this will simply be passed through to -`git clone`, which will clone remote. +If `target-directory` doesn't exist or isn't a git repository then the +arguments will simply be passed through to `git clone`. -If `target-directory` *does* exist then this will: +If `target-directory` exists and is a git repository then this will: - Remove all remotes - Set the origin remote to `{remote_url}` and fetch the remote @@ -36,7 +36,7 @@ If `target-directory` *does* exist then this will: ## OPTIONS `{remote_url}` - The URL for a git remote repository of which to make a clone. -`{destination_path}` - A path to the directory to clone into. +`{destination_path}` - A path to the local git repository location to clone into. `--branch {branch_name}` - After cloning, checkout this branch. ## EXAMPLES