bk log(7.3ce) BitKeeper User's Manual bk log(7.3ce) NAME bk log - print file revision history and/or metadata SYNOPSIS bk log [-dDfS] [-c<d>] [-C<r>] [-L[<url>]] [-r<r>] [<file> ... | -] DESCRIPTION The bk log command is used to extract revision history and or metadata from a file or set of files. The default behavior is to print a sum- mary of each revision to each of the specified files. There are options to restrict the set of revisions to print, a very commonly used one is -r+, which restricts the set to the most recent revision. With no options bk log output defaults to giving information on all revisions of all files in the present directory that are under Bit- Keeper control. Output is given as follows: the name of the file and range of revisions is followed by a detailed account of each revision. Revision number, revision date and time, user who made that revision, what the relative path from root of repository is to that file, the comments that go with that revision, and documents if the file has been renamed. OPTIONS -<n> A numeric argument that limits the number of deltas printed per file. --begin=<script> The dspec v2 language (see that section below and the examples) is awk like and has optional begin/end sec- tions. This option allows you to specify the body of a begin section (if the file also has a begin section then both are run; order is undefined). It is typi- cally used with an on disk dspec in a dspec file that has been written in a way to do one thing by default and another if a variable is set to non-zero. -c<date> Cut-off dates. See range specifications (below) or bk help range for details. -C<rev> Make the range be all revs that are the same cset as <rev>. -d<spec> Override the default output format (see below). --dspecf=<file> Like -d but read the dspec from a file. -D Do not skip files in the BitKeeper/deleted directory. -f Print the changes in forward (oldest to newest) order. The default is backward. -L[<url>] Show all deltas unique to this repository relative to the (outgoing) parent or <url> if one was specified. May not be combined with -c or -r. --lattice Restrict the deltas to those on the lattice between the two range endpoints. Unlike a range, the lower bound is included in the output. --longest Restrict the deltas to those on the longest line between the two range endpoints. Unlike a range, the lower bound is included in the output. -n Add a newline to each printed record. -r<rev> Specify a revision, or part of a range. (Or key or ment.") -S --standalone Use with -L in a nested component when you want the component to act like a standalone repository. RANGE SPECIFICATIONS -r+ prints the most recent delta -r1.3..1.6 prints all deltas that are in 1.6's history but are not in 1.3's history. -c2006/07..2006 prints all deltas from July 1 2006 to Dec 31 2006 -c2006..2006 prints all deltas from Jan 1 2006 to Dec 31 2006 -c-1d.. prints all deltas made in the last 24 hours; similarly for s, m, h, d, M, and Y for seconds,minutes, hours, days, months, and years. DEFAULT OUTPUT FORMAT The bk changes, and bk log commands have a default output format which may be modified on a per user or per repository basis. The default formats are named as follows: dspec-changes Specifies the format for bk changes [-v] output (without -vv). dspec-changes-vv Specifies the format for bk changes -vv output (ver- bose with diffs). dspec-log Specifies the format for bk log output. There are other dspec-* files, if you want some fun look at dspec-*-json, if you understand those you understand that dspecs are a little programming language. The shipped files live in `bk bin`/dspec-* These files can live in multiple places, this is the search order for "dspec-changes", the first one found is used: <repo>/BitKeeper/etc/dspec-changes <product>/BitKeeper/etc/dspec-changes (BitKeeper/Nested only) `bk dotbk`/dspec-changes /etc/BitKeeper/etc/dspec-changes `bk bin`/dspec-changes MODIFYING THE OUTPUT FORMAT There are many different pieces of information in a BitKeeper file and virtually all of them can be extracted using a non-default output for- mat. To extract specific information, a "dspec" (data specification) string is provided and where there are keywords surrounded by colons the key- words are expanded much like printf(3). This can lead to many useful one liners; want to see which file is the most busy in a repository? bk -U log -r+ -nd':DS: :GFILE' | sort -nr | head If you are familiar with awk(1) the processing here is similar. In awk, each line is a record, here each delta is a record. For each delta, you can provide a dspec which says what it is you want to print from that delta. Looking at the dspec-* files in the installation directory is recommended, there are $begin/$end sections that mimic awk. The dspecs can contain the following set of escaped characters which will be replaced with the specified string: \b Backspace \f Form feed \n Newline \r Carriage return \t Tab \123 3 digit octal value (pad with leading zeros) \<c> c for any other character "c" In almost all cases, a trailing newline is not provided by any of the keywords and one should be provided as needed. The newline can be added in line or you can specify the -n option which will add a newline after each delta is processed. Multi-line keywords are normally printed as is, but you can format each line separately using a $each() loop (see ITERATIVE OUTPUT below). CONDITIONAL OUTPUT The dspec can produce output conditionally. The following prints the revisions of foo.c that were made by joe: bk log -nd'$if(:USER:=joe){:REV:}' foo.c CONDITIONAL STATEMENTS $if(<expr>){<anything>} prints <anything> if <expr> is true. If <expr> is a keyword, i.e., :MERGE:, then the keyword is examined and returns true if it has a value. <anything> can contain keywords, i.e., :REV:. $unless(<expr>){<anything>} prints <anything> unless <expr> is true. $if(<expr>){<stuff if true>}$else{<stuff if false>} both $if and $unless can have an optional $else clause that prints if the preceding clause was not printed. CONDITIONAL OPERATORS strings <lhs>=<rhs> true if <lhs> is identical to <rhs>. <lhs>!=<rhs> true if <lhs> is different than <rhs>. <str>=~<glob> true if <str> matches <glob>. <str>=~</regexp/> true if <str> matches the regular expres- sion; /regexp/i does a case-insensitive match and /glob/g does a glob match. Note: spaces immediately before and after the operator are ignored. To match against such a leading or trailing space, escape it. numbers <lhs> -eq <rhs> equality; <lhs> -ge <rhs> equal or greater than; <lhs> -gt <rhs> greater than; <lhs> -le <rhs> equal or less than; <lhs> -lt <rhs> less than. Note: spaces are required on both sides of the operator. COMPOUND EXPRESSIONS As of BitKeeper release 4.1, dspecs support logical AND (A && B), logi- cal OR (A || B), and parenthesized subexpressions ((A && B) || (C && D)). Note: spaces immediately before and after the operator are ignored. To match against such a leading or trailing space, escape it. ITERATIVE OUTPUT Some keywords, such as comments or tags, may be multi-line. To print a prefix in front of each of these lines, the dspec is: bk log -d'$each(:C:){C (:C:)\n}' foo.c This iteration works for all keywords, but normally is only used for keywords that can contain multiple lines like ALL_TAGS, C, TAGGED, and TAGS. The $first() builtin can be used to print only the first line of one of these multi-line keywords. Some development efforts tend to use the first line of commit comments as a summary of the change; the rest are more details. The dspec that ships with BitKeeper for bk changes uses this feature to implement bk changes --short. To print the first line of each comment in a changeset: bk changes -nd'$first(:C:)' VARIABLES When the bk log command is run over multiple revisions of one or more files, the data specification ("dspec") is evaluated once for each revision, and it sometimes is useful to use dspec variables, denoted as '$0', '$1', up to '$9', to remember values across revisions or files. A variable is assigned a value with: ${0=<dspec>} where <dspec> is a recursively evaluated data specification. Once assigned, a variable retains its value for the remainder of the command (or until reassigned). Variables start out as empty strings (evaluate to zero in numeric context), and when re-assigned the old value is dis- carded. DSPEC VERSION 2 This little printf like language outgrew itself, having if/then/else and other control flow was too much on just one line. We came up with a version 2 language that allows you to have dspecs that look like a programming language. All of the output from BitKeeper commands is generated via the v2 language in external files (see dspec-* in the bin directory). All of the commands that take dspecs also can take a file containing the dspec; that's how the BitKeeper commands work, they just load those files. To switch into the new language a dspec has to begin like so: # dspec-v2 The language is awk like, comments start with "#" and go to the end of the line. Whitespace is ignored unless it is inside double quotes, then it is reproduced exactly. Keywords are expanded inside of double quotes as are the escaped characters mentioned above. Control flow and begin/end blocks (all the $something) are unquoted. Like awk, there are optional begin/end blocks: $begin { "[\n" } $end { $if($0 -eq 1) { "\n" } "]\n" } Those are from dspec-changes-json-v which is the dspec that is used for bk changes -v --json which (surprise!) produces json format output. If you want to understand this language that is a good file to read. In many cases, all keywords will expand in begin/end blocks, those are run in the context of the first/last delta selected. If a range is selected that has no matching deltas (think bk changes -L for example) then none of the keywords will be expanded, the current system expands keywords if and only if there is a valid delta in play. A simple dspec-v2 example is the old bk prs output (SCCS compat): # dspec-v2 ":DT: :REV: :D: :T::TZ: :USERHOST: :DS: :DP: :LI:/:LD:/:LU:\n" "P :DPN:\n" $each(:TAGGED:){"S (:TAGGED:)\n"} $each(:C:){"C (:C:)\n"} "------------------------------------------------\n" KEYWORDS Some keywords are per repository and are marked with R in the T column below. Some keywords are per file and are marked with F in the T col- umn below; other keywords are per delta and are marked with D. Some keywords only make sense if they are changesets, they are marked with C. Those keywords that can be either on a changeset or on a file are marked with D. Name T What is printed ========================================================== :AGE: D D's age, i.e., seven hours, two weeks, etc. :ALL_TAGS: C The tag[s] if this changeset has or ever had them :ATTACHED_ID: F package id to be used for new deltas :BAM: F true if the file is managed as a BAM file :C: D D's comments :CHANGESET: F true if ChangeSet file, false for user files :COMMENTS: D comments portion of :PRS: :COMPRESSION: D D's compression (gzip|none) :CSETKEY: D delta key if D is at a changeset boundary :CSETREV: D revision of first cset boundary after D :D: D D's date as YYYY/MM/DD :D_: D D's date as YYYY-MM-DD :DANGLING: D D's rev if & only if D is a dangling delta :DI: D D's includes/excludes as +I,I/-X,X (serials) :DIFFS: D D's changes in the form of traditional diffs :DIFFS_U: D D's changes in the form of unified diffs :DIFFS_UP: D D's changes in the form of unified/procedural diffs :DL: D lines inserted/deleted/unchanged in D :DM: D month part of D's date (Jan..Dec) :DOMAIN: D the domain part of the hostname of D :DP: D the serial number of the parent of D :DPN: D the pathname of g.file as of D :DS: D the serial number of D :DSUM: D D's 16 bit unsigned checksum (%05u) :DSUMMARY: D first line of :PRS: :DT: D D's type: (D|R|T) meaning (Data|Removed|Tag) :Dd: D day part of D's date as DD :Dm: D month part of D's date as MM :Dn: D serial numbers of D's includes, if any :Dt: D D's data as :DT::REV::D::T::USER::DS::DP: :Dx: D serial numbers of D's excludes, if any :Dy: D year part of D's date as YY or YYYY :ENC: F current encoding scheme (ascii|uuencode|BAM) :EVEN: D True on every other delta processed :F: F basename of the BitKeeper file :FLAGS: D file flags as of D in words (HASH, YEAR4...) :FSUM: F 16 bit unsigned checksum of the s.file :FUDGE: D time stamp fudge used make time monotonic :FULLHOST: D same as :HOST: or :HOST:/:REALHOST: :FULLUSER: D same as :USER: or :USER:/:REALUSER: :G: F basename of the gfile :GB: D file as of version D :GCA: D find the graph GCA for D's parents :GCA2: D find the set GCA for D's parents :GFILE: F pathname of the gfile :GREV: F for a file with conflicts, the GCA of the unmerged tips :HASHCOUNT: D count of key/value pairs in D if & only if hash file :HOST: D hostname where D was made; could be BK_HOST :HTML_AGE: D age in a form suitable for web pages :HTML_C: D comments in a form suitable for web pages :ID: D the package id in effect when the delta was made :IMPORTER: D name of the importer of D, if D was an emailed patch :KEY: D BitKeeper key of D :KID: D D's direct kid in D's branch :KIDS: D all of D's direct kids in the graph :L: D the second field in D's rev (R.L.B.S) :LD: D lines deleted in D (%u) :LI: D lines inserted in D (%u) :LREV: F for a file with conflicts, the LOCAL unmerged tip :LU: D lines unchanged in D (%u) :Ld: D lines deleted in D (%05u) :Li: D lines inserted in D (%05u) :Lu: D lines unchanged in D (%05u) :MD5KEY: D crypto based BitKeeper key of D :MERGE: D D's rev if & only if D has a merge parent :MGP: D D's merge parent's serial number :MODE: D D's file modes as an octal (777) :MPARENT: D D's merge parent's revision :N: D Number of deltas, use instead of DS, DS may have gaps. :NEXT: D next entry after D in delta table :ODD: D True on every other delta processed :PACKAGE_ID: R per repository unique id (like bk id) :PARENT: D D's parent's revision :PREV: D previous entry before D in delta table :PRODUCT_ID: R per repository unique id (like bk -P id) :PRS: D old-style bk prs default output :R: D the first field in D's rev (R.L.B.S) :RANDOM: F random bits part of ROOTKEY :REALHOST: D real host where D was made, regardless of BK_HOST :REALUSER: D real programmer who made D, not assumed identity :RENAME: D D's path if different from parent's path :REPO_ID: R per repository unique id (like bk id -r) :REPOTYPE: R repotype: product|component|standalone :REV: D D's revision number :RI: D revision numbers of D's includes/excludes :ROOTKEY: F key of the 1.0 delta, file's internal name :RREV: F for a file with conflicts, the REMOTE unmerged tip :RWXMODE: D D's file modes as ascii (-rwxrwxrwx) :Rn: D revision numbers of D's includes :Rx: D revision numbers of D's excludes :S: D last field in D's rev (R.L.B.S) :SFILE: F pathname of s.file :SIBLINGS: D rev sibling's pointer in D :SPN: D pathname of s.file as of D :SYMLINK: D value of D's symlink target :T: D time of D as HH:MM:SS :TAGGED: C The tag[s] iff currently on this changeset :TAGS: C Tag[s] if on, or were on, this changeset (with notes) :TIME_T: D D's date as GMT time_t, TZ and Fudge adjusted :TIP: D D's rev if D is at the tip (TOT) :TZ: D offset from GMT as +/-HH:MM :Th: D hour part of D's date as HH :Tm: D minute part of D's date as MM :Ts: D seconds part of D's date as SS :USER: D programmer who made D; could be assumed identity :UTC: D D's time stamp as YYYYMMDDHHMMSS in GMT :UTC-FUDGE: D like UTC but without the date fudge :VERSION: F file format version NOTES Keywords marked with type C or D above can optionally be restricted to a given revision as follows: :KW|1.0: # as of a literal revision :KW|PARENT: # as of the current rev's parent :KW|MPARENT: # as of the current rev's merge parent (if any) For example: bk log -hnd'$if(:DPN|PARENT: != :DPN:){rename :DPN|PARENT: => :DPN:}' Things that can go in the second part include a specific revision (eg 1.0), PARENT, and MPARENT. EXAMPLES To extract a key, which is a stable name for a changeset: bk changes -r1.234 -nd:MD5KEY: To count up all the deltas in all user files: bk -U log -nd:REV: | wc -l List users who have made changes in a subdirectory in the last month: bk -rSubDir log -c-1M.. -nd:USER: | sort -u To see the number of merge changesets: bk changes -nd'$if(:MERGE:){merge}' | wc -l To see the number of merges in user files: bk -U log -nd'$if(:MERGE:){merge}' | wc -l Count up lines inserted by a particular user in all non-merge deltas: bk -U log -nd'$if(:USER: = joe && !:MERGE:){:LI:}' | awk '{ lines += $1 } END { print lines }' The default dspec for the bk changes command is below. This is an example of a dspec-v2 style of writing dspecs, much more pleasant for complex reports. # dspec-v2 # The default dspec used by 'bk changes' and 'bk changes -v' ":INDENT:" # 2 spaces + 2 in component ":DPN:@:REV:" ${1=:USERHOST:} # 1 == user || user@host $if (:CHANGESET: && !:COMPONENT_V:) { ", :Dy:-:Dm:-:Dd: :T::TZ:, $1" ${0=$1} # 0 == user of last cset } $else { $if($0 != $1){ # print user if different ", $1" } } $unless (:CHANGESET:) { " +:LI: -:LD:" # lines added/deleted } "\n" # bk changes --short is an alias for # bk changes --begin='${9=1}' # so $9 is the switch that decides between short/long form $if ($9 -eq 0) { # comments (long form) $each(:C:) { ":INDENT: (:C:)\n" } } $else { # comments (short form) ":INDENT: " $first(:C:) "\n" } $each(:TAGS:) { " TAG: (:TAGS:)\n" } $if ($9 -eq 0) { # skip merge info for --short $if (:MERGE:) { # merge shows both parents ":INDENT: MERGE: :MPARENT:\n" } } "\n" SEE ALSO bk range CATEGORY File BitKeeper Inc 1E1 bk log(7.3ce)