Skip to content
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

Feature/git fork #628

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ install:
if grep "$(COMMAND)" need_git_commit >/dev/null; then \
cat ./helper/has-git-commit >> $(TEMPFILE); \
fi; \
if grep "# needed_by: $(COMMAND)" helper/config-value >/dev/null; then \
cat ./helper/config-value | grep -v "# needed_by:" >> $(TEMPFILE); \
fi; \
tail -n +2 bin/$(COMMAND) >> $(TEMPFILE); \
cp -f $(TEMPFILE) $(DESTDIR)$(BINPREFIX)/$(COMMAND); \
fi; \
Expand Down
85 changes: 63 additions & 22 deletions bin/git-fork
Original file line number Diff line number Diff line change
Expand Up @@ -10,42 +10,77 @@ test -z "$url" && url=$(git remote get-url origin 2> /dev/null) && origin=true
# validate repo url
test -z "$url" && abort "github repo needs to be specified as an argument"

# validate user
echo "Enter your github username"
read user
[ -n "$user" ] || abort "git username required"
echo "Enter github two-factor authentication code (leave blank if not set up)"
read MFA_CODE

# extract owner + project from repo url
project=${url##*/}
owner=${url%/$project}
project=${project%.git}
if [[ $owner == git@* ]]; then
owner=${owner##*:}
else
owner=${owner##*/}
fi
owner=${owner##*[/:]}
# Yes, the following extracts server for both SSH and https references
server=${url##*@}
server=${server##*://}
server=${server%%[/:]*}

# validate
[ -z "$project" -o -z "$owner" ] && abort "github repo needs to be specified as an argument"

# determine github credentials
user=$(get_config_value "$server.user")
token=$(get_config_value "$server.token")
if [[ $(get_config_value "$server.add-api") == "true" ]]; then
api_server="api.$server"
else
api_server=$server
fi
# retrieve the API prefix and clean it up (i.e. "api/v3/")
api_prefix="$(get_config_value "$server.api-prefix")/"
api_prefix=${api_prefix#/}
api_prefix=${api_prefix/%\/\//\/}

if [[ -z "$user" ]]; then
# validate user
echo "Enter your github username"
read user
[ -n "$user" ] || abort "git username required"
fi

if [[ -z "$token" ]]; then
echo "Enter github password"
read password
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer let curl ask for my password instead of passing it in the option.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understandable. Although one is already being asked for the MFA code. Does that make sense to be asked for the MFA code before the password?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hickey
I think the order is not critical. Just because let curl ask for your password is simpler.

echo "Enter github two-factor authentication code (leave blank if not set up)"
read MFA_CODE
fi

auth_info=''
header_info=''
if [[ -n "$token" ]]; then
header_info="-H \"Authorization: token ${token}\""
elif [[ -n "$MFA_CODE" ]]; then
auth_info="-u \"$user:$password\""
header_info="-H \"X-GitHub-OTP: $MFA_CODE\""
elif [[ -n "$password" ]]; then
auth_info="-u \"$user:$password\""
else
echo "No login credentials specified."
exit 1
fi

# create fork
curl -qs \
-X POST \
-u "$user" \
-H "X-GitHub-OTP: $MFA_CODE" \
"https://api.github.com/repos/$owner/$project/forks"
IFS="'" cmd="curl -qs -X POST $auth_info $header_info https://$api_server/${api_prefix}repos/$owner/$project/forks"
eval $cmd >/dev/null

[ $? = 0 ] || abort "fork failed"

echo "Add GitHub remote branch via SSH (you will be prompted to verify the server's credentials)? (y/n)"
read use_ssh
use_ssh=$(get_config_value "$server.use-ssh")
if [[ -z "$use_ssh" ]]; then
echo "Add GitHub remote branch via SSH (you will be prompted to verify the server's credentials)? (y/n)"
read use_ssh
fi

# Check if user has ssh configured with GitHub
if [ -n "$use_ssh" ] && ssh -T git@github.com 2>&1 | grep -qi 'success'; then
remote_prefix="git@github.com:"
if [ -n "$use_ssh" ] && ssh -T git@$server 2>&1 | grep -qi 'success'; then
remote_prefix="git@$server:"
else
remote_prefix="https://github.com/"
remote_prefix="https://$server/"
fi

if [ "$origin" = true ]; then
Expand All @@ -55,6 +90,12 @@ if [ "$origin" = true ]; then
else
# clone forked repo into current dir
git clone "${remote_prefix}${user}/${project}.git" "$project"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could check if $project exists before cloning into it?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Git clone will not allow one to clone over an already existing repo directory.

0:0 ᐅ git clone [email protected]:hickey/git-extras.git git-extras
fatal: destination path 'git-extras' already exists and is not an empty directory.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hickey
Ah ha, this idea is inspired by your -d $project check.
If there is $project already, we can directly exit the script, skip git clone, cd, and so on.

until [[ -d "$project" ]]; do
echo "Github is being slow today. Waiting 10 secs to attempt clone."
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are various causes that should be responsible for the failure of git clone.
Fortunately, the error message from git clone is clear enough.
Better shorten the message and remove our guess.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue I see there is that git clone is very verbose and just adds a lot of cruf to the terminal that does not need to be there (see below). Although I just realized that I should be sending stderr to /dev/null to avoid the excess cruf.

Since the fork is asynchronous there is no way to determine when the fork is complete other than trying it. If I execute the fork command against github.com, the repo exists by the time that the clone is attempted. But if I try the fork command against the internal Github Enterprise instance, I end up waiting at least once for the fork to complete. (probably the fast LAN vs 200 ms across the net).

The more that I think about it, the attempt to clone should probably only be attempted for about a minute or so. After that point the command should exit with an error code and a message that the repo could not be cloned locally.

0:128 ᐅ git clone [email protected]:hickey/fioo.git foo  
Cloning into 'foo'...
ERROR: Repository not found.
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

sleep 10
git clone "${remote_prefix}${user}/${project}.git" "$project"
done

# add reference to origin fork so can merge in upstream changes
cd "$project"
git remote add upstream "${remote_prefix}${owner}/${project}.git"
Expand Down
11 changes: 11 additions & 0 deletions helper/config-value
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# needed_by: git-fork

get_config_value() {
echo $(git config git-extras.$1)
}

# set_config_value "key" "value" "global|system" (global or system not required)
set_config_value() {
$(git config ${3+--$3} git-extras.$1 $2)
echo $?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO, I prefer to use git config ${3+--$3} git-extras.$1 $2 directly.
When writing shell script, people are more relied on $? that the return value to check if the previous command succeeded.

}
69 changes: 68 additions & 1 deletion man/git-fork.1
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "GIT\-FORK" "1" "August 2016" "" "Git Extras"
.TH "GIT\-FORK" "1" "February 2017" "" ""
.
.SH "NAME"
\fBgit\-fork\fR \- Fork a repo on github
Expand Down Expand Up @@ -40,7 +40,71 @@ adds the forked repo as a remote called \fBorigin\fR
.P
Remotes will use ssh if you have it configured with GitHub, if not, https will be used\.
.
.P
Your Github settings can not be saved as git config values instead of specifying them each time\. To enable this you need to execute a few git config commands like the following\.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe you meant now rather than "not be saved" here.

.
.IP "" 4
.
.nf

$ git config \-\-global git\-extras\.github\.com\.user greatcoder99
.
.fi
.
.IP "" 0
.
.P
Assuming that your username is \'greatcoder99\'\. All the configuration values are prefixed with \'git\-extras\.\' followed by server hostname and then finally by the variable name (defined below)\.
.
.P
In addition, other Github instances may be used other than just github\.com\. So if you have a Github Enterprise instance, then using that hostname instead of github\.com will work as expected\.
.
.P
Variables that are currently supported:
.
.IP "" 4
.
.nf

user: The username that the Github instance knows you as

token: The personal access token that has been generated to allow
password\-less access to the API\.

add\-api: In most cases this should be set to true\. This adds the \'api\'
hostname to the repo location (i\.e\. github\.com becomes api\.github\.com)
to access the Github API\. The time you would not set this is when
your API hostname is the same as Github instance hostname\.

api\-prefix: Github Enterprise much of the time uses "/api/v3/" as a
entry point to the API\. Regular Github access does not need to
have a prefix specified\. Consult your Github administrator for
the correct prefix to use\.

use\-ssh: Set to true in order to set the upstream remote reference
to use SSH instead of https\.
.
.fi
.
.IP "" 0
.
.SH "EXAMPLE"
Create settings to prevent answering questions:
.
.IP "" 4
.
.nf

$ git config \-\-global git\-extras\.github\.com\.user bigdog
$ git config \-\-global git\-extras\.github\.com\.token d149feb47\.\.\.\.
$ git config \-\-global git\-extras\.github\.com\.add\-api true
$ git config \-\-global git\-extras\.github\.com\.use\-ssl true
.
.fi
.
.IP "" 0
.
.P
Fork expect\.js:
.
.IP "" 4
Expand Down Expand Up @@ -102,6 +166,9 @@ $ git fork
.SH "AUTHOR"
Written by Andrew Griffiths <\fImail@andrewgriffithsonline\.com\fR>
.
.P
Github Enterprise support and settings by Gerard Hickey <\fIhickey@kinetic\-compute\.com\fR>
.
.SH "REPORTING BUGS"
<\fIhttps://github\.com/tj/git\-extras/issues\fR>
.
Expand Down
52 changes: 49 additions & 3 deletions man/git-fork.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 43 additions & 0 deletions man/git-fork.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,49 @@ git-fork(1) -- Fork a repo on github

Remotes will use ssh if you have it configured with GitHub, if not, https will be used.

Your Github settings can not be saved as git config values instead of
Copy link

@sacres sacres Feb 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here as well: s/not be saved/now be saved/

specifying them each time. To enable this you need to execute a few git
config commands like the following.

$ git config --global git-extras.github.com.user greatcoder99

Assuming that your username is 'greatcoder99'. All the configuration
values are prefixed with 'git-extras.' followed by server hostname and
then finally by the variable name (defined below).

In addition, other Github instances may be used other than just
github.com. So if you have a Github Enterprise instance, then using that
hostname instead of github.com will work as expected.

Variables that are currently supported:

user: The username that the Github instance knows you as

token: The personal access token that has been generated to allow
password-less access to the API.

add-api: In most cases this should be set to true. This adds the 'api'
hostname to the repo location (i.e. github.com becomes api.github.com)
to access the Github API. The time you would not set this is when
your API hostname is the same as Github instance hostname.

api-prefix: Github Enterprise much of the time uses "/api/v3/" as a
entry point to the API. Regular Github access does not need to
have a prefix specified. Consult your Github administrator for
the correct prefix to use.

use-ssh: Set to true in order to set the upstream remote reference
to use SSH instead of https.

## EXAMPLE

Create settings to prevent answering questions:

$ git config --global git-extras.github.com.user bigdog
$ git config --global git-extras.github.com.token d149feb47....
$ git config --global git-extras.github.com.add-api true
$ git config --global git-extras.github.com.use-ssl true

Fork expect.js:

$ git fork https://github.com/LearnBoost/expect.js
Expand Down Expand Up @@ -51,6 +92,8 @@ git-fork(1) -- Fork a repo on github

Written by Andrew Griffiths &lt;<[email protected]>&gt;

Github Enterprise support and settings by Gerard Hickey &lt;<[email protected]>&gt;

## REPORTING BUGS

&lt;<https://github.com/tj/git-extras/issues>&gt;
Expand Down