File Annotation
Not logged in
dbda8d6ce9 2007-07-21       drh: /*
dbda8d6ce9 2007-07-21       drh: ** Copyright (c) 2007 D. Richard Hipp
dbda8d6ce9 2007-07-21       drh: **
dbda8d6ce9 2007-07-21       drh: ** This program is free software; you can redistribute it and/or
dbda8d6ce9 2007-07-21       drh: ** modify it under the terms of the GNU General Public
dbda8d6ce9 2007-07-21       drh: ** License version 2 as published by the Free Software Foundation.
dbda8d6ce9 2007-07-21       drh: **
dbda8d6ce9 2007-07-21       drh: ** This program is distributed in the hope that it will be useful,
dbda8d6ce9 2007-07-21       drh: ** but WITHOUT ANY WARRANTY; without even the implied warranty of
dbda8d6ce9 2007-07-21       drh: ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
dbda8d6ce9 2007-07-21       drh: ** General Public License for more details.
dbda8d6ce9 2007-07-21       drh: **
dbda8d6ce9 2007-07-21       drh: ** You should have received a copy of the GNU General Public
dbda8d6ce9 2007-07-21       drh: ** License along with this library; if not, write to the
dbda8d6ce9 2007-07-21       drh: ** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
dbda8d6ce9 2007-07-21       drh: ** Boston, MA  02111-1307, USA.
dbda8d6ce9 2007-07-21       drh: **
dbda8d6ce9 2007-07-21       drh: ** Author contact information:
dbda8d6ce9 2007-07-21       drh: **   drh@hwaci.com
dbda8d6ce9 2007-07-21       drh: **   http://www.hwaci.com/drh/
dbda8d6ce9 2007-07-21       drh: **
dbda8d6ce9 2007-07-21       drh: *******************************************************************************
dbda8d6ce9 2007-07-21       drh: **
dbda8d6ce9 2007-07-21       drh: ** This file contains code used to implement the "diff" command
dbda8d6ce9 2007-07-21       drh: */
dbda8d6ce9 2007-07-21       drh: #include "config.h"
dbda8d6ce9 2007-07-21       drh: #include "diffcmd.h"
dbda8d6ce9 2007-07-21       drh: #include <assert.h>
dbda8d6ce9 2007-07-21       drh: 
dbda8d6ce9 2007-07-21       drh: /*
dbda8d6ce9 2007-07-21       drh: ** Shell-escape the given string.  Append the result to a blob.
dbda8d6ce9 2007-07-21       drh: */
dbda8d6ce9 2007-07-21       drh: static void shell_escape(Blob *pBlob, const char *zIn){
dbda8d6ce9 2007-07-21       drh:   int n = blob_size(pBlob);
dbda8d6ce9 2007-07-21       drh:   int k = strlen(zIn);
c863ec1a98 2008-10-21       drh:   int i, c;
dbda8d6ce9 2007-07-21       drh:   char *z;
c863ec1a98 2008-10-21       drh:   for(i=0; (c = zIn[i])!=0; i++){
c863ec1a98 2008-10-21       drh:     if( isspace(c) || c=='"' || (c=='\\' && zIn[i+1]!=0) ){
c863ec1a98 2008-10-21       drh:       blob_appendf(pBlob, "\"%s\"", zIn);
c863ec1a98 2008-10-21       drh:       z = blob_buffer(pBlob);
c863ec1a98 2008-10-21       drh:       for(i=n+1; i<=n+k; i++){
c863ec1a98 2008-10-21       drh:         if( z[i]=='"' ) z[i] = '_';
dbda8d6ce9 2007-07-21       drh:       }
c863ec1a98 2008-10-21       drh:       return;
dbda8d6ce9 2007-07-21       drh:     }
dbda8d6ce9 2007-07-21       drh:   }
c863ec1a98 2008-10-21       drh:   blob_append(pBlob, zIn, -1);
c863ec1a98 2008-10-21       drh: }
c863ec1a98 2008-10-21       drh: 
c863ec1a98 2008-10-21       drh: /*
0eb08b860c 2009-09-16       drh: ** This function implements a cross-platform "system()" interface.
0eb08b860c 2009-09-16       drh: */
0eb08b860c 2009-09-16       drh: int portable_system(char *zOrigCmd){
0eb08b860c 2009-09-16       drh:   int rc;
0eb08b860c 2009-09-16       drh: #ifdef __MINGW32__
0eb08b860c 2009-09-16       drh:   /* On windows, we have to put double-quotes around the entire command.
0eb08b860c 2009-09-16       drh:   ** Who knows why - this is just the way windows works.
0eb08b860c 2009-09-16       drh:   */
0eb08b860c 2009-09-16       drh:   char *zNewCmd = mprintf("\"%s\"", zOrigCmd);
0eb08b860c 2009-09-16       drh:   rc = system(zNewCmd);
0eb08b860c 2009-09-16       drh:   free(zNewCmd);
0eb08b860c 2009-09-16       drh: #else
0eb08b860c 2009-09-16       drh:   /* On unix, evaluate the command directly.
0eb08b860c 2009-09-16       drh:   */
0eb08b860c 2009-09-16       drh:   rc = system(zOrigCmd);
0eb08b860c 2009-09-16       drh: #endif
0eb08b860c 2009-09-16       drh:   return rc;
c82fb61775 2007-09-24       jnc: }
c82fb61775 2007-09-24       jnc: 
c82fb61775 2007-09-24       jnc: /*
a51808c0a5 2009-11-06       drh: ** Show the difference between two files, one in memory and one on disk.
c82fb61775 2007-09-24       jnc: **
a51808c0a5 2009-11-06       drh: ** The difference is the set of edits needed to transform pFile1 into
a51808c0a5 2009-11-06       drh: ** zFile2.  The content of pFile1 is in memory.  zFile2 exists on disk.
c82fb61775 2007-09-24       jnc: **
a51808c0a5 2009-11-06       drh: ** Use the internal diff logic if zDiffCmd is NULL.  Otherwise call the
a51808c0a5 2009-11-06       drh: ** command zDiffCmd to do the diffing.
c82fb61775 2007-09-24       jnc: */
a51808c0a5 2009-11-06       drh: static void diff_file(
a51808c0a5 2009-11-06       drh:   Blob *pFile1,             /* In memory content to compare from */
a51808c0a5 2009-11-06       drh:   const char *zFile2,       /* On disk content to compare to */
a51808c0a5 2009-11-06       drh:   const char *zName,        /* Display name of the file */
a51808c0a5 2009-11-06       drh:   const char *zDiffCmd      /* Command for comparison */
a51808c0a5 2009-11-06       drh: ){
a51808c0a5 2009-11-06       drh:   if( zDiffCmd==0 ){
a51808c0a5 2009-11-06       drh:     Blob out;      /* Diff output text */
a51808c0a5 2009-11-06       drh:     Blob file2;    /* Content of zFile2 */
a51808c0a5 2009-11-06       drh: 
a51808c0a5 2009-11-06       drh:     /* Read content of zFile2 into memory */
a51808c0a5 2009-11-06       drh:     blob_zero(&file2);
a51808c0a5 2009-11-06       drh:     blob_read_from_file(&file2, zFile2);
a51808c0a5 2009-11-06       drh: 
a51808c0a5 2009-11-06       drh:     /* Compute and output the differences */
a51808c0a5 2009-11-06       drh:     blob_zero(&out);
a51808c0a5 2009-11-06       drh:     text_diff(pFile1, &file2, &out, 5);
a51808c0a5 2009-11-06       drh:     printf("--- %s\n+++ %s\n", zName, zName);
a51808c0a5 2009-11-06       drh:     printf("%s\n", blob_str(&out));
a51808c0a5 2009-11-06       drh: 
a51808c0a5 2009-11-06       drh:     /* Release memory resources */
a51808c0a5 2009-11-06       drh:     blob_reset(&file2);
a51808c0a5 2009-11-06       drh:     blob_reset(&out);
a51808c0a5 2009-11-06       drh:   }else{
dbda8d6ce9 2007-07-21       drh:     int cnt = 0;
a51808c0a5 2009-11-06       drh:     Blob nameFile1;    /* Name of temporary file to old pFile1 content */
a51808c0a5 2009-11-06       drh:     Blob cmd;          /* Text of command to run */
a51808c0a5 2009-11-06       drh: 
a51808c0a5 2009-11-06       drh:     /* Construct a temporary file to hold pFile1 based on the name of
a51808c0a5 2009-11-06       drh:     ** zFile2 */
a51808c0a5 2009-11-06       drh:     blob_zero(&nameFile1);
dbda8d6ce9 2007-07-21       drh:     do{
a51808c0a5 2009-11-06       drh:       blob_reset(&nameFile1);
a51808c0a5 2009-11-06       drh:       blob_appendf(&nameFile1, "%s~%d", zFile2, cnt++);
a51808c0a5 2009-11-06       drh:     }while( access(blob_str(&nameFile1),0)==0 );
a51808c0a5 2009-11-06       drh:     blob_write_to_file(pFile1, blob_str(&nameFile1));
01ce2cf3dc 2007-09-24       jnc: 
a51808c0a5 2009-11-06       drh:     /* Construct the external diff command */
01ce2cf3dc 2007-09-24       jnc:     blob_zero(&cmd);
a51808c0a5 2009-11-06       drh:     blob_appendf(&cmd, "%s ", zDiffCmd);
a51808c0a5 2009-11-06       drh:     shell_escape(&cmd, blob_str(&nameFile1));
c863ec1a98 2008-10-21       drh:     blob_append(&cmd, " ", 1);
a51808c0a5 2009-11-06       drh:     shell_escape(&cmd, zFile2);
574763bab9 2007-09-26       jnc: 
a51808c0a5 2009-11-06       drh:     /* Run the external diff command */
a51808c0a5 2009-11-06       drh:     portable_system(blob_str(&cmd));
574763bab9 2007-09-26       jnc: 
a51808c0a5 2009-11-06       drh:     /* Delete the temporary file and clean up memory used */
a51808c0a5 2009-11-06       drh:     unlink(blob_str(&nameFile1));
a51808c0a5 2009-11-06       drh:     blob_reset(&nameFile1);
dbda8d6ce9 2007-07-21       drh:     blob_reset(&cmd);
dbda8d6ce9 2007-07-21       drh:   }
a51808c0a5 2009-11-06       drh: }
01ce2cf3dc 2007-09-24       jnc: 
a51808c0a5 2009-11-06       drh: /*
a51808c0a5 2009-11-06       drh: ** Do a diff against a single file named in g.argv[2] from version zFrom
a51808c0a5 2009-11-06       drh: ** against the same file on disk.
a51808c0a5 2009-11-06       drh: */
a51808c0a5 2009-11-06       drh: static void diff_one_against_disk(const char *zFrom, const char *zDiffCmd){
a51808c0a5 2009-11-06       drh:   Blob fname;
a51808c0a5 2009-11-06       drh:   Blob content;
a51808c0a5 2009-11-06       drh:   file_tree_name(g.argv[2], &fname, 1);
a51808c0a5 2009-11-06       drh:   historical_version_of_file(zFrom, blob_str(&fname), &content);
a51808c0a5 2009-11-06       drh:   diff_file(&content, g.argv[2], g.argv[2], zDiffCmd);
a51808c0a5 2009-11-06       drh:   blob_reset(&content);
dbda8d6ce9 2007-07-21       drh:   blob_reset(&fname);
a51808c0a5 2009-11-06       drh: }
a51808c0a5 2009-11-06       drh: 
a51808c0a5 2009-11-06       drh: /*
a51808c0a5 2009-11-06       drh: ** Run a diff between the version zFrom and files on disk.  zFrom might
a51808c0a5 2009-11-06       drh: ** be NULL which means to simply show the difference between the edited
a51808c0a5 2009-11-06       drh: ** files on disk and the check-out on which they are based.
a51808c0a5 2009-11-06       drh: */
a51808c0a5 2009-11-06       drh: static void diff_all_against_disk(const char *zFrom, const char *zDiffCmd){
a51808c0a5 2009-11-06       drh:   int vid;
a51808c0a5 2009-11-06       drh:   Blob sql;
a51808c0a5 2009-11-06       drh:   Stmt q;
c863ec1a98 2008-10-21       drh: 
a51808c0a5 2009-11-06       drh:   vid = db_lget_int("checkout", 0);
149945beea 2009-11-06       drh:   vfile_check_signature(vid);
a51808c0a5 2009-11-06       drh:   blob_zero(&sql);
a51808c0a5 2009-11-06       drh:   db_begin_transaction();
a51808c0a5 2009-11-06       drh:   if( zFrom ){
a51808c0a5 2009-11-06       drh:     int rid = name_to_rid(zFrom);
a51808c0a5 2009-11-06       drh:     if( !is_a_version(rid) ){
a51808c0a5 2009-11-06       drh:       fossil_fatal("no such check-in: %s", zFrom);
a51808c0a5 2009-11-06       drh:     }
a51808c0a5 2009-11-06       drh:     load_vfile_from_rid(rid);
a51808c0a5 2009-11-06       drh:     blob_appendf(&sql,
a51808c0a5 2009-11-06       drh:       "SELECT v2.pathname, v2.deleted, v2.chnged, v2.rid==0, v1.rid"
a51808c0a5 2009-11-06       drh:       "  FROM vfile v1, vfile v2 "
a51808c0a5 2009-11-06       drh:       " WHERE v1.pathname=v2.pathname AND v1.vid=%d AND v2.vid=%d"
db608ee72f 2009-11-06       drh:       "   AND (v2.deleted OR v2.chnged OR v1.rid!=v2.rid)"
a51808c0a5 2009-11-06       drh:       "UNION "
a51808c0a5 2009-11-06       drh:       "SELECT pathname, 1, 0, 0, 0"
a51808c0a5 2009-11-06       drh:       "  FROM vfile v1"
a51808c0a5 2009-11-06       drh:       " WHERE v1.vid=%d"
a51808c0a5 2009-11-06       drh:       "   AND NOT EXISTS(SELECT 1 FROM vfile v2"
a51808c0a5 2009-11-06       drh:                         " WHERE v2.vid=%d AND v2.pathname=v1.pathname)"
a51808c0a5 2009-11-06       drh:       "UNION "
a51808c0a5 2009-11-06       drh:       "SELECT pathname, 0, 0, 1, 0"
a51808c0a5 2009-11-06       drh:       "  FROM vfile v2"
a51808c0a5 2009-11-06       drh:       " WHERE v2.vid=%d"
a51808c0a5 2009-11-06       drh:       "   AND NOT EXISTS(SELECT 1 FROM vfile v1"
a51808c0a5 2009-11-06       drh:                         " WHERE v1.vid=%d AND v1.pathname=v2.pathname)"
a51808c0a5 2009-11-06       drh:       " ORDER BY 1",
a51808c0a5 2009-11-06       drh:       rid, vid, rid, vid, vid, rid
a51808c0a5 2009-11-06       drh:     );
a51808c0a5 2009-11-06       drh:   }else{
a51808c0a5 2009-11-06       drh:     blob_appendf(&sql,
a51808c0a5 2009-11-06       drh:       "SELECT pathname, deleted, chnged , rid==0, rid"
a51808c0a5 2009-11-06       drh:       "  FROM vfile"
a51808c0a5 2009-11-06       drh:       " WHERE vid=%d"
a51808c0a5 2009-11-06       drh:       "   AND (deleted OR chnged OR rid==0)"
a51808c0a5 2009-11-06       drh:       " ORDER BY pathname",
a51808c0a5 2009-11-06       drh:       vid
a51808c0a5 2009-11-06       drh:     );
a51808c0a5 2009-11-06       drh:   }
a51808c0a5 2009-11-06       drh:   db_prepare(&q, blob_str(&sql));
c863ec1a98 2008-10-21       drh:   while( db_step(&q)==SQLITE_ROW ){
c863ec1a98 2008-10-21       drh:     const char *zPathname = db_column_text(&q,0);
c863ec1a98 2008-10-21       drh:     int isDeleted = db_column_int(&q, 1);
c863ec1a98 2008-10-21       drh:     int isChnged = db_column_int(&q,2);
a51808c0a5 2009-11-06       drh:     int isNew = db_column_int(&q,3);
a51808c0a5 2009-11-06       drh:     char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname);
c863ec1a98 2008-10-21       drh:     if( isDeleted ){
c863ec1a98 2008-10-21       drh:       printf("DELETED  %s\n", zPathname);
c863ec1a98 2008-10-21       drh:     }else if( access(zFullName, 0) ){
c863ec1a98 2008-10-21       drh:       printf("MISSING  %s\n", zPathname);
c863ec1a98 2008-10-21       drh:     }else if( isNew ){
c863ec1a98 2008-10-21       drh:       printf("ADDED    %s\n", zPathname);
c863ec1a98 2008-10-21       drh:     }else if( isDeleted ){
c863ec1a98 2008-10-21       drh:       printf("DELETED  %s\n", zPathname);
c863ec1a98 2008-10-21       drh:     }else if( isChnged==3 ){
c863ec1a98 2008-10-21       drh:       printf("ADDED_BY_MERGE %s\n", zPathname);
c863ec1a98 2008-10-21       drh:     }else{
a51808c0a5 2009-11-06       drh:       int srcid = db_column_int(&q, 4);
a51808c0a5 2009-11-06       drh:       Blob content;
a51808c0a5 2009-11-06       drh:       content_get(srcid, &content);
85670cfcc8 2008-10-24       drh:       printf("Index: %s\n======================================="
85670cfcc8 2008-10-24       drh:              "============================\n",
85670cfcc8 2008-10-24       drh:              zPathname
85670cfcc8 2008-10-24       drh:       );
a51808c0a5 2009-11-06       drh:       diff_file(&content, zFullName, zPathname, zDiffCmd);
a51808c0a5 2009-11-06       drh:       blob_reset(&content);
c863ec1a98 2008-10-21       drh:     }
c863ec1a98 2008-10-21       drh:     free(zFullName);
c863ec1a98 2008-10-21       drh:   }
c863ec1a98 2008-10-21       drh:   db_finalize(&q);
a51808c0a5 2009-11-06       drh:   db_end_transaction(1);
c863ec1a98 2008-10-21       drh: }
a51808c0a5 2009-11-06       drh: 
a51808c0a5 2009-11-06       drh: 
c863ec1a98 2008-10-21       drh: 
c863ec1a98 2008-10-21       drh: /*
c863ec1a98 2008-10-21       drh: ** COMMAND: diff
c863ec1a98 2008-10-21       drh: ** COMMAND: gdiff
c863ec1a98 2008-10-21       drh: **
a51808c0a5 2009-11-06       drh: ** Usage: %fossil diff|gdiff ?options? ?FILE?
c863ec1a98 2008-10-21       drh: **
a51808c0a5 2009-11-06       drh: ** Show the difference between the current version of FILE (as it
a51808c0a5 2009-11-06       drh: ** exists on disk) and that same file as it was checked out.  Or
a51808c0a5 2009-11-06       drh: ** if the FILE argument is omitted, show the unsaved changed currently
a51808c0a5 2009-11-06       drh: ** in the working check-out.
c863ec1a98 2008-10-21       drh: **
a51808c0a5 2009-11-06       drh: ** If the "--from VERSION" or "-r VERSION" option is used it specifies
a51808c0a5 2009-11-06       drh: ** the source check-in for the diff operation.  If not specified, the
a51808c0a5 2009-11-06       drh: ** source check-in is the base check-in for the current check-out.
c863ec1a98 2008-10-21       drh: **
a51808c0a5 2009-11-06       drh: ** If the "--to VERSION" option appears, it specifies the check-in from
a51808c0a5 2009-11-06       drh: ** which the second version of the file or files is taken.  If there is
a51808c0a5 2009-11-06       drh: ** no "--to" option then the (possibly edited) files in the current check-out
a51808c0a5 2009-11-06       drh: ** are used.
c863ec1a98 2008-10-21       drh: **
a51808c0a5 2009-11-06       drh: ** The "-i" command-line option forces the use of the internal diff logic
a51808c0a5 2009-11-06       drh: ** rather than any external diff program that might be configured using
a51808c0a5 2009-11-06       drh: ** the "setting" command.  If no external diff program is configured, then
a51808c0a5 2009-11-06       drh: ** the "-i" option is a no-op.  The "-i" option converts "gdiff" into "diff".
c863ec1a98 2008-10-21       drh: */
c863ec1a98 2008-10-21       drh: void diff_cmd(void){
85670cfcc8 2008-10-24       drh:   int isGDiff;               /* True for gdiff.  False for normal diff */
a51808c0a5 2009-11-06       drh:   int isInternDiff;          /* True for internal diff */
a51808c0a5 2009-11-06       drh:   const char *zFrom;         /* Source version number */
a51808c0a5 2009-11-06       drh:   const char *zTo;           /* Target version number */
a51808c0a5 2009-11-06       drh:   const char *zDiffCmd = 0;  /* External diff command. NULL for internal diff */
c863ec1a98 2008-10-21       drh: 
85670cfcc8 2008-10-24       drh:   isGDiff = g.argv[1][0]=='g';
a51808c0a5 2009-11-06       drh:   isInternDiff = find_option("internal","i",0)!=0;
a51808c0a5 2009-11-06       drh:   zFrom = find_option("from", "r", 1);
a51808c0a5 2009-11-06       drh:   zTo = find_option("to", 0, 1);
c863ec1a98 2008-10-21       drh: 
a51808c0a5 2009-11-06       drh:   if( zTo==0 ){
a51808c0a5 2009-11-06       drh:     db_must_be_within_tree();
db608ee72f 2009-11-06       drh:     verify_all_options();
db608ee72f 2009-11-06       drh:     if( !isInternDiff && g.argc==3 ){
a51808c0a5 2009-11-06       drh:       zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0);
a51808c0a5 2009-11-06       drh:     }
a51808c0a5 2009-11-06       drh:     if( g.argc==3 ){
a51808c0a5 2009-11-06       drh:       diff_one_against_disk(zFrom, zDiffCmd);
85670cfcc8 2008-10-24       drh:     }else{
a51808c0a5 2009-11-06       drh:       diff_all_against_disk(zFrom, zDiffCmd);
aeaee1f385 2009-09-10       drh:     }
a51808c0a5 2009-11-06       drh:   }else if( zFrom==0 ){
a51808c0a5 2009-11-06       drh:     fossil_fatal("must use --from if --to is present");
a51808c0a5 2009-11-06       drh:   }else{
a51808c0a5 2009-11-06       drh:     db_find_and_open_repository(1);
db608ee72f 2009-11-06       drh:     verify_all_options();
db608ee72f 2009-11-06       drh:     if( !isInternDiff && g.argc==3 ){
a51808c0a5 2009-11-06       drh:       zDiffCmd = db_get(isGDiff ? "gdiff-command" : "diff-command", 0);
85670cfcc8 2008-10-24       drh:     }
a51808c0a5 2009-11-06       drh:     fossil_fatal("--to not yet implemented");
a51808c0a5 2009-11-06       drh: #if 0
a51808c0a5 2009-11-06       drh:     if( g.argc==3 ){
a51808c0a5 2009-11-06       drh:       diff_one_two_versions(zFrom, zTo, zDiffCmd);
c863ec1a98 2008-10-21       drh:     }else{
a51808c0a5 2009-11-06       drh:       diff_all_two_versions(zFrom, zTo, zDiffCmd);
c863ec1a98 2008-10-21       drh:     }
aeaee1f385 2009-09-10       drh: #endif
a51808c0a5 2009-11-06       drh:   }
dbda8d6ce9 2007-07-21       drh: }