Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Improved error messages when a commit fails due to a lock conflict: Show the age of the lock in addition to the user. Suggest using the new --override-lock option instead of --allow-fork. Expire locks after any successful check-in from the same check-out. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA3-256: |
3a5caa86dbaebba1fd4a956042ebdab2 |
| User & Date: | drh 2019-07-12 13:58:43.459 |
Context
|
2019-07-12
| ||
| 14:08 | Add documentation on the "pragma ci-unlock" message in the sync protocol. ... (check-in: 1eefb8dbec user: drh tags: trunk) | |
| 13:58 | Improved error messages when a commit fails due to a lock conflict: Show the age of the lock in addition to the user. Suggest using the new --override-lock option instead of --allow-fork. Expire locks after any successful check-in from the same check-out. ... (check-in: 3a5caa86db user: drh tags: trunk) | |
| 13:24 | Clarified the scale of the Linux vs SQLite projects in the same-named section of the Git vs Fossil doc, tightened up the discussion a bit, and added hard SLOC stats to ground it all. Most controversially, added the rattle wrench analogy. ... (check-in: 69e7827d26 user: wyoung tags: trunk) | |
Changes
Changes to src/checkin.c.
| ︙ | ︙ | |||
2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 | ** --mimetype MIMETYPE mimetype of check-in comment ** -n|--dry-run If given, display instead of run actions ** --no-prompt This option disables prompting the user for ** input and assumes an answer of 'No' for every ** question. ** --no-warnings omit all warnings about file contents ** --nosign do not attempt to sign this commit with gpg ** --private do not sync changes and their descendants ** --hash verify file status using hashing rather ** than relying on file mtimes ** --tag TAG-NAME assign given tag TAG-NAME to the check-in ** --date-override DATETIME DATE to use instead of 'now' ** --user-override USER USER to use instead of the current default ** | > | 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 | ** --mimetype MIMETYPE mimetype of check-in comment ** -n|--dry-run If given, display instead of run actions ** --no-prompt This option disables prompting the user for ** input and assumes an answer of 'No' for every ** question. ** --no-warnings omit all warnings about file contents ** --nosign do not attempt to sign this commit with gpg ** --override-lock allow a check-in even though parent is locked ** --private do not sync changes and their descendants ** --hash verify file status using hashing rather ** than relying on file mtimes ** --tag TAG-NAME assign given tag TAG-NAME to the check-in ** --date-override DATETIME DATE to use instead of 'now' ** --user-override USER USER to use instead of the current default ** |
| ︙ | ︙ | |||
2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 |
dryRunFlag = find_option("test",0,0)!=0; /* deprecated */
}
zComment = find_option("comment","m",1);
forceFlag = find_option("force", "f", 0)!=0;
allowConflict = find_option("allow-conflict",0,0)!=0;
allowEmpty = find_option("allow-empty",0,0)!=0;
allowFork = find_option("allow-fork",0,0)!=0;
allowOlder = find_option("allow-older",0,0)!=0;
noPrompt = find_option("no-prompt", 0, 0)!=0;
noWarningFlag = find_option("no-warnings", 0, 0)!=0;
sCiInfo.zBranch = find_option("branch","b",1);
sCiInfo.zColor = find_option("bgcolor",0,1);
sCiInfo.zBrClr = find_option("branchcolor",0,1);
sCiInfo.closeFlag = find_option("close",0,0)!=0;
| > | 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 |
dryRunFlag = find_option("test",0,0)!=0; /* deprecated */
}
zComment = find_option("comment","m",1);
forceFlag = find_option("force", "f", 0)!=0;
allowConflict = find_option("allow-conflict",0,0)!=0;
allowEmpty = find_option("allow-empty",0,0)!=0;
allowFork = find_option("allow-fork",0,0)!=0;
if( find_option("override-lock",0,0)!=0 ) allowFork = 1;
allowOlder = find_option("allow-older",0,0)!=0;
noPrompt = find_option("no-prompt", 0, 0)!=0;
noWarningFlag = find_option("no-warnings", 0, 0)!=0;
sCiInfo.zBranch = find_option("branch","b",1);
sCiInfo.zColor = find_option("bgcolor",0,1);
sCiInfo.zBrClr = find_option("branchcolor",0,1);
sCiInfo.closeFlag = find_option("close",0,0)!=0;
|
| ︙ | ︙ | |||
2270 2271 2272 2273 2274 2275 2276 | } /* ** Do not allow a commit that will cause a fork unless the --allow-fork ** or --force flags is used, or unless this is a private check-in. ** The initial commit MUST have tags "trunk" and "sym-trunk". */ | < | | < < < < < < | | > < < < < > > > > > | < > > | 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 |
}
/*
** Do not allow a commit that will cause a fork unless the --allow-fork
** or --force flags is used, or unless this is a private check-in.
** The initial commit MUST have tags "trunk" and "sym-trunk".
*/
if( sCiInfo.zBranch==0
&& allowFork==0
&& forceFlag==0
&& g.markPrivate==0
&& (vid==0 || !is_a_leaf(vid) || g.ckinLockFail)
){
if( g.ckinLockFail ){
fossil_fatal("Might fork due to a check-in race with user \"%s\"\n"
"Try \"update\" first, or --branch, or use --override-lock",
g.ckinLockFail);
}else{
fossil_fatal("Would fork. \"update\" first or use --branch or "
"--allow-fork.");
}
}
/*
** Do not allow a commit against a closed leaf unless the commit
** ends up on a different branch.
*/
if(
|
| ︙ | ︙ |
Changes to src/main.c.
| ︙ | ︙ | |||
188 189 190 191 192 193 194 | int th1Flags; /* The TH1 integration state flags */ FILE *httpIn; /* Accept HTTP input from here */ FILE *httpOut; /* Send HTTP output here */ int xlinkClusterOnly; /* Set when cloning. Only process clusters */ int fTimeFormat; /* 1 for UTC. 2 for localtime. 0 not yet selected */ int *aCommitFile; /* Array of files to be committed */ int markPrivate; /* All new artifacts are private if true */ | | | 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 | int th1Flags; /* The TH1 integration state flags */ FILE *httpIn; /* Accept HTTP input from here */ FILE *httpOut; /* Send HTTP output here */ int xlinkClusterOnly; /* Set when cloning. Only process clusters */ int fTimeFormat; /* 1 for UTC. 2 for localtime. 0 not yet selected */ int *aCommitFile; /* Array of files to be committed */ int markPrivate; /* All new artifacts are private if true */ char *ckinLockFail; /* Check-in lock failure received from server */ int clockSkewSeen; /* True if clocks on client and server out of sync */ int wikiFlags; /* Wiki conversion flags applied to %W */ char isHTTP; /* True if server/CGI modes, else assume CLI. */ char javascriptHyperlink; /* If true, set href= using script, not HTML */ Blob httpHeader; /* Complete text of the HTTP request header */ UrlData url; /* Information about current URL */ const char *zLogin; /* Login name. NULL or "" if not logged in. */ |
| ︙ | ︙ |
Changes to src/xfer.c.
| ︙ | ︙ | |||
1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 |
*/
if( blob_eq(&xfer.aToken[1], "ci-lock")
&& xfer.nToken==4
&& blob_is_hname(&xfer.aToken[2])
){
Stmt q;
sqlite3_int64 iNow = time(0);
int seenFault = 0;
db_prepare(&q,
"SELECT json_extract(value,'$.login'),"
" mtime,"
" json_extract(value,'$.clientid'),"
" (SELECT rid FROM blob WHERE uuid=substr(name,9)),"
" name"
" FROM config WHERE name GLOB 'ci-lock-*'"
);
while( db_step(&q)==SQLITE_ROW ){
int x = db_column_int(&q,3);
const char *zName = db_column_text(&q,4);
| > | | | | 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 |
*/
if( blob_eq(&xfer.aToken[1], "ci-lock")
&& xfer.nToken==4
&& blob_is_hname(&xfer.aToken[2])
){
Stmt q;
sqlite3_int64 iNow = time(0);
const sqlite3_int64 maxAge = 3600*24; /* Locks expire after 24 hours */
int seenFault = 0;
db_prepare(&q,
"SELECT json_extract(value,'$.login'),"
" mtime,"
" json_extract(value,'$.clientid'),"
" (SELECT rid FROM blob WHERE uuid=substr(name,9)),"
" name"
" FROM config WHERE name GLOB 'ci-lock-*'"
);
while( db_step(&q)==SQLITE_ROW ){
int x = db_column_int(&q,3);
const char *zName = db_column_text(&q,4);
if( db_column_int64(&q,1)<iNow-maxAge || !is_a_leaf(x) ){
/* check-in locks expire after maxAge seconds, or when the
** check-in is no longer a leaf */
db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
continue;
}
if( fossil_strcmp(zName+8, blob_str(&xfer.aToken[2]))==0 ){
const char *zClientId = db_column_text(&q, 2);
const char *zLogin = db_column_text(&q,0);
sqlite3_int64 mtime = db_column_int64(&q, 1);
|
| ︙ | ︙ | |||
1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 |
"REPLACE INTO config(name,value,mtime)"
"VALUES('ci-lock-%q',json_object('login',%Q,'clientid',%Q),now())",
blob_str(&xfer.aToken[2]), g.zLogin,
blob_str(&xfer.aToken[3])
);
}
}
}else
/* Unknown message
*/
{
cgi_reset_content();
@ error bad\scommand:\s%F(blob_str(&xfer.line))
| > > > > > > > > > > > > > > > > > > > | 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 |
"REPLACE INTO config(name,value,mtime)"
"VALUES('ci-lock-%q',json_object('login',%Q,'clientid',%Q),now())",
blob_str(&xfer.aToken[2]), g.zLogin,
blob_str(&xfer.aToken[3])
);
}
}
/* pragma ci-unlock CLIENT-ID
**
** Remove any locks previously held by CLIENT-ID. Clients send this
** pragma with their own ID whenever they know that they no longer
** have any commits pending.
*/
if( blob_eq(&xfer.aToken[1], "ci-unlock")
&& xfer.nToken==3
&& blob_is_hname(&xfer.aToken[2])
){
db_multi_exec(
"DELETE FROM config"
" WHERE name GLOB 'ci-lock-*'"
" AND json_extract(value,'$.clientid')=%Q",
blob_str(&xfer.aToken[2])
);
}
}else
/* Unknown message
*/
{
cgi_reset_content();
@ error bad\scommand:\s%F(blob_str(&xfer.line))
|
| ︙ | ︙ | |||
1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 |
int uvHashSent = 0; /* The "pragma uv-hash" message has been sent */
int uvDoPush = 0; /* Generate uvfile messages to send to server */
int nUvGimmeSent = 0; /* Number of uvgimme cards sent on this cycle */
int nUvFileRcvd = 0; /* Number of uvfile cards received on this cycle */
sqlite3_int64 mtime; /* Modification time on a UV file */
int autopushFailed = 0; /* Autopush following commit failed if true */
const char *zCkinLock; /* Name of check-in to lock. NULL for none */
if( db_get_boolean("dont-push", 0) ) syncFlags &= ~SYNC_PUSH;
if( (syncFlags & (SYNC_PUSH|SYNC_PULL|SYNC_CLONE|SYNC_UNVERSIONED))==0
&& configRcvMask==0 && configSendMask==0 ) return 0;
if( syncFlags & SYNC_FROMPARENT ){
configRcvMask = 0;
configSendMask = 0;
| > | 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 |
int uvHashSent = 0; /* The "pragma uv-hash" message has been sent */
int uvDoPush = 0; /* Generate uvfile messages to send to server */
int nUvGimmeSent = 0; /* Number of uvgimme cards sent on this cycle */
int nUvFileRcvd = 0; /* Number of uvfile cards received on this cycle */
sqlite3_int64 mtime; /* Modification time on a UV file */
int autopushFailed = 0; /* Autopush following commit failed if true */
const char *zCkinLock; /* Name of check-in to lock. NULL for none */
const char *zClientId; /* A unique identifier for this check-out */
if( db_get_boolean("dont-push", 0) ) syncFlags &= ~SYNC_PUSH;
if( (syncFlags & (SYNC_PUSH|SYNC_PULL|SYNC_CLONE|SYNC_UNVERSIONED))==0
&& configRcvMask==0 && configSendMask==0 ) return 0;
if( syncFlags & SYNC_FROMPARENT ){
configRcvMask = 0;
configSendMask = 0;
|
| ︙ | ︙ | |||
1795 1796 1797 1798 1799 1800 1801 | blobarray_zero(xfer.aToken, count(xfer.aToken)); blob_zero(&send); blob_zero(&recv); blob_zero(&xfer.err); blob_zero(&xfer.line); origConfigRcvMask = 0; | | | 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 |
blobarray_zero(xfer.aToken, count(xfer.aToken));
blob_zero(&send);
blob_zero(&recv);
blob_zero(&xfer.err);
blob_zero(&xfer.line);
origConfigRcvMask = 0;
zClientId = db_lget("client-id", 0);
/* Send the send-private pragma if we are trying to sync private data */
if( syncFlags & SYNC_PRIVATE ){
blob_append(&send, "pragma send-private\n", -1);
}
/* Figure out which check-in to lock */
|
| ︙ | ︙ | |||
1974 1975 1976 1977 1978 1979 1980 |
db_finalize(&uvq);
if( rc==SQLITE_DONE ) uvDoPush = 0;
}
}
/* Lock the current check-out */
if( zCkinLock ){
| < < > > | 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 |
db_finalize(&uvq);
if( rc==SQLITE_DONE ) uvDoPush = 0;
}
}
/* Lock the current check-out */
if( zCkinLock ){
if( zClientId==0 ){
zClientId = db_text(0, "SELECT lower(hex(randomblob(20)))");
db_lset("client-id", zClientId);
}
blob_appendf(&send, "pragma ci-lock %s %s\n", zCkinLock, zClientId);
zCkinLock = 0;
}else if( zClientId ){
blob_appendf(&send, "pragma ci-unlock %s\n", zClientId);
}
/* Append randomness to the end of the message. This makes all
** messages unique so that that the login-card nonce will always
** be unique.
*/
zRandomness = db_text(0, "SELECT hex(randomblob(20))");
|
| ︙ | ︙ | |||
2354 2355 2356 2357 2358 2359 2360 2361 |
** is attempted on a check-in for which there is an existing
** lock. USER-HOLDING-LOCK is the name of the user who originated
** the lock, and LOCK-TIME is the timestamp (seconds since 1970)
** when the lock was taken.
*/
else if( blob_eq(&xfer.aToken[1], "ci-lock-fail") && xfer.nToken==4 ){
char *zUser = blob_terminate(&xfer.aToken[2]);
defossilize(zUser);
| > > > > > > > | > | | 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 |
** is attempted on a check-in for which there is an existing
** lock. USER-HOLDING-LOCK is the name of the user who originated
** the lock, and LOCK-TIME is the timestamp (seconds since 1970)
** when the lock was taken.
*/
else if( blob_eq(&xfer.aToken[1], "ci-lock-fail") && xfer.nToken==4 ){
char *zUser = blob_terminate(&xfer.aToken[2]);
sqlite3_int64 mtime, iNow;
defossilize(zUser);
iNow = time(NULL);
if( blob_is_int64(&xfer.aToken[3], &mtime) && iNow>mtime ){
iNow = time(NULL);
fossil_print("\nParent check-in lock by %s %s ago\n",
zUser, human_readable_age((iNow+1-mtime)/86400.0));
}else{
fossil_print("\nParent check-in locked by %s\n", zUser);
}
g.ckinLockFail = fossil_strdup(zUser);
}
}else
/* error MESSAGE
**
** Report an error and abandon the sync session.
**
|
| ︙ | ︙ |