├── .gitignore ├── README.txt ├── git-svn-check-unpushed ├── git-svn-clone-externals ├── git-svn-externals-check └── git-svn-externals-update /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | git-svn-clone-externals 2 | ======================= 3 | 4 | This is a very simple shell script to make git-svn clone your svn:externals 5 | definitions. Place the script in a directory where you have one or more 6 | svn:externals definitions, run it, and it will: 7 | 8 | * git svn clone each external into a .git_externals/ directory. 9 | * symlink the cloned repository in .git_externals/ to the proper directory 10 | name. 11 | * add the symlink and .git_externals/ to the .git/info/excludes/ file, so that 12 | you're not pestered about it when performing a git status. 13 | 14 | That's pretty much about it. Low-tech and cheap and cheery, but I couldn't 15 | find anything else like it after extensive Googling, so hopefully some other 16 | people out there with low-tech minds like mine will find this useful. 17 | 18 | You could certainly make the script a lot more complex and do things such as 19 | share svn:externals repositories between different git repositories, traverse 20 | through the entire git repository to detect svn:externals definitions instead 21 | of having to place the script in the correct directory, etc... but this works, 22 | it's simple, and it does just the one thing, unlike a lot of other git/svn 23 | integration scripts that I've found. I absolutely do welcome those features, 24 | but I figured I'd push this out since it works for me and is probably useful 25 | for others. 26 | 27 | NB: This assumes you have passwordless svn. 28 | 29 | Enjoy, 30 | 31 | - Andre Pang 32 | 33 | 34 | Tools 35 | ===== 36 | 37 | The git-svn-clone-externals script does a great job of cloning the 38 | svn:externals. However, in day-to-day work I want to check whether I 39 | need to push stuff and update a buch of 'external' repositories in one 40 | go. Therefore I creates some additional scripts. 41 | 42 | * ``git-svn-check-unpushed`` tries to determine whether there are 43 | commits which are not yet pushed back to the subversion 44 | repository. Originally I took this idea from Magit (an interface to 45 | the version control system Git, implemented as an extension to 46 | Emacs) and implemented it in Python instead of Lisp. 47 | 48 | This script can be run in every location of a git repository. 49 | 50 | * git-svn-externals-check is a script that displays whether there are 51 | uncommitted changes or commits that are not pushed to the subversion 52 | repository yet. Basically it executes ``git status`` and the 53 | ``git-svn-check-unpushed`` scripts for each directory in the current 54 | directory. 55 | 56 | This script must be run in the directory where the original 57 | svn:externals property was set. It does not walk the complete tree 58 | to search for these kind of repositories. 59 | 60 | * git-svn-externals-update complements the git-svn-externals-check 61 | script. The update script does an ``git svn fetch`` and ``git svn 62 | rebase`` for every directory in the location where the script is 63 | executed. 64 | 65 | This script must also be run in the directory where the original 66 | svn:externals property was set. 67 | 68 | 69 | Feel free to use and improve these scripts. 70 | 71 | - Mark van Lent 72 | 73 | Options 74 | ======= 75 | 76 | * External repository's url can be rewritten, to use SVN+SSH instead of 77 | plain HTTP or HTTPS. To do so, do `export USE_SSH=yes` in your environment. 78 | This can be useful if you use ssh authentication, but other developers don't. 79 | * If you don't want to pull all external repositories, you can create a 80 | .git_externals_exclude file which contains the local paths to be excluded, 81 | one per line, the same way they show up on the first field of git svn show-externals 82 | 83 | - Alexander Artemenko 84 | 85 | -------------------------------------------------------------------------------- /git-svn-check-unpushed: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from commands import getoutput 4 | import sys 5 | import logging 6 | 7 | logger = logging.getLogger() 8 | console = logging.StreamHandler() 9 | formatter = logging.Formatter('%(levelname)-8s %(message)s') 10 | console.setFormatter(formatter) 11 | logger.addHandler(console) 12 | logger.level = logging.INFO 13 | 14 | 15 | def list_references(): 16 | """List the references in the local repo. 17 | 18 | Return a tuple with the SHA1 of HEAD and a dictionary with the 19 | SHA1 of the references as keys and the reference name as value. 20 | """ 21 | references = getoutput('git show-ref --head') 22 | HEAD_ref = None 23 | refs = {} 24 | for item in references.split('\n'): 25 | sha1, name = item.split() 26 | if name == 'HEAD': 27 | HEAD_ref = sha1 28 | refs[sha1] = name 29 | return HEAD_ref, refs 30 | 31 | 32 | def commit_objects(): 33 | """List commit objects in reverse chronological order. 34 | 35 | Return a dict with the SHA1 of the commits as keys and the SHA1 of 36 | the parents as values. 37 | """ 38 | commit_list = getoutput('git rev-list --all --parents') 39 | commits = {} 40 | for item in commit_list.split('\n'): 41 | splitted_item = item.split() 42 | if len(splitted_item) != 2: 43 | commit = splitted_item[0] 44 | parent = None 45 | else: 46 | commit, parent = splitted_item 47 | commits[commit] = parent 48 | return commits 49 | 50 | 51 | def find_svn_branch_name(): 52 | """Return the reference name of the current remote branch.""" 53 | head, references = list_references() 54 | commits = commit_objects() 55 | 56 | current_commit = head 57 | while current_commit: 58 | if current_commit in references: 59 | reference_name = references[current_commit] 60 | if ('remote' in reference_name or 61 | 'trunk' in reference_name or 62 | 'git-svn' in reference_name): 63 | logger.debug('Found remote: %s', reference_name) 64 | return reference_name 65 | # find the parent of the current commit 66 | # and make it the next commit to investigate 67 | if current_commit in commits: 68 | current_commit = commits[current_commit] 69 | return None 70 | 71 | 72 | def find_uncommitted(svn_branch): 73 | """Given the name of the remote branch, show log of the commits.""" 74 | output = getoutput('git log --pretty="format:%%h %%s" %s..HEAD' % 75 | svn_branch) 76 | if output: 77 | print 'Possible unpushed commits (against %s):' % svn_branch 78 | print output 79 | else: 80 | print 'No unpushed commits found.' 81 | 82 | 83 | if __name__ == '__main__': 84 | status = getoutput('git status') 85 | if status.startswith('fatal'): 86 | print status 87 | sys.exit(1) 88 | svn_branch = find_svn_branch_name() 89 | if svn_branch is None: 90 | print "No svn branch found" 91 | sys.exit(1) 92 | logger.debug('Found branch: %s', svn_branch) 93 | find_uncommitted(svn_branch) 94 | -------------------------------------------------------------------------------- /git-svn-clone-externals: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | toplevel_directory="$(git rev-parse --show-cdup)" 6 | [ -n "$toplevel_directory" ] && { echo "please run from the toplevel directory"; exit 1; } 7 | 8 | 9 | function call() 10 | { 11 | cmd="$@" 12 | echo "$cmd" 13 | eval "$cmd" 14 | return "$?" 15 | } 16 | 17 | function do_clone() 18 | { 19 | test -d .git_externals || return 1 20 | module=`echo $remote_url|sed 's,\(.*\)\(/trunk\|/branch.*\|/tag.*\),\1,'` 21 | branch=`echo $remote_url|sed 's,\(.*\)\(/trunk\|/branch.*\|/tag.*\),\2,'|sed 's,^/,,'` 22 | if [[ $branch = $remote_url ]]; then 23 | branch="" 24 | fi 25 | ( 26 | cd .git_externals 27 | if [ -d "$local_directory" ]; then 28 | ( 29 | cd "$local_directory" 30 | call git svn fetch --all 31 | ) 32 | else 33 | tags="tags" 34 | brch="branches" 35 | branchpath=$(echo $branch|cut -f1 -d/) 36 | echo $tags|grep $branchpath >/dev/null 2>&1 && tags=$branchpath 37 | echo $brch|grep $branchpath >/dev/null 2>&1 && brch=$branchpath 38 | 39 | if [ "$module" = "$remote_url" ]; then 40 | # URL does not contains any trunk, branches or tags part, so we dont need 41 | # additional options for git-svn 42 | call git svn clone "$revision" "$module" "$local_directory" 43 | else 44 | call git svn clone "$revision" "$module" -T trunk -b $brch -t $tags "$local_directory" 45 | fi 46 | 47 | fi 48 | ( 49 | branch="$(echo ${branch}|sed 's,/$,,')" 50 | if [ -n "$branch" ]; then 51 | cd "$local_directory" 52 | call git reset --hard $branch 53 | fi 54 | ) 55 | ) 56 | } 57 | 58 | function do_link() 59 | { 60 | dir="$1" 61 | base="$(dirname $dir)" 62 | ( 63 | mkdir -p "$base" 64 | cd $base 65 | rel=$(git rev-parse --show-cdup) 66 | ln -sf ${rel}.git_externals/"$dir" 67 | ) 68 | } 69 | 70 | function do_excludes() 71 | { 72 | dir="$1" 73 | git_excludes_path=.git/info/exclude 74 | if ! grep -q '^.git_externals$' "$git_excludes_path" 75 | then 76 | echo .git_externals >> "$git_excludes_path" 77 | fi 78 | 79 | if ! grep -q '^'"$dir"'$' "$git_excludes_path" 80 | then 81 | echo "$dir" >> "$git_excludes_path" 82 | fi 83 | } 84 | 85 | function is_excluded() 86 | { 87 | local result=0 88 | if [ -f .git_externals_exclude ] ; then 89 | matches=`grep -v "^#" .git_externals_exclude|grep "^/$1$"|wc -l` 90 | if [ $matches -gt 0 ] ; then 91 | local result=1 92 | fi 93 | fi 94 | echo $result 95 | return 96 | } 97 | 98 | 99 | git svn show-externals | grep -vE '#|^$' | \ 100 | sed 's/\(-r\)[ ]*\([0-9]\{1,\}\)/\1\2/' | \ 101 | while read svn_externals 102 | do 103 | 104 | number_fields="$(echo ${svn_externals}|awk '{print NF}')" 105 | case $number_fields in 106 | 2) 107 | local_directory="$(echo ${svn_externals} | awk '{print $1}' | sed 's,^/,,')" 108 | revision="" 109 | remote_url="$(echo ${svn_externals} | awk '{print $2}')" 110 | ;; 111 | 3) 112 | local_directory="$(echo ${svn_externals} | awk '{print $1}' | sed 's,^/,,')" 113 | revision=""$(echo ${svn_externals} | awk '{print $2}') 114 | remote_url="$(echo ${svn_externals} | awk '{print $3}')" 115 | ;; 116 | *) continue ;; 117 | esac 118 | 119 | check_excluded=$(is_excluded $local_directory) 120 | if [ $check_excluded -eq 0 ] ; then 121 | if [ -n "$USE_SSH" ]; then 122 | echo "Rewriting url to use SVN+SSH." 123 | shopt -s extglob 124 | remote_url="${remote_url/+(http|https)/svn+ssh}" 125 | fi 126 | 127 | [ -z "${remote_url}" ] && continue 128 | 129 | export local_directory revision remote_url 130 | 131 | echo "$local_directory -> $remote_url" 132 | 133 | dir=`dirname $local_directory` 134 | [ -d ".git_externals/$dir" ] || mkdir -p ".git_externals/$dir" 135 | 136 | do_clone "$revision" "$remote_url" "$local_directory" || exit 137 | do_link "$local_directory" 138 | do_excludes "$local_directory" 139 | fi 140 | 141 | done -------------------------------------------------------------------------------- /git-svn-externals-check: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for dir in *; do 4 | if [ -d $dir ]; then 5 | cd $dir 6 | STATUS=$(git status) 7 | UNPUSHED=$(git-svn-check-unpushed) 8 | if [ $(echo $STATUS|grep -c "clean") -lt 1 -o \ 9 | $(echo $UNPUSHED|grep -c "No unpushed") -lt 1 ]; then 10 | echo '>>>>>>>>>>>>>>>>' $dir '<<<<<<<<<<<<<<<<' 11 | git status 12 | git-svn-check-unpushed 13 | echo '----------------------------------------' 14 | else 15 | echo $dir 'is clean' 16 | fi 17 | cd .. 18 | fi 19 | done 20 | -------------------------------------------------------------------------------- /git-svn-externals-update: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | toplevel_directory="$(git rev-parse --show-cdup)" 4 | [ -n "$toplevel_directory" ] && { echo "please run from the toplevel directory"; exit 1; } 5 | 6 | find .git_externals -type d -name .git | while read gitdir; do 7 | dir=$(dirname "$gitdir") 8 | if [ -d $dir ]; then 9 | pushd $dir 10 | echo $dir 11 | git svn fetch 12 | git svn rebase 13 | popd 14 | fi 15 | done 16 | --------------------------------------------------------------------------------