summaryrefslogtreecommitdiffstats
path: root/scripts/mageia-import.sh
blob: 09404c3bc3b4e612aa0a32763de09def67816aad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
#!/bin/bash

NEWSVNURL="file:///home/colin/svn-git/soft"
NEWSVNUUID=$(svn info $NEWSVNURL | grep "^Repository UUID: " | cut -d' ' -f3)

SOFTWARE=$1
if [ -z $SOFTWARE ]; then
  echo "Missing software to convert..."
  exit 1
fi
BASESOFTWARE=$(basename $SOFTWARE)

if [ -d $BASESOFTWARE-origin ]; then
  echo "It seems the svn-git repository already exists ($BASESOFTWARE-origin)" >&2
  exit 1
fi

if [ -d $BASESOFTWARE.git ]; then
  echo "It seems the converted git repository already exists ($BASESOFTWARE.git)" >&2
  exit 1
fi

AUTHORMAP=$(realpath $(dirname $(realpath $0))/../metadata/mageia-user-map.txt)
if [ ! -f "$AUTHORMAP" ]; then
  echo "Cannot find author map." >&2
  exit 1
fi

skiprevisions=
skiprevisionsx="$2"
if [ -n "$skiprevisionsx" ]; then
  for rev in $skiprevisionsx; do
    rev=$(echo $rev | sed 's/r//g')
    rev=$(( $rev + 0 ))
    if [ $rev -gt 0 ]; then
      skiprevisions="$skiprevisions $rev"
    fi
  done
fi

echo "Identified software name: $SOFTWARE"
echo "SVN Revisions to skip:    $skiprevisions"
echo

STDLAYOUT=--stdlayout
if [ "$(svn ls $NEWSVNURL/$SOFTWARE/trunk 2>/dev/null | wc -l)" == "0" ]; then
  STDLAYOUT=
fi

git svn init $NEWSVNURL/$SOFTWARE --no-metadata $STDLAYOUT $BASESOFTWARE-origin
cd $BASESOFTWARE-origin
revision=0
if [ -n "$skiprevisions" ]; then
  for rev in $skiprevisions; do
    git svn fetch -A $AUTHORMAP -r $(( $revision + 1 )):$(( $rev - 1 ))
    revision=$rev
  done
fi
git svn fetch -A $AUTHORMAP -r $(( $revision + 1 )):HEAD
if [ -n "$STDLAYOUT" ]; then
  git reset --hard trunk
fi
echo
echo "done"


echo
echo "Now creating bare git repository"
git init --bare ../$BASESOFTWARE.git
git remote rm origin
git remote add origin ../$BASESOFTWARE.git
#git push --set-upstream master
git push origin master 'refs/remotes/*:refs/heads/*'

pushd ../$BASESOFTWARE.git
git branch -D trunk 2>/dev/null
git branch -D git-svn 2>/dev/null
git branch -D origin/master 2>/dev/null

# Tagging logic inspired by https://github.com/nothingmuch/git-svn-abandon/blob/master/git-svn-abandon-fix-refs
# Keep a map of tags and their original sha1 for the SQL database cerated below
declare -A tagmap
tags=$(git for-each-ref --format='%(refname)' refs/heads/tags | cut -d / -f 4)
for tag in $tags; do
  ref="refs/heads/tags/$tag"
  refsha1=$(git rev-parse "$ref")
  reftreesha1=$(git rev-parse "$ref":)

  # Find the oldest ancestor for which the tree is the same
  parentref="$ref"
  while [ "$(git rev-parse --quiet --verify "$parentref"^:)" = "$reftreesha1" ]; do
    parentref="$parentref"^
  done
  parent=$(git rev-parse "$parentref")

  # If this ancestor is in master then we can just tag it
  # otherwise the tag has diverged from master and it's actually more like a
  # branch than a tag
  merge=$(git merge-base master $parent)
  if [ "$merge" = "$parent" ]; then
    targetref=$parent
  else
    targetref=$refsha1
  fi

  tagmap[$refsha1]=$tag

  # create an annotated tag based on the last commit in the tag, and delete the "branchy" ref for the tag
  git show -s --pretty='format:%s%n%n%b' "$ref" | \
    env GIT_COMMITTER_NAME="$(git show -s --pretty='format:%an' "$ref")" \
        GIT_COMMITTER_EMAIL="$(git show -s --pretty='format:%ae' "$ref")" \
        GIT_COMMITTER_DATE="$(git show -s --pretty='format:%ad' "$ref")" \
        GIT_AUTHOR_NAME="$(git show -s --pretty='format:%an' "$ref")" \
        GIT_AUTHOR_EMAIL="$(git show -s --pretty='format:%ae' "$ref")" \
        GIT_AUTHOR_DATE="$(git show -s --pretty='format:%ad' "$ref")" \
        git tag -a -F - "$tag" "$targetref"

  git update-ref -d "$ref"
done

# Implement a branch name policy
#   1. Branches matching ([1-3]) are renamed to distro/mga\1
#   2. Branches matching (20[0-9\.]+) are renamed to distro/mdv\1
#   3. Branches matching (mes[0-9].*) are renamed to distro/\1
#   4. All other branches are renamed to topic/\1 (excl. master)

branches=$(git for-each-ref --format='%(refname)' refs/heads | cut -d / -f 3-)
for branch in $branches; do
  ref="refs/heads/$branch"
  refsha1=$(git rev-parse "$ref")
  reftreesha1=$(git rev-parse "$ref":)

  # Find the oldest ancestor for which the tree is the same
  parentref="$ref"
  while [ "$(git rev-parse --quiet --verify "$parentref"^:)" = "$reftreesha1" ]; do
    parentref="$parentref"^
  done
  parent=$(git rev-parse "$parentref")

  # If this ancestor is in master then we can just squash it
  # otherwise the branch has diverged from master and it's actually a proper
  # branch
  merge=$(git merge-base master $parent)
  if [ "$merge" = "$parent" ]; then
    git update-ref "$ref" $parent $refsha1
  fi

  if (echo $branch | grep -qE '^[0-3]$'); then
    git branch -m $branch distro/mga$branch
  elif (echo $branch | grep -qE '^20[0-9\.]+$'); then
    git branch -m $branch distro/mdv$branch
  elif (echo $branch | grep -qE '^mes[0-9].*$'); then
    git branch -m $branch distro/$branch
  elif [ "$branch" != "master" ]; then
    git branch -m $branch topic/$branch
  fi
done

git gc --aggressive
popd


# And finally we create some SQL for creating a nice revision map database

# CREATE TABLE refs (distro char(3) NOT NULL, soft varchar(255) NOT NULL, revision int(10) unsigned NOT NULL,  sha1 char(40) NOT NULL, head varchar(255) NOT NULL);

# Parse a git-svn revmap file into SQL
# $1 = Name of revmap file
# $2 = Distro ('mdv' or 'mga')
# $3 = head name i.e. branch or tag name (only used when not processing trunk/master)
# $4 = Special SHA1 (last sha1 to look for if 'mdv', fake sha1 commit if 'mga')
parsesvnrevmap()
{
  if [ ! -f "$1" ]; then
    echo "No such file '$1'" >&2
    exit 1
  fi
  for map in $(cat "$1" | xxd -c24 -g24  | cut -b 10-57); do
    sha1=$(echo $map | cut -b 9-)
    if [ "$sha1" = "0000000000000000000000000000000000000000" ]; then
      continue
    fi
    if [ -n "$4" -a "$2" = "mga" -a "$sha1" = "$4" ]; then
      continue
    fi
    rev=$(printf "%d" 0x$(echo $map | cut -b 1-8))

    if [ -n "${tagmap[$sha1]}" ]; then
      echo "INSERT INTO refs VALUES('$2', '$SOFTWARE', $rev, '', '${tagmap[$sha1]}');"
    else
      branch=$3
      if [ -n "$branch" ]; then
        if (echo $branch | grep -qE '^[0-3]$'); then
          branch=distro/mga$branch
        elif (echo $branch | grep -qE '^20[0-9\.]+$'); then
          branch=distro/mdv$branch
        elif (echo $branch | grep -qE '^mes[0-9].*$'); then
          branch=distro/$branch
        elif [ "$branch" != "master" ]; then
          branch=topic/$branch
        fi
      fi
      echo "INSERT INTO refs VALUES('$2', '$SOFTWARE', $rev, '$sha1', '$branch');"
    fi

    # Exit if we've reached our reset sha1 as any future revisions are not used
    # by Mageia
    if [ -n "$4" -a "$2" = "mdv" -a "$sha1" = "$4" ]; then
      break
    fi
  done
}

echo
echo -n "Creating revision -> sha1 map SQL... "

sql="../$BASESOFTWARE-revmap.sql"
rm -f "$sql" "$sql".xz

# Mageia commits
TRUNKNAME=trunk
if [ -z "$STDLAYOUT" ]; then
  TRUNKNAME=git-svn
fi
revmap=".git/svn/refs/remotes/$TRUNKNAME/.rev_map.$NEWSVNUUID"
if [ -f "$revmap" ]; then
  parsesvnrevmap "$revmap" "mga" "" $fakesha1 >>"$sql"
fi
for revmap in $(find .git/svn/refs/remotes/tags -name .rev_map.$NEWSVNUUID 2>/dev/null); do
  # Note, still pass in the tag name in case the tag is really more of a branch...
  parsesvnrevmap "$revmap" "mga" "$(echo $revmap | cut -d'/' -f6)" >>"$sql"
done
for revmap in $(find .git/svn/refs/remotes -maxdepth 2 -not -iwholename ".git/svn/refs/remotes/$TRUNKNAME/.rev_map.*" -name .rev_map.$NEWSVNUUID 2>/dev/null); do
  parsesvnrevmap "$revmap" "mga" "$(echo $revmap | cut -d'/' -f5)" >>"$sql"
done

xz "$sql"

cd ..
echo "done"