Fossil SCM
Add new --attribute option to the 'fossil import --git' command that enables attributing commits to a username rather than the corresponding Git committer/author contact info of a given commit. Conversely, construct a full 'user <emailaddr>' Git committer string for commits when using 'fossil git export' by querying the new 'fx_git' table if it exists, or the 'info' column in the 'user' table. If no user specified emailaddr can be parsed, then use the generic '[email protected]' string.
Commit
cd4fbdee000875dde44054a2c4e0a8394f279bf5f30235494e1259c946586e80
Parent
97cac02b8a1a4cd…
2 files changed
+23
-3
+52
-1
+23
-3
| --- src/export.c | ||
| +++ src/export.c | ||
| @@ -1088,10 +1088,11 @@ | ||
| 1088 | 1088 | Blob sql; /* String of SQL for part of the query */ |
| 1089 | 1089 | Blob comment; /* The comment text for the check-in */ |
| 1090 | 1090 | int nErr = 0; /* Number of errors */ |
| 1091 | 1091 | int bPhantomOk; /* True if phantom files should be ignored */ |
| 1092 | 1092 | char buf[24]; |
| 1093 | + char *zEmail; /* Contact info for Git committer field */ | |
| 1093 | 1094 | |
| 1094 | 1095 | pMan = manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 1095 | 1096 | if( pMan==0 ){ |
| 1096 | 1097 | /* Must be a phantom. Return without doing anything, and in particular |
| 1097 | 1098 | ** without creating a mark for this check-in. */ |
| @@ -1164,13 +1165,32 @@ | ||
| 1164 | 1165 | fprintf(xCmd, "mark %s\n", zMark); |
| 1165 | 1166 | fossil_free(zMark); |
| 1166 | 1167 | sqlite3_snprintf(sizeof(buf), buf, "%lld", |
| 1167 | 1168 | (sqlite3_int64)((pMan->rDate-2440587.5)*86400.0) |
| 1168 | 1169 | ); |
| 1169 | - fprintf(xCmd, "committer %s <%[email protected]> %s +0000\n", | |
| 1170 | - pMan->zUser, pMan->zUser, buf | |
| 1171 | - ); | |
| 1170 | + /* | |
| 1171 | + ** Check for 'fx_' table from previous Git import, otherwise take contact info | |
| 1172 | + ** from user table for <emailaddr> in committer field. If no emailaddr, check | |
| 1173 | + ** if username is in email form, otherwise use generic '[email protected]'. | |
| 1174 | + */ | |
| 1175 | + if (db_table_exists("repository", "fx_git")) { | |
| 1176 | + zEmail = db_text(0, "SELECT email FROM fx_git WHERE user=%Q", pMan->zUser); | |
| 1177 | + } else { | |
| 1178 | + zEmail = db_text(0, "SELECT info FROM user WHERE login=%Q", pMan->zUser); | |
| 1179 | + } | |
| 1180 | + /* Some repo 'info' fields return an empty string hence the second check */ | |
| 1181 | + if (zEmail == NULL || zEmail[0] == '\0') { | |
| 1182 | + /* If username is in emailaddr form, don't append '@noemail.net' */ | |
| 1183 | + if (strchr(pMan->zUser, '@') == NULL) { | |
| 1184 | + zEmail = mprintf("%[email protected]", pMan->zUser); | |
| 1185 | + } else { | |
| 1186 | + zEmail = fossil_strdup(pMan->zUser); | |
| 1187 | + } | |
| 1188 | + } | |
| 1189 | + fprintf(xCmd, "committer %s <%s> %s +0000\n", pMan->zUser, | |
| 1190 | + strchr(pMan->zUser, '@') == NULL ? zEmail : pMan->zUser, buf); | |
| 1191 | + fossil_free(zEmail); | |
| 1172 | 1192 | blob_init(&comment, pMan->zComment, -1); |
| 1173 | 1193 | if( blob_size(&comment)==0 ){ |
| 1174 | 1194 | blob_append(&comment, "(no comment)", -1); |
| 1175 | 1195 | } |
| 1176 | 1196 | blob_appendf(&comment, "\n\nFossilOrigin-Name: %s", zUuid); |
| 1177 | 1197 |
| --- src/export.c | |
| +++ src/export.c | |
| @@ -1088,10 +1088,11 @@ | |
| 1088 | Blob sql; /* String of SQL for part of the query */ |
| 1089 | Blob comment; /* The comment text for the check-in */ |
| 1090 | int nErr = 0; /* Number of errors */ |
| 1091 | int bPhantomOk; /* True if phantom files should be ignored */ |
| 1092 | char buf[24]; |
| 1093 | |
| 1094 | pMan = manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 1095 | if( pMan==0 ){ |
| 1096 | /* Must be a phantom. Return without doing anything, and in particular |
| 1097 | ** without creating a mark for this check-in. */ |
| @@ -1164,13 +1165,32 @@ | |
| 1164 | fprintf(xCmd, "mark %s\n", zMark); |
| 1165 | fossil_free(zMark); |
| 1166 | sqlite3_snprintf(sizeof(buf), buf, "%lld", |
| 1167 | (sqlite3_int64)((pMan->rDate-2440587.5)*86400.0) |
| 1168 | ); |
| 1169 | fprintf(xCmd, "committer %s <%[email protected]> %s +0000\n", |
| 1170 | pMan->zUser, pMan->zUser, buf |
| 1171 | ); |
| 1172 | blob_init(&comment, pMan->zComment, -1); |
| 1173 | if( blob_size(&comment)==0 ){ |
| 1174 | blob_append(&comment, "(no comment)", -1); |
| 1175 | } |
| 1176 | blob_appendf(&comment, "\n\nFossilOrigin-Name: %s", zUuid); |
| 1177 |
| --- src/export.c | |
| +++ src/export.c | |
| @@ -1088,10 +1088,11 @@ | |
| 1088 | Blob sql; /* String of SQL for part of the query */ |
| 1089 | Blob comment; /* The comment text for the check-in */ |
| 1090 | int nErr = 0; /* Number of errors */ |
| 1091 | int bPhantomOk; /* True if phantom files should be ignored */ |
| 1092 | char buf[24]; |
| 1093 | char *zEmail; /* Contact info for Git committer field */ |
| 1094 | |
| 1095 | pMan = manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 1096 | if( pMan==0 ){ |
| 1097 | /* Must be a phantom. Return without doing anything, and in particular |
| 1098 | ** without creating a mark for this check-in. */ |
| @@ -1164,13 +1165,32 @@ | |
| 1165 | fprintf(xCmd, "mark %s\n", zMark); |
| 1166 | fossil_free(zMark); |
| 1167 | sqlite3_snprintf(sizeof(buf), buf, "%lld", |
| 1168 | (sqlite3_int64)((pMan->rDate-2440587.5)*86400.0) |
| 1169 | ); |
| 1170 | /* |
| 1171 | ** Check for 'fx_' table from previous Git import, otherwise take contact info |
| 1172 | ** from user table for <emailaddr> in committer field. If no emailaddr, check |
| 1173 | ** if username is in email form, otherwise use generic '[email protected]'. |
| 1174 | */ |
| 1175 | if (db_table_exists("repository", "fx_git")) { |
| 1176 | zEmail = db_text(0, "SELECT email FROM fx_git WHERE user=%Q", pMan->zUser); |
| 1177 | } else { |
| 1178 | zEmail = db_text(0, "SELECT info FROM user WHERE login=%Q", pMan->zUser); |
| 1179 | } |
| 1180 | /* Some repo 'info' fields return an empty string hence the second check */ |
| 1181 | if (zEmail == NULL || zEmail[0] == '\0') { |
| 1182 | /* If username is in emailaddr form, don't append '@noemail.net' */ |
| 1183 | if (strchr(pMan->zUser, '@') == NULL) { |
| 1184 | zEmail = mprintf("%[email protected]", pMan->zUser); |
| 1185 | } else { |
| 1186 | zEmail = fossil_strdup(pMan->zUser); |
| 1187 | } |
| 1188 | } |
| 1189 | fprintf(xCmd, "committer %s <%s> %s +0000\n", pMan->zUser, |
| 1190 | strchr(pMan->zUser, '@') == NULL ? zEmail : pMan->zUser, buf); |
| 1191 | fossil_free(zEmail); |
| 1192 | blob_init(&comment, pMan->zComment, -1); |
| 1193 | if( blob_size(&comment)==0 ){ |
| 1194 | blob_append(&comment, "(no comment)", -1); |
| 1195 | } |
| 1196 | blob_appendf(&comment, "\n\nFossilOrigin-Name: %s", zUuid); |
| 1197 |
+52
-1
| --- src/import.c | ||
| +++ src/import.c | ||
| @@ -540,10 +540,15 @@ | ||
| 540 | 540 | |
| 541 | 541 | |
| 542 | 542 | static struct{ |
| 543 | 543 | const char *zMasterName; /* Name of master branch */ |
| 544 | 544 | int authorFlag; /* Use author as checkin committer */ |
| 545 | + int nGitAttr; /* Number of Git --attribute entries */ | |
| 546 | + struct { /* Git --attribute details */ | |
| 547 | + char *zUser; | |
| 548 | + char *zEmail; | |
| 549 | + } *gitUserInfo; | |
| 545 | 550 | } ggit; |
| 546 | 551 | |
| 547 | 552 | /* |
| 548 | 553 | ** Read the git-fast-import format from pIn and insert the corresponding |
| 549 | 554 | ** content into the database. |
| @@ -664,11 +669,15 @@ | ||
| 664 | 669 | sqlite3_int64 secSince1970; |
| 665 | 670 | z = strchr(zLine, ' '); |
| 666 | 671 | while( fossil_isspace(*z) ) z++; |
| 667 | 672 | if( (zTo=strchr(z, '>'))==NULL ) goto malformed_line; |
| 668 | 673 | *(++zTo) = '\0'; |
| 669 | - /* Lookup user by contact info. */ | |
| 674 | + /* | |
| 675 | + ** If --attribute requested, lookup user in fx_ table by email address, | |
| 676 | + ** otherwise lookup Git {author,committer} contact info in user table. If | |
| 677 | + ** no matches, use email address as username for check-in attribution. | |
| 678 | + */ | |
| 670 | 679 | fossil_free(gg.zUser); |
| 671 | 680 | gg.zUser = db_text(0, "SELECT login FROM user WHERE info=%Q", z); |
| 672 | 681 | if( gg.zUser==NULL ){ |
| 673 | 682 | /* If there is no user with this contact info, |
| 674 | 683 | * then use the email address as the username. */ |
| @@ -675,10 +684,14 @@ | ||
| 675 | 684 | if ( (z=strchr(z, '<'))==NULL ) goto malformed_line; |
| 676 | 685 | z++; |
| 677 | 686 | *(zTo-1) = '\0'; |
| 678 | 687 | gg.zUser = fossil_strdup(z); |
| 679 | 688 | } |
| 689 | + if (ggit.nGitAttr > 0) { | |
| 690 | + gg.zUser = db_text(gg.zUser, | |
| 691 | + "SELECT user FROM fx_git WHERE email=%Q", z); | |
| 692 | + } | |
| 680 | 693 | secSince1970 = 0; |
| 681 | 694 | for(zTo++; fossil_isdigit(*zTo); zTo++){ |
| 682 | 695 | secSince1970 = secSince1970*10 + *zTo - '0'; |
| 683 | 696 | } |
| 684 | 697 | fossil_free(gg.zDate); |
| @@ -1648,10 +1661,12 @@ | ||
| 1648 | 1661 | ** Options: |
| 1649 | 1662 | ** --import-marks FILE Restore marks table from FILE |
| 1650 | 1663 | ** --export-marks FILE Save marks table to FILE |
| 1651 | 1664 | ** --rename-master NAME Renames the master branch to NAME |
| 1652 | 1665 | ** --use-author Uses author as the committer |
| 1666 | +** --attribute "EMAIL USER" Attribute commits to USER | |
| 1667 | +** instead of Git committer EMAIL address | |
| 1653 | 1668 | ** |
| 1654 | 1669 | ** --svn Import from the svnadmin-dump file format. The default |
| 1655 | 1670 | ** behaviour (unless overridden by --flat) is to treat 3 |
| 1656 | 1671 | ** folders in the SVN root as special, following the |
| 1657 | 1672 | ** common layout of SVN repositories. These are (by |
| @@ -1690,10 +1705,15 @@ | ||
| 1690 | 1705 | ** |
| 1691 | 1706 | ** --ignore-tree is useful for importing Subversion repositories which |
| 1692 | 1707 | ** move branches to subdirectories of "branches/deleted" instead of |
| 1693 | 1708 | ** deleting them. It can be supplied multiple times if necessary. |
| 1694 | 1709 | ** |
| 1710 | +** The --attribute option takes a quoted string argument comprised of a | |
| 1711 | +** Git committer email and the username to be attributed to corresponding | |
| 1712 | +** check-ins in the Fossil repository. This option can be repeated. For | |
| 1713 | +** example, --attribute "[email protected] drh" --attribute "[email protected] X" | |
| 1714 | +** | |
| 1695 | 1715 | ** See also: export |
| 1696 | 1716 | */ |
| 1697 | 1717 | void import_cmd(void){ |
| 1698 | 1718 | char *zPassword; |
| 1699 | 1719 | FILE *pIn; |
| @@ -1776,10 +1796,23 @@ | ||
| 1776 | 1796 | markfile_out = find_option("export-marks", 0, 1); |
| 1777 | 1797 | if( !(ggit.zMasterName = find_option("rename-master", 0, 1)) ){ |
| 1778 | 1798 | ggit.zMasterName = "master"; |
| 1779 | 1799 | } |
| 1780 | 1800 | ggit.authorFlag = find_option("use-author", 0, 0)!=0; |
| 1801 | + /* | |
| 1802 | + ** Extract --attribute 'emailaddr username' args that will populate | |
| 1803 | + ** new 'fx_' table to later match username for check-in attribution. | |
| 1804 | + */ | |
| 1805 | + const char *zGitUser = find_option("attribute", 0, 1); | |
| 1806 | + while( zGitUser != 0 ){ | |
| 1807 | + ggit.gitUserInfo = fossil_realloc(ggit.gitUserInfo, ++ggit.nGitAttr | |
| 1808 | + * sizeof(ggit.gitUserInfo[0])); | |
| 1809 | + char *currGitUser = fossil_strdup(zGitUser); | |
| 1810 | + ggit.gitUserInfo[ggit.nGitAttr-1].zEmail = next_token(&currGitUser); | |
| 1811 | + ggit.gitUserInfo[ggit.nGitAttr-1].zUser = rest_of_line(&currGitUser); | |
| 1812 | + zGitUser = find_option("attribute", 0, 1); | |
| 1813 | + } | |
| 1781 | 1814 | } |
| 1782 | 1815 | verify_all_options(); |
| 1783 | 1816 | |
| 1784 | 1817 | if( g.argc!=3 && g.argc!=4 ){ |
| 1785 | 1818 | usage("--git|--svn ?OPTIONS? NEW-REPOSITORY ?INPUT-FILE?"); |
| @@ -1901,10 +1934,28 @@ | ||
| 1901 | 1934 | } |
| 1902 | 1935 | fclose(f); |
| 1903 | 1936 | } |
| 1904 | 1937 | |
| 1905 | 1938 | manifest_crosslink_begin(); |
| 1939 | + /* | |
| 1940 | + ** The following 'fx_' table is used to hold information needed for | |
| 1941 | + ** importing and exporting to attribute Fossil check-ins or Git commits | |
| 1942 | + ** to either a desired username or full contact information string. | |
| 1943 | + */ | |
| 1944 | + if(ggit.nGitAttr > 0) { | |
| 1945 | + db_unprotect(PROTECT_ALL); | |
| 1946 | + db_multi_exec( | |
| 1947 | + "CREATE TABLE fx_git(user TEXT, email TEXT UNIQUE);" | |
| 1948 | + ); | |
| 1949 | + for( int idx = 0; idx < ggit.nGitAttr; ++idx ){ | |
| 1950 | + db_multi_exec( | |
| 1951 | + "INSERT OR IGNORE INTO fx_git(user, email) VALUES(%Q, %Q)", | |
| 1952 | + ggit.gitUserInfo[idx].zUser, ggit.gitUserInfo[idx].zEmail | |
| 1953 | + ); | |
| 1954 | + } | |
| 1955 | + db_protect_pop(); | |
| 1956 | + } | |
| 1906 | 1957 | git_fast_import(pIn); |
| 1907 | 1958 | db_prepare(&q, "SELECT tcontent FROM xtag"); |
| 1908 | 1959 | while( db_step(&q)==SQLITE_ROW ){ |
| 1909 | 1960 | Blob record; |
| 1910 | 1961 | db_ephemeral_blob(&q, 0, &record); |
| 1911 | 1962 |
| --- src/import.c | |
| +++ src/import.c | |
| @@ -540,10 +540,15 @@ | |
| 540 | |
| 541 | |
| 542 | static struct{ |
| 543 | const char *zMasterName; /* Name of master branch */ |
| 544 | int authorFlag; /* Use author as checkin committer */ |
| 545 | } ggit; |
| 546 | |
| 547 | /* |
| 548 | ** Read the git-fast-import format from pIn and insert the corresponding |
| 549 | ** content into the database. |
| @@ -664,11 +669,15 @@ | |
| 664 | sqlite3_int64 secSince1970; |
| 665 | z = strchr(zLine, ' '); |
| 666 | while( fossil_isspace(*z) ) z++; |
| 667 | if( (zTo=strchr(z, '>'))==NULL ) goto malformed_line; |
| 668 | *(++zTo) = '\0'; |
| 669 | /* Lookup user by contact info. */ |
| 670 | fossil_free(gg.zUser); |
| 671 | gg.zUser = db_text(0, "SELECT login FROM user WHERE info=%Q", z); |
| 672 | if( gg.zUser==NULL ){ |
| 673 | /* If there is no user with this contact info, |
| 674 | * then use the email address as the username. */ |
| @@ -675,10 +684,14 @@ | |
| 675 | if ( (z=strchr(z, '<'))==NULL ) goto malformed_line; |
| 676 | z++; |
| 677 | *(zTo-1) = '\0'; |
| 678 | gg.zUser = fossil_strdup(z); |
| 679 | } |
| 680 | secSince1970 = 0; |
| 681 | for(zTo++; fossil_isdigit(*zTo); zTo++){ |
| 682 | secSince1970 = secSince1970*10 + *zTo - '0'; |
| 683 | } |
| 684 | fossil_free(gg.zDate); |
| @@ -1648,10 +1661,12 @@ | |
| 1648 | ** Options: |
| 1649 | ** --import-marks FILE Restore marks table from FILE |
| 1650 | ** --export-marks FILE Save marks table to FILE |
| 1651 | ** --rename-master NAME Renames the master branch to NAME |
| 1652 | ** --use-author Uses author as the committer |
| 1653 | ** |
| 1654 | ** --svn Import from the svnadmin-dump file format. The default |
| 1655 | ** behaviour (unless overridden by --flat) is to treat 3 |
| 1656 | ** folders in the SVN root as special, following the |
| 1657 | ** common layout of SVN repositories. These are (by |
| @@ -1690,10 +1705,15 @@ | |
| 1690 | ** |
| 1691 | ** --ignore-tree is useful for importing Subversion repositories which |
| 1692 | ** move branches to subdirectories of "branches/deleted" instead of |
| 1693 | ** deleting them. It can be supplied multiple times if necessary. |
| 1694 | ** |
| 1695 | ** See also: export |
| 1696 | */ |
| 1697 | void import_cmd(void){ |
| 1698 | char *zPassword; |
| 1699 | FILE *pIn; |
| @@ -1776,10 +1796,23 @@ | |
| 1776 | markfile_out = find_option("export-marks", 0, 1); |
| 1777 | if( !(ggit.zMasterName = find_option("rename-master", 0, 1)) ){ |
| 1778 | ggit.zMasterName = "master"; |
| 1779 | } |
| 1780 | ggit.authorFlag = find_option("use-author", 0, 0)!=0; |
| 1781 | } |
| 1782 | verify_all_options(); |
| 1783 | |
| 1784 | if( g.argc!=3 && g.argc!=4 ){ |
| 1785 | usage("--git|--svn ?OPTIONS? NEW-REPOSITORY ?INPUT-FILE?"); |
| @@ -1901,10 +1934,28 @@ | |
| 1901 | } |
| 1902 | fclose(f); |
| 1903 | } |
| 1904 | |
| 1905 | manifest_crosslink_begin(); |
| 1906 | git_fast_import(pIn); |
| 1907 | db_prepare(&q, "SELECT tcontent FROM xtag"); |
| 1908 | while( db_step(&q)==SQLITE_ROW ){ |
| 1909 | Blob record; |
| 1910 | db_ephemeral_blob(&q, 0, &record); |
| 1911 |
| --- src/import.c | |
| +++ src/import.c | |
| @@ -540,10 +540,15 @@ | |
| 540 | |
| 541 | |
| 542 | static struct{ |
| 543 | const char *zMasterName; /* Name of master branch */ |
| 544 | int authorFlag; /* Use author as checkin committer */ |
| 545 | int nGitAttr; /* Number of Git --attribute entries */ |
| 546 | struct { /* Git --attribute details */ |
| 547 | char *zUser; |
| 548 | char *zEmail; |
| 549 | } *gitUserInfo; |
| 550 | } ggit; |
| 551 | |
| 552 | /* |
| 553 | ** Read the git-fast-import format from pIn and insert the corresponding |
| 554 | ** content into the database. |
| @@ -664,11 +669,15 @@ | |
| 669 | sqlite3_int64 secSince1970; |
| 670 | z = strchr(zLine, ' '); |
| 671 | while( fossil_isspace(*z) ) z++; |
| 672 | if( (zTo=strchr(z, '>'))==NULL ) goto malformed_line; |
| 673 | *(++zTo) = '\0'; |
| 674 | /* |
| 675 | ** If --attribute requested, lookup user in fx_ table by email address, |
| 676 | ** otherwise lookup Git {author,committer} contact info in user table. If |
| 677 | ** no matches, use email address as username for check-in attribution. |
| 678 | */ |
| 679 | fossil_free(gg.zUser); |
| 680 | gg.zUser = db_text(0, "SELECT login FROM user WHERE info=%Q", z); |
| 681 | if( gg.zUser==NULL ){ |
| 682 | /* If there is no user with this contact info, |
| 683 | * then use the email address as the username. */ |
| @@ -675,10 +684,14 @@ | |
| 684 | if ( (z=strchr(z, '<'))==NULL ) goto malformed_line; |
| 685 | z++; |
| 686 | *(zTo-1) = '\0'; |
| 687 | gg.zUser = fossil_strdup(z); |
| 688 | } |
| 689 | if (ggit.nGitAttr > 0) { |
| 690 | gg.zUser = db_text(gg.zUser, |
| 691 | "SELECT user FROM fx_git WHERE email=%Q", z); |
| 692 | } |
| 693 | secSince1970 = 0; |
| 694 | for(zTo++; fossil_isdigit(*zTo); zTo++){ |
| 695 | secSince1970 = secSince1970*10 + *zTo - '0'; |
| 696 | } |
| 697 | fossil_free(gg.zDate); |
| @@ -1648,10 +1661,12 @@ | |
| 1661 | ** Options: |
| 1662 | ** --import-marks FILE Restore marks table from FILE |
| 1663 | ** --export-marks FILE Save marks table to FILE |
| 1664 | ** --rename-master NAME Renames the master branch to NAME |
| 1665 | ** --use-author Uses author as the committer |
| 1666 | ** --attribute "EMAIL USER" Attribute commits to USER |
| 1667 | ** instead of Git committer EMAIL address |
| 1668 | ** |
| 1669 | ** --svn Import from the svnadmin-dump file format. The default |
| 1670 | ** behaviour (unless overridden by --flat) is to treat 3 |
| 1671 | ** folders in the SVN root as special, following the |
| 1672 | ** common layout of SVN repositories. These are (by |
| @@ -1690,10 +1705,15 @@ | |
| 1705 | ** |
| 1706 | ** --ignore-tree is useful for importing Subversion repositories which |
| 1707 | ** move branches to subdirectories of "branches/deleted" instead of |
| 1708 | ** deleting them. It can be supplied multiple times if necessary. |
| 1709 | ** |
| 1710 | ** The --attribute option takes a quoted string argument comprised of a |
| 1711 | ** Git committer email and the username to be attributed to corresponding |
| 1712 | ** check-ins in the Fossil repository. This option can be repeated. For |
| 1713 | ** example, --attribute "[email protected] drh" --attribute "[email protected] X" |
| 1714 | ** |
| 1715 | ** See also: export |
| 1716 | */ |
| 1717 | void import_cmd(void){ |
| 1718 | char *zPassword; |
| 1719 | FILE *pIn; |
| @@ -1776,10 +1796,23 @@ | |
| 1796 | markfile_out = find_option("export-marks", 0, 1); |
| 1797 | if( !(ggit.zMasterName = find_option("rename-master", 0, 1)) ){ |
| 1798 | ggit.zMasterName = "master"; |
| 1799 | } |
| 1800 | ggit.authorFlag = find_option("use-author", 0, 0)!=0; |
| 1801 | /* |
| 1802 | ** Extract --attribute 'emailaddr username' args that will populate |
| 1803 | ** new 'fx_' table to later match username for check-in attribution. |
| 1804 | */ |
| 1805 | const char *zGitUser = find_option("attribute", 0, 1); |
| 1806 | while( zGitUser != 0 ){ |
| 1807 | ggit.gitUserInfo = fossil_realloc(ggit.gitUserInfo, ++ggit.nGitAttr |
| 1808 | * sizeof(ggit.gitUserInfo[0])); |
| 1809 | char *currGitUser = fossil_strdup(zGitUser); |
| 1810 | ggit.gitUserInfo[ggit.nGitAttr-1].zEmail = next_token(&currGitUser); |
| 1811 | ggit.gitUserInfo[ggit.nGitAttr-1].zUser = rest_of_line(&currGitUser); |
| 1812 | zGitUser = find_option("attribute", 0, 1); |
| 1813 | } |
| 1814 | } |
| 1815 | verify_all_options(); |
| 1816 | |
| 1817 | if( g.argc!=3 && g.argc!=4 ){ |
| 1818 | usage("--git|--svn ?OPTIONS? NEW-REPOSITORY ?INPUT-FILE?"); |
| @@ -1901,10 +1934,28 @@ | |
| 1934 | } |
| 1935 | fclose(f); |
| 1936 | } |
| 1937 | |
| 1938 | manifest_crosslink_begin(); |
| 1939 | /* |
| 1940 | ** The following 'fx_' table is used to hold information needed for |
| 1941 | ** importing and exporting to attribute Fossil check-ins or Git commits |
| 1942 | ** to either a desired username or full contact information string. |
| 1943 | */ |
| 1944 | if(ggit.nGitAttr > 0) { |
| 1945 | db_unprotect(PROTECT_ALL); |
| 1946 | db_multi_exec( |
| 1947 | "CREATE TABLE fx_git(user TEXT, email TEXT UNIQUE);" |
| 1948 | ); |
| 1949 | for( int idx = 0; idx < ggit.nGitAttr; ++idx ){ |
| 1950 | db_multi_exec( |
| 1951 | "INSERT OR IGNORE INTO fx_git(user, email) VALUES(%Q, %Q)", |
| 1952 | ggit.gitUserInfo[idx].zUser, ggit.gitUserInfo[idx].zEmail |
| 1953 | ); |
| 1954 | } |
| 1955 | db_protect_pop(); |
| 1956 | } |
| 1957 | git_fast_import(pIn); |
| 1958 | db_prepare(&q, "SELECT tcontent FROM xtag"); |
| 1959 | while( db_step(&q)==SQLITE_ROW ){ |
| 1960 | Blob record; |
| 1961 | db_ephemeral_blob(&q, 0, &record); |
| 1962 |