Fossil SCM
Add the "describe" command. This shows the commit hash along with (if applicable) its youngest ancestor with a non-propagating tag and the number of commits since that.
Commit
3f06ed14feddb8e2bb1102510aac168254b261ff0ddcc0f0a6fa4ec0363e585f
Parent
b80ae0215eb65fb…
1 file changed
+173
+173
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -3678,5 +3678,178 @@ | ||
| 3678 | 3678 | db_column_text(&q,2), |
| 3679 | 3679 | db_column_text(&q,3)); |
| 3680 | 3680 | } |
| 3681 | 3681 | db_finalize(&q); |
| 3682 | 3682 | } |
| 3683 | + | |
| 3684 | +#if INTERFACE | |
| 3685 | +typedef struct CommitDescr { | |
| 3686 | + char zLastTag[100]; | |
| 3687 | + int nCommitsSince; | |
| 3688 | + char zArtifactHash[65]; | |
| 3689 | + int isDirty; | |
| 3690 | +} CommitDescr; | |
| 3691 | +#endif | |
| 3692 | + | |
| 3693 | +int describe_commit(const char *zName, const char *matchGlob, | |
| 3694 | + CommitDescr *descr){ | |
| 3695 | + int rid; | |
| 3696 | + const char *zUuid; | |
| 3697 | + int nRet = 0; | |
| 3698 | + Stmt q; | |
| 3699 | + | |
| 3700 | + rid = symbolic_name_to_rid(zName, "ci"); /* only commits */ | |
| 3701 | + | |
| 3702 | + if( rid<=0 ){ | |
| 3703 | + /* Commit does not exist */ | |
| 3704 | + *(descr->zLastTag) = 0; | |
| 3705 | + descr->nCommitsSince = -1; | |
| 3706 | + *(descr->zArtifactHash) = 0; | |
| 3707 | + descr->isDirty = -1; | |
| 3708 | + return -1; | |
| 3709 | + } | |
| 3710 | + | |
| 3711 | + zUuid = rid_to_uuid(rid); | |
| 3712 | + memcpy(descr->zArtifactHash, zUuid, strlen(zUuid)+1); | |
| 3713 | + descr->isDirty = unsaved_changes(0); | |
| 3714 | + | |
| 3715 | + db_multi_exec( | |
| 3716 | + "DROP TABLE IF EXISTS singletonTaggedAncestors;" | |
| 3717 | + "CREATE TEMP TABLE singletonTaggedAncestors AS" | |
| 3718 | + " WITH RECURSIVE " | |
| 3719 | + " singletonTaggedCommits(rid,mtime,shorttag) AS (" | |
| 3720 | + " SELECT DISTINCT b.rid,e.mtime,substr(t.tagname,5) AS shorttag" | |
| 3721 | + " FROM blob b" | |
| 3722 | + " INNER JOIN event e ON e.objid=b.rid" | |
| 3723 | + " INNER JOIN tagxref tx ON tx.rid=b.rid" | |
| 3724 | + " INNER JOIN tag t ON t.tagid=tx.tagid" | |
| 3725 | + " WHERE e.type='ci'" | |
| 3726 | + " AND tx.tagtype=1" | |
| 3727 | + " AND t.tagname GLOB 'sym-%q'" | |
| 3728 | + " )," | |
| 3729 | + " parent(pid,cid,isCP,isPrim) AS (" | |
| 3730 | + " SELECT plink.pid, plink.cid, 0, isPrim FROM plink" | |
| 3731 | + " UNION ALL" | |
| 3732 | + " SELECT parentid, childid, 1, 0 FROM cherrypick WHERE NOT isExclude" | |
| 3733 | + " )," | |
| 3734 | + " ancestor(rid, mtime, isCP, isPrim) AS (" | |
| 3735 | + " SELECT objid, mtime, 0, 1 FROM event WHERE objid=%d" | |
| 3736 | + " UNION" | |
| 3737 | + " SELECT parent.pid, event.mtime, parent.isCP, parent.isPrim" | |
| 3738 | + " FROM ancestor, parent, event" | |
| 3739 | + " WHERE parent.cid=ancestor.rid" | |
| 3740 | + " AND event.objid=parent.pid" | |
| 3741 | + " AND NOT ancestor.isCP" | |
| 3742 | + " AND (event.mtime >= " | |
| 3743 | + " (SELECT max(mtime) FROM singletonTaggedCommits" | |
| 3744 | + " WHERE mtime<=(SELECT mtime FROM event WHERE objid=%d)))" | |
| 3745 | + " ORDER BY mtime DESC" | |
| 3746 | + " LIMIT 1000000" | |
| 3747 | + " ) " | |
| 3748 | + "SELECT rid, mtime, isCP, isPrim, ROW_NUMBER() OVER (ORDER BY mtime DESC) rn" | |
| 3749 | + " FROM ancestor", | |
| 3750 | + (matchGlob ? matchGlob : "*"), rid, rid | |
| 3751 | + ); | |
| 3752 | + | |
| 3753 | + db_prepare(&q, | |
| 3754 | + "SELECT ta.rid, ta.mtime, ta.rn, b.uuid, substr(t.tagname, 5)" | |
| 3755 | + " FROM singletonTaggedAncestors ta" | |
| 3756 | + " INNER JOIN blob b ON b.rid=ta.rid" | |
| 3757 | + " INNER JOIN tagxref tx ON tx.rid=ta.rid" | |
| 3758 | + " INNER JOIN tag t ON tx.tagid=t.tagid" | |
| 3759 | + " WHERE tx.tagtype=1 AND t.tagname GLOB 'sym-%q' " | |
| 3760 | + " AND rn=(SELECT MAX(rn) FROM singletonTaggedAncestors)" | |
| 3761 | + " ORDER BY tx.mtime DESC, t.tagname DESC LIMIT 1", | |
| 3762 | + (matchGlob ? matchGlob : "*") | |
| 3763 | + ); | |
| 3764 | + | |
| 3765 | + if( db_step(&q)==SQLITE_ROW ){ | |
| 3766 | + const char *lastTag = db_column_text(&q, 4); | |
| 3767 | + memcpy(descr->zLastTag, lastTag, strlen(lastTag)+1); | |
| 3768 | + descr->nCommitsSince = db_column_int(&q, 2)-1; | |
| 3769 | + nRet = 0; | |
| 3770 | + }else{ | |
| 3771 | + /* no ancestor commit with a fitting singleton tag found */ | |
| 3772 | + *(descr->zLastTag) = 0; | |
| 3773 | + descr->nCommitsSince = -1; | |
| 3774 | + nRet = -2; | |
| 3775 | + } | |
| 3776 | + | |
| 3777 | + db_finalize(&q); | |
| 3778 | + return nRet; | |
| 3779 | +} | |
| 3780 | + | |
| 3781 | +/* | |
| 3782 | +** COMMAND: describe | |
| 3783 | +** | |
| 3784 | +** Usage: %fossil describe ?VERSION? ?OPTIONS? | |
| 3785 | +** | |
| 3786 | +** Provide a description of the given VERSION by showing a non-propagating | |
| 3787 | +** tag of the youngest tagged ancestor, followed by the number of commits | |
| 3788 | +** since that, and the short hash of VERSION. If VERSION and the found | |
| 3789 | +** ancestor refer to the same commit, the last two components are omitted, | |
| 3790 | +** unless --long is provided. | |
| 3791 | +** | |
| 3792 | +** If no VERSION is provided, describe the current checked-out version. When | |
| 3793 | +** no fitting tagged ancestor is found, show only the short hash of VERSION. | |
| 3794 | +** | |
| 3795 | +** Options: | |
| 3796 | +** | |
| 3797 | +** --digits Display so many hex digits of the hash (default 10) | |
| 3798 | +** -d|--dirty Show whether there are changes to be committed | |
| 3799 | +** --long Always show all three components | |
| 3800 | +** --match GLOB Consider only non-propagating tags matching GLOB | |
| 3801 | +*/ | |
| 3802 | +void describe_cmd(void){ | |
| 3803 | + const char *zName; | |
| 3804 | + const char *zMatchGlob; | |
| 3805 | + const char *zDigits; | |
| 3806 | + int nDigits; | |
| 3807 | + int bDirtyFlag = 0; | |
| 3808 | + int bLongFlag = 0; | |
| 3809 | + CommitDescr descr; | |
| 3810 | + | |
| 3811 | + db_find_and_open_repository(0,0); | |
| 3812 | + bDirtyFlag = find_option("dirty","d",0)!=0; | |
| 3813 | + bLongFlag = find_option("long","",0)!=0; | |
| 3814 | + zMatchGlob = find_option("match", 0, 1); | |
| 3815 | + zDigits = find_option("digits", 0, 1); | |
| 3816 | + | |
| 3817 | + if ( !zDigits || ((nDigits=atoi(zDigits))==0) ){ | |
| 3818 | + nDigits = 10; | |
| 3819 | + } | |
| 3820 | + | |
| 3821 | + /* We should be done with options.. */ | |
| 3822 | + verify_all_options(); | |
| 3823 | + if( g.argc<3 ){ | |
| 3824 | + zName = "current"; | |
| 3825 | + }else{ | |
| 3826 | + zName = g.argv[2]; | |
| 3827 | + } | |
| 3828 | + | |
| 3829 | + if( bDirtyFlag ){ | |
| 3830 | + if ( g.argc>=3 ) fossil_fatal("cannot use --dirty with specific checkin"); | |
| 3831 | + } | |
| 3832 | + | |
| 3833 | + switch( describe_commit(zName, zMatchGlob, &descr) ){ | |
| 3834 | + case -1: | |
| 3835 | + fossil_fatal("commit %s does not exist", zName); | |
| 3836 | + break; | |
| 3837 | + case -2: | |
| 3838 | + fossil_print("%.*s%s\n", nDigits, descr.zArtifactHash, | |
| 3839 | + bDirtyFlag ? (descr.isDirty ? "-dirty" : "") : ""); | |
| 3840 | + break; | |
| 3841 | + case 0: | |
| 3842 | + if( descr.nCommitsSince==0 && !bLongFlag ){ | |
| 3843 | + fossil_print("%s%s\n", descr.zLastTag, | |
| 3844 | + bDirtyFlag ? (descr.isDirty ? "-dirty" : "") : ""); | |
| 3845 | + }else{ | |
| 3846 | + fossil_print("%s-%d-%.*s%s\n", descr.zLastTag, | |
| 3847 | + descr.nCommitsSince, nDigits, descr.zArtifactHash, | |
| 3848 | + bDirtyFlag ? (descr.isDirty ? "-dirty" : "") : ""); | |
| 3849 | + } | |
| 3850 | + break; | |
| 3851 | + default: | |
| 3852 | + fossil_fatal("cannot describe commit"); | |
| 3853 | + break; | |
| 3854 | + } | |
| 3855 | +} | |
| 3683 | 3856 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -3678,5 +3678,178 @@ | |
| 3678 | db_column_text(&q,2), |
| 3679 | db_column_text(&q,3)); |
| 3680 | } |
| 3681 | db_finalize(&q); |
| 3682 | } |
| 3683 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -3678,5 +3678,178 @@ | |
| 3678 | db_column_text(&q,2), |
| 3679 | db_column_text(&q,3)); |
| 3680 | } |
| 3681 | db_finalize(&q); |
| 3682 | } |
| 3683 | |
| 3684 | #if INTERFACE |
| 3685 | typedef struct CommitDescr { |
| 3686 | char zLastTag[100]; |
| 3687 | int nCommitsSince; |
| 3688 | char zArtifactHash[65]; |
| 3689 | int isDirty; |
| 3690 | } CommitDescr; |
| 3691 | #endif |
| 3692 | |
| 3693 | int describe_commit(const char *zName, const char *matchGlob, |
| 3694 | CommitDescr *descr){ |
| 3695 | int rid; |
| 3696 | const char *zUuid; |
| 3697 | int nRet = 0; |
| 3698 | Stmt q; |
| 3699 | |
| 3700 | rid = symbolic_name_to_rid(zName, "ci"); /* only commits */ |
| 3701 | |
| 3702 | if( rid<=0 ){ |
| 3703 | /* Commit does not exist */ |
| 3704 | *(descr->zLastTag) = 0; |
| 3705 | descr->nCommitsSince = -1; |
| 3706 | *(descr->zArtifactHash) = 0; |
| 3707 | descr->isDirty = -1; |
| 3708 | return -1; |
| 3709 | } |
| 3710 | |
| 3711 | zUuid = rid_to_uuid(rid); |
| 3712 | memcpy(descr->zArtifactHash, zUuid, strlen(zUuid)+1); |
| 3713 | descr->isDirty = unsaved_changes(0); |
| 3714 | |
| 3715 | db_multi_exec( |
| 3716 | "DROP TABLE IF EXISTS singletonTaggedAncestors;" |
| 3717 | "CREATE TEMP TABLE singletonTaggedAncestors AS" |
| 3718 | " WITH RECURSIVE " |
| 3719 | " singletonTaggedCommits(rid,mtime,shorttag) AS (" |
| 3720 | " SELECT DISTINCT b.rid,e.mtime,substr(t.tagname,5) AS shorttag" |
| 3721 | " FROM blob b" |
| 3722 | " INNER JOIN event e ON e.objid=b.rid" |
| 3723 | " INNER JOIN tagxref tx ON tx.rid=b.rid" |
| 3724 | " INNER JOIN tag t ON t.tagid=tx.tagid" |
| 3725 | " WHERE e.type='ci'" |
| 3726 | " AND tx.tagtype=1" |
| 3727 | " AND t.tagname GLOB 'sym-%q'" |
| 3728 | " )," |
| 3729 | " parent(pid,cid,isCP,isPrim) AS (" |
| 3730 | " SELECT plink.pid, plink.cid, 0, isPrim FROM plink" |
| 3731 | " UNION ALL" |
| 3732 | " SELECT parentid, childid, 1, 0 FROM cherrypick WHERE NOT isExclude" |
| 3733 | " )," |
| 3734 | " ancestor(rid, mtime, isCP, isPrim) AS (" |
| 3735 | " SELECT objid, mtime, 0, 1 FROM event WHERE objid=%d" |
| 3736 | " UNION" |
| 3737 | " SELECT parent.pid, event.mtime, parent.isCP, parent.isPrim" |
| 3738 | " FROM ancestor, parent, event" |
| 3739 | " WHERE parent.cid=ancestor.rid" |
| 3740 | " AND event.objid=parent.pid" |
| 3741 | " AND NOT ancestor.isCP" |
| 3742 | " AND (event.mtime >= " |
| 3743 | " (SELECT max(mtime) FROM singletonTaggedCommits" |
| 3744 | " WHERE mtime<=(SELECT mtime FROM event WHERE objid=%d)))" |
| 3745 | " ORDER BY mtime DESC" |
| 3746 | " LIMIT 1000000" |
| 3747 | " ) " |
| 3748 | "SELECT rid, mtime, isCP, isPrim, ROW_NUMBER() OVER (ORDER BY mtime DESC) rn" |
| 3749 | " FROM ancestor", |
| 3750 | (matchGlob ? matchGlob : "*"), rid, rid |
| 3751 | ); |
| 3752 | |
| 3753 | db_prepare(&q, |
| 3754 | "SELECT ta.rid, ta.mtime, ta.rn, b.uuid, substr(t.tagname, 5)" |
| 3755 | " FROM singletonTaggedAncestors ta" |
| 3756 | " INNER JOIN blob b ON b.rid=ta.rid" |
| 3757 | " INNER JOIN tagxref tx ON tx.rid=ta.rid" |
| 3758 | " INNER JOIN tag t ON tx.tagid=t.tagid" |
| 3759 | " WHERE tx.tagtype=1 AND t.tagname GLOB 'sym-%q' " |
| 3760 | " AND rn=(SELECT MAX(rn) FROM singletonTaggedAncestors)" |
| 3761 | " ORDER BY tx.mtime DESC, t.tagname DESC LIMIT 1", |
| 3762 | (matchGlob ? matchGlob : "*") |
| 3763 | ); |
| 3764 | |
| 3765 | if( db_step(&q)==SQLITE_ROW ){ |
| 3766 | const char *lastTag = db_column_text(&q, 4); |
| 3767 | memcpy(descr->zLastTag, lastTag, strlen(lastTag)+1); |
| 3768 | descr->nCommitsSince = db_column_int(&q, 2)-1; |
| 3769 | nRet = 0; |
| 3770 | }else{ |
| 3771 | /* no ancestor commit with a fitting singleton tag found */ |
| 3772 | *(descr->zLastTag) = 0; |
| 3773 | descr->nCommitsSince = -1; |
| 3774 | nRet = -2; |
| 3775 | } |
| 3776 | |
| 3777 | db_finalize(&q); |
| 3778 | return nRet; |
| 3779 | } |
| 3780 | |
| 3781 | /* |
| 3782 | ** COMMAND: describe |
| 3783 | ** |
| 3784 | ** Usage: %fossil describe ?VERSION? ?OPTIONS? |
| 3785 | ** |
| 3786 | ** Provide a description of the given VERSION by showing a non-propagating |
| 3787 | ** tag of the youngest tagged ancestor, followed by the number of commits |
| 3788 | ** since that, and the short hash of VERSION. If VERSION and the found |
| 3789 | ** ancestor refer to the same commit, the last two components are omitted, |
| 3790 | ** unless --long is provided. |
| 3791 | ** |
| 3792 | ** If no VERSION is provided, describe the current checked-out version. When |
| 3793 | ** no fitting tagged ancestor is found, show only the short hash of VERSION. |
| 3794 | ** |
| 3795 | ** Options: |
| 3796 | ** |
| 3797 | ** --digits Display so many hex digits of the hash (default 10) |
| 3798 | ** -d|--dirty Show whether there are changes to be committed |
| 3799 | ** --long Always show all three components |
| 3800 | ** --match GLOB Consider only non-propagating tags matching GLOB |
| 3801 | */ |
| 3802 | void describe_cmd(void){ |
| 3803 | const char *zName; |
| 3804 | const char *zMatchGlob; |
| 3805 | const char *zDigits; |
| 3806 | int nDigits; |
| 3807 | int bDirtyFlag = 0; |
| 3808 | int bLongFlag = 0; |
| 3809 | CommitDescr descr; |
| 3810 | |
| 3811 | db_find_and_open_repository(0,0); |
| 3812 | bDirtyFlag = find_option("dirty","d",0)!=0; |
| 3813 | bLongFlag = find_option("long","",0)!=0; |
| 3814 | zMatchGlob = find_option("match", 0, 1); |
| 3815 | zDigits = find_option("digits", 0, 1); |
| 3816 | |
| 3817 | if ( !zDigits || ((nDigits=atoi(zDigits))==0) ){ |
| 3818 | nDigits = 10; |
| 3819 | } |
| 3820 | |
| 3821 | /* We should be done with options.. */ |
| 3822 | verify_all_options(); |
| 3823 | if( g.argc<3 ){ |
| 3824 | zName = "current"; |
| 3825 | }else{ |
| 3826 | zName = g.argv[2]; |
| 3827 | } |
| 3828 | |
| 3829 | if( bDirtyFlag ){ |
| 3830 | if ( g.argc>=3 ) fossil_fatal("cannot use --dirty with specific checkin"); |
| 3831 | } |
| 3832 | |
| 3833 | switch( describe_commit(zName, zMatchGlob, &descr) ){ |
| 3834 | case -1: |
| 3835 | fossil_fatal("commit %s does not exist", zName); |
| 3836 | break; |
| 3837 | case -2: |
| 3838 | fossil_print("%.*s%s\n", nDigits, descr.zArtifactHash, |
| 3839 | bDirtyFlag ? (descr.isDirty ? "-dirty" : "") : ""); |
| 3840 | break; |
| 3841 | case 0: |
| 3842 | if( descr.nCommitsSince==0 && !bLongFlag ){ |
| 3843 | fossil_print("%s%s\n", descr.zLastTag, |
| 3844 | bDirtyFlag ? (descr.isDirty ? "-dirty" : "") : ""); |
| 3845 | }else{ |
| 3846 | fossil_print("%s-%d-%.*s%s\n", descr.zLastTag, |
| 3847 | descr.nCommitsSince, nDigits, descr.zArtifactHash, |
| 3848 | bDirtyFlag ? (descr.isDirty ? "-dirty" : "") : ""); |
| 3849 | } |
| 3850 | break; |
| 3851 | default: |
| 3852 | fossil_fatal("cannot describe commit"); |
| 3853 | break; |
| 3854 | } |
| 3855 | } |
| 3856 |