linuxsvndiffmeld

How to visually compare two revisions of a folder versioned by SVN on Linux?


I can compare a current folder state to its latest revision using the following command:

meld .

meld shows a list of changed files. One can click on each file and see a diff.

Is it possible to compare a current folder state to its specific revision (not the latest one)?

TortoiseSVN allows to compare arbitrary revisions, however it works on Windows only. SmartSVN is proprietary. SVN CLI is unusable for big changesets (CLI diff is fine for small commits, but it's very hard to use it for branch comparision with a lot of changes).

Maybe I could checkout the revision to compare into a separate folder and compare two folders using meld. But I guess there should be a simpler approach.


Solution

  • Folder comparision is usually required for code review or branch merging. It seems that the simplest approach is as follows:

    1. Find last trunk revision merged to a current brach
    2. If the current branch was not merged from trunk, then find the branch creation revision
    3. Checkout trunk with a specified revision to some folder
    4. Compare the trunk folder and the brach folder

    I didn't found any existing tools supporting it. Here is a bash script I use, maybe it will be useful for someone:

    TRUNK_BASE_PATH=~/tmp
    
    BRANCH_RELATIVE_URL=$(svn info --no-newline --show-item relative-url)
    if [[ "$BRANCH_RELATIVE_URL" != *"/branches/"* ]]; then
      echo "Run it in a branch. Relative URL should contain /branches/. Given: $BRANCH_RELATIVE_URL"
      exit 1
    fi
    
    TRUNK_RELATIVE_URL=${BRANCH_RELATIVE_URL%%/branches/*}/trunk
    echo "Trunk relative URL: $TRUNK_RELATIVE_URL"
    
    ROOT_URL=$(svn info --no-newline --show-item repos-root-url)
    TRUNK_URL=${ROOT_URL}${TRUNK_RELATIVE_URL:1}
    echo "Trunk URL: $TRUNK_URL"
    
    TRUNK_PATH=${TRUNK_BASE_PATH}/${TRUNK_URL#*://}
    echo "Trunk local copy path: $TRUNK_PATH"
    
    BRANCH_PATH=$(svn info --no-newline --show-item wc-root)
    echo "Branch local copy path: $BRANCH_PATH"
    
    SUBFOLDER=$(realpath --relative-to="$BRANCH_PATH" .)
    echo "Comparing subfolders: $SUBFOLDER"
    
    TRUNK_REVISION=$(svn mergeinfo --show-revs merged -R "$TRUNK_RELATIVE_URL" "$BRANCH_PATH" | tail -n 1)
    if [[ -z "$TRUNK_REVISION" ]]; then
      TRUNK_REVISION=$(svn log -r 1:HEAD --limit 1 --stop-on-copy -q | grep -oP "^r\K[0-9]+")
      echo "Comparison with trunk@$TRUNK_REVISION from which the current branch was copied"
    else
      echo "Comparison with trunk@$TRUNK_REVISION with which the current branch was last merged"
    fi
    
    if [[ -d "$TRUNK_PATH/.svn" ]]; then
      echo "Found .svn subfolder in the local trunk copy, updating it"
      svn update -r $TRUNK_REVISION "$TRUNK_PATH"
    else
      echo "Not found .svn subfolder in the local trunk copy, checking out it"
      svn checkout "$TRUNK_URL" -r $TRUNK_REVISION "$TRUNK_PATH"
    fi
    
    meld "$TRUNK_PATH/$SUBFOLDER" . &!