Reputation: 2321
One annoyance of git is having to use git remote add upstream URL
for every clone I make of a repo which is forked from some upstream repo (such as Github forks). Ideally I should be able to store the upstream location in the forked repo itself so that all clones automatically have it set. Is there a way to accomplish this using git?
Upvotes: 2
Views: 1977
Reputation: 2321
As other answers state, git does not currently have built-in functionality for linking repos together.
Thus I wrote a small Python script called clone
which uses BeatifulSoup 4 to parse the upstream repo for BitBucket and GitHub and add it as a remote called upstream
. This works for both git and Mercurial. It is hosted on GitHub, but for completion I am including the full script in this answer:
#! /usr/bin/env python3
description='''
A wrapper for repo cloning commands for various DVCS.
Automatically adds the upstream URL when cloning a forked repo from
popular DVCS web hosts.
Currently supports:
(DVCS: git, Mercurial)
(hosts: GitHub, Bitbucket)
'''
# Created by Jesse Johnson a.k.a. holocronweaver (2014-10-08).
import argparse
import re
import subprocess
import urllib.request
from bs4 import BeautifulSoup
def find(regex, string, errorMessage):
'''Return value at regex, or else print error message and exit program.'''
m = re.search(regex, string)
if (m):
return m.group(1)
else:
print('Error:', errorMessage)
exit()
parser = argparse.ArgumentParser(description=description)
parser.add_argument('origin', metavar='O', type=str,
help='the SSH URL of origin repo, optionally'
'followed by its destination folder')
args = parser.parse_args()
# Parse destination.
splitArgs = re.split('\s+', args.origin)
if len(splitArgs) > 1:
origin = splitArgs[0]
destination = splitArgs[1]
else:
origin = args.origin
destination = find('@.*\.\w+[:|/].*/(.*)[.git]?$', origin,
'Error: Could not parse destination folder from origin URL.')
destination = re.sub('\.git', '', destination)
print('destination folder:', destination)
# Unfortunately HTTPS URLs do not contain enough info to clarify which
# DVCS is being used, so SSH is easiest to support.
vcs = find('^[ssh://]?(.*)@', origin,
'URL does not contain SSH user (e.g., git@ or hg@).')
print('version control system:', vcs)
domain = find('@(.*\.\w+)[:|/]', origin,
'Error: Could not parse domain from origin URL.')
print('domain:', domain)
path = find('@.*\.\w+([:|/].*)[.git]?$', origin,
'Error: Could not parse repo path from origin URL.')
path = re.sub(':', '/', path)
print('repo path:', path)
homepage = 'https://' + domain + path
print(homepage)
data = urllib.request.urlopen(homepage).read()
soup = BeautifulSoup(data)
# Version control system specifics.
if ('git@' in origin):
ext = '.git'
clone = 'git clone %s %s'
setUpstream = 'git remote add upstream %s'
elif ('hg@' in origin):
ext = ''
clone = 'hg clone %s %s'
setUpstream = 'echo "upstream = %s \n" >> .hg/hgrc'
else:
print('Error: Version control system not supported.')
exit()
upstream = None
if ('github.com' in origin):
element = soup.find('span', class_='fork-flag')
if element:
upstreamBase = element.span.a['href']
upstream = 'https://github.com' + upstreamBase + ext
elif ('bitbucket.org' in origin):
element = soup.find('a', class_='fork-of')
if element:
upstreamBase = element['href']
upstream = 'https://bitbucket.org' + upstreamBase + ext
else:
print('Warning: Web host not supported.')
print('Warning: Continuing to clone without adding upstream repo.')
print(upstream)
subprocess.Popen(clone % (origin, destination), shell=True).wait()
if upstream:
subprocess.Popen(setUpstream % upstream, cwd=destination, shell=True).wait()
Upvotes: 2
Reputation: 66324
As far as Git itself is concerned, there is no link between the original repo (i.e. the repo you forked from) and a clone of your fork; no information about the former's URL is contained in the latter. Therefore, one way or another, you have to add the original repo as a remote of the clone after cloning.
To automate things a tiny bit and avoid having to type URLs every time, you could just define an alias for each project you forked from:
git config alias.remaddfoo "remote add upstream <URL-of-original-project-foo>"
After cloning your fork, you would have to cd
inside the new clone and remember to invoke the corresponding alias.
Taking automation one step further would involve writing some kind of wrapper around git clone
that would automatically cd
inside the clone and run git remote add...
, but parsing git clone
's arguments correctly may be a bit too complicated.
Upvotes: 3
Reputation: 14863
By default git clone
calls your remote "origin". If you want it called something else use
git clone -o upstream <path_to_repo>
--origin <name>
-o <name>
Instead of using the remote name origin to keep track of the upstream repository, use <name>.
EDIT: For your case of needing to track the remote of a remote ... I've never found a good way to do this. You can write a wrapper around git clone
, but it has to be hardcoded for each repository.
Upvotes: 1