|
0f024d3…
|
drh
|
1 |
/* |
|
f15ce59…
|
mistachkin
|
2 |
** Copyright (c) 2010 D. Richard Hipp |
|
0f024d3…
|
drh
|
3 |
** |
|
0f024d3…
|
drh
|
4 |
** This program is free software; you can redistribute it and/or |
|
0f024d3…
|
drh
|
5 |
** modify it under the terms of the Simplified BSD License (also |
|
0f024d3…
|
drh
|
6 |
** known as the "2-Clause License" or "FreeBSD License".) |
|
0f024d3…
|
drh
|
7 |
|
|
0f024d3…
|
drh
|
8 |
** This program is distributed in the hope that it will be useful, |
|
0f024d3…
|
drh
|
9 |
** but without any warranty; without even the implied warranty of |
|
0f024d3…
|
drh
|
10 |
** merchantability or fitness for a particular purpose. |
|
0f024d3…
|
drh
|
11 |
** |
|
0f024d3…
|
drh
|
12 |
** Author contact information: |
|
0f024d3…
|
drh
|
13 |
** [email protected] |
|
0f024d3…
|
drh
|
14 |
** |
|
0f024d3…
|
drh
|
15 |
******************************************************************************* |
|
0f024d3…
|
drh
|
16 |
** |
|
0f024d3…
|
drh
|
17 |
** This file contains code used to export the content of a Fossil |
|
0f024d3…
|
drh
|
18 |
** repository in the git-fast-import format. |
|
0f024d3…
|
drh
|
19 |
*/ |
|
0f024d3…
|
drh
|
20 |
#include "config.h" |
|
0f024d3…
|
drh
|
21 |
#include "export.h" |
|
0f024d3…
|
drh
|
22 |
#include <assert.h> |
|
0f024d3…
|
drh
|
23 |
|
|
3bba2a6…
|
jan.nijtmans
|
24 |
/* |
|
3bba2a6…
|
jan.nijtmans
|
25 |
** State information common to all export types. |
|
3bba2a6…
|
jan.nijtmans
|
26 |
*/ |
|
3bba2a6…
|
jan.nijtmans
|
27 |
static struct { |
|
3bba2a6…
|
jan.nijtmans
|
28 |
const char *zTrunkName; /* Name of trunk branch */ |
|
3bba2a6…
|
jan.nijtmans
|
29 |
} gexport; |
|
3bba2a6…
|
jan.nijtmans
|
30 |
|
|
b3acfa2…
|
drh
|
31 |
#if INTERFACE |
|
b3acfa2…
|
drh
|
32 |
/* |
|
e2bdc10…
|
danield
|
33 |
** Each line in a git-fast-export "mark" file is an instance of |
|
7029594…
|
drh
|
34 |
** this object. |
|
b3acfa2…
|
drh
|
35 |
*/ |
|
7029594…
|
drh
|
36 |
struct mark_t { |
|
7029594…
|
drh
|
37 |
char *name; /* Name of the mark. Also starts with ":" */ |
|
7029594…
|
drh
|
38 |
int rid; /* Corresponding object in the BLOB table */ |
|
7029594…
|
drh
|
39 |
char uuid[65]; /* The GIT hash name for this object */ |
|
b3acfa2…
|
drh
|
40 |
}; |
|
b3acfa2…
|
drh
|
41 |
#endif |
|
b3acfa2…
|
drh
|
42 |
|
|
b3acfa2…
|
drh
|
43 |
/* |
|
0f024d3…
|
drh
|
44 |
** Output a "committer" record for the given user. |
|
76d9a45…
|
ashepilko
|
45 |
** NOTE: the given user name may be an email itself. |
|
0f024d3…
|
drh
|
46 |
*/ |
|
0f024d3…
|
drh
|
47 |
static void print_person(const char *zUser){ |
|
0f024d3…
|
drh
|
48 |
static Stmt q; |
|
0f024d3…
|
drh
|
49 |
const char *zContact; |
|
0f024d3…
|
drh
|
50 |
char *zName; |
|
0f024d3…
|
drh
|
51 |
char *zEmail; |
|
0f024d3…
|
drh
|
52 |
int i, j; |
|
76d9a45…
|
ashepilko
|
53 |
int isBracketed, atEmailFirst, atEmailLast; |
|
0f024d3…
|
drh
|
54 |
|
|
0f024d3…
|
drh
|
55 |
if( zUser==0 ){ |
|
0f024d3…
|
drh
|
56 |
printf(" <unknown>"); |
|
0f024d3…
|
drh
|
57 |
return; |
|
0f024d3…
|
drh
|
58 |
} |
|
0f024d3…
|
drh
|
59 |
db_static_prepare(&q, "SELECT info FROM user WHERE login=:user"); |
|
0f024d3…
|
drh
|
60 |
db_bind_text(&q, ":user", zUser); |
|
0f024d3…
|
drh
|
61 |
if( db_step(&q)!=SQLITE_ROW ){ |
|
0f024d3…
|
drh
|
62 |
db_reset(&q); |
|
4c3e172…
|
danield
|
63 |
zName = fossil_strdup(zUser); |
|
76d9a45…
|
ashepilko
|
64 |
for(i=j=0; zName[i]; i++){ |
|
76d9a45…
|
ashepilko
|
65 |
if( zName[i]!='<' && zName[i]!='>' && zName[i]!='"' ){ |
|
76d9a45…
|
ashepilko
|
66 |
zName[j++] = zName[i]; |
|
76d9a45…
|
ashepilko
|
67 |
} |
|
0f024d3…
|
drh
|
68 |
} |
|
76d9a45…
|
ashepilko
|
69 |
zName[j] = 0; |
|
76d9a45…
|
ashepilko
|
70 |
printf(" %s <%s>", zName, zName); |
|
76d9a45…
|
ashepilko
|
71 |
free(zName); |
|
76d9a45…
|
ashepilko
|
72 |
return; |
|
76d9a45…
|
ashepilko
|
73 |
} |
|
76d9a45…
|
ashepilko
|
74 |
|
|
76d9a45…
|
ashepilko
|
75 |
/* |
|
76d9a45…
|
ashepilko
|
76 |
** We have contact information. |
|
76d9a45…
|
ashepilko
|
77 |
** It may or may not contain an email address. |
|
76d9a45…
|
ashepilko
|
78 |
** |
|
76d9a45…
|
ashepilko
|
79 |
** ASSUME: |
|
76d9a45…
|
ashepilko
|
80 |
** - General case:"Name Unicoded" <[email protected]> other info |
|
76d9a45…
|
ashepilko
|
81 |
** - If contact information contains more than an email address, |
|
76d9a45…
|
ashepilko
|
82 |
** then the email address is enclosed between <> |
|
76d9a45…
|
ashepilko
|
83 |
** - When only email address is specified, then it's stored verbatim |
|
76d9a45…
|
ashepilko
|
84 |
** - When name part is absent or all-blanks, use zUser instead |
|
76d9a45…
|
ashepilko
|
85 |
*/ |
|
76d9a45…
|
ashepilko
|
86 |
zName = NULL; |
|
76d9a45…
|
ashepilko
|
87 |
zEmail = NULL; |
|
76d9a45…
|
ashepilko
|
88 |
zContact = db_column_text(&q, 0); |
|
76d9a45…
|
ashepilko
|
89 |
atEmailFirst = -1; |
|
76d9a45…
|
ashepilko
|
90 |
atEmailLast = -1; |
|
76d9a45…
|
ashepilko
|
91 |
isBracketed = 0; |
|
76d9a45…
|
ashepilko
|
92 |
for(i=0; zContact[i] && zContact[i]!='@'; i++){ |
|
76d9a45…
|
ashepilko
|
93 |
if( zContact[i]=='<' ){ |
|
76d9a45…
|
ashepilko
|
94 |
isBracketed = 1; |
|
76d9a45…
|
ashepilko
|
95 |
atEmailFirst = i+1; |
|
76d9a45…
|
ashepilko
|
96 |
} |
|
76d9a45…
|
ashepilko
|
97 |
else if( zContact[i]=='>' ){ |
|
76d9a45…
|
ashepilko
|
98 |
isBracketed = 0; |
|
76d9a45…
|
ashepilko
|
99 |
atEmailFirst = i+1; |
|
76d9a45…
|
ashepilko
|
100 |
} |
|
76d9a45…
|
ashepilko
|
101 |
else if( zContact[i]==' ' && !isBracketed ){ |
|
76d9a45…
|
ashepilko
|
102 |
atEmailFirst = i+1; |
|
76d9a45…
|
ashepilko
|
103 |
} |
|
76d9a45…
|
ashepilko
|
104 |
} |
|
76d9a45…
|
ashepilko
|
105 |
if( zContact[i]==0 ){ |
|
76d9a45…
|
ashepilko
|
106 |
/* No email address found. Take as user info if not empty */ |
|
4c3e172…
|
danield
|
107 |
zName = fossil_strdup(zContact[0] ? zContact : zUser); |
|
0f024d3…
|
drh
|
108 |
for(i=j=0; zName[i]; i++){ |
|
76d9a45…
|
ashepilko
|
109 |
if( zName[i]!='<' && zName[i]!='>' && zName[i]!='"' ){ |
|
0f024d3…
|
drh
|
110 |
zName[j++] = zName[i]; |
|
0f024d3…
|
drh
|
111 |
} |
|
0f024d3…
|
drh
|
112 |
} |
|
0f024d3…
|
drh
|
113 |
zName[j] = 0; |
|
76d9a45…
|
ashepilko
|
114 |
|
|
76d9a45…
|
ashepilko
|
115 |
printf(" %s <%s>", zName, zName); |
|
76d9a45…
|
ashepilko
|
116 |
free(zName); |
|
76d9a45…
|
ashepilko
|
117 |
db_reset(&q); |
|
76d9a45…
|
ashepilko
|
118 |
return; |
|
76d9a45…
|
ashepilko
|
119 |
} |
|
76d9a45…
|
ashepilko
|
120 |
for(j=i+1; zContact[j] && zContact[j]!=' '; j++){ |
|
76d9a45…
|
ashepilko
|
121 |
if( zContact[j]=='>' ) |
|
76d9a45…
|
ashepilko
|
122 |
atEmailLast = j-1; |
|
76d9a45…
|
ashepilko
|
123 |
} |
|
76d9a45…
|
ashepilko
|
124 |
if ( atEmailLast==-1 ) atEmailLast = j-1; |
|
76d9a45…
|
ashepilko
|
125 |
if ( atEmailFirst==-1 ) atEmailFirst = 0; /* Found only email */ |
|
76d9a45…
|
ashepilko
|
126 |
|
|
76d9a45…
|
ashepilko
|
127 |
/* |
|
76d9a45…
|
ashepilko
|
128 |
** Found beginning and end of email address. |
|
76d9a45…
|
ashepilko
|
129 |
** Extract the address (trimmed and sanitized). |
|
76d9a45…
|
ashepilko
|
130 |
*/ |
|
76d9a45…
|
ashepilko
|
131 |
for(j=atEmailFirst; zContact[j] && zContact[j]==' '; j++){} |
|
76d9a45…
|
ashepilko
|
132 |
zEmail = mprintf("%.*s", atEmailLast-j+1, &zContact[j]); |
|
76d9a45…
|
ashepilko
|
133 |
|
|
76d9a45…
|
ashepilko
|
134 |
for(i=j=0; zEmail[i]; i++){ |
|
76d9a45…
|
ashepilko
|
135 |
if( zEmail[i]!='<' && zEmail[i]!='>' ){ |
|
76d9a45…
|
ashepilko
|
136 |
zEmail[j++] = zEmail[i]; |
|
76d9a45…
|
ashepilko
|
137 |
} |
|
76d9a45…
|
ashepilko
|
138 |
} |
|
76d9a45…
|
ashepilko
|
139 |
zEmail[j] = 0; |
|
76d9a45…
|
ashepilko
|
140 |
|
|
76d9a45…
|
ashepilko
|
141 |
/* |
|
76d9a45…
|
ashepilko
|
142 |
** When bracketed email, extract the string _before_ |
|
76d9a45…
|
ashepilko
|
143 |
** email as user name (may be enquoted). |
|
76d9a45…
|
ashepilko
|
144 |
** If missing or all-blank name, use zUser. |
|
76d9a45…
|
ashepilko
|
145 |
*/ |
|
76d9a45…
|
ashepilko
|
146 |
if( isBracketed && (atEmailFirst-1) > 0){ |
|
76d9a45…
|
ashepilko
|
147 |
for(i=atEmailFirst-2; i>=0 && zContact[i] && zContact[i]==' '; i--){} |
|
76d9a45…
|
ashepilko
|
148 |
if( i>=0 ){ |
|
76d9a45…
|
ashepilko
|
149 |
for(j=0; j<i && zContact[j] && zContact[j]==' '; j++){} |
|
76d9a45…
|
ashepilko
|
150 |
zName = mprintf("%.*s", i-j+1, &zContact[j]); |
|
76d9a45…
|
ashepilko
|
151 |
} |
|
76d9a45…
|
ashepilko
|
152 |
} |
|
76d9a45…
|
ashepilko
|
153 |
|
|
4c3e172…
|
danield
|
154 |
if( zName==NULL ) zName = fossil_strdup(zUser); |
|
76d9a45…
|
ashepilko
|
155 |
for(i=j=0; zName[i]; i++){ |
|
76d9a45…
|
ashepilko
|
156 |
if( zName[i]!='<' && zName[i]!='>' && zName[i]!='"' ){ |
|
76d9a45…
|
ashepilko
|
157 |
zName[j++] = zName[i]; |
|
76d9a45…
|
ashepilko
|
158 |
} |
|
76d9a45…
|
ashepilko
|
159 |
} |
|
76d9a45…
|
ashepilko
|
160 |
zName[j] = 0; |
|
b66b99c…
|
jan.nijtmans
|
161 |
|
|
76d9a45…
|
ashepilko
|
162 |
printf(" %s <%s>", zName, zEmail); |
|
76d9a45…
|
ashepilko
|
163 |
free(zName); |
|
76d9a45…
|
ashepilko
|
164 |
free(zEmail); |
|
76d9a45…
|
ashepilko
|
165 |
db_reset(&q); |
|
3bba2a6…
|
jan.nijtmans
|
166 |
} |
|
3bba2a6…
|
jan.nijtmans
|
167 |
|
|
89872d1…
|
andybradford
|
168 |
#define REFREPLACEMENT '_' |
|
3bba2a6…
|
jan.nijtmans
|
169 |
|
|
3bba2a6…
|
jan.nijtmans
|
170 |
/* |
|
3bba2a6…
|
jan.nijtmans
|
171 |
** Output a sanitized git named reference. |
|
3bba2a6…
|
jan.nijtmans
|
172 |
** https://git-scm.com/docs/git-check-ref-format |
|
3bba2a6…
|
jan.nijtmans
|
173 |
** This implementation assumes we are only printing |
|
3bba2a6…
|
jan.nijtmans
|
174 |
** the branch or tag part of the reference. |
|
3bba2a6…
|
jan.nijtmans
|
175 |
*/ |
|
3bba2a6…
|
jan.nijtmans
|
176 |
static void print_ref(const char *zRef){ |
|
4c3e172…
|
danield
|
177 |
char *zEncoded = fossil_strdup(zRef); |
|
3bba2a6…
|
jan.nijtmans
|
178 |
int i, w; |
|
3bba2a6…
|
jan.nijtmans
|
179 |
if (zEncoded[0]=='@' && zEncoded[1]=='\0'){ |
|
3bba2a6…
|
jan.nijtmans
|
180 |
putchar(REFREPLACEMENT); |
|
3bba2a6…
|
jan.nijtmans
|
181 |
return; |
|
3bba2a6…
|
jan.nijtmans
|
182 |
} |
|
3bba2a6…
|
jan.nijtmans
|
183 |
for(i=0, w=0; zEncoded[i]; i++, w++){ |
|
3bba2a6…
|
jan.nijtmans
|
184 |
if( i!=0 ){ /* Two letter tests */ |
|
3bba2a6…
|
jan.nijtmans
|
185 |
if( (zEncoded[i-1]=='.' && zEncoded[i]=='.') || |
|
3bba2a6…
|
jan.nijtmans
|
186 |
(zEncoded[i-1]=='@' && zEncoded[i]=='{') ){ |
|
3bba2a6…
|
jan.nijtmans
|
187 |
zEncoded[w]=zEncoded[w-1]=REFREPLACEMENT; |
|
3bba2a6…
|
jan.nijtmans
|
188 |
continue; |
|
3bba2a6…
|
jan.nijtmans
|
189 |
} |
|
3bba2a6…
|
jan.nijtmans
|
190 |
if( zEncoded[i-1]=='/' && zEncoded[i]=='/' ){ |
|
3bba2a6…
|
jan.nijtmans
|
191 |
w--; /* Normalise to a single / by rolling back w */ |
|
3bba2a6…
|
jan.nijtmans
|
192 |
continue; |
|
3bba2a6…
|
jan.nijtmans
|
193 |
} |
|
3bba2a6…
|
jan.nijtmans
|
194 |
} |
|
3bba2a6…
|
jan.nijtmans
|
195 |
/* No control characters */ |
|
3bba2a6…
|
jan.nijtmans
|
196 |
if( (unsigned)zEncoded[i]<0x20 || zEncoded[i]==0x7f ){ |
|
3bba2a6…
|
jan.nijtmans
|
197 |
zEncoded[w]=REFREPLACEMENT; |
|
3bba2a6…
|
jan.nijtmans
|
198 |
continue; |
|
3bba2a6…
|
jan.nijtmans
|
199 |
} |
|
3bba2a6…
|
jan.nijtmans
|
200 |
switch( zEncoded[i] ){ |
|
3bba2a6…
|
jan.nijtmans
|
201 |
case ' ': |
|
3bba2a6…
|
jan.nijtmans
|
202 |
case '^': |
|
3bba2a6…
|
jan.nijtmans
|
203 |
case ':': |
|
3bba2a6…
|
jan.nijtmans
|
204 |
case '?': |
|
3bba2a6…
|
jan.nijtmans
|
205 |
case '*': |
|
3bba2a6…
|
jan.nijtmans
|
206 |
case '[': |
|
3bba2a6…
|
jan.nijtmans
|
207 |
case '\\': |
|
3bba2a6…
|
jan.nijtmans
|
208 |
zEncoded[w]=REFREPLACEMENT; |
|
89872d1…
|
andybradford
|
209 |
break; |
|
3bba2a6…
|
jan.nijtmans
|
210 |
} |
|
3bba2a6…
|
jan.nijtmans
|
211 |
} |
|
3bba2a6…
|
jan.nijtmans
|
212 |
/* Cannot begin with a . or / */ |
|
3bba2a6…
|
jan.nijtmans
|
213 |
if( zEncoded[0]=='.' || zEncoded[0] == '/' ) zEncoded[0]=REFREPLACEMENT; |
|
3bba2a6…
|
jan.nijtmans
|
214 |
if( i>0 ){ |
|
3bba2a6…
|
jan.nijtmans
|
215 |
i--; w--; |
|
3bba2a6…
|
jan.nijtmans
|
216 |
/* Or end with a . or / */ |
|
3bba2a6…
|
jan.nijtmans
|
217 |
if( zEncoded[i]=='.' || zEncoded[i] == '/' ) zEncoded[w]=REFREPLACEMENT; |
|
3bba2a6…
|
jan.nijtmans
|
218 |
/* Cannot end with .lock */ |
|
3bba2a6…
|
jan.nijtmans
|
219 |
if ( i>4 && strcmp((zEncoded+i)-5, ".lock")==0 ) |
|
3bba2a6…
|
jan.nijtmans
|
220 |
memset((zEncoded+w)-5, REFREPLACEMENT, 5); |
|
3bba2a6…
|
jan.nijtmans
|
221 |
} |
|
3bba2a6…
|
jan.nijtmans
|
222 |
printf("%s", zEncoded); |
|
3bba2a6…
|
jan.nijtmans
|
223 |
free(zEncoded); |
|
22d414e…
|
joerg
|
224 |
} |
|
22d414e…
|
joerg
|
225 |
|
|
22d414e…
|
joerg
|
226 |
#define BLOBMARK(rid) ((rid) * 2) |
|
22d414e…
|
joerg
|
227 |
#define COMMITMARK(rid) ((rid) * 2 + 1) |
|
22d414e…
|
joerg
|
228 |
|
|
22d414e…
|
joerg
|
229 |
/* |
|
b3acfa2…
|
drh
|
230 |
** insert_commit_xref() |
|
b3acfa2…
|
drh
|
231 |
** Insert a new (mark,rid,uuid) entry into the 'xmark' table. |
|
b3acfa2…
|
drh
|
232 |
** zName and zUuid must be non-null and must point to NULL-terminated strings. |
|
b3acfa2…
|
drh
|
233 |
*/ |
|
b3acfa2…
|
drh
|
234 |
void insert_commit_xref(int rid, const char *zName, const char *zUuid){ |
|
b3acfa2…
|
drh
|
235 |
db_multi_exec( |
|
b3acfa2…
|
drh
|
236 |
"INSERT OR IGNORE INTO xmark(tname, trid, tuuid)" |
|
b3acfa2…
|
drh
|
237 |
"VALUES(%Q,%d,%Q)", |
|
b3acfa2…
|
drh
|
238 |
zName, rid, zUuid |
|
b3acfa2…
|
drh
|
239 |
); |
|
b3acfa2…
|
drh
|
240 |
} |
|
b3acfa2…
|
drh
|
241 |
|
|
b3acfa2…
|
drh
|
242 |
/* |
|
b3acfa2…
|
drh
|
243 |
** create_mark() |
|
b3acfa2…
|
drh
|
244 |
** Create a new (mark,rid,uuid) entry for the given rid in the 'xmark' table, |
|
b3acfa2…
|
drh
|
245 |
** and return that information as a struct mark_t in *mark. |
|
27c8985…
|
mistachkin
|
246 |
** *unused_mark is a value representing a mark that is free for use--that is, |
|
27c8985…
|
mistachkin
|
247 |
** it does not appear in the marks file, and has not been used during this |
|
27c8985…
|
mistachkin
|
248 |
** export run. Specifically, it is the supremum of the set of used marks |
|
27c8985…
|
mistachkin
|
249 |
** plus one. |
|
b3acfa2…
|
drh
|
250 |
** This function returns -1 in the case where 'rid' does not exist, otherwise |
|
b3acfa2…
|
drh
|
251 |
** it returns 0. |
|
b3acfa2…
|
drh
|
252 |
** mark->name is dynamically allocated and is owned by the caller upon return. |
|
b3acfa2…
|
drh
|
253 |
*/ |
|
27c8985…
|
mistachkin
|
254 |
int create_mark(int rid, struct mark_t *mark, unsigned int *unused_mark){ |
|
b3acfa2…
|
drh
|
255 |
char sid[13]; |
|
b3acfa2…
|
drh
|
256 |
char *zUuid = rid_to_uuid(rid); |
|
27c8985…
|
mistachkin
|
257 |
if( !zUuid ){ |
|
b3acfa2…
|
drh
|
258 |
fossil_trace("Undefined rid=%d\n", rid); |
|
b3acfa2…
|
drh
|
259 |
return -1; |
|
b3acfa2…
|
drh
|
260 |
} |
|
b3acfa2…
|
drh
|
261 |
mark->rid = rid; |
|
27c8985…
|
mistachkin
|
262 |
sqlite3_snprintf(sizeof(sid), sid, ":%d", *unused_mark); |
|
27c8985…
|
mistachkin
|
263 |
*unused_mark += 1; |
|
b3acfa2…
|
drh
|
264 |
mark->name = fossil_strdup(sid); |
|
460b93b…
|
andybradford
|
265 |
sqlite3_snprintf(sizeof(mark->uuid), mark->uuid, "%s", zUuid); |
|
b3acfa2…
|
drh
|
266 |
free(zUuid); |
|
b3acfa2…
|
drh
|
267 |
insert_commit_xref(mark->rid, mark->name, mark->uuid); |
|
b3acfa2…
|
drh
|
268 |
return 0; |
|
b3acfa2…
|
drh
|
269 |
} |
|
b3acfa2…
|
drh
|
270 |
|
|
b3acfa2…
|
drh
|
271 |
/* |
|
b3acfa2…
|
drh
|
272 |
** mark_name_from_rid() |
|
b3acfa2…
|
drh
|
273 |
** Find the mark associated with the given rid. Mark names always start |
|
b3acfa2…
|
drh
|
274 |
** with ':', and are pulled from the 'xmark' temporary table. |
|
27c8985…
|
mistachkin
|
275 |
** If the given rid doesn't have a mark associated with it yet, one is |
|
27c8985…
|
mistachkin
|
276 |
** created with a value of *unused_mark. |
|
27c8985…
|
mistachkin
|
277 |
** *unused_mark functions exactly as in create_mark(). |
|
27c8985…
|
mistachkin
|
278 |
** This function returns NULL if the rid does not have an associated UUID, |
|
27c8985…
|
mistachkin
|
279 |
** (i.e. is not valid). Otherwise, it returns the name of the mark, which is |
|
27c8985…
|
mistachkin
|
280 |
** dynamically allocated and is owned by the caller of this function. |
|
b3acfa2…
|
drh
|
281 |
*/ |
|
27c8985…
|
mistachkin
|
282 |
char * mark_name_from_rid(int rid, unsigned int *unused_mark){ |
|
b3acfa2…
|
drh
|
283 |
char *zMark = db_text(0, "SELECT tname FROM xmark WHERE trid=%d", rid); |
|
27c8985…
|
mistachkin
|
284 |
if( zMark==NULL ){ |
|
b3acfa2…
|
drh
|
285 |
struct mark_t mark; |
|
27c8985…
|
mistachkin
|
286 |
if( create_mark(rid, &mark, unused_mark)==0 ){ |
|
b3acfa2…
|
drh
|
287 |
zMark = mark.name; |
|
b3acfa2…
|
drh
|
288 |
}else{ |
|
b3acfa2…
|
drh
|
289 |
return NULL; |
|
b3acfa2…
|
drh
|
290 |
} |
|
b3acfa2…
|
drh
|
291 |
} |
|
b3acfa2…
|
drh
|
292 |
return zMark; |
|
b3acfa2…
|
drh
|
293 |
} |
|
b3acfa2…
|
drh
|
294 |
|
|
b3acfa2…
|
drh
|
295 |
/* |
|
7029594…
|
drh
|
296 |
** Parse a single line of the mark file. Store the result in the mark object. |
|
7029594…
|
drh
|
297 |
** |
|
7029594…
|
drh
|
298 |
** "line" is a single line of input. |
|
7029594…
|
drh
|
299 |
** This function returns -1 in the case that the line is blank, malformed, or |
|
7029594…
|
drh
|
300 |
** the rid/uuid named in 'line' does not match what is in the repository |
|
7029594…
|
drh
|
301 |
** database. Otherwise, 0 is returned. |
|
7029594…
|
drh
|
302 |
** |
|
7029594…
|
drh
|
303 |
** mark->name is dynamically allocated, and owned by the caller. |
|
b3acfa2…
|
drh
|
304 |
*/ |
|
b3acfa2…
|
drh
|
305 |
int parse_mark(char *line, struct mark_t *mark){ |
|
b3acfa2…
|
drh
|
306 |
char *cur_tok; |
|
27c8985…
|
mistachkin
|
307 |
char type_; |
|
b3acfa2…
|
drh
|
308 |
cur_tok = strtok(line, " \t"); |
|
27c8985…
|
mistachkin
|
309 |
if( !cur_tok || strlen(cur_tok)<2 ){ |
|
b3acfa2…
|
drh
|
310 |
return -1; |
|
b3acfa2…
|
drh
|
311 |
} |
|
b3acfa2…
|
drh
|
312 |
mark->rid = atoi(&cur_tok[1]); |
|
27c8985…
|
mistachkin
|
313 |
type_ = cur_tok[0]; |
|
27c8985…
|
mistachkin
|
314 |
if( type_!='c' && type_!='b' ){ |
|
b3acfa2…
|
drh
|
315 |
/* This is probably a blob mark */ |
|
b3acfa2…
|
drh
|
316 |
mark->name = NULL; |
|
b3acfa2…
|
drh
|
317 |
return 0; |
|
b3acfa2…
|
drh
|
318 |
} |
|
b3acfa2…
|
drh
|
319 |
|
|
b3acfa2…
|
drh
|
320 |
cur_tok = strtok(NULL, " \t"); |
|
27c8985…
|
mistachkin
|
321 |
if( !cur_tok ){ |
|
b3acfa2…
|
drh
|
322 |
/* This mark was generated by an older version of Fossil and doesn't |
|
b3acfa2…
|
drh
|
323 |
** include the mark name and uuid. create_mark() will name the new mark |
|
b3acfa2…
|
drh
|
324 |
** exactly as it was when exported to git, so that we should have a |
|
fd9b7bd…
|
drh
|
325 |
** valid mapping from git hash<->mark name<->fossil hash. */ |
|
27c8985…
|
mistachkin
|
326 |
unsigned int mid; |
|
27c8985…
|
mistachkin
|
327 |
if( type_=='c' ){ |
|
27c8985…
|
mistachkin
|
328 |
mid = COMMITMARK(mark->rid); |
|
27c8985…
|
mistachkin
|
329 |
} |
|
27c8985…
|
mistachkin
|
330 |
else{ |
|
27c8985…
|
mistachkin
|
331 |
mid = BLOBMARK(mark->rid); |
|
27c8985…
|
mistachkin
|
332 |
} |
|
27c8985…
|
mistachkin
|
333 |
return create_mark(mark->rid, mark, &mid); |
|
b3acfa2…
|
drh
|
334 |
}else{ |
|
b3acfa2…
|
drh
|
335 |
mark->name = fossil_strdup(cur_tok); |
|
b3acfa2…
|
drh
|
336 |
} |
|
b3acfa2…
|
drh
|
337 |
|
|
b3acfa2…
|
drh
|
338 |
cur_tok = strtok(NULL, "\n"); |
|
90b07ed…
|
jan.nijtmans
|
339 |
if( !cur_tok || (strlen(cur_tok)!=40 && strlen(cur_tok)!=64) ){ |
|
b3acfa2…
|
drh
|
340 |
free(mark->name); |
|
90b07ed…
|
jan.nijtmans
|
341 |
fossil_trace("Invalid SHA-1/SHA-3 in marks file: %s\n", cur_tok); |
|
b3acfa2…
|
drh
|
342 |
return -1; |
|
b3acfa2…
|
drh
|
343 |
}else{ |
|
460b93b…
|
andybradford
|
344 |
sqlite3_snprintf(sizeof(mark->uuid), mark->uuid, "%s", cur_tok); |
|
b3acfa2…
|
drh
|
345 |
} |
|
b3acfa2…
|
drh
|
346 |
|
|
b3acfa2…
|
drh
|
347 |
/* make sure that rid corresponds to UUID */ |
|
27c8985…
|
mistachkin
|
348 |
if( fast_uuid_to_rid(mark->uuid)!=mark->rid ){ |
|
b3acfa2…
|
drh
|
349 |
free(mark->name); |
|
bad4d28…
|
jan.nijtmans
|
350 |
fossil_trace("Non-existent SHA-1/SHA-3 in marks file: %s\n", mark->uuid); |
|
b3acfa2…
|
drh
|
351 |
return -1; |
|
b3acfa2…
|
drh
|
352 |
} |
|
b3acfa2…
|
drh
|
353 |
|
|
b3acfa2…
|
drh
|
354 |
/* insert a cross-ref into the 'xmark' table */ |
|
b3acfa2…
|
drh
|
355 |
insert_commit_xref(mark->rid, mark->name, mark->uuid); |
|
b3acfa2…
|
drh
|
356 |
return 0; |
|
b3acfa2…
|
drh
|
357 |
} |
|
b3acfa2…
|
drh
|
358 |
|
|
b3acfa2…
|
drh
|
359 |
/* |
|
7029594…
|
drh
|
360 |
** Import the marks specified in file 'f'; |
|
7029594…
|
drh
|
361 |
** If 'blobs' is non-null, insert all blob marks into it. |
|
7029594…
|
drh
|
362 |
** If 'vers' is non-null, insert all commit marks into it. |
|
7029594…
|
drh
|
363 |
** If 'unused_marks' is non-null, upon return of this function, all values |
|
7029594…
|
drh
|
364 |
** x >= *unused_marks are free to use as marks, i.e. they do not clash with |
|
7029594…
|
drh
|
365 |
** any marks appearing in the marks file. |
|
7029594…
|
drh
|
366 |
** |
|
7029594…
|
drh
|
367 |
** Each line in the file must be at most 100 characters in length. This |
|
7029594…
|
drh
|
368 |
** seems like a reasonable maximum for a 40-character uuid, and 1-13 |
|
7029594…
|
drh
|
369 |
** character rid. |
|
7029594…
|
drh
|
370 |
** |
|
7029594…
|
drh
|
371 |
** The function returns -1 if any of the lines in file 'f' are malformed, |
|
7029594…
|
drh
|
372 |
** or the rid/uuid information doesn't match what is in the repository |
|
7029594…
|
drh
|
373 |
** database. Otherwise, 0 is returned. |
|
b3acfa2…
|
drh
|
374 |
*/ |
|
27c8985…
|
mistachkin
|
375 |
int import_marks(FILE* f, Bag *blobs, Bag *vers, unsigned int *unused_mark){ |
|
b3acfa2…
|
drh
|
376 |
char line[101]; |
|
b3acfa2…
|
drh
|
377 |
while(fgets(line, sizeof(line), f)){ |
|
b3acfa2…
|
drh
|
378 |
struct mark_t mark; |
|
27c8985…
|
mistachkin
|
379 |
if( strlen(line)==100 && line[99]!='\n' ){ |
|
b3acfa2…
|
drh
|
380 |
/* line too long */ |
|
b3acfa2…
|
drh
|
381 |
return -1; |
|
b3acfa2…
|
drh
|
382 |
} |
|
b3acfa2…
|
drh
|
383 |
if( parse_mark(line, &mark)<0 ){ |
|
b3acfa2…
|
drh
|
384 |
return -1; |
|
b3acfa2…
|
drh
|
385 |
}else if( line[0]=='b' ){ |
|
27c8985…
|
mistachkin
|
386 |
if( blobs!=NULL ){ |
|
b3acfa2…
|
drh
|
387 |
bag_insert(blobs, mark.rid); |
|
b3acfa2…
|
drh
|
388 |
} |
|
27c8985…
|
mistachkin
|
389 |
}else{ |
|
27c8985…
|
mistachkin
|
390 |
if( vers!=NULL ){ |
|
27c8985…
|
mistachkin
|
391 |
bag_insert(vers, mark.rid); |
|
27c8985…
|
mistachkin
|
392 |
} |
|
27c8985…
|
mistachkin
|
393 |
} |
|
27c8985…
|
mistachkin
|
394 |
if( unused_mark!=NULL ){ |
|
27c8985…
|
mistachkin
|
395 |
unsigned int mid = atoi(mark.name + 1); |
|
27c8985…
|
mistachkin
|
396 |
if( mid>=*unused_mark ){ |
|
bf9669f…
|
andygoth
|
397 |
*unused_mark = mid + 1; |
|
27c8985…
|
mistachkin
|
398 |
} |
|
b3acfa2…
|
drh
|
399 |
} |
|
b3acfa2…
|
drh
|
400 |
free(mark.name); |
|
b3acfa2…
|
drh
|
401 |
} |
|
b3acfa2…
|
drh
|
402 |
return 0; |
|
27c8985…
|
mistachkin
|
403 |
} |
|
27c8985…
|
mistachkin
|
404 |
|
|
27c8985…
|
mistachkin
|
405 |
void export_mark(FILE* f, int rid, char obj_type) |
|
27c8985…
|
mistachkin
|
406 |
{ |
|
27c8985…
|
mistachkin
|
407 |
unsigned int z = 0; |
|
27c8985…
|
mistachkin
|
408 |
char *zUuid = rid_to_uuid(rid); |
|
27c8985…
|
mistachkin
|
409 |
char *zMark; |
|
27c8985…
|
mistachkin
|
410 |
if( zUuid==NULL ){ |
|
27c8985…
|
mistachkin
|
411 |
fossil_trace("No uuid matching rid=%d when exporting marks\n", rid); |
|
27c8985…
|
mistachkin
|
412 |
return; |
|
27c8985…
|
mistachkin
|
413 |
} |
|
27c8985…
|
mistachkin
|
414 |
/* Since rid is already in the 'xmark' table, the value of z won't be |
|
27c8985…
|
mistachkin
|
415 |
** used, but pass in a valid pointer just to be safe. */ |
|
27c8985…
|
mistachkin
|
416 |
zMark = mark_name_from_rid(rid, &z); |
|
27c8985…
|
mistachkin
|
417 |
fprintf(f, "%c%d %s %s\n", obj_type, rid, zMark, zUuid); |
|
27c8985…
|
mistachkin
|
418 |
free(zMark); |
|
27c8985…
|
mistachkin
|
419 |
free(zUuid); |
|
b3acfa2…
|
drh
|
420 |
} |
|
b3acfa2…
|
drh
|
421 |
|
|
b3acfa2…
|
drh
|
422 |
/* |
|
b3acfa2…
|
drh
|
423 |
** If 'blobs' is non-null, it must point to a Bag of blob rids to be |
|
b3acfa2…
|
drh
|
424 |
** written to disk. Blob rids are written as 'b<rid>'. |
|
b3acfa2…
|
drh
|
425 |
** If 'vers' is non-null, it must point to a Bag of commit rids to be |
|
b3acfa2…
|
drh
|
426 |
** written to disk. Commit rids are written as 'c<rid> :<mark> <uuid>'. |
|
b3acfa2…
|
drh
|
427 |
** All commit (mark,rid,uuid) tuples are stored in 'xmark' table. |
|
b3acfa2…
|
drh
|
428 |
** This function does not fail, but may produce errors if a uuid cannot |
|
b3acfa2…
|
drh
|
429 |
** be found for an rid in 'vers'. |
|
b3acfa2…
|
drh
|
430 |
*/ |
|
b3acfa2…
|
drh
|
431 |
void export_marks(FILE* f, Bag *blobs, Bag *vers){ |
|
b3acfa2…
|
drh
|
432 |
int rid; |
|
27c8985…
|
mistachkin
|
433 |
|
|
b3acfa2…
|
drh
|
434 |
if( blobs!=NULL ){ |
|
b3acfa2…
|
drh
|
435 |
rid = bag_first(blobs); |
|
27c8985…
|
mistachkin
|
436 |
if( rid!=0 ){ |
|
b3acfa2…
|
drh
|
437 |
do{ |
|
27c8985…
|
mistachkin
|
438 |
export_mark(f, rid, 'b'); |
|
27c8985…
|
mistachkin
|
439 |
}while( (rid = bag_next(blobs, rid))!=0 ); |
|
b3acfa2…
|
drh
|
440 |
} |
|
b3acfa2…
|
drh
|
441 |
} |
|
b3acfa2…
|
drh
|
442 |
if( vers!=NULL ){ |
|
b3acfa2…
|
drh
|
443 |
rid = bag_first(vers); |
|
b3acfa2…
|
drh
|
444 |
if( rid!=0 ){ |
|
b3acfa2…
|
drh
|
445 |
do{ |
|
27c8985…
|
mistachkin
|
446 |
export_mark(f, rid, 'c'); |
|
b3acfa2…
|
drh
|
447 |
}while( (rid = bag_next(vers, rid))!=0 ); |
|
b3acfa2…
|
drh
|
448 |
} |
|
b3acfa2…
|
drh
|
449 |
} |
|
b3acfa2…
|
drh
|
450 |
} |
|
b3acfa2…
|
drh
|
451 |
|
|
7029594…
|
drh
|
452 |
/* This is the original header command (and hence documentation) for |
|
7029594…
|
drh
|
453 |
** the "fossil export" command: |
|
275da70…
|
danield
|
454 |
** |
|
2210be1…
|
drh
|
455 |
** Usage: %fossil export --git ?OPTIONS? ?REPOSITORY? |
|
0f024d3…
|
drh
|
456 |
** |
|
0f024d3…
|
drh
|
457 |
** Write an export of all check-ins to standard output. The export is |
|
1b53667…
|
drh
|
458 |
** written in the git-fast-export file format assuming the --git option is |
|
080ab8c…
|
jan.nijtmans
|
459 |
** provided. The git-fast-export format is currently the only VCS |
|
1b53667…
|
drh
|
460 |
** interchange format supported, though other formats may be added in |
|
1b53667…
|
drh
|
461 |
** the future. |
|
0f024d3…
|
drh
|
462 |
** |
|
bc36fdc…
|
danield
|
463 |
** Run this command within a check-out. Or use the -R or --repository |
|
0f024d3…
|
drh
|
464 |
** option to specify a Fossil repository to be exported. |
|
0f024d3…
|
drh
|
465 |
** |
|
080ab8c…
|
jan.nijtmans
|
466 |
** Only check-ins are exported using --git. Git does not support tickets |
|
2fd471d…
|
mistachkin
|
467 |
** or wiki or tech notes or attachments, so none of those are exported. |
|
c2ccd82…
|
joerg
|
468 |
** |
|
c2ccd82…
|
joerg
|
469 |
** If the "--import-marks FILE" option is used, it contains a list of |
|
c2ccd82…
|
joerg
|
470 |
** rids to skip. |
|
c2ccd82…
|
joerg
|
471 |
** |
|
c2ccd82…
|
joerg
|
472 |
** If the "--export-marks FILE" option is used, the rid of all commits and |
|
c2ccd82…
|
joerg
|
473 |
** blobs written on exit for use with "--import-marks" on the next run. |
|
2210be1…
|
drh
|
474 |
** |
|
2210be1…
|
drh
|
475 |
** Options: |
|
2f7c93f…
|
stephan
|
476 |
** --export-marks FILE Export rids of exported data to FILE |
|
2f7c93f…
|
stephan
|
477 |
** --import-marks FILE Read rids of data to ignore from FILE |
|
2f7c93f…
|
stephan
|
478 |
** --rename-trunk NAME Use NAME as name of exported trunk branch |
|
2f7c93f…
|
stephan
|
479 |
** -R|--repository REPO Export the given REPOSITORY |
|
080ab8c…
|
jan.nijtmans
|
480 |
** |
|
2210be1…
|
drh
|
481 |
** See also: import |
|
7029594…
|
drh
|
482 |
*/ |
|
7029594…
|
drh
|
483 |
/* |
|
7029594…
|
drh
|
484 |
** COMMAND: export* |
|
2f7c93f…
|
stephan
|
485 |
** |
|
b097e68…
|
drh
|
486 |
** Usage: %fossil export --git [REPOSITORY] |
|
b097e68…
|
drh
|
487 |
** |
|
7029594…
|
drh
|
488 |
** This command is deprecated. Use "fossil git export" instead. |
|
a9d1d46…
|
jan.nijtmans
|
489 |
*/ |
|
0f024d3…
|
drh
|
490 |
void export_cmd(void){ |
|
dd74352…
|
drh
|
491 |
Stmt q, q2, q3; |
|
0f024d3…
|
drh
|
492 |
Bag blobs, vers; |
|
27c8985…
|
mistachkin
|
493 |
unsigned int unused_mark = 1; |
|
c2ccd82…
|
joerg
|
494 |
const char *markfile_in; |
|
c2ccd82…
|
joerg
|
495 |
const char *markfile_out; |
|
ca0d66b…
|
danield
|
496 |
const char *zMainBranch = db_main_branch(); |
|
c2ccd82…
|
joerg
|
497 |
|
|
0f024d3…
|
drh
|
498 |
bag_init(&blobs); |
|
0f024d3…
|
drh
|
499 |
bag_init(&vers); |
|
0f024d3…
|
drh
|
500 |
|
|
1b53667…
|
drh
|
501 |
find_option("git", 0, 0); /* Ignore the --git option for now */ |
|
c2ccd82…
|
joerg
|
502 |
markfile_in = find_option("import-marks", 0, 1); |
|
c2ccd82…
|
joerg
|
503 |
markfile_out = find_option("export-marks", 0, 1); |
|
c2ccd82…
|
joerg
|
504 |
|
|
3bba2a6…
|
jan.nijtmans
|
505 |
if( !(gexport.zTrunkName = find_option("rename-trunk", 0, 1)) ){ |
|
3a6dd83…
|
drh
|
506 |
gexport.zTrunkName = fossil_strdup(zMainBranch); |
|
3bba2a6…
|
jan.nijtmans
|
507 |
} |
|
3bba2a6…
|
jan.nijtmans
|
508 |
|
|
1b53667…
|
drh
|
509 |
db_find_and_open_repository(0, 2); |
|
1b53667…
|
drh
|
510 |
verify_all_options(); |
|
1b53667…
|
drh
|
511 |
if( g.argc!=2 && g.argc!=3 ){ usage("--git ?REPOSITORY?"); } |
|
1b53667…
|
drh
|
512 |
|
|
dd74352…
|
drh
|
513 |
db_multi_exec("CREATE TEMPORARY TABLE oldblob(rid INTEGER PRIMARY KEY)"); |
|
dd74352…
|
drh
|
514 |
db_multi_exec("CREATE TEMPORARY TABLE oldcommit(rid INTEGER PRIMARY KEY)"); |
|
7029594…
|
drh
|
515 |
db_multi_exec("CREATE TEMP TABLE xmark(tname TEXT UNIQUE, trid INT," |
|
7029594…
|
drh
|
516 |
" tuuid TEXT)"); |
|
48d466a…
|
joerg
|
517 |
db_multi_exec("CREATE INDEX xmark_trid ON xmark(trid)"); |
|
c2ccd82…
|
joerg
|
518 |
if( markfile_in!=0 ){ |
|
c2ccd82…
|
joerg
|
519 |
Stmt qb,qc; |
|
c2ccd82…
|
joerg
|
520 |
FILE *f; |
|
b3acfa2…
|
drh
|
521 |
int rid; |
|
c2ccd82…
|
joerg
|
522 |
|
|
14733d1…
|
jan.nijtmans
|
523 |
f = fossil_fopen(markfile_in, "r"); |
|
c2ccd82…
|
joerg
|
524 |
if( f==0 ){ |
|
320f143…
|
drh
|
525 |
fossil_fatal("cannot open %s for reading", markfile_in); |
|
320f143…
|
drh
|
526 |
} |
|
27c8985…
|
mistachkin
|
527 |
if( import_marks(f, &blobs, &vers, &unused_mark)<0 ){ |
|
2c42b21…
|
andygoth
|
528 |
fossil_fatal("error importing marks from file: %s", markfile_in); |
|
b3acfa2…
|
drh
|
529 |
} |
|
dd74352…
|
drh
|
530 |
db_prepare(&qb, "INSERT OR IGNORE INTO oldblob VALUES (:rid)"); |
|
dd74352…
|
drh
|
531 |
db_prepare(&qc, "INSERT OR IGNORE INTO oldcommit VALUES (:rid)"); |
|
b3acfa2…
|
drh
|
532 |
rid = bag_first(&blobs); |
|
27c8985…
|
mistachkin
|
533 |
if( rid!=0 ){ |
|
b3acfa2…
|
drh
|
534 |
do{ |
|
b3acfa2…
|
drh
|
535 |
db_bind_int(&qb, ":rid", rid); |
|
c2ccd82…
|
joerg
|
536 |
db_step(&qb); |
|
c2ccd82…
|
joerg
|
537 |
db_reset(&qb); |
|
b3acfa2…
|
drh
|
538 |
}while((rid = bag_next(&blobs, rid))!=0); |
|
b3acfa2…
|
drh
|
539 |
} |
|
b3acfa2…
|
drh
|
540 |
rid = bag_first(&vers); |
|
27c8985…
|
mistachkin
|
541 |
if( rid!=0 ){ |
|
b3acfa2…
|
drh
|
542 |
do{ |
|
b3acfa2…
|
drh
|
543 |
db_bind_int(&qc, ":rid", rid); |
|
c2ccd82…
|
joerg
|
544 |
db_step(&qc); |
|
c2ccd82…
|
joerg
|
545 |
db_reset(&qc); |
|
b3acfa2…
|
drh
|
546 |
}while((rid = bag_next(&vers, rid))!=0); |
|
c2ccd82…
|
joerg
|
547 |
} |
|
c2ccd82…
|
joerg
|
548 |
db_finalize(&qb); |
|
c2ccd82…
|
joerg
|
549 |
db_finalize(&qc); |
|
c2ccd82…
|
joerg
|
550 |
fclose(f); |
|
c2ccd82…
|
joerg
|
551 |
} |
|
c2ccd82…
|
joerg
|
552 |
|
|
0f024d3…
|
drh
|
553 |
/* Step 1: Generate "blob" records for every artifact that is part |
|
080ab8c…
|
jan.nijtmans
|
554 |
** of a check-in |
|
0f024d3…
|
drh
|
555 |
*/ |
|
b408ece…
|
drh
|
556 |
fossil_binary_mode(stdout); |
|
c3bfb00…
|
drh
|
557 |
db_multi_exec("CREATE TEMP TABLE newblob(rid INTEGER KEY, srcid INTEGER)"); |
|
dd74352…
|
drh
|
558 |
db_multi_exec("CREATE INDEX newblob_src ON newblob(srcid)"); |
|
dd74352…
|
drh
|
559 |
db_multi_exec( |
|
dd74352…
|
drh
|
560 |
"INSERT INTO newblob" |
|
dd74352…
|
drh
|
561 |
" SELECT DISTINCT fid," |
|
c3bfb00…
|
drh
|
562 |
" CASE WHEN EXISTS(SELECT 1 FROM delta" |
|
c3bfb00…
|
drh
|
563 |
" WHERE rid=fid" |
|
c3bfb00…
|
drh
|
564 |
" AND NOT EXISTS(SELECT 1 FROM oldblob" |
|
c3bfb00…
|
drh
|
565 |
" WHERE srcid=fid))" |
|
dd74352…
|
drh
|
566 |
" THEN (SELECT srcid FROM delta WHERE rid=fid)" |
|
dd74352…
|
drh
|
567 |
" ELSE 0" |
|
dd74352…
|
drh
|
568 |
" END" |
|
dd74352…
|
drh
|
569 |
" FROM mlink" |
|
dd74352…
|
drh
|
570 |
" WHERE fid>0 AND NOT EXISTS(SELECT 1 FROM oldblob WHERE rid=fid)"); |
|
c2ccd82…
|
joerg
|
571 |
db_prepare(&q, |
|
c2ccd82…
|
joerg
|
572 |
"SELECT DISTINCT fid FROM mlink" |
|
c2ccd82…
|
joerg
|
573 |
" WHERE fid>0 AND NOT EXISTS(SELECT 1 FROM oldblob WHERE rid=fid)"); |
|
c2ccd82…
|
joerg
|
574 |
db_prepare(&q2, "INSERT INTO oldblob VALUES (:rid)"); |
|
dd74352…
|
drh
|
575 |
db_prepare(&q3, "SELECT rid FROM newblob WHERE srcid= (:srcid)"); |
|
0f024d3…
|
drh
|
576 |
while( db_step(&q)==SQLITE_ROW ){ |
|
0f024d3…
|
drh
|
577 |
int rid = db_column_int(&q, 0); |
|
0f024d3…
|
drh
|
578 |
Blob content; |
|
dd74352…
|
drh
|
579 |
|
|
dd74352…
|
drh
|
580 |
while( !bag_find(&blobs, rid) ){ |
|
27c8985…
|
mistachkin
|
581 |
char *zMark; |
|
dd74352…
|
drh
|
582 |
content_get(rid, &content); |
|
dd74352…
|
drh
|
583 |
db_bind_int(&q2, ":rid", rid); |
|
dd74352…
|
drh
|
584 |
db_step(&q2); |
|
dd74352…
|
drh
|
585 |
db_reset(&q2); |
|
27c8985…
|
mistachkin
|
586 |
zMark = mark_name_from_rid(rid, &unused_mark); |
|
27c8985…
|
mistachkin
|
587 |
printf("blob\nmark %s\ndata %d\n", zMark, blob_size(&content)); |
|
27c8985…
|
mistachkin
|
588 |
free(zMark); |
|
dd74352…
|
drh
|
589 |
bag_insert(&blobs, rid); |
|
dd74352…
|
drh
|
590 |
fwrite(blob_buffer(&content), 1, blob_size(&content), stdout); |
|
dd74352…
|
drh
|
591 |
printf("\n"); |
|
dd74352…
|
drh
|
592 |
blob_reset(&content); |
|
dd74352…
|
drh
|
593 |
|
|
dd74352…
|
drh
|
594 |
db_bind_int(&q3, ":srcid", rid); |
|
dd74352…
|
drh
|
595 |
if( db_step(&q3) != SQLITE_ROW ){ |
|
dd74352…
|
drh
|
596 |
db_reset(&q3); |
|
dd74352…
|
drh
|
597 |
break; |
|
dd74352…
|
drh
|
598 |
} |
|
dd74352…
|
drh
|
599 |
rid = db_column_int(&q3, 0); |
|
dd74352…
|
drh
|
600 |
db_reset(&q3); |
|
dd74352…
|
drh
|
601 |
} |
|
0f024d3…
|
drh
|
602 |
} |
|
0f024d3…
|
drh
|
603 |
db_finalize(&q); |
|
c2ccd82…
|
joerg
|
604 |
db_finalize(&q2); |
|
dd74352…
|
drh
|
605 |
db_finalize(&q3); |
|
0f024d3…
|
drh
|
606 |
|
|
0f024d3…
|
drh
|
607 |
/* Output the commit records. |
|
0f024d3…
|
drh
|
608 |
*/ |
|
c0a3e9f…
|
drh
|
609 |
topological_sort_checkins(0); |
|
0f024d3…
|
drh
|
610 |
db_prepare(&q, |
|
83e4f86…
|
drh
|
611 |
"SELECT strftime('%%s',mtime), objid, coalesce(ecomment,comment)," |
|
83e4f86…
|
drh
|
612 |
" coalesce(euser,user)," |
|
0f024d3…
|
drh
|
613 |
" (SELECT value FROM tagxref WHERE rid=objid AND tagid=%d)" |
|
c0a3e9f…
|
drh
|
614 |
" FROM toponode, event" |
|
c0a3e9f…
|
drh
|
615 |
" WHERE toponode.tid=event.objid" |
|
c0a3e9f…
|
drh
|
616 |
" AND event.type='ci'" |
|
c0a3e9f…
|
drh
|
617 |
" AND NOT EXISTS (SELECT 1 FROM oldcommit WHERE toponode.tid=rid)" |
|
c0a3e9f…
|
drh
|
618 |
" ORDER BY toponode.tseq ASC", |
|
0f024d3…
|
drh
|
619 |
TAG_BRANCH |
|
0f024d3…
|
drh
|
620 |
); |
|
c2ccd82…
|
joerg
|
621 |
db_prepare(&q2, "INSERT INTO oldcommit VALUES (:rid)"); |
|
0f024d3…
|
drh
|
622 |
while( db_step(&q)==SQLITE_ROW ){ |
|
dd74352…
|
drh
|
623 |
Stmt q4; |
|
be467e9…
|
drh
|
624 |
const char *zSecondsSince1970 = db_column_text(&q, 0); |
|
0f024d3…
|
drh
|
625 |
int ckinId = db_column_int(&q, 1); |
|
0f024d3…
|
drh
|
626 |
const char *zComment = db_column_text(&q, 2); |
|
0f024d3…
|
drh
|
627 |
const char *zUser = db_column_text(&q, 3); |
|
0f024d3…
|
drh
|
628 |
const char *zBranch = db_column_text(&q, 4); |
|
b3acfa2…
|
drh
|
629 |
char *zMark; |
|
0f024d3…
|
drh
|
630 |
|
|
0f024d3…
|
drh
|
631 |
bag_insert(&vers, ckinId); |
|
c2ccd82…
|
joerg
|
632 |
db_bind_int(&q2, ":rid", ckinId); |
|
c2ccd82…
|
joerg
|
633 |
db_step(&q2); |
|
c2ccd82…
|
joerg
|
634 |
db_reset(&q2); |
|
3a6dd83…
|
drh
|
635 |
if( zBranch==0 || fossil_strcmp(zBranch, zMainBranch)==0 ){ |
|
c0a3e9f…
|
drh
|
636 |
zBranch = gexport.zTrunkName; |
|
c0a3e9f…
|
drh
|
637 |
} |
|
27c8985…
|
mistachkin
|
638 |
zMark = mark_name_from_rid(ckinId, &unused_mark); |
|
3bba2a6…
|
jan.nijtmans
|
639 |
printf("commit refs/heads/"); |
|
3bba2a6…
|
jan.nijtmans
|
640 |
print_ref(zBranch); |
|
3bba2a6…
|
jan.nijtmans
|
641 |
printf("\nmark %s\n", zMark); |
|
27c8985…
|
mistachkin
|
642 |
free(zMark); |
|
0f024d3…
|
drh
|
643 |
printf("committer"); |
|
0f024d3…
|
drh
|
644 |
print_person(zUser); |
|
be467e9…
|
drh
|
645 |
printf(" %s +0000\n", zSecondsSince1970); |
|
0f024d3…
|
drh
|
646 |
if( zComment==0 ) zComment = "null comment"; |
|
5fa4dfc…
|
drh
|
647 |
printf("data %d\n%s\n", (int)strlen(zComment), zComment); |
|
c3bfb00…
|
drh
|
648 |
db_prepare(&q3, |
|
c3bfb00…
|
drh
|
649 |
"SELECT pid FROM plink" |
|
c3bfb00…
|
drh
|
650 |
" WHERE cid=%d AND isprim" |
|
c3bfb00…
|
drh
|
651 |
" AND pid IN (SELECT objid FROM event)", |
|
c3bfb00…
|
drh
|
652 |
ckinId |
|
c3bfb00…
|
drh
|
653 |
); |
|
fca3073…
|
joerg
|
654 |
if( db_step(&q3) == SQLITE_ROW ){ |
|
b3acfa2…
|
drh
|
655 |
int pid = db_column_int(&q3, 0); |
|
27c8985…
|
mistachkin
|
656 |
zMark = mark_name_from_rid(pid, &unused_mark); |
|
b3acfa2…
|
drh
|
657 |
printf("from %s\n", zMark); |
|
b3acfa2…
|
drh
|
658 |
free(zMark); |
|
c2ccd82…
|
joerg
|
659 |
db_prepare(&q4, |
|
bc8d368…
|
joerg
|
660 |
"SELECT pid FROM plink" |
|
bc8d368…
|
joerg
|
661 |
" WHERE cid=%d AND NOT isprim" |
|
b71b32d…
|
joerg
|
662 |
" AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=pid)" |
|
bc8d368…
|
joerg
|
663 |
" ORDER BY pid", |
|
bc8d368…
|
joerg
|
664 |
ckinId); |
|
c2ccd82…
|
joerg
|
665 |
while( db_step(&q4)==SQLITE_ROW ){ |
|
27c8985…
|
mistachkin
|
666 |
zMark = mark_name_from_rid(db_column_int(&q4, 0), &unused_mark); |
|
b3acfa2…
|
drh
|
667 |
printf("merge %s\n", zMark); |
|
b3acfa2…
|
drh
|
668 |
free(zMark); |
|
fca3073…
|
joerg
|
669 |
} |
|
fca3073…
|
joerg
|
670 |
db_finalize(&q4); |
|
fca3073…
|
joerg
|
671 |
}else{ |
|
fca3073…
|
joerg
|
672 |
printf("deleteall\n"); |
|
fca3073…
|
joerg
|
673 |
} |
|
fca3073…
|
joerg
|
674 |
|
|
fca3073…
|
joerg
|
675 |
db_prepare(&q4, |
|
fca3073…
|
joerg
|
676 |
"SELECT filename.name, mlink.fid, mlink.mperm FROM mlink" |
|
fca3073…
|
joerg
|
677 |
" JOIN filename ON filename.fnid=mlink.fnid" |
|
fca3073…
|
joerg
|
678 |
" WHERE mlink.mid=%d", |
|
dd74352…
|
drh
|
679 |
ckinId |
|
fca3073…
|
joerg
|
680 |
); |
|
fca3073…
|
joerg
|
681 |
while( db_step(&q4)==SQLITE_ROW ){ |
|
fca3073…
|
joerg
|
682 |
const char *zName = db_column_text(&q4,0); |
|
fca3073…
|
joerg
|
683 |
int zNew = db_column_int(&q4,1); |
|
fca3073…
|
joerg
|
684 |
int mPerm = db_column_int(&q4,2); |
|
27c8985…
|
mistachkin
|
685 |
if( zNew==0 ){ |
|
fca3073…
|
joerg
|
686 |
printf("D %s\n", zName); |
|
27c8985…
|
mistachkin
|
687 |
}else if( bag_find(&blobs, zNew) ){ |
|
e4f1c1f…
|
drh
|
688 |
const char *zPerm; |
|
2bb3a8e…
|
mistachkin
|
689 |
zMark = mark_name_from_rid(zNew, &unused_mark); |
|
e4f1c1f…
|
drh
|
690 |
switch( mPerm ){ |
|
e4f1c1f…
|
drh
|
691 |
case PERM_LNK: zPerm = "120000"; break; |
|
e4f1c1f…
|
drh
|
692 |
case PERM_EXE: zPerm = "100755"; break; |
|
e4f1c1f…
|
drh
|
693 |
default: zPerm = "100644"; break; |
|
e4f1c1f…
|
drh
|
694 |
} |
|
27c8985…
|
mistachkin
|
695 |
printf("M %s %s %s\n", zPerm, zMark, zName); |
|
27c8985…
|
mistachkin
|
696 |
free(zMark); |
|
e4f1c1f…
|
drh
|
697 |
} |
|
fca3073…
|
joerg
|
698 |
} |
|
fca3073…
|
joerg
|
699 |
db_finalize(&q4); |
|
c2ccd82…
|
joerg
|
700 |
db_finalize(&q3); |
|
0f024d3…
|
drh
|
701 |
printf("\n"); |
|
0f024d3…
|
drh
|
702 |
} |
|
c2ccd82…
|
joerg
|
703 |
db_finalize(&q2); |
|
06bc2fb…
|
drh
|
704 |
db_finalize(&q); |
|
7aaa2aa…
|
drh
|
705 |
manifest_cache_clear(); |
|
06bc2fb…
|
drh
|
706 |
|
|
06bc2fb…
|
drh
|
707 |
|
|
06bc2fb…
|
drh
|
708 |
/* Output tags */ |
|
06bc2fb…
|
drh
|
709 |
db_prepare(&q, |
|
3bba2a6…
|
jan.nijtmans
|
710 |
"SELECT tagname, rid, strftime('%%s',mtime)," |
|
3bba2a6…
|
jan.nijtmans
|
711 |
" (SELECT coalesce(euser, user) FROM event WHERE objid=rid)," |
|
3bba2a6…
|
jan.nijtmans
|
712 |
" value" |
|
06bc2fb…
|
drh
|
713 |
" FROM tagxref JOIN tag USING(tagid)" |
|
06bc2fb…
|
drh
|
714 |
" WHERE tagtype=1 AND tagname GLOB 'sym-*'" |
|
06bc2fb…
|
drh
|
715 |
); |
|
06bc2fb…
|
drh
|
716 |
while( db_step(&q)==SQLITE_ROW ){ |
|
06bc2fb…
|
drh
|
717 |
const char *zTagname = db_column_text(&q, 0); |
|
06bc2fb…
|
drh
|
718 |
int rid = db_column_int(&q, 1); |
|
27c8985…
|
mistachkin
|
719 |
char *zMark = mark_name_from_rid(rid, &unused_mark); |
|
be467e9…
|
drh
|
720 |
const char *zSecSince1970 = db_column_text(&q, 2); |
|
3bba2a6…
|
jan.nijtmans
|
721 |
const char *zUser = db_column_text(&q, 3); |
|
3bba2a6…
|
jan.nijtmans
|
722 |
const char *zValue = db_column_text(&q, 4); |
|
06bc2fb…
|
drh
|
723 |
if( rid==0 || !bag_find(&vers, rid) ) continue; |
|
06bc2fb…
|
drh
|
724 |
zTagname += 4; |
|
3bba2a6…
|
jan.nijtmans
|
725 |
printf("tag "); |
|
3bba2a6…
|
jan.nijtmans
|
726 |
print_ref(zTagname); |
|
3bba2a6…
|
jan.nijtmans
|
727 |
printf("\nfrom %s\n", zMark); |
|
27c8985…
|
mistachkin
|
728 |
free(zMark); |
|
3bba2a6…
|
jan.nijtmans
|
729 |
printf("tagger"); |
|
3bba2a6…
|
jan.nijtmans
|
730 |
print_person(zUser); |
|
3bba2a6…
|
jan.nijtmans
|
731 |
printf(" %s +0000\n", zSecSince1970); |
|
a9d1d46…
|
jan.nijtmans
|
732 |
printf("data %d\n", zValue==NULL?0:(int)strlen(zValue)+1); |
|
3bba2a6…
|
jan.nijtmans
|
733 |
if( zValue!=NULL ) printf("%s\n",zValue); |
|
c2ccd82…
|
joerg
|
734 |
} |
|
c2ccd82…
|
joerg
|
735 |
db_finalize(&q); |
|
c2ccd82…
|
joerg
|
736 |
|
|
c2ccd82…
|
joerg
|
737 |
if( markfile_out!=0 ){ |
|
c2ccd82…
|
joerg
|
738 |
FILE *f; |
|
14733d1…
|
jan.nijtmans
|
739 |
f = fossil_fopen(markfile_out, "w"); |
|
c2ccd82…
|
joerg
|
740 |
if( f == 0 ){ |
|
320f143…
|
drh
|
741 |
fossil_fatal("cannot open %s for writing", markfile_out); |
|
320f143…
|
drh
|
742 |
} |
|
b3acfa2…
|
drh
|
743 |
export_marks(f, &blobs, &vers); |
|
27c8985…
|
mistachkin
|
744 |
if( ferror(f)!=0 || fclose(f)!=0 ){ |
|
320f143…
|
drh
|
745 |
fossil_fatal("error while writing %s", markfile_out); |
|
320f143…
|
drh
|
746 |
} |
|
b3acfa2…
|
drh
|
747 |
} |
|
b3acfa2…
|
drh
|
748 |
bag_clear(&blobs); |
|
b3acfa2…
|
drh
|
749 |
bag_clear(&vers); |
|
c0a3e9f…
|
drh
|
750 |
} |
|
c0a3e9f…
|
drh
|
751 |
|
|
c0a3e9f…
|
drh
|
752 |
/* |
|
c0a3e9f…
|
drh
|
753 |
** Construct the temporary table toposort as follows: |
|
c0a3e9f…
|
drh
|
754 |
** |
|
c0a3e9f…
|
drh
|
755 |
** CREATE TEMP TABLE toponode( |
|
c0a3e9f…
|
drh
|
756 |
** tid INTEGER PRIMARY KEY, -- Check-in id |
|
c0a3e9f…
|
drh
|
757 |
** tseq INT -- integer total order on check-ins. |
|
c0a3e9f…
|
drh
|
758 |
** ); |
|
c0a3e9f…
|
drh
|
759 |
** |
|
c0a3e9f…
|
drh
|
760 |
** This table contains all check-ins of the repository in topological |
|
c0a3e9f…
|
drh
|
761 |
** order. "Topological order" means that every parent check-in comes |
|
c0a3e9f…
|
drh
|
762 |
** before all of its children. Topological order is *almost* the same |
|
7029594…
|
drh
|
763 |
** thing as "ORDER BY event.mtime". Differences only arise when there |
|
e2bdc10…
|
danield
|
764 |
** are timewarps. Inasmuch as Git hates timewarps, we have to compute |
|
c0a3e9f…
|
drh
|
765 |
** a correct topological order when doing an export. |
|
c0a3e9f…
|
drh
|
766 |
** |
|
c0a3e9f…
|
drh
|
767 |
** Since mtime is a usually already nearly in topological order, the |
|
c0a3e9f…
|
drh
|
768 |
** algorithm is to start with mtime, then make adjustments as necessary |
|
c0a3e9f…
|
drh
|
769 |
** for timewarps. This is not a great algorithm for the general case, |
|
c0a3e9f…
|
drh
|
770 |
** but it is very fast for the overwhelmingly common case where there |
|
c0a3e9f…
|
drh
|
771 |
** are few timewarps. |
|
c0a3e9f…
|
drh
|
772 |
*/ |
|
c0a3e9f…
|
drh
|
773 |
int topological_sort_checkins(int bVerbose){ |
|
c0a3e9f…
|
drh
|
774 |
int nChange = 0; |
|
c0a3e9f…
|
drh
|
775 |
Stmt q1; |
|
c0a3e9f…
|
drh
|
776 |
Stmt chng; |
|
c0a3e9f…
|
drh
|
777 |
db_multi_exec( |
|
c0a3e9f…
|
drh
|
778 |
"CREATE TEMP TABLE toponode(\n" |
|
c0a3e9f…
|
drh
|
779 |
" tid INTEGER PRIMARY KEY,\n" |
|
c0a3e9f…
|
drh
|
780 |
" tseq INT\n" |
|
c0a3e9f…
|
drh
|
781 |
");\n" |
|
c0a3e9f…
|
drh
|
782 |
"INSERT INTO toponode(tid,tseq) " |
|
c0a3e9f…
|
drh
|
783 |
" SELECT objid, CAST(mtime*8640000 AS int) FROM event WHERE type='ci';\n" |
|
c0a3e9f…
|
drh
|
784 |
"CREATE TEMP TABLE topolink(\n" |
|
c0a3e9f…
|
drh
|
785 |
" tparent INT,\n" |
|
c0a3e9f…
|
drh
|
786 |
" tchild INT,\n" |
|
c0a3e9f…
|
drh
|
787 |
" PRIMARY KEY(tparent,tchild)\n" |
|
c0a3e9f…
|
drh
|
788 |
") WITHOUT ROWID;" |
|
c0a3e9f…
|
drh
|
789 |
"INSERT INTO topolink(tparent,tchild)" |
|
c0a3e9f…
|
drh
|
790 |
" SELECT pid, cid FROM plink;\n" |
|
c0a3e9f…
|
drh
|
791 |
"CREATE INDEX topolink_child ON topolink(tchild);\n" |
|
c0a3e9f…
|
drh
|
792 |
); |
|
c0a3e9f…
|
drh
|
793 |
|
|
c0a3e9f…
|
drh
|
794 |
/* Find a timewarp instance */ |
|
c0a3e9f…
|
drh
|
795 |
db_prepare(&q1, |
|
961787d…
|
jan.nijtmans
|
796 |
"SELECT P.tseq, C.tid, C.tseq\n" |
|
c0a3e9f…
|
drh
|
797 |
" FROM toponode P, toponode C, topolink X\n" |
|
c0a3e9f…
|
drh
|
798 |
" WHERE X.tparent=P.tid\n" |
|
c0a3e9f…
|
drh
|
799 |
" AND X.tchild=C.tid\n" |
|
c0a3e9f…
|
drh
|
800 |
" AND P.tseq>=C.tseq;" |
|
c0a3e9f…
|
drh
|
801 |
); |
|
c0a3e9f…
|
drh
|
802 |
|
|
c0a3e9f…
|
drh
|
803 |
/* Update the timestamp on :tid to have value :tseq */ |
|
c0a3e9f…
|
drh
|
804 |
db_prepare(&chng, |
|
c0a3e9f…
|
drh
|
805 |
"UPDATE toponode SET tseq=:tseq WHERE tid=:tid" |
|
c0a3e9f…
|
drh
|
806 |
); |
|
c0a3e9f…
|
drh
|
807 |
|
|
c0a3e9f…
|
drh
|
808 |
while( db_step(&q1)==SQLITE_ROW ){ |
|
961787d…
|
jan.nijtmans
|
809 |
i64 iParentTime = db_column_int64(&q1, 0); |
|
961787d…
|
jan.nijtmans
|
810 |
int iChild = db_column_int(&q1, 1); |
|
961787d…
|
jan.nijtmans
|
811 |
i64 iChildTime = db_column_int64(&q1, 2); |
|
c0a3e9f…
|
drh
|
812 |
nChange++; |
|
c0a3e9f…
|
drh
|
813 |
if( nChange>10000 ){ |
|
c0a3e9f…
|
drh
|
814 |
fossil_fatal("failed to fix all timewarps after 100000 attempts"); |
|
c0a3e9f…
|
drh
|
815 |
} |
|
c0a3e9f…
|
drh
|
816 |
db_reset(&q1); |
|
c0a3e9f…
|
drh
|
817 |
db_bind_int64(&chng, ":tid", iChild); |
|
c0a3e9f…
|
drh
|
818 |
db_bind_int64(&chng, ":tseq", iParentTime+1); |
|
c0a3e9f…
|
drh
|
819 |
db_step(&chng); |
|
c0a3e9f…
|
drh
|
820 |
db_reset(&chng); |
|
c0a3e9f…
|
drh
|
821 |
if( bVerbose ){ |
|
c0a3e9f…
|
drh
|
822 |
fossil_print("moving %d from %lld to %lld\n", |
|
c0a3e9f…
|
drh
|
823 |
iChild, iChildTime, iParentTime+1); |
|
c0a3e9f…
|
drh
|
824 |
} |
|
c0a3e9f…
|
drh
|
825 |
} |
|
c0a3e9f…
|
drh
|
826 |
|
|
c0a3e9f…
|
drh
|
827 |
db_finalize(&q1); |
|
c0a3e9f…
|
drh
|
828 |
db_finalize(&chng); |
|
c0a3e9f…
|
drh
|
829 |
return nChange; |
|
c0a3e9f…
|
drh
|
830 |
} |
|
c0a3e9f…
|
drh
|
831 |
|
|
c0a3e9f…
|
drh
|
832 |
/* |
|
c0a3e9f…
|
drh
|
833 |
** COMMAND: test-topological-sort |
|
c0a3e9f…
|
drh
|
834 |
** |
|
c0a3e9f…
|
drh
|
835 |
** Invoke the topological_sort_checkins() interface for testing |
|
c0a3e9f…
|
drh
|
836 |
** purposes. |
|
c0a3e9f…
|
drh
|
837 |
*/ |
|
c0a3e9f…
|
drh
|
838 |
void test_topological_sort(void){ |
|
c0a3e9f…
|
drh
|
839 |
int n; |
|
c0a3e9f…
|
drh
|
840 |
db_find_and_open_repository(0, 0); |
|
c0a3e9f…
|
drh
|
841 |
n = topological_sort_checkins(1); |
|
c0a3e9f…
|
drh
|
842 |
fossil_print("%d reorderings required\n", n); |
|
7029594…
|
drh
|
843 |
} |
|
7029594…
|
drh
|
844 |
|
|
7029594…
|
drh
|
845 |
/*************************************************************************** |
|
7029594…
|
drh
|
846 |
** Implementation of the "fossil git" command follows. We hope that the |
|
7029594…
|
drh
|
847 |
** new code that follows will largely replace the legacy "fossil export" |
|
7029594…
|
drh
|
848 |
** and "fossil import" code above. |
|
7029594…
|
drh
|
849 |
*/ |
|
7029594…
|
drh
|
850 |
|
|
de0bbcb…
|
drh
|
851 |
/* Verbosity level. Higher means more output. |
|
de0bbcb…
|
drh
|
852 |
** |
|
de0bbcb…
|
drh
|
853 |
** 0 print nothing at all |
|
de0bbcb…
|
drh
|
854 |
** 1 Errors only |
|
de0bbcb…
|
drh
|
855 |
** 2 Progress information (This is the default) |
|
de0bbcb…
|
drh
|
856 |
** 3 Extra details |
|
de0bbcb…
|
drh
|
857 |
*/ |
|
de0bbcb…
|
drh
|
858 |
#define VERB_ERROR 1 |
|
de0bbcb…
|
drh
|
859 |
#define VERB_NORMAL 2 |
|
de0bbcb…
|
drh
|
860 |
#define VERB_EXTRA 3 |
|
de0bbcb…
|
drh
|
861 |
static int gitmirror_verbosity = VERB_NORMAL; |
|
4c384ba…
|
drh
|
862 |
|
|
3a6dd83…
|
drh
|
863 |
/* The main branch in the Git repository. The main branch of the |
|
3a6dd83…
|
drh
|
864 |
** Fossil repository (usually "trunk") is renamed to be this branch name. |
|
4c384ba…
|
drh
|
865 |
*/ |
|
dbcf66b…
|
drh
|
866 |
static const char *gitmirror_mainbranch = 0; |
|
c9a592d…
|
drh
|
867 |
|
|
de0bbcb…
|
drh
|
868 |
/* |
|
de0bbcb…
|
drh
|
869 |
** Output routine that depends on verbosity |
|
de0bbcb…
|
drh
|
870 |
*/ |
|
de0bbcb…
|
drh
|
871 |
static void gitmirror_message(int iLevel, const char *zFormat, ...){ |
|
de0bbcb…
|
drh
|
872 |
va_list ap; |
|
de0bbcb…
|
drh
|
873 |
if( iLevel>gitmirror_verbosity ) return; |
|
de0bbcb…
|
drh
|
874 |
va_start(ap, zFormat); |
|
de0bbcb…
|
drh
|
875 |
fossil_vprint(zFormat, ap); |
|
de0bbcb…
|
drh
|
876 |
va_end(ap); |
|
de0bbcb…
|
drh
|
877 |
} |
|
de0bbcb…
|
drh
|
878 |
|
|
7029594…
|
drh
|
879 |
/* |
|
7029594…
|
drh
|
880 |
** Convert characters of z[] that are not allowed to be in branch or |
|
7029594…
|
drh
|
881 |
** tag names into "_". |
|
7029594…
|
drh
|
882 |
*/ |
|
7029594…
|
drh
|
883 |
static void gitmirror_sanitize_name(char *z){ |
|
7029594…
|
drh
|
884 |
static unsigned char aSafe[] = { |
|
7029594…
|
drh
|
885 |
/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */ |
|
7029594…
|
drh
|
886 |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x */ |
|
7029594…
|
drh
|
887 |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1x */ |
|
c9a592d…
|
drh
|
888 |
0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, /* 2x */ |
|
c9a592d…
|
drh
|
889 |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, /* 3x */ |
|
7029594…
|
drh
|
890 |
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */ |
|
7029594…
|
drh
|
891 |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, /* 5x */ |
|
c9a592d…
|
drh
|
892 |
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */ |
|
7029594…
|
drh
|
893 |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, /* 7x */ |
|
7029594…
|
drh
|
894 |
}; |
|
7029594…
|
drh
|
895 |
unsigned char *zu = (unsigned char*)z; |
|
7029594…
|
drh
|
896 |
int i; |
|
7029594…
|
drh
|
897 |
for(i=0; zu[i]; i++){ |
|
7029594…
|
drh
|
898 |
if( zu[i]>0x7f || !aSafe[zu[i]] ){ |
|
7029594…
|
drh
|
899 |
zu[i] = '_'; |
|
7029594…
|
drh
|
900 |
}else if( zu[i]=='/' && (i==0 || zu[i+1]==0 || zu[i+1]=='/') ){ |
|
7029594…
|
drh
|
901 |
zu[i] = '_'; |
|
7029594…
|
drh
|
902 |
}else if( zu[i]=='.' && (zu[i+1]==0 || zu[i+1]=='.' |
|
7029594…
|
drh
|
903 |
|| (i>0 && zu[i-1]=='.')) ){ |
|
7029594…
|
drh
|
904 |
zu[i] = '_'; |
|
7029594…
|
drh
|
905 |
} |
|
7029594…
|
drh
|
906 |
} |
|
c9a592d…
|
drh
|
907 |
} |
|
c9a592d…
|
drh
|
908 |
|
|
c9a592d…
|
drh
|
909 |
/* |
|
c9a592d…
|
drh
|
910 |
** COMMAND: test-sanitize-name |
|
c9a592d…
|
drh
|
911 |
** |
|
c9a592d…
|
drh
|
912 |
** Usage: %fossil ARG... |
|
c9a592d…
|
drh
|
913 |
** |
|
c9a592d…
|
drh
|
914 |
** This sanitizes each argument and make it part of an "echo" command |
|
c9a592d…
|
drh
|
915 |
** run by the shell. |
|
c9a592d…
|
drh
|
916 |
*/ |
|
c9a592d…
|
drh
|
917 |
void test_sanitize_name_cmd(void){ |
|
c9a592d…
|
drh
|
918 |
sqlite3_str *pStr; |
|
c9a592d…
|
drh
|
919 |
int i; |
|
c9a592d…
|
drh
|
920 |
char *zCmd; |
|
c9a592d…
|
drh
|
921 |
pStr = sqlite3_str_new(0); |
|
c9a592d…
|
drh
|
922 |
sqlite3_str_appendall(pStr, "echo"); |
|
c9a592d…
|
drh
|
923 |
for(i=2; i<g.argc; i++){ |
|
c9a592d…
|
drh
|
924 |
char *z = fossil_strdup(g.argv[i]); |
|
c9a592d…
|
drh
|
925 |
gitmirror_sanitize_name(z); |
|
c9a592d…
|
drh
|
926 |
sqlite3_str_appendf(pStr, " \"%s\"", z); |
|
c9a592d…
|
drh
|
927 |
fossil_free(z); |
|
c9a592d…
|
drh
|
928 |
} |
|
c9a592d…
|
drh
|
929 |
zCmd = sqlite3_str_finish(pStr); |
|
c9a592d…
|
drh
|
930 |
fossil_print("Command: %s\n", zCmd); |
|
c9a592d…
|
drh
|
931 |
fossil_system(zCmd); |
|
c9a592d…
|
drh
|
932 |
sqlite3_free(zCmd); |
|
7029594…
|
drh
|
933 |
} |
|
7029594…
|
drh
|
934 |
|
|
7029594…
|
drh
|
935 |
/* |
|
7029594…
|
drh
|
936 |
** Quote a filename as a C-style string using \\ and \" if necessary. |
|
7029594…
|
drh
|
937 |
** If quoting is not necessary, just return a copy of the input string. |
|
7029594…
|
drh
|
938 |
** |
|
7029594…
|
drh
|
939 |
** The return value is a held in memory obtained from fossil_malloc() |
|
7029594…
|
drh
|
940 |
** and must be freed by the caller. |
|
7029594…
|
drh
|
941 |
*/ |
|
7029594…
|
drh
|
942 |
static char *gitmirror_quote_filename_if_needed(const char *zIn){ |
|
7029594…
|
drh
|
943 |
int i, j; |
|
7029594…
|
drh
|
944 |
char c; |
|
7029594…
|
drh
|
945 |
int nSpecial = 0; |
|
7029594…
|
drh
|
946 |
char *zOut; |
|
7029594…
|
drh
|
947 |
for(i=0; (c = zIn[i])!=0; i++){ |
|
7029594…
|
drh
|
948 |
if( c=='\\' || c=='"' || c=='\n' ){ |
|
7029594…
|
drh
|
949 |
nSpecial++; |
|
7029594…
|
drh
|
950 |
} |
|
7029594…
|
drh
|
951 |
} |
|
7029594…
|
drh
|
952 |
if( nSpecial==0 ){ |
|
7029594…
|
drh
|
953 |
return fossil_strdup(zIn); |
|
7029594…
|
drh
|
954 |
} |
|
7029594…
|
drh
|
955 |
zOut = fossil_malloc( i+nSpecial+3 ); |
|
7029594…
|
drh
|
956 |
zOut[0] = '"'; |
|
7029594…
|
drh
|
957 |
for(i=0, j=1; (c = zIn[i])!=0; i++){ |
|
7029594…
|
drh
|
958 |
if( c=='\\' || c=='"' || c=='\n' ){ |
|
7029594…
|
drh
|
959 |
zOut[j++] = '\\'; |
|
7029594…
|
drh
|
960 |
if( c=='\n' ){ |
|
7029594…
|
drh
|
961 |
zOut[j++] = 'n'; |
|
7029594…
|
drh
|
962 |
}else{ |
|
7029594…
|
drh
|
963 |
zOut[j++] = c; |
|
7029594…
|
drh
|
964 |
} |
|
7029594…
|
drh
|
965 |
}else{ |
|
7029594…
|
drh
|
966 |
zOut[j++] = c; |
|
7029594…
|
drh
|
967 |
} |
|
7029594…
|
drh
|
968 |
} |
|
7029594…
|
drh
|
969 |
zOut[j++] = '"'; |
|
7029594…
|
drh
|
970 |
zOut[j] = 0; |
|
7029594…
|
drh
|
971 |
return zOut; |
|
7029594…
|
drh
|
972 |
} |
|
7029594…
|
drh
|
973 |
|
|
7029594…
|
drh
|
974 |
/* |
|
8e6f3ce…
|
drh
|
975 |
** Find the Git-name corresponding to the Fossil-name zUuid. |
|
7029594…
|
drh
|
976 |
** |
|
7029594…
|
drh
|
977 |
** If the mark does not exist and if the bCreate flag is false, then |
|
8e6f3ce…
|
drh
|
978 |
** return NULL. If the mark does not exist and the bCreate flag is true, |
|
7029594…
|
drh
|
979 |
** then create the mark. |
|
8e6f3ce…
|
drh
|
980 |
** |
|
8e6f3ce…
|
drh
|
981 |
** The string returned is obtained from fossil_malloc() and should |
|
8e6f3ce…
|
drh
|
982 |
** be freed by the caller. |
|
7029594…
|
drh
|
983 |
*/ |
|
fdde65e…
|
drh
|
984 |
static char *gitmirror_find_mark(const char *zUuid, int isFile, int bCreate){ |
|
7029594…
|
drh
|
985 |
static Stmt sFind, sIns; |
|
7029594…
|
drh
|
986 |
db_static_prepare(&sFind, |
|
8e6f3ce…
|
drh
|
987 |
"SELECT coalesce(githash,printf(':%%d',id))" |
|
fdde65e…
|
drh
|
988 |
" FROM mirror.mmark WHERE uuid=:uuid AND isfile=:isfile" |
|
7029594…
|
drh
|
989 |
); |
|
7029594…
|
drh
|
990 |
db_bind_text(&sFind, ":uuid", zUuid); |
|
fdde65e…
|
drh
|
991 |
db_bind_int(&sFind, ":isfile", isFile!=0); |
|
7029594…
|
drh
|
992 |
if( db_step(&sFind)==SQLITE_ROW ){ |
|
8e6f3ce…
|
drh
|
993 |
char *zMark = fossil_strdup(db_column_text(&sFind, 0)); |
|
7029594…
|
drh
|
994 |
db_reset(&sFind); |
|
8e6f3ce…
|
drh
|
995 |
return zMark; |
|
7029594…
|
drh
|
996 |
} |
|
7029594…
|
drh
|
997 |
db_reset(&sFind); |
|
8e6f3ce…
|
drh
|
998 |
if( !bCreate ){ |
|
8e6f3ce…
|
drh
|
999 |
return 0; |
|
8e6f3ce…
|
drh
|
1000 |
} |
|
7029594…
|
drh
|
1001 |
db_static_prepare(&sIns, |
|
fdde65e…
|
drh
|
1002 |
"INSERT INTO mirror.mmark(uuid,isfile) VALUES(:uuid,:isfile)" |
|
7029594…
|
drh
|
1003 |
); |
|
7029594…
|
drh
|
1004 |
db_bind_text(&sIns, ":uuid", zUuid); |
|
fdde65e…
|
drh
|
1005 |
db_bind_int(&sIns, ":isfile", isFile!=0); |
|
7029594…
|
drh
|
1006 |
db_step(&sIns); |
|
7029594…
|
drh
|
1007 |
db_reset(&sIns); |
|
8e6f3ce…
|
drh
|
1008 |
return mprintf(":%d", db_last_insert_rowid()); |
|
7029594…
|
drh
|
1009 |
} |
|
7029594…
|
drh
|
1010 |
|
|
7029594…
|
drh
|
1011 |
/* This is the SHA3-256 hash of an empty file */ |
|
275da70…
|
danield
|
1012 |
static const char zEmptySha3[] = |
|
7029594…
|
drh
|
1013 |
"a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a"; |
|
7029594…
|
drh
|
1014 |
|
|
7029594…
|
drh
|
1015 |
/* |
|
7029594…
|
drh
|
1016 |
** Export a single file named by zUuid. |
|
de0bbcb…
|
drh
|
1017 |
** |
|
de0bbcb…
|
drh
|
1018 |
** Return 0 on success and non-zero on any failure. |
|
de0bbcb…
|
drh
|
1019 |
** |
|
de0bbcb…
|
drh
|
1020 |
** If zUuid is a shunned file, then treat it as if it were any empty file. |
|
de0bbcb…
|
drh
|
1021 |
** But files that are missing from the repository but have not been officially |
|
de0bbcb…
|
drh
|
1022 |
** shunned cause an error return. Except, if bPhantomOk is true, then missing |
|
de0bbcb…
|
drh
|
1023 |
** files are replaced by an empty file. |
|
7029594…
|
drh
|
1024 |
*/ |
|
de0bbcb…
|
drh
|
1025 |
static int gitmirror_send_file(FILE *xCmd, const char *zUuid, int bPhantomOk){ |
|
8e6f3ce…
|
drh
|
1026 |
char *zMark; |
|
7029594…
|
drh
|
1027 |
int rid; |
|
7029594…
|
drh
|
1028 |
int rc; |
|
7029594…
|
drh
|
1029 |
Blob data; |
|
7029594…
|
drh
|
1030 |
rid = fast_uuid_to_rid(zUuid); |
|
7029594…
|
drh
|
1031 |
if( rid<0 ){ |
|
de0bbcb…
|
drh
|
1032 |
if( bPhantomOk || uuid_is_shunned(zUuid) ){ |
|
de0bbcb…
|
drh
|
1033 |
gitmirror_message(VERB_EXTRA, "missing file: %s\n", zUuid); |
|
de0bbcb…
|
drh
|
1034 |
zUuid = zEmptySha3; |
|
de0bbcb…
|
drh
|
1035 |
}else{ |
|
de0bbcb…
|
drh
|
1036 |
return 1; |
|
de0bbcb…
|
drh
|
1037 |
} |
|
7029594…
|
drh
|
1038 |
}else{ |
|
7029594…
|
drh
|
1039 |
rc = content_get(rid, &data); |
|
7029594…
|
drh
|
1040 |
if( rc==0 ){ |
|
de0bbcb…
|
drh
|
1041 |
if( bPhantomOk ){ |
|
de0bbcb…
|
drh
|
1042 |
blob_init(&data, 0, 0); |
|
de0bbcb…
|
drh
|
1043 |
gitmirror_message(VERB_EXTRA, "missing file: %s\n", zUuid); |
|
de0bbcb…
|
drh
|
1044 |
zUuid = zEmptySha3; |
|
275da70…
|
danield
|
1045 |
}else{ |
|
de0bbcb…
|
drh
|
1046 |
return 1; |
|
de0bbcb…
|
drh
|
1047 |
} |
|
7029594…
|
drh
|
1048 |
} |
|
7029594…
|
drh
|
1049 |
} |
|
fdde65e…
|
drh
|
1050 |
zMark = gitmirror_find_mark(zUuid, 1, 1); |
|
8e6f3ce…
|
drh
|
1051 |
if( zMark[0]==':' ){ |
|
8e6f3ce…
|
drh
|
1052 |
fprintf(xCmd, "blob\nmark %s\ndata %d\n", zMark, blob_size(&data)); |
|
8e6f3ce…
|
drh
|
1053 |
fwrite(blob_buffer(&data), 1, blob_size(&data), xCmd); |
|
8e6f3ce…
|
drh
|
1054 |
fprintf(xCmd, "\n"); |
|
8e6f3ce…
|
drh
|
1055 |
} |
|
8e6f3ce…
|
drh
|
1056 |
fossil_free(zMark); |
|
7029594…
|
drh
|
1057 |
blob_reset(&data); |
|
de0bbcb…
|
drh
|
1058 |
return 0; |
|
7029594…
|
drh
|
1059 |
} |
|
7029594…
|
drh
|
1060 |
|
|
7029594…
|
drh
|
1061 |
/* |
|
7029594…
|
drh
|
1062 |
** Transfer a check-in over to the mirror. "rid" is the BLOB.RID for |
|
7029594…
|
drh
|
1063 |
** the check-in to export. |
|
7029594…
|
drh
|
1064 |
** |
|
7029594…
|
drh
|
1065 |
** If any ancestor of the check-in has not yet been exported, then |
|
7029594…
|
drh
|
1066 |
** invoke this routine recursively to export the ancestor first. |
|
7029594…
|
drh
|
1067 |
** This can only happen on a timewarp, so deep nesting is unlikely. |
|
7029594…
|
drh
|
1068 |
** |
|
7029594…
|
drh
|
1069 |
** Before sending the check-in, first make sure all associated files |
|
7029594…
|
drh
|
1070 |
** have already been exported, and send "blob" records for any that |
|
7029594…
|
drh
|
1071 |
** have not been. Update the MIRROR.MMARK table so that it holds the |
|
de0bbcb…
|
drh
|
1072 |
** marks for the exported files. |
|
de0bbcb…
|
drh
|
1073 |
** |
|
de0bbcb…
|
drh
|
1074 |
** Return zero on success and non-zero if the export should be stopped. |
|
7029594…
|
drh
|
1075 |
*/ |
|
de0bbcb…
|
drh
|
1076 |
static int gitmirror_send_checkin( |
|
7029594…
|
drh
|
1077 |
FILE *xCmd, /* Write fast-import text on this pipe */ |
|
7029594…
|
drh
|
1078 |
int rid, /* BLOB.RID for the check-in to export */ |
|
7029594…
|
drh
|
1079 |
const char *zUuid, /* BLOB.UUID for the check-in to export */ |
|
dfc0f1b…
|
drh
|
1080 |
int *pnLimit /* Stop when the counter reaches zero */ |
|
7029594…
|
drh
|
1081 |
){ |
|
7029594…
|
drh
|
1082 |
Manifest *pMan; /* The check-in to be output */ |
|
7029594…
|
drh
|
1083 |
int i; /* Loop counter */ |
|
7029594…
|
drh
|
1084 |
int iParent; /* Which immediate ancestor is primary. -1 for none */ |
|
7029594…
|
drh
|
1085 |
Stmt q; /* An SQL query */ |
|
7029594…
|
drh
|
1086 |
char *zBranch; /* The branch of the check-in */ |
|
8e6f3ce…
|
drh
|
1087 |
char *zMark; /* The Git-name of the check-in */ |
|
7029594…
|
drh
|
1088 |
Blob sql; /* String of SQL for part of the query */ |
|
7029594…
|
drh
|
1089 |
Blob comment; /* The comment text for the check-in */ |
|
de0bbcb…
|
drh
|
1090 |
int nErr = 0; /* Number of errors */ |
|
de0bbcb…
|
drh
|
1091 |
int bPhantomOk; /* True if phantom files should be ignored */ |
|
f6524ae…
|
jan.nijtmans
|
1092 |
char buf[24]; |
|
cd4fbde…
|
jamsek
|
1093 |
char *zEmail; /* Contact info for Git committer field */ |
|
dfc0f1b…
|
drh
|
1094 |
int fManifest; /* Should the manifest files be included? */ |
|
dfc0f1b…
|
drh
|
1095 |
int fPManifest = 0; /* OR of the manifest files for all parents */ |
|
ca0d66b…
|
danield
|
1096 |
const char *zMainBranch; |
|
7029594…
|
drh
|
1097 |
|
|
7029594…
|
drh
|
1098 |
pMan = manifest_get(rid, CFTYPE_MANIFEST, 0); |
|
7029594…
|
drh
|
1099 |
if( pMan==0 ){ |
|
7029594…
|
drh
|
1100 |
/* Must be a phantom. Return without doing anything, and in particular |
|
7029594…
|
drh
|
1101 |
** without creating a mark for this check-in. */ |
|
de0bbcb…
|
drh
|
1102 |
gitmirror_message(VERB_NORMAL, "missing check-in: %s\n", zUuid); |
|
de0bbcb…
|
drh
|
1103 |
return 0; |
|
7029594…
|
drh
|
1104 |
} |
|
7029594…
|
drh
|
1105 |
|
|
7029594…
|
drh
|
1106 |
/* Check to see if any parent logins have not yet been processed, and |
|
7029594…
|
drh
|
1107 |
** if so, create them */ |
|
7029594…
|
drh
|
1108 |
for(i=0; i<pMan->nParent; i++){ |
|
fdde65e…
|
drh
|
1109 |
char *zPMark = gitmirror_find_mark(pMan->azParent[i], 0, 0); |
|
8e6f3ce…
|
drh
|
1110 |
if( zPMark==0 ){ |
|
7029594…
|
drh
|
1111 |
int prid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", |
|
7029594…
|
drh
|
1112 |
pMan->azParent[i]); |
|
de0bbcb…
|
drh
|
1113 |
int rc = gitmirror_send_checkin(xCmd, prid, pMan->azParent[i], |
|
dfc0f1b…
|
drh
|
1114 |
pnLimit); |
|
de0bbcb…
|
drh
|
1115 |
if( rc || *pnLimit<=0 ){ |
|
7029594…
|
drh
|
1116 |
manifest_destroy(pMan); |
|
de0bbcb…
|
drh
|
1117 |
return 1; |
|
7029594…
|
drh
|
1118 |
} |
|
7029594…
|
drh
|
1119 |
} |
|
8e6f3ce…
|
drh
|
1120 |
fossil_free(zPMark); |
|
7029594…
|
drh
|
1121 |
} |
|
de0bbcb…
|
drh
|
1122 |
|
|
de0bbcb…
|
drh
|
1123 |
/* Ignore phantom files on check-ins that are over one year old */ |
|
de0bbcb…
|
drh
|
1124 |
bPhantomOk = db_int(0, "SELECT %.6f<julianday('now','-1 year')", |
|
de0bbcb…
|
drh
|
1125 |
pMan->rDate); |
|
7029594…
|
drh
|
1126 |
|
|
7029594…
|
drh
|
1127 |
/* Make sure all necessary files have been exported */ |
|
7029594…
|
drh
|
1128 |
db_prepare(&q, |
|
7029594…
|
drh
|
1129 |
"SELECT uuid FROM files_of_checkin(%Q)" |
|
7029594…
|
drh
|
1130 |
" WHERE uuid NOT IN (SELECT uuid FROM mirror.mmark)", |
|
7029594…
|
drh
|
1131 |
zUuid |
|
7029594…
|
drh
|
1132 |
); |
|
7029594…
|
drh
|
1133 |
while( db_step(&q)==SQLITE_ROW ){ |
|
7029594…
|
drh
|
1134 |
const char *zFUuid = db_column_text(&q, 0); |
|
de0bbcb…
|
drh
|
1135 |
int n = gitmirror_send_file(xCmd, zFUuid, bPhantomOk); |
|
de0bbcb…
|
drh
|
1136 |
nErr += n; |
|
de0bbcb…
|
drh
|
1137 |
if( n ) gitmirror_message(VERB_ERROR, "missing file: %s\n", zFUuid); |
|
7029594…
|
drh
|
1138 |
} |
|
7029594…
|
drh
|
1139 |
db_finalize(&q); |
|
de0bbcb…
|
drh
|
1140 |
|
|
de0bbcb…
|
drh
|
1141 |
/* If some required files could not be exported, abandon the check-in |
|
de0bbcb…
|
drh
|
1142 |
** export */ |
|
de0bbcb…
|
drh
|
1143 |
if( nErr ){ |
|
de0bbcb…
|
drh
|
1144 |
gitmirror_message(VERB_ERROR, |
|
de0bbcb…
|
drh
|
1145 |
"export of %s abandoned due to missing files\n", zUuid); |
|
de0bbcb…
|
drh
|
1146 |
*pnLimit = 0; |
|
953fac2…
|
drh
|
1147 |
manifest_destroy(pMan); |
|
de0bbcb…
|
drh
|
1148 |
return 1; |
|
de0bbcb…
|
drh
|
1149 |
} |
|
7029594…
|
drh
|
1150 |
|
|
7029594…
|
drh
|
1151 |
/* Figure out which branch this check-in is a member of */ |
|
7029594…
|
drh
|
1152 |
zBranch = db_text(0, |
|
7029594…
|
drh
|
1153 |
"SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=%d", |
|
7029594…
|
drh
|
1154 |
TAG_BRANCH, rid |
|
7029594…
|
drh
|
1155 |
); |
|
ca0d66b…
|
danield
|
1156 |
zMainBranch = db_main_branch(); |
|
3a6dd83…
|
drh
|
1157 |
if( fossil_strcmp(zBranch, zMainBranch)==0 ){ |
|
dbcf66b…
|
drh
|
1158 |
assert( gitmirror_mainbranch!=0 ); |
|
7029594…
|
drh
|
1159 |
fossil_free(zBranch); |
|
4c3e172…
|
danield
|
1160 |
zBranch = fossil_strdup(gitmirror_mainbranch); |
|
7029594…
|
drh
|
1161 |
}else if( zBranch==0 ){ |
|
7029594…
|
drh
|
1162 |
zBranch = mprintf("unknown"); |
|
7029594…
|
drh
|
1163 |
}else{ |
|
7029594…
|
drh
|
1164 |
gitmirror_sanitize_name(zBranch); |
|
7029594…
|
drh
|
1165 |
} |
|
7029594…
|
drh
|
1166 |
|
|
7029594…
|
drh
|
1167 |
/* Export the check-in */ |
|
7029594…
|
drh
|
1168 |
fprintf(xCmd, "commit refs/heads/%s\n", zBranch); |
|
7029594…
|
drh
|
1169 |
fossil_free(zBranch); |
|
fdde65e…
|
drh
|
1170 |
zMark = gitmirror_find_mark(zUuid,0,1); |
|
8e6f3ce…
|
drh
|
1171 |
fprintf(xCmd, "mark %s\n", zMark); |
|
8e6f3ce…
|
drh
|
1172 |
fossil_free(zMark); |
|
f6524ae…
|
jan.nijtmans
|
1173 |
sqlite3_snprintf(sizeof(buf), buf, "%lld", |
|
7029594…
|
drh
|
1174 |
(sqlite3_int64)((pMan->rDate-2440587.5)*86400.0) |
|
7029594…
|
drh
|
1175 |
); |
|
b001a1c…
|
drh
|
1176 |
|
|
cd4fbde…
|
jamsek
|
1177 |
/* |
|
cd4fbde…
|
jamsek
|
1178 |
** Check for 'fx_' table from previous Git import, otherwise take contact info |
|
cd4fbde…
|
jamsek
|
1179 |
** from user table for <emailaddr> in committer field. If no emailaddr, check |
|
cd4fbde…
|
jamsek
|
1180 |
** if username is in email form, otherwise use generic '[email protected]'. |
|
cd4fbde…
|
jamsek
|
1181 |
*/ |
|
cd4fbde…
|
jamsek
|
1182 |
if (db_table_exists("repository", "fx_git")) { |
|
cd4fbde…
|
jamsek
|
1183 |
zEmail = db_text(0, "SELECT email FROM fx_git WHERE user=%Q", pMan->zUser); |
|
cd4fbde…
|
jamsek
|
1184 |
} else { |
|
cd4fbde…
|
jamsek
|
1185 |
zEmail = db_text(0, "SELECT info FROM user WHERE login=%Q", pMan->zUser); |
|
cd4fbde…
|
jamsek
|
1186 |
} |
|
b001a1c…
|
drh
|
1187 |
|
|
cd4fbde…
|
jamsek
|
1188 |
/* Some repo 'info' fields return an empty string hence the second check */ |
|
b001a1c…
|
drh
|
1189 |
if( zEmail==0 ){ |
|
cd4fbde…
|
jamsek
|
1190 |
/* If username is in emailaddr form, don't append '@noemail.net' */ |
|
b001a1c…
|
drh
|
1191 |
if( pMan->zUser==0 || strchr(pMan->zUser, '@')==0 ){ |
|
cd4fbde…
|
jamsek
|
1192 |
zEmail = mprintf("%[email protected]", pMan->zUser); |
|
cd4fbde…
|
jamsek
|
1193 |
} else { |
|
cd4fbde…
|
jamsek
|
1194 |
zEmail = fossil_strdup(pMan->zUser); |
|
cd4fbde…
|
jamsek
|
1195 |
} |
|
b001a1c…
|
drh
|
1196 |
}else{ |
|
b001a1c…
|
drh
|
1197 |
char *zTmp = strchr(zEmail, '<'); |
|
b001a1c…
|
drh
|
1198 |
if( zTmp ){ |
|
b001a1c…
|
drh
|
1199 |
char *zTmpEnd = strchr(zTmp+1, '>'); |
|
b001a1c…
|
drh
|
1200 |
char *zNew; |
|
b001a1c…
|
drh
|
1201 |
int i; |
|
b001a1c…
|
drh
|
1202 |
if( zTmpEnd ) *(zTmpEnd) = 0; |
|
b001a1c…
|
drh
|
1203 |
zNew = fossil_strdup(zTmp+1); |
|
b001a1c…
|
drh
|
1204 |
fossil_free(zEmail); |
|
b001a1c…
|
drh
|
1205 |
zEmail = zNew; |
|
b001a1c…
|
drh
|
1206 |
for(i=0; zEmail[i] && !fossil_isspace(zEmail[i]); i++){} |
|
b001a1c…
|
drh
|
1207 |
zEmail[i] = 0; |
|
b001a1c…
|
drh
|
1208 |
} |
|
cd4fbde…
|
jamsek
|
1209 |
} |
|
7e96191…
|
drh
|
1210 |
fprintf(xCmd, "# rid=%d\n", rid); |
|
52fb87b…
|
jamsek
|
1211 |
fprintf(xCmd, "committer %s <%s> %s +0000\n", pMan->zUser, zEmail, buf); |
|
cd4fbde…
|
jamsek
|
1212 |
fossil_free(zEmail); |
|
7029594…
|
drh
|
1213 |
blob_init(&comment, pMan->zComment, -1); |
|
7029594…
|
drh
|
1214 |
if( blob_size(&comment)==0 ){ |
|
7029594…
|
drh
|
1215 |
blob_append(&comment, "(no comment)", -1); |
|
7029594…
|
drh
|
1216 |
} |
|
7029594…
|
drh
|
1217 |
blob_appendf(&comment, "\n\nFossilOrigin-Name: %s", zUuid); |
|
7e96191…
|
drh
|
1218 |
fprintf(xCmd, "data %d\n%s\n", blob_strlen(&comment), blob_str(&comment)); |
|
7029594…
|
drh
|
1219 |
blob_reset(&comment); |
|
7029594…
|
drh
|
1220 |
iParent = -1; /* Which ancestor is the primary parent */ |
|
7029594…
|
drh
|
1221 |
for(i=0; i<pMan->nParent; i++){ |
|
fdde65e…
|
drh
|
1222 |
char *zOther = gitmirror_find_mark(pMan->azParent[i],0,0); |
|
8e6f3ce…
|
drh
|
1223 |
if( zOther==0 ) continue; |
|
dfc0f1b…
|
drh
|
1224 |
fPManifest |= db_get_manifest_setting(pMan->azParent[i]); |
|
7029594…
|
drh
|
1225 |
if( iParent<0 ){ |
|
7029594…
|
drh
|
1226 |
iParent = i; |
|
8e6f3ce…
|
drh
|
1227 |
fprintf(xCmd, "from %s\n", zOther); |
|
7029594…
|
drh
|
1228 |
}else{ |
|
8e6f3ce…
|
drh
|
1229 |
fprintf(xCmd, "merge %s\n", zOther); |
|
7029594…
|
drh
|
1230 |
} |
|
8e6f3ce…
|
drh
|
1231 |
fossil_free(zOther); |
|
7029594…
|
drh
|
1232 |
} |
|
7029594…
|
drh
|
1233 |
if( iParent>=0 ){ |
|
7029594…
|
drh
|
1234 |
db_prepare(&q, |
|
7029594…
|
drh
|
1235 |
"SELECT filename FROM files_of_checkin(%Q)" |
|
7029594…
|
drh
|
1236 |
" EXCEPT SELECT filename FROM files_of_checkin(%Q)", |
|
7029594…
|
drh
|
1237 |
pMan->azParent[iParent], zUuid |
|
7029594…
|
drh
|
1238 |
); |
|
7029594…
|
drh
|
1239 |
while( db_step(&q)==SQLITE_ROW ){ |
|
7029594…
|
drh
|
1240 |
fprintf(xCmd, "D %s\n", db_column_text(&q,0)); |
|
7029594…
|
drh
|
1241 |
} |
|
7029594…
|
drh
|
1242 |
db_finalize(&q); |
|
7029594…
|
drh
|
1243 |
} |
|
7029594…
|
drh
|
1244 |
blob_init(&sql, 0, 0); |
|
7029594…
|
drh
|
1245 |
blob_append_sql(&sql, |
|
7029594…
|
drh
|
1246 |
"SELECT filename, uuid, perm FROM files_of_checkin(%Q)", |
|
7029594…
|
drh
|
1247 |
zUuid |
|
7029594…
|
drh
|
1248 |
); |
|
7029594…
|
drh
|
1249 |
if( pMan->nParent ){ |
|
7029594…
|
drh
|
1250 |
blob_append_sql(&sql, |
|
7029594…
|
drh
|
1251 |
" EXCEPT SELECT filename, uuid, perm FROM files_of_checkin(%Q)", |
|
7029594…
|
drh
|
1252 |
pMan->azParent[0]); |
|
7029594…
|
drh
|
1253 |
} |
|
7029594…
|
drh
|
1254 |
db_prepare(&q, |
|
8e6f3ce…
|
drh
|
1255 |
"SELECT x.filename, x.perm," |
|
8e6f3ce…
|
drh
|
1256 |
" coalesce(mmark.githash,printf(':%%d',mmark.id))" |
|
8e6f3ce…
|
drh
|
1257 |
" FROM (%s) AS x, mirror.mmark" |
|
fdde65e…
|
drh
|
1258 |
" WHERE mmark.uuid=x.uuid AND isfile", |
|
7029594…
|
drh
|
1259 |
blob_sql_text(&sql) |
|
7029594…
|
drh
|
1260 |
); |
|
7029594…
|
drh
|
1261 |
blob_reset(&sql); |
|
7029594…
|
drh
|
1262 |
while( db_step(&q)==SQLITE_ROW ){ |
|
7029594…
|
drh
|
1263 |
const char *zFilename = db_column_text(&q,0); |
|
7029594…
|
drh
|
1264 |
const char *zMode = db_column_text(&q,1); |
|
8e6f3ce…
|
drh
|
1265 |
const char *zMark = db_column_text(&q,2); |
|
7029594…
|
drh
|
1266 |
const char *zGitMode = "100644"; |
|
7029594…
|
drh
|
1267 |
char *zFNQuoted = 0; |
|
7029594…
|
drh
|
1268 |
if( zMode ){ |
|
7029594…
|
drh
|
1269 |
if( strchr(zMode,'x') ) zGitMode = "100755"; |
|
7029594…
|
drh
|
1270 |
if( strchr(zMode,'l') ) zGitMode = "120000"; |
|
7029594…
|
drh
|
1271 |
} |
|
7029594…
|
drh
|
1272 |
zFNQuoted = gitmirror_quote_filename_if_needed(zFilename); |
|
8e6f3ce…
|
drh
|
1273 |
fprintf(xCmd,"M %s %s %s\n", zGitMode, zMark, zFNQuoted); |
|
7029594…
|
drh
|
1274 |
fossil_free(zFNQuoted); |
|
7029594…
|
drh
|
1275 |
} |
|
7029594…
|
drh
|
1276 |
db_finalize(&q); |
|
953fac2…
|
drh
|
1277 |
manifest_destroy(pMan); |
|
953fac2…
|
drh
|
1278 |
pMan = 0; |
|
7029594…
|
drh
|
1279 |
|
|
7029594…
|
drh
|
1280 |
/* Include Fossil-generated auxiliary files in the check-in */ |
|
dfc0f1b…
|
drh
|
1281 |
fManifest = db_get_manifest_setting(zUuid); |
|
7029594…
|
drh
|
1282 |
if( fManifest & MFESTFLG_RAW ){ |
|
7029594…
|
drh
|
1283 |
Blob manifest; |
|
7029594…
|
drh
|
1284 |
content_get(rid, &manifest); |
|
4ff45df…
|
drh
|
1285 |
sterilize_manifest(&manifest, CFTYPE_MANIFEST); |
|
7029594…
|
drh
|
1286 |
fprintf(xCmd,"M 100644 inline manifest\ndata %d\n%s\n", |
|
7e96191…
|
drh
|
1287 |
blob_strlen(&manifest), blob_str(&manifest)); |
|
7029594…
|
drh
|
1288 |
blob_reset(&manifest); |
|
dfc0f1b…
|
drh
|
1289 |
}else if( fPManifest & MFESTFLG_RAW ){ |
|
dfc0f1b…
|
drh
|
1290 |
fprintf(xCmd, "D manifest\n"); |
|
7029594…
|
drh
|
1291 |
} |
|
7029594…
|
drh
|
1292 |
if( fManifest & MFESTFLG_UUID ){ |
|
7029594…
|
drh
|
1293 |
int n = (int)strlen(zUuid); |
|
fc8d476…
|
drh
|
1294 |
fprintf(xCmd,"M 100644 inline manifest.uuid\ndata %d\n%s\n\n", n+1, zUuid); |
|
dfc0f1b…
|
drh
|
1295 |
}else if( fPManifest & MFESTFLG_UUID ){ |
|
dfc0f1b…
|
drh
|
1296 |
fprintf(xCmd, "D manifest.uuid\n"); |
|
7029594…
|
drh
|
1297 |
} |
|
7029594…
|
drh
|
1298 |
if( fManifest & MFESTFLG_TAGS ){ |
|
7029594…
|
drh
|
1299 |
Blob tagslist; |
|
7029594…
|
drh
|
1300 |
blob_init(&tagslist, 0, 0); |
|
7029594…
|
drh
|
1301 |
get_checkin_taglist(rid, &tagslist); |
|
7029594…
|
drh
|
1302 |
fprintf(xCmd,"M 100644 inline manifest.tags\ndata %d\n%s\n", |
|
7e96191…
|
drh
|
1303 |
blob_strlen(&tagslist), blob_str(&tagslist)); |
|
7029594…
|
drh
|
1304 |
blob_reset(&tagslist); |
|
dfc0f1b…
|
drh
|
1305 |
}else if( fPManifest & MFESTFLG_TAGS ){ |
|
dfc0f1b…
|
drh
|
1306 |
fprintf(xCmd, "D manifest.tags\n"); |
|
7029594…
|
drh
|
1307 |
} |
|
7029594…
|
drh
|
1308 |
|
|
7029594…
|
drh
|
1309 |
/* The check-in is finished, so decrement the counter */ |
|
7029594…
|
drh
|
1310 |
(*pnLimit)--; |
|
de0bbcb…
|
drh
|
1311 |
return 0; |
|
953fac2…
|
drh
|
1312 |
} |
|
953fac2…
|
drh
|
1313 |
|
|
953fac2…
|
drh
|
1314 |
/* |
|
953fac2…
|
drh
|
1315 |
** Create a new Git repository at zMirror to use as the mirror. |
|
953fac2…
|
drh
|
1316 |
** Try to make zMainBr be the main branch for the new repository. |
|
953fac2…
|
drh
|
1317 |
** |
|
953fac2…
|
drh
|
1318 |
** A side-effect of this routine is that current-working directory |
|
953fac2…
|
drh
|
1319 |
** is changed to zMirror. |
|
953fac2…
|
drh
|
1320 |
** |
|
953fac2…
|
drh
|
1321 |
** If zMainBr is initially NULL, then the return value will be the |
|
953fac2…
|
drh
|
1322 |
** name of the default branch to be used by Git. If zMainBr is |
|
953fac2…
|
drh
|
1323 |
** initially non-NULL, then the return value will be a copy of zMainBr. |
|
953fac2…
|
drh
|
1324 |
*/ |
|
953fac2…
|
drh
|
1325 |
static char *gitmirror_init( |
|
953fac2…
|
drh
|
1326 |
const char *zMirror, |
|
953fac2…
|
drh
|
1327 |
char *zMainBr |
|
953fac2…
|
drh
|
1328 |
){ |
|
953fac2…
|
drh
|
1329 |
char *zCmd; |
|
953fac2…
|
drh
|
1330 |
int rc; |
|
953fac2…
|
drh
|
1331 |
|
|
953fac2…
|
drh
|
1332 |
/* Create a new Git repository at zMirror */ |
|
953fac2…
|
drh
|
1333 |
zCmd = mprintf("git init %$", zMirror); |
|
953fac2…
|
drh
|
1334 |
gitmirror_message(VERB_NORMAL, "%s\n", zCmd); |
|
953fac2…
|
drh
|
1335 |
rc = fossil_system(zCmd); |
|
953fac2…
|
drh
|
1336 |
if( rc ){ |
|
da5faf1…
|
drh
|
1337 |
fossil_fatal("cannot initialize git repository using: %s", zCmd); |
|
953fac2…
|
drh
|
1338 |
} |
|
953fac2…
|
drh
|
1339 |
fossil_free(zCmd); |
|
953fac2…
|
drh
|
1340 |
|
|
953fac2…
|
drh
|
1341 |
/* Must be in the new Git repository directory for subsequent commands */ |
|
953fac2…
|
drh
|
1342 |
rc = file_chdir(zMirror, 0); |
|
953fac2…
|
drh
|
1343 |
if( rc ){ |
|
953fac2…
|
drh
|
1344 |
fossil_fatal("cannot change to directory \"%s\"", zMirror); |
|
953fac2…
|
drh
|
1345 |
} |
|
953fac2…
|
drh
|
1346 |
|
|
953fac2…
|
drh
|
1347 |
if( zMainBr ){ |
|
953fac2…
|
drh
|
1348 |
/* Set the current branch to zMainBr */ |
|
953fac2…
|
drh
|
1349 |
zCmd = mprintf("git symbolic-ref HEAD refs/heads/%s", zMainBr); |
|
953fac2…
|
drh
|
1350 |
gitmirror_message(VERB_NORMAL, "%s\n", zCmd); |
|
953fac2…
|
drh
|
1351 |
rc = fossil_system(zCmd); |
|
953fac2…
|
drh
|
1352 |
if( rc ){ |
|
953fac2…
|
drh
|
1353 |
fossil_fatal("git command failed: %s", zCmd); |
|
953fac2…
|
drh
|
1354 |
} |
|
953fac2…
|
drh
|
1355 |
fossil_free(zCmd); |
|
953fac2…
|
drh
|
1356 |
}else{ |
|
953fac2…
|
drh
|
1357 |
/* If zMainBr is not specified, then check to see what branch |
|
953fac2…
|
drh
|
1358 |
** name Git chose for itself */ |
|
953fac2…
|
drh
|
1359 |
char *z; |
|
953fac2…
|
drh
|
1360 |
char zLine[1000]; |
|
953fac2…
|
drh
|
1361 |
FILE *xCmd; |
|
953fac2…
|
drh
|
1362 |
int i; |
|
953fac2…
|
drh
|
1363 |
zCmd = "git symbolic-ref --short HEAD"; |
|
953fac2…
|
drh
|
1364 |
gitmirror_message(VERB_NORMAL, "%s\n", zCmd); |
|
953fac2…
|
drh
|
1365 |
xCmd = popen(zCmd, "r"); |
|
953fac2…
|
drh
|
1366 |
if( xCmd==0 ){ |
|
953fac2…
|
drh
|
1367 |
fossil_fatal("git command failed: %s", zCmd); |
|
953fac2…
|
drh
|
1368 |
} |
|
275da70…
|
danield
|
1369 |
|
|
953fac2…
|
drh
|
1370 |
z = fgets(zLine, sizeof(zLine), xCmd); |
|
953fac2…
|
drh
|
1371 |
pclose(xCmd); |
|
953fac2…
|
drh
|
1372 |
if( z==0 ){ |
|
953fac2…
|
drh
|
1373 |
fossil_fatal("no output from \"%s\"", zCmd); |
|
953fac2…
|
drh
|
1374 |
} |
|
953fac2…
|
drh
|
1375 |
for(i=0; z[i] && !fossil_isspace(z[i]); i++){} |
|
953fac2…
|
drh
|
1376 |
z[i] = 0; |
|
953fac2…
|
drh
|
1377 |
zMainBr = fossil_strdup(z); |
|
953fac2…
|
drh
|
1378 |
} |
|
953fac2…
|
drh
|
1379 |
return zMainBr; |
|
275da70…
|
danield
|
1380 |
} |
|
7029594…
|
drh
|
1381 |
|
|
7029594…
|
drh
|
1382 |
/* |
|
7029594…
|
drh
|
1383 |
** Implementation of the "fossil git export" command. |
|
7029594…
|
drh
|
1384 |
*/ |
|
7029594…
|
drh
|
1385 |
void gitmirror_export_command(void){ |
|
7029594…
|
drh
|
1386 |
const char *zLimit; /* Text of the --limit flag */ |
|
7029594…
|
drh
|
1387 |
int nLimit = 0x7fffffff; /* Numeric value of the --limit flag */ |
|
7029594…
|
drh
|
1388 |
int nTotal = 0; /* Total number of check-ins to export */ |
|
7029594…
|
drh
|
1389 |
char *zMirror; /* Name of the mirror */ |
|
7029594…
|
drh
|
1390 |
char *z; /* Generic string */ |
|
7029594…
|
drh
|
1391 |
char *zCmd; /* git command to run as a subprocess */ |
|
7029594…
|
drh
|
1392 |
const char *zDebug = 0; /* Value of the --debug flag */ |
|
ac5ae7b…
|
drh
|
1393 |
const char *zAutoPush = 0; /* Value of the --autopush flag */ |
|
953fac2…
|
drh
|
1394 |
char *zMainBr = 0; /* Value of the --mainbranch flag */ |
|
ac5ae7b…
|
drh
|
1395 |
char *zPushUrl; /* URL to sync the mirror to */ |
|
7029594…
|
drh
|
1396 |
double rEnd; /* time of most recent export */ |
|
7029594…
|
drh
|
1397 |
int rc; /* Result code */ |
|
fe98905…
|
drh
|
1398 |
int bForce; /* Do the export and sync even if no changes*/ |
|
e4d87cd…
|
drh
|
1399 |
int bNeedRepack = 0; /* True if we should run repack at the end */ |
|
9d53f2f…
|
drh
|
1400 |
int bIfExists; /* The --if-mirrored flag */ |
|
7029594…
|
drh
|
1401 |
FILE *xCmd; /* Pipe to the "git fast-import" command */ |
|
8e6f3ce…
|
drh
|
1402 |
FILE *pMarks; /* Git mark files */ |
|
7029594…
|
drh
|
1403 |
Stmt q; /* Queries */ |
|
7029594…
|
drh
|
1404 |
char zLine[200]; /* One line of a mark file */ |
|
7029594…
|
drh
|
1405 |
|
|
7029594…
|
drh
|
1406 |
zDebug = find_option("debug",0,1); |
|
7029594…
|
drh
|
1407 |
db_find_and_open_repository(0, 0); |
|
7029594…
|
drh
|
1408 |
zLimit = find_option("limit", 0, 1); |
|
7029594…
|
drh
|
1409 |
if( zLimit ){ |
|
7029594…
|
drh
|
1410 |
nLimit = (unsigned int)atoi(zLimit); |
|
7029594…
|
drh
|
1411 |
if( nLimit<=0 ) fossil_fatal("--limit must be positive"); |
|
7029594…
|
drh
|
1412 |
} |
|
ac5ae7b…
|
drh
|
1413 |
zAutoPush = find_option("autopush",0,1); |
|
953fac2…
|
drh
|
1414 |
zMainBr = (char*)find_option("mainbranch",0,1); |
|
fe98905…
|
drh
|
1415 |
bForce = find_option("force","f",0)!=0; |
|
9d53f2f…
|
drh
|
1416 |
bIfExists = find_option("if-mirrored",0,0)!=0; |
|
de0bbcb…
|
drh
|
1417 |
gitmirror_verbosity = VERB_NORMAL; |
|
74d5ce3…
|
florian
|
1418 |
if( g.fQuiet ){ gitmirror_verbosity--; } /* Global option not repeatable. */ |
|
de0bbcb…
|
drh
|
1419 |
while( find_option("quiet","q",0)!=0 ){ gitmirror_verbosity--; } |
|
de0bbcb…
|
drh
|
1420 |
while( find_option("verbose","v",0)!=0 ){ gitmirror_verbosity++; } |
|
7029594…
|
drh
|
1421 |
verify_all_options(); |
|
fe98905…
|
drh
|
1422 |
if( g.argc!=4 && g.argc!=3 ){ usage("export ?MIRROR?"); } |
|
fe98905…
|
drh
|
1423 |
if( g.argc==4 ){ |
|
fe98905…
|
drh
|
1424 |
Blob mirror; |
|
fe98905…
|
drh
|
1425 |
file_canonical_name(g.argv[3], &mirror, 0); |
|
fe98905…
|
drh
|
1426 |
db_set("last-git-export-repo", blob_str(&mirror), 0); |
|
fe98905…
|
drh
|
1427 |
blob_reset(&mirror); |
|
fe98905…
|
drh
|
1428 |
} |
|
fe98905…
|
drh
|
1429 |
zMirror = db_get("last-git-export-repo", 0); |
|
fe98905…
|
drh
|
1430 |
if( zMirror==0 ){ |
|
9d53f2f…
|
drh
|
1431 |
if( bIfExists ) return; |
|
fe98905…
|
drh
|
1432 |
fossil_fatal("no Git repository specified"); |
|
9d53f2f…
|
drh
|
1433 |
} |
|
9d53f2f…
|
drh
|
1434 |
|
|
953fac2…
|
drh
|
1435 |
if( zMainBr ){ |
|
953fac2…
|
drh
|
1436 |
z = fossil_strdup(zMainBr); |
|
953fac2…
|
drh
|
1437 |
gitmirror_sanitize_name(z); |
|
953fac2…
|
drh
|
1438 |
if( strcmp(z, zMainBr) ){ |
|
953fac2…
|
drh
|
1439 |
fossil_fatal("\"%s\" is not a legal branch name for Git", zMainBr); |
|
953fac2…
|
drh
|
1440 |
} |
|
953fac2…
|
drh
|
1441 |
fossil_free(z); |
|
953fac2…
|
drh
|
1442 |
} |
|
953fac2…
|
drh
|
1443 |
|
|
7029594…
|
drh
|
1444 |
/* Make sure the GIT repository directory exists */ |
|
7029594…
|
drh
|
1445 |
rc = file_mkdir(zMirror, ExtFILE, 0); |
|
7029594…
|
drh
|
1446 |
if( rc ) fossil_fatal("cannot create directory \"%s\"", zMirror); |
|
7029594…
|
drh
|
1447 |
|
|
7029594…
|
drh
|
1448 |
/* Make sure GIT has been initialized */ |
|
7029594…
|
drh
|
1449 |
z = mprintf("%s/.git", zMirror); |
|
7029594…
|
drh
|
1450 |
if( !file_isdir(z, ExtFILE) ){ |
|
953fac2…
|
drh
|
1451 |
zMainBr = gitmirror_init(zMirror, zMainBr); |
|
e4d87cd…
|
drh
|
1452 |
bNeedRepack = 1; |
|
7029594…
|
drh
|
1453 |
} |
|
7029594…
|
drh
|
1454 |
fossil_free(z); |
|
275da70…
|
danield
|
1455 |
|
|
7029594…
|
drh
|
1456 |
/* Make sure the .mirror_state subdirectory exists */ |
|
7029594…
|
drh
|
1457 |
z = mprintf("%s/.mirror_state", zMirror); |
|
7029594…
|
drh
|
1458 |
rc = file_mkdir(z, ExtFILE, 0); |
|
7029594…
|
drh
|
1459 |
if( rc ) fossil_fatal("cannot create directory \"%s\"", z); |
|
7029594…
|
drh
|
1460 |
fossil_free(z); |
|
7029594…
|
drh
|
1461 |
|
|
7029594…
|
drh
|
1462 |
/* Attach the .mirror_state/db database */ |
|
7029594…
|
drh
|
1463 |
db_multi_exec("ATTACH '%q/.mirror_state/db' AS mirror;", zMirror); |
|
7029594…
|
drh
|
1464 |
db_begin_write(); |
|
7029594…
|
drh
|
1465 |
db_multi_exec( |
|
7029594…
|
drh
|
1466 |
"CREATE TABLE IF NOT EXISTS mirror.mconfig(\n" |
|
7029594…
|
drh
|
1467 |
" key TEXT PRIMARY KEY,\n" |
|
7029594…
|
drh
|
1468 |
" Value ANY\n" |
|
7029594…
|
drh
|
1469 |
") WITHOUT ROWID;\n" |
|
7029594…
|
drh
|
1470 |
"CREATE TABLE IF NOT EXISTS mirror.mmark(\n" |
|
7029594…
|
drh
|
1471 |
" id INTEGER PRIMARY KEY,\n" |
|
fdde65e…
|
drh
|
1472 |
" uuid TEXT,\n" |
|
fdde65e…
|
drh
|
1473 |
" isfile BOOLEAN,\n" |
|
fdde65e…
|
drh
|
1474 |
" githash TEXT,\n" |
|
fdde65e…
|
drh
|
1475 |
" UNIQUE(uuid,isfile)\n" |
|
7029594…
|
drh
|
1476 |
");" |
|
7029594…
|
drh
|
1477 |
); |
|
fdde65e…
|
drh
|
1478 |
if( !db_table_has_column("mirror","mmark","isfile") ){ |
|
fdde65e…
|
drh
|
1479 |
db_multi_exec( |
|
fdde65e…
|
drh
|
1480 |
"ALTER TABLE mirror.mmark RENAME TO mmark_old;" |
|
fdde65e…
|
drh
|
1481 |
"CREATE TABLE IF NOT EXISTS mirror.mmark(\n" |
|
fdde65e…
|
drh
|
1482 |
" id INTEGER PRIMARY KEY,\n" |
|
fdde65e…
|
drh
|
1483 |
" uuid TEXT,\n" |
|
fdde65e…
|
drh
|
1484 |
" isfile BOOLEAN,\n" |
|
fdde65e…
|
drh
|
1485 |
" githash TEXT,\n" |
|
fdde65e…
|
drh
|
1486 |
" UNIQUE(uuid,isfile)\n" |
|
fdde65e…
|
drh
|
1487 |
");" |
|
84949f5…
|
drh
|
1488 |
"INSERT OR IGNORE INTO mirror.mmark(id,uuid,githash,isfile)" |
|
fdde65e…
|
drh
|
1489 |
" SELECT id,uuid,githash," |
|
84949f5…
|
drh
|
1490 |
" NOT EXISTS(SELECT 1 FROM repository.event, repository.blob" |
|
84949f5…
|
drh
|
1491 |
" WHERE event.objid=blob.rid" |
|
fdde65e…
|
drh
|
1492 |
" AND blob.uuid=mmark_old.uuid)" |
|
fdde65e…
|
drh
|
1493 |
" FROM mirror.mmark_old;\n" |
|
fdde65e…
|
drh
|
1494 |
"DROP TABLE mirror.mmark_old;\n" |
|
fdde65e…
|
drh
|
1495 |
); |
|
fdde65e…
|
drh
|
1496 |
} |
|
ac5ae7b…
|
drh
|
1497 |
|
|
ac5ae7b…
|
drh
|
1498 |
/* Change the autopush setting if the --autopush flag is present */ |
|
ac5ae7b…
|
drh
|
1499 |
if( zAutoPush ){ |
|
ac5ae7b…
|
drh
|
1500 |
if( is_false(zAutoPush) ){ |
|
ac5ae7b…
|
drh
|
1501 |
db_multi_exec("DELETE FROM mirror.mconfig WHERE key='autopush'"); |
|
ac5ae7b…
|
drh
|
1502 |
}else{ |
|
ac5ae7b…
|
drh
|
1503 |
db_multi_exec( |
|
ac5ae7b…
|
drh
|
1504 |
"REPLACE INTO mirror.mconfig(key,value)" |
|
ac5ae7b…
|
drh
|
1505 |
"VALUES('autopush',%Q)", |
|
ac5ae7b…
|
drh
|
1506 |
zAutoPush |
|
ac5ae7b…
|
drh
|
1507 |
); |
|
ac5ae7b…
|
drh
|
1508 |
} |
|
ac5ae7b…
|
drh
|
1509 |
} |
|
ac5ae7b…
|
drh
|
1510 |
|
|
4c384ba…
|
drh
|
1511 |
/* Change the mainbranch setting if the --mainbranch flag is present */ |
|
4c384ba…
|
drh
|
1512 |
if( zMainBr && zMainBr[0] ){ |
|
4c384ba…
|
drh
|
1513 |
db_multi_exec( |
|
4c384ba…
|
drh
|
1514 |
"REPLACE INTO mirror.mconfig(key,value)" |
|
4c384ba…
|
drh
|
1515 |
"VALUES('mainbranch',%Q)", |
|
4c384ba…
|
drh
|
1516 |
zMainBr |
|
4c384ba…
|
drh
|
1517 |
); |
|
dbcf66b…
|
drh
|
1518 |
gitmirror_mainbranch = fossil_strdup(zMainBr); |
|
dbcf66b…
|
drh
|
1519 |
}else{ |
|
dbcf66b…
|
drh
|
1520 |
/* Recover the saved name of the main branch */ |
|
dbcf66b…
|
drh
|
1521 |
gitmirror_mainbranch = db_text("master", |
|
dbcf66b…
|
drh
|
1522 |
"SELECT value FROM mconfig WHERE key='mainbranch'"); |
|
4c384ba…
|
drh
|
1523 |
} |
|
dbcf66b…
|
drh
|
1524 |
|
|
4c384ba…
|
drh
|
1525 |
|
|
7029594…
|
drh
|
1526 |
/* See if there is any work to be done. Exit early if not, before starting |
|
7029594…
|
drh
|
1527 |
** the "git fast-import" command. */ |
|
fe98905…
|
drh
|
1528 |
if( !bForce |
|
fe98905…
|
drh
|
1529 |
&& !db_exists("SELECT 1 FROM event WHERE type IN ('ci','t')" |
|
7029594…
|
drh
|
1530 |
" AND mtime>coalesce((SELECT value FROM mconfig" |
|
7029594…
|
drh
|
1531 |
" WHERE key='start'),0.0)") |
|
7029594…
|
drh
|
1532 |
){ |
|
de0bbcb…
|
drh
|
1533 |
gitmirror_message(VERB_NORMAL, "no changes\n"); |
|
ac5ae7b…
|
drh
|
1534 |
db_commit_transaction(); |
|
7029594…
|
drh
|
1535 |
return; |
|
7029594…
|
drh
|
1536 |
} |
|
7029594…
|
drh
|
1537 |
|
|
7029594…
|
drh
|
1538 |
/* Change to the MIRROR directory so that the Git commands will work */ |
|
7029594…
|
drh
|
1539 |
rc = file_chdir(zMirror, 0); |
|
7029594…
|
drh
|
1540 |
if( rc ) fossil_fatal("cannot change the working directory to \"%s\"", |
|
7029594…
|
drh
|
1541 |
zMirror); |
|
7029594…
|
drh
|
1542 |
|
|
7029594…
|
drh
|
1543 |
/* Start up the git fast-import command */ |
|
7029594…
|
drh
|
1544 |
if( zDebug ){ |
|
7029594…
|
drh
|
1545 |
if( fossil_strcmp(zDebug,"stdout")==0 ){ |
|
7029594…
|
drh
|
1546 |
xCmd = stdout; |
|
7029594…
|
drh
|
1547 |
}else{ |
|
7029594…
|
drh
|
1548 |
xCmd = fopen(zDebug, "wb"); |
|
7029594…
|
drh
|
1549 |
if( xCmd==0 ) fossil_fatal("cannot open file \"%s\" for writing", zDebug); |
|
7029594…
|
drh
|
1550 |
} |
|
7029594…
|
drh
|
1551 |
}else{ |
|
7029594…
|
drh
|
1552 |
zCmd = mprintf("git fast-import" |
|
8e6f3ce…
|
drh
|
1553 |
" --export-marks=.mirror_state/marks.txt" |
|
7029594…
|
drh
|
1554 |
" --quiet --done"); |
|
de0bbcb…
|
drh
|
1555 |
gitmirror_message(VERB_NORMAL, "%s\n", zCmd); |
|
0dd6976…
|
drh
|
1556 |
#ifdef _WIN32 |
|
0dd6976…
|
drh
|
1557 |
xCmd = popen(zCmd, "wb"); |
|
0dd6976…
|
drh
|
1558 |
#else |
|
7029594…
|
drh
|
1559 |
xCmd = popen(zCmd, "w"); |
|
0dd6976…
|
drh
|
1560 |
#endif |
|
df5be59…
|
drh
|
1561 |
if( xCmd==0 ){ |
|
7029594…
|
drh
|
1562 |
fossil_fatal("cannot start the \"git fast-import\" command"); |
|
7029594…
|
drh
|
1563 |
} |
|
7029594…
|
drh
|
1564 |
fossil_free(zCmd); |
|
7029594…
|
drh
|
1565 |
} |
|
7029594…
|
drh
|
1566 |
|
|
7029594…
|
drh
|
1567 |
/* Run the export */ |
|
7029594…
|
drh
|
1568 |
rEnd = 0.0; |
|
7029594…
|
drh
|
1569 |
db_multi_exec( |
|
7029594…
|
drh
|
1570 |
"CREATE TEMP TABLE tomirror(objid,mtime,uuid);\n" |
|
7029594…
|
drh
|
1571 |
"INSERT INTO tomirror " |
|
7029594…
|
drh
|
1572 |
"SELECT objid, mtime, blob.uuid FROM event, blob\n" |
|
7029594…
|
drh
|
1573 |
" WHERE type='ci'" |
|
7029594…
|
drh
|
1574 |
" AND mtime>coalesce((SELECT value FROM mconfig WHERE key='start'),0.0)" |
|
7029594…
|
drh
|
1575 |
" AND blob.rid=event.objid" |
|
fbd3b58…
|
danield
|
1576 |
" AND blob.uuid NOT IN (SELECT uuid FROM mirror.mmark WHERE NOT isfile)" |
|
fbd3b58…
|
danield
|
1577 |
" AND NOT EXISTS (SELECT 1 FROM private WHERE rid=blob.rid);" |
|
7029594…
|
drh
|
1578 |
); |
|
7029594…
|
drh
|
1579 |
nTotal = db_int(0, "SELECT count(*) FROM tomirror"); |
|
7029594…
|
drh
|
1580 |
if( nLimit<nTotal ){ |
|
7029594…
|
drh
|
1581 |
nTotal = nLimit; |
|
7029594…
|
drh
|
1582 |
}else if( nLimit>nTotal ){ |
|
7029594…
|
drh
|
1583 |
nLimit = nTotal; |
|
7029594…
|
drh
|
1584 |
} |
|
7029594…
|
drh
|
1585 |
db_prepare(&q, |
|
7029594…
|
drh
|
1586 |
"SELECT objid, mtime, uuid FROM tomirror ORDER BY mtime" |
|
7029594…
|
drh
|
1587 |
); |
|
7029594…
|
drh
|
1588 |
while( nLimit && db_step(&q)==SQLITE_ROW ){ |
|
7029594…
|
drh
|
1589 |
int rid = db_column_int(&q, 0); |
|
7029594…
|
drh
|
1590 |
double rMTime = db_column_double(&q, 1); |
|
7029594…
|
drh
|
1591 |
const char *zUuid = db_column_text(&q, 2); |
|
7029594…
|
drh
|
1592 |
if( rMTime>rEnd ) rEnd = rMTime; |
|
dfc0f1b…
|
drh
|
1593 |
rc = gitmirror_send_checkin(xCmd, rid, zUuid, &nLimit); |
|
de0bbcb…
|
drh
|
1594 |
if( rc ) break; |
|
de0bbcb…
|
drh
|
1595 |
gitmirror_message(VERB_NORMAL,"%d/%d \r", nTotal-nLimit, nTotal); |
|
7029594…
|
drh
|
1596 |
fflush(stdout); |
|
7029594…
|
drh
|
1597 |
} |
|
7029594…
|
drh
|
1598 |
db_finalize(&q); |
|
7029594…
|
drh
|
1599 |
fprintf(xCmd, "done\n"); |
|
7029594…
|
drh
|
1600 |
if( zDebug ){ |
|
7029594…
|
drh
|
1601 |
if( xCmd!=stdout ) fclose(xCmd); |
|
7029594…
|
drh
|
1602 |
}else{ |
|
7029594…
|
drh
|
1603 |
pclose(xCmd); |
|
7029594…
|
drh
|
1604 |
} |
|
de0bbcb…
|
drh
|
1605 |
gitmirror_message(VERB_NORMAL, "%d check-ins added to the %s\n", |
|
de0bbcb…
|
drh
|
1606 |
nTotal-nLimit, zMirror); |
|
7029594…
|
drh
|
1607 |
|
|
7029594…
|
drh
|
1608 |
/* Read the export-marks file. Transfer the new marks over into |
|
7029594…
|
drh
|
1609 |
** the import-marks file. |
|
7029594…
|
drh
|
1610 |
*/ |
|
8e6f3ce…
|
drh
|
1611 |
pMarks = fopen(".mirror_state/marks.txt", "rb"); |
|
8e6f3ce…
|
drh
|
1612 |
if( pMarks ){ |
|
7029594…
|
drh
|
1613 |
db_prepare(&q, "UPDATE mirror.mmark SET githash=:githash WHERE id=:id"); |
|
8e6f3ce…
|
drh
|
1614 |
while( fgets(zLine, sizeof(zLine), pMarks) ){ |
|
7029594…
|
drh
|
1615 |
int j, k; |
|
7029594…
|
drh
|
1616 |
if( zLine[0]!=':' ) continue; |
|
7029594…
|
drh
|
1617 |
db_bind_int(&q, ":id", atoi(zLine+1)); |
|
7029594…
|
drh
|
1618 |
for(j=1; zLine[j] && zLine[j]!=' '; j++){} |
|
7029594…
|
drh
|
1619 |
if( zLine[j]!=' ' ) continue; |
|
7029594…
|
drh
|
1620 |
j++; |
|
7029594…
|
drh
|
1621 |
if( zLine[j]==0 ) continue; |
|
7029594…
|
drh
|
1622 |
for(k=j; fossil_isalnum(zLine[k]); k++){} |
|
7029594…
|
drh
|
1623 |
zLine[k] = 0; |
|
7029594…
|
drh
|
1624 |
db_bind_text(&q, ":githash", &zLine[j]); |
|
7029594…
|
drh
|
1625 |
db_step(&q); |
|
7029594…
|
drh
|
1626 |
db_reset(&q); |
|
7029594…
|
drh
|
1627 |
} |
|
7029594…
|
drh
|
1628 |
db_finalize(&q); |
|
8e6f3ce…
|
drh
|
1629 |
fclose(pMarks); |
|
8e6f3ce…
|
drh
|
1630 |
file_delete(".mirror_state/marks.txt"); |
|
7029594…
|
drh
|
1631 |
}else{ |
|
7029594…
|
drh
|
1632 |
fossil_fatal("git fast-import didn't generate a marks file!"); |
|
7029594…
|
drh
|
1633 |
} |
|
7029594…
|
drh
|
1634 |
db_multi_exec( |
|
7029594…
|
drh
|
1635 |
"CREATE INDEX IF NOT EXISTS mirror.mmarkx1 ON mmark(githash);" |
|
7029594…
|
drh
|
1636 |
); |
|
7029594…
|
drh
|
1637 |
|
|
7029594…
|
drh
|
1638 |
/* Do any tags that have been created since the start time */ |
|
7029594…
|
drh
|
1639 |
db_prepare(&q, |
|
7029594…
|
drh
|
1640 |
"SELECT substr(tagname,5), githash" |
|
7029594…
|
drh
|
1641 |
" FROM (SELECT tagxref.tagid AS xtagid, tagname, rid, max(mtime) AS mtime" |
|
7029594…
|
drh
|
1642 |
" FROM tagxref JOIN tag ON tag.tagid=tagxref.tagid" |
|
7029594…
|
drh
|
1643 |
" WHERE tag.tagname GLOB 'sym-*'" |
|
7029594…
|
drh
|
1644 |
" AND tagxref.tagtype=1" |
|
7029594…
|
drh
|
1645 |
" AND tagxref.mtime > coalesce((SELECT value FROM mconfig" |
|
7029594…
|
drh
|
1646 |
" WHERE key='start'),0.0)" |
|
7029594…
|
drh
|
1647 |
" GROUP BY tagxref.tagid) AS tx" |
|
7029594…
|
drh
|
1648 |
" JOIN blob ON tx.rid=blob.rid" |
|
7029594…
|
drh
|
1649 |
" JOIN mmark ON mmark.uuid=blob.uuid;" |
|
7029594…
|
drh
|
1650 |
); |
|
7029594…
|
drh
|
1651 |
while( db_step(&q)==SQLITE_ROW ){ |
|
7029594…
|
drh
|
1652 |
char *zTagname = fossil_strdup(db_column_text(&q,0)); |
|
7029594…
|
drh
|
1653 |
const char *zObj = db_column_text(&q,1); |
|
7029594…
|
drh
|
1654 |
char *zTagCmd; |
|
7029594…
|
drh
|
1655 |
gitmirror_sanitize_name(zTagname); |
|
2209f55…
|
drh
|
1656 |
zTagCmd = mprintf("git tag -f %$ %$", zTagname, zObj); |
|
7029594…
|
drh
|
1657 |
fossil_free(zTagname); |
|
de0bbcb…
|
drh
|
1658 |
gitmirror_message(VERB_NORMAL, "%s\n", zTagCmd); |
|
7029594…
|
drh
|
1659 |
fossil_system(zTagCmd); |
|
7029594…
|
drh
|
1660 |
fossil_free(zTagCmd); |
|
ab68afe…
|
drh
|
1661 |
} |
|
ab68afe…
|
drh
|
1662 |
db_finalize(&q); |
|
ab68afe…
|
drh
|
1663 |
|
|
ab68afe…
|
drh
|
1664 |
/* Update all references that might have changed since the start time */ |
|
ab68afe…
|
drh
|
1665 |
db_prepare(&q, |
|
ab68afe…
|
drh
|
1666 |
"SELECT" |
|
ab68afe…
|
drh
|
1667 |
" tagxref.value AS name," |
|
ab68afe…
|
drh
|
1668 |
" max(event.mtime) AS mtime," |
|
ab68afe…
|
drh
|
1669 |
" mmark.githash AS gitckin" |
|
ab68afe…
|
drh
|
1670 |
" FROM tagxref, tag, event, blob, mmark" |
|
ab68afe…
|
drh
|
1671 |
" WHERE tagxref.tagid=tag.tagid" |
|
ab68afe…
|
drh
|
1672 |
" AND tagxref.tagtype>0" |
|
ab68afe…
|
drh
|
1673 |
" AND tag.tagname='branch'" |
|
ab68afe…
|
drh
|
1674 |
" AND event.objid=tagxref.rid" |
|
ab68afe…
|
drh
|
1675 |
" AND event.mtime > coalesce((SELECT value FROM mconfig" |
|
ab68afe…
|
drh
|
1676 |
" WHERE key='start'),0.0)" |
|
ab68afe…
|
drh
|
1677 |
" AND blob.rid=tagxref.rid" |
|
ab68afe…
|
drh
|
1678 |
" AND mmark.uuid=blob.uuid" |
|
ab68afe…
|
drh
|
1679 |
" GROUP BY 1" |
|
ab68afe…
|
drh
|
1680 |
); |
|
ab68afe…
|
drh
|
1681 |
while( db_step(&q)==SQLITE_ROW ){ |
|
ab68afe…
|
drh
|
1682 |
char *zBrname = fossil_strdup(db_column_text(&q,0)); |
|
ab68afe…
|
drh
|
1683 |
const char *zObj = db_column_text(&q,2); |
|
ab68afe…
|
drh
|
1684 |
char *zRefCmd; |
|
ab68afe…
|
drh
|
1685 |
if( fossil_strcmp(zBrname,"trunk")==0 ){ |
|
ab68afe…
|
drh
|
1686 |
fossil_free(zBrname); |
|
4c384ba…
|
drh
|
1687 |
zBrname = fossil_strdup(gitmirror_mainbranch); |
|
ab68afe…
|
drh
|
1688 |
}else{ |
|
ab68afe…
|
drh
|
1689 |
gitmirror_sanitize_name(zBrname); |
|
ab68afe…
|
drh
|
1690 |
} |
|
2209f55…
|
drh
|
1691 |
zRefCmd = mprintf("git update-ref \"refs/heads/%s\" %$", zBrname, zObj); |
|
ab68afe…
|
drh
|
1692 |
fossil_free(zBrname); |
|
ab68afe…
|
drh
|
1693 |
gitmirror_message(VERB_NORMAL, "%s\n", zRefCmd); |
|
ab68afe…
|
drh
|
1694 |
fossil_system(zRefCmd); |
|
ab68afe…
|
drh
|
1695 |
fossil_free(zRefCmd); |
|
fe98905…
|
drh
|
1696 |
} |
|
7029594…
|
drh
|
1697 |
db_finalize(&q); |
|
fe98905…
|
drh
|
1698 |
|
|
fe98905…
|
drh
|
1699 |
/* Update the start time */ |
|
fe98905…
|
drh
|
1700 |
if( rEnd>0.0 ){ |
|
fe98905…
|
drh
|
1701 |
db_prepare(&q, "REPLACE INTO mirror.mconfig(key,value) VALUES('start',:x)"); |
|
fe98905…
|
drh
|
1702 |
db_bind_double(&q, ":x", rEnd); |
|
fe98905…
|
drh
|
1703 |
db_step(&q); |
|
fe98905…
|
drh
|
1704 |
db_finalize(&q); |
|
fe98905…
|
drh
|
1705 |
} |
|
7029594…
|
drh
|
1706 |
db_commit_transaction(); |
|
fe98905…
|
drh
|
1707 |
|
|
e4d87cd…
|
drh
|
1708 |
/* Maybe run a git repack */ |
|
e4d87cd…
|
drh
|
1709 |
if( bNeedRepack ){ |
|
e4d87cd…
|
drh
|
1710 |
const char *zRepack = "git repack -adf"; |
|
e4d87cd…
|
drh
|
1711 |
gitmirror_message(VERB_NORMAL, "%s\n", zRepack); |
|
e4d87cd…
|
drh
|
1712 |
fossil_system(zRepack); |
|
e4d87cd…
|
drh
|
1713 |
} |
|
e4d87cd…
|
drh
|
1714 |
|
|
7029594…
|
drh
|
1715 |
/* Optionally do a "git push" */ |
|
ac5ae7b…
|
drh
|
1716 |
zPushUrl = db_text(0, "SELECT value FROM mconfig WHERE key='autopush'"); |
|
ac5ae7b…
|
drh
|
1717 |
if( zPushUrl ){ |
|
aaa937a…
|
drh
|
1718 |
char *zPushCmd; |
|
aaa937a…
|
drh
|
1719 |
UrlData url; |
|
cd212e8…
|
drh
|
1720 |
if( sqlite3_strglob("http*", zPushUrl)==0 ){ |
|
cd212e8…
|
drh
|
1721 |
url_parse_local(zPushUrl, 0, &url); |
|
cd212e8…
|
drh
|
1722 |
zPushCmd = mprintf("git push --mirror %s", url.canonical); |
|
cd212e8…
|
drh
|
1723 |
}else{ |
|
cd212e8…
|
drh
|
1724 |
zPushCmd = mprintf("git push --mirror %s", zPushUrl); |
|
cd212e8…
|
drh
|
1725 |
} |
|
de0bbcb…
|
drh
|
1726 |
gitmirror_message(VERB_NORMAL, "%s\n", zPushCmd); |
|
aaa937a…
|
drh
|
1727 |
fossil_free(zPushCmd); |
|
2209f55…
|
drh
|
1728 |
zPushCmd = mprintf("git push --mirror %$", zPushUrl); |
|
da5faf1…
|
drh
|
1729 |
rc = fossil_system(zPushCmd); |
|
da5faf1…
|
drh
|
1730 |
if( rc ){ |
|
da5faf1…
|
drh
|
1731 |
fossil_fatal("cannot push content using: %s", zPushCmd); |
|
458b35a…
|
drh
|
1732 |
}else if( db_is_writeable("repository") ){ |
|
458b35a…
|
drh
|
1733 |
db_unprotect(PROTECT_CONFIG); |
|
458b35a…
|
drh
|
1734 |
db_multi_exec("REPLACE INTO config(name,value,mtime)" |
|
ebcce3c…
|
drh
|
1735 |
"VALUES('gitpush:%q','{}',now())", zPushUrl); |
|
458b35a…
|
drh
|
1736 |
db_protect_pop(); |
|
da5faf1…
|
drh
|
1737 |
} |
|
ac5ae7b…
|
drh
|
1738 |
fossil_free(zPushCmd); |
|
ac5ae7b…
|
drh
|
1739 |
} |
|
ac5ae7b…
|
drh
|
1740 |
} |
|
ac5ae7b…
|
drh
|
1741 |
|
|
ac5ae7b…
|
drh
|
1742 |
/* |
|
f77ebee…
|
drh
|
1743 |
** Implementation of the "fossil git status" command. |
|
f77ebee…
|
drh
|
1744 |
** |
|
f77ebee…
|
drh
|
1745 |
** Show the status of a "git export". |
|
f77ebee…
|
drh
|
1746 |
*/ |
|
f77ebee…
|
drh
|
1747 |
void gitmirror_status_command(void){ |
|
f77ebee…
|
drh
|
1748 |
char *zMirror; |
|
f77ebee…
|
drh
|
1749 |
char *z; |
|
a0a5be0…
|
drh
|
1750 |
int n, k; |
|
999d838…
|
drh
|
1751 |
int rc; |
|
999d838…
|
drh
|
1752 |
char *zSql; |
|
9179663…
|
drh
|
1753 |
int bQuiet = 0; |
|
9179663…
|
drh
|
1754 |
int bByAll = 0; /* Undocumented option meaning this command was invoked |
|
9179663…
|
drh
|
1755 |
** from "fossil all" and should modify output accordingly */ |
|
9179663…
|
drh
|
1756 |
|
|
f77ebee…
|
drh
|
1757 |
db_find_and_open_repository(0, 0); |
|
74d5ce3…
|
florian
|
1758 |
bQuiet = g.fQuiet; |
|
275da70…
|
danield
|
1759 |
bByAll = find_option("by-all",0,0)!=0; |
|
f77ebee…
|
drh
|
1760 |
verify_all_options(); |
|
f77ebee…
|
drh
|
1761 |
zMirror = db_get("last-git-export-repo", 0); |
|
f77ebee…
|
drh
|
1762 |
if( zMirror==0 ){ |
|
999d838…
|
drh
|
1763 |
if( bQuiet ) return; |
|
999d838…
|
drh
|
1764 |
if( bByAll ) return; |
|
999d838…
|
drh
|
1765 |
fossil_print("Git mirror: none\n"); |
|
999d838…
|
drh
|
1766 |
return; |
|
999d838…
|
drh
|
1767 |
} |
|
999d838…
|
drh
|
1768 |
zSql = sqlite3_mprintf("ATTACH '%q/.mirror_state/db' AS mirror", zMirror); |
|
999d838…
|
drh
|
1769 |
if( zSql==0 ) fossil_fatal("out of memory"); |
|
999d838…
|
drh
|
1770 |
g.dbIgnoreErrors++; |
|
999d838…
|
drh
|
1771 |
rc = sqlite3_exec(g.db, zSql, 0, 0, 0); |
|
999d838…
|
drh
|
1772 |
g.dbIgnoreErrors--; |
|
999d838…
|
drh
|
1773 |
sqlite3_free(zSql); |
|
999d838…
|
drh
|
1774 |
if( rc ){ |
|
999d838…
|
drh
|
1775 |
if( bQuiet ) return; |
|
999d838…
|
drh
|
1776 |
if( bByAll ) return; |
|
999d838…
|
drh
|
1777 |
fossil_print("Git mirror: %s (Inactive)\n", zMirror); |
|
9179663…
|
drh
|
1778 |
return; |
|
9179663…
|
drh
|
1779 |
} |
|
9179663…
|
drh
|
1780 |
if( bByAll ){ |
|
9179663…
|
drh
|
1781 |
size_t len = strlen(g.zRepositoryName); |
|
9179663…
|
drh
|
1782 |
int n; |
|
9179663…
|
drh
|
1783 |
if( len>60 ) len = 60; |
|
9179663…
|
drh
|
1784 |
n = (int)(65 - len); |
|
9179663…
|
drh
|
1785 |
fossil_print("%.12c %s %.*c\n", '*', g.zRepositoryName, n, '*'); |
|
9179663…
|
drh
|
1786 |
} |
|
f77ebee…
|
drh
|
1787 |
fossil_print("Git mirror: %s\n", zMirror); |
|
f77ebee…
|
drh
|
1788 |
z = db_text(0, "SELECT datetime(value) FROM mconfig WHERE key='start'"); |
|
f77ebee…
|
drh
|
1789 |
if( z ){ |
|
8b18d01…
|
drh
|
1790 |
double rAge = db_double(0.0, "SELECT julianday('now') - value" |
|
8b18d01…
|
drh
|
1791 |
" FROM mconfig WHERE key='start'"); |
|
8b18d01…
|
drh
|
1792 |
if( rAge>1.0/86400.0 ){ |
|
8b18d01…
|
drh
|
1793 |
fossil_print("Last export: %s (%z ago)\n", z, human_readable_age(rAge)); |
|
8b18d01…
|
drh
|
1794 |
}else{ |
|
8b18d01…
|
drh
|
1795 |
fossil_print("Last export: %s (moments ago)\n", z); |
|
8b18d01…
|
drh
|
1796 |
} |
|
f77ebee…
|
drh
|
1797 |
} |
|
f77ebee…
|
drh
|
1798 |
z = db_text(0, "SELECT value FROM mconfig WHERE key='autopush'"); |
|
f77ebee…
|
drh
|
1799 |
if( z==0 ){ |
|
f77ebee…
|
drh
|
1800 |
fossil_print("Autopush: off\n"); |
|
f77ebee…
|
drh
|
1801 |
}else{ |
|
f77ebee…
|
drh
|
1802 |
UrlData url; |
|
0028303…
|
drh
|
1803 |
if( sqlite3_strglob("http*", z)==0 ){ |
|
0028303…
|
drh
|
1804 |
url_parse_local(z, 0, &url); |
|
0028303…
|
drh
|
1805 |
fossil_print("Autopush: %s\n", url.canonical); |
|
0028303…
|
drh
|
1806 |
}else{ |
|
0298a6f…
|
drh
|
1807 |
fossil_print("Autopush: %s\n", z); |
|
0028303…
|
drh
|
1808 |
} |
|
4c384ba…
|
drh
|
1809 |
fossil_free(z); |
|
a0a5be0…
|
drh
|
1810 |
} |
|
a0a5be0…
|
drh
|
1811 |
n = db_int(0, |
|
a0a5be0…
|
drh
|
1812 |
"SELECT count(*) FROM event" |
|
a0a5be0…
|
drh
|
1813 |
" WHERE type='ci'" |
|
a0a5be0…
|
drh
|
1814 |
" AND mtime>coalesce((SELECT value FROM mconfig" |
|
a0a5be0…
|
drh
|
1815 |
" WHERE key='start'),0.0)" |
|
a0a5be0…
|
drh
|
1816 |
); |
|
4c384ba…
|
drh
|
1817 |
z = db_text("master", "SELECT value FROM mconfig WHERE key='mainbranch'"); |
|
4c384ba…
|
drh
|
1818 |
fossil_print("Main-Branch: %s\n",z); |
|
a0a5be0…
|
drh
|
1819 |
if( n==0 ){ |
|
a0a5be0…
|
drh
|
1820 |
fossil_print("Status: up-to-date\n"); |
|
a0a5be0…
|
drh
|
1821 |
}else{ |
|
8b18d01…
|
drh
|
1822 |
fossil_print("Status: %d check-in%s awaiting export\n", |
|
8b18d01…
|
drh
|
1823 |
n, n==1 ? "" : "s"); |
|
f77ebee…
|
drh
|
1824 |
} |
|
a0a5be0…
|
drh
|
1825 |
n = db_int(0, "SELECT count(*) FROM mmark WHERE isfile"); |
|
999d838…
|
drh
|
1826 |
k = db_int(0, "SELECT count(*) FROM mmark WHERE NOT isfile"); |
|
a0a5be0…
|
drh
|
1827 |
fossil_print("Exported: %d check-ins and %d file blobs\n", k, n); |
|
f77ebee…
|
drh
|
1828 |
} |
|
f77ebee…
|
drh
|
1829 |
|
|
f77ebee…
|
drh
|
1830 |
/* |
|
84f697e…
|
drh
|
1831 |
** COMMAND: git* |
|
7029594…
|
drh
|
1832 |
** |
|
7029594…
|
drh
|
1833 |
** Usage: %fossil git SUBCOMMAND |
|
7029594…
|
drh
|
1834 |
** |
|
7029594…
|
drh
|
1835 |
** Do incremental import or export operations between Fossil and Git. |
|
7029594…
|
drh
|
1836 |
** Subcommands: |
|
7029594…
|
drh
|
1837 |
** |
|
e58c76a…
|
drh
|
1838 |
** > fossil git export [MIRROR] [OPTIONS] |
|
7029594…
|
drh
|
1839 |
** |
|
7029594…
|
drh
|
1840 |
** Write content from the Fossil repository into the Git repository |
|
7029594…
|
drh
|
1841 |
** in directory MIRROR. The Git repository is created if it does not |
|
7029594…
|
drh
|
1842 |
** already exist. If the Git repository does already exist, then |
|
7029594…
|
drh
|
1843 |
** new content added to fossil since the previous export is appended. |
|
7029594…
|
drh
|
1844 |
** |
|
bc36fdc…
|
danield
|
1845 |
** Repeat this command whenever new check-ins are added to the Fossil |
|
fe98905…
|
drh
|
1846 |
** repository in order to reflect those changes into the mirror. If |
|
fe98905…
|
drh
|
1847 |
** the MIRROR option is omitted, the repository from the previous |
|
fe98905…
|
drh
|
1848 |
** invocation is used. |
|
7029594…
|
drh
|
1849 |
** |
|
7029594…
|
drh
|
1850 |
** The MIRROR directory will contain a subdirectory named |
|
7029594…
|
drh
|
1851 |
** ".mirror_state" that contains information that Fossil needs to |
|
7029594…
|
drh
|
1852 |
** do incremental exports. Do not attempt to manage or edit the files |
|
7029594…
|
drh
|
1853 |
** in that directory since doing so can disrupt future incremental |
|
7029594…
|
drh
|
1854 |
** exports. |
|
7029594…
|
drh
|
1855 |
** |
|
7029594…
|
drh
|
1856 |
** Options: |
|
ac5ae7b…
|
drh
|
1857 |
** --autopush URL Automatically do a 'git push' to URL. The |
|
ac5ae7b…
|
drh
|
1858 |
** URL is remembered and used on subsequent exports |
|
ac5ae7b…
|
drh
|
1859 |
** to the same repository. Or if URL is "off" the |
|
ac5ae7b…
|
drh
|
1860 |
** auto-push mechanism is disabled |
|
7029594…
|
drh
|
1861 |
** --debug FILE Write fast-export text to FILE rather than |
|
2e56ef4…
|
km
|
1862 |
** piping it into "git fast-import" |
|
ba7b431…
|
stephan
|
1863 |
** -f|--force Do the export even if nothing has changed |
|
2e56ef4…
|
km
|
1864 |
** --if-mirrored No-op if the mirror does not already exist |
|
7029594…
|
drh
|
1865 |
** --limit N Add no more than N new check-ins to MIRROR. |
|
7029594…
|
drh
|
1866 |
** Useful for debugging |
|
4c384ba…
|
drh
|
1867 |
** --mainbranch NAME Use NAME as the name of the main branch in Git. |
|
4c384ba…
|
drh
|
1868 |
** The "trunk" branch of the Fossil repository is |
|
4c384ba…
|
drh
|
1869 |
** mapped into this name. "master" is used if |
|
4c384ba…
|
drh
|
1870 |
** this option is omitted. |
|
ba7b431…
|
stephan
|
1871 |
** -q|--quiet Reduce output. Repeat for even less output. |
|
2e56ef4…
|
km
|
1872 |
** -v|--verbose More output |
|
e58c76a…
|
drh
|
1873 |
** |
|
e58c76a…
|
drh
|
1874 |
** > fossil git import MIRROR |
|
e58c76a…
|
drh
|
1875 |
** |
|
275da70…
|
danield
|
1876 |
** TBD... |
|
e58c76a…
|
drh
|
1877 |
** |
|
e58c76a…
|
drh
|
1878 |
** > fossil git status |
|
7029594…
|
drh
|
1879 |
** |
|
f77ebee…
|
drh
|
1880 |
** Show the status of the current Git mirror, if there is one. |
|
9179663…
|
drh
|
1881 |
** |
|
9179663…
|
drh
|
1882 |
** -q|--quiet No output if there is nothing to report |
|
7029594…
|
drh
|
1883 |
*/ |
|
7029594…
|
drh
|
1884 |
void gitmirror_command(void){ |
|
7029594…
|
drh
|
1885 |
char *zCmd; |
|
7029594…
|
drh
|
1886 |
int nCmd; |
|
7029594…
|
drh
|
1887 |
if( g.argc<3 ){ |
|
1aa3188…
|
danield
|
1888 |
usage("SUBCOMMAND ..."); |
|
7029594…
|
drh
|
1889 |
} |
|
7029594…
|
drh
|
1890 |
zCmd = g.argv[2]; |
|
7029594…
|
drh
|
1891 |
nCmd = (int)strlen(zCmd); |
|
7029594…
|
drh
|
1892 |
if( nCmd>2 && strncmp(zCmd,"export",nCmd)==0 ){ |
|
7029594…
|
drh
|
1893 |
gitmirror_export_command(); |
|
7029594…
|
drh
|
1894 |
}else |
|
7029594…
|
drh
|
1895 |
if( nCmd>2 && strncmp(zCmd,"import",nCmd)==0 ){ |
|
7029594…
|
drh
|
1896 |
fossil_fatal("not yet implemented - check back later"); |
|
7029594…
|
drh
|
1897 |
}else |
|
f77ebee…
|
drh
|
1898 |
if( nCmd>2 && strncmp(zCmd,"status",nCmd)==0 ){ |
|
f77ebee…
|
drh
|
1899 |
gitmirror_status_command(); |
|
f77ebee…
|
drh
|
1900 |
}else |
|
7029594…
|
drh
|
1901 |
{ |
|
7029594…
|
drh
|
1902 |
fossil_fatal("unknown subcommand \"%s\": should be one of " |
|
f77ebee…
|
drh
|
1903 |
"\"export\", \"import\", \"status\"", |
|
7029594…
|
drh
|
1904 |
zCmd); |
|
7029594…
|
drh
|
1905 |
} |
|
0f024d3…
|
drh
|
1906 |
} |