diffdriver: Added --text option for easy diffing of the contents of annexed text files

This was already possible, but it was rather hard to come up with the
complex shell command needed.

Note that the diff output starts with "diff a/... b/...".
I left off the "--git" because it's not a git format diff.
This commit is contained in:
Joey Hess 2023-06-28 15:26:46 -04:00
parent 2c18fc1d60
commit d5c6197791
No known key found for this signature in database
GPG key ID: DB12DB0FF05F8F38
6 changed files with 98 additions and 26 deletions

View file

@ -1,6 +1,8 @@
git-annex (10.20230627) UNRELEASED; urgency=medium
* reinject: Added --guesskeys option.
* diffdriver: Added --text option for easy diffing of the contents of
annexed text files.
-- Joey Hess <id@joeyh.name> Mon, 26 Jun 2023 13:10:40 -0400

View file

@ -1,6 +1,6 @@
{- git-annex command
-
- Copyright 2014-2021 Joey Hess <id@joeyh.name>
- Copyright 2014-2023 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU AGPL version 3 or higher.
-}
@ -15,13 +15,26 @@ import Git.Types
cmd :: Command
cmd = dontCheck repoExists $
command "diffdriver" SectionPlumbing
"external git diff driver shim"
("-- cmd --") (withParams seek)
"git diff driver"
("-- cmd --") (seek <$$> optParser)
seek :: CmdParams -> CommandSeek
seek = withWords (commandAction . start)
data Options = Options
{ textDiff :: Bool
, restOptions :: CmdParams
}
start :: [String] -> CommandStart
optParser :: CmdParamsDesc -> Parser Options
optParser desc = Options
<$> switch
( long "text"
<> help "diff text files with diff(1)"
)
<*> cmdParams desc
seek :: Options -> CommandSeek
seek = commandAction . start
start :: Options -> CommandStart
start opts = do
let (req, differ) = parseReq opts
void $ liftIO . exitBool =<< liftIO . differ =<< fixupReq req
@ -55,10 +68,12 @@ serializeReq req@(Req {}) = map Param
, rNewMode req
]
parseReq :: [String] -> (Req, Differ)
parseReq opts = case separate (== "--") opts of
(c:ps, l) -> (mk l, externalDiffer c ps)
([],_) -> badopts
parseReq :: Options -> (Req, Differ)
parseReq opts
| textDiff opts = (mk (restOptions opts), textDiffer)
| otherwise = case separate (== "--") (restOptions opts) of
(c:ps, l) -> (mk l, externalDiffer c ps)
([],_) -> badopts
where
mk (path:old_file:old_hex:old_mode:new_file:new_hex:new_mode:[]) =
Req
@ -73,7 +88,7 @@ parseReq opts = case separate (== "--") opts of
mk (unmergedpath:[]) = UnmergedReq { rPath = unmergedpath }
mk _ = badopts
badopts = giveup $ "Unexpected input: " ++ unwords opts
badopts = giveup $ "Unexpected input: " ++ unwords (restOptions opts)
{- Check if either file is a symlink to a git-annex object,
- which git-diff will leave as a normal file containing the link text.
@ -101,3 +116,14 @@ fixupReq req@(Req {}) =
externalDiffer :: String -> [String] -> Differ
externalDiffer c ps = \req -> boolSystem c (map Param ps ++ serializeReq req )
textDiffer :: Differ
textDiffer req = do
putStrLn ("diff a/" ++ rPath req ++ " b/" ++ rPath req)
-- diff exits nonzero on difference, so ignore exit status
void $ boolSystem "diff"
[ Param "-u"
, Param (rOldFile req)
, Param (rNewFile req)
]
return True

View file

@ -1,35 +1,50 @@
# NAME
git-annex diffdriver - external git diff driver shim
git-annex diffdriver - git diff driver
# SYNOPSIS
git annex diffdriver --text
git annex diffdriver `-- cmd --opts --`
# DESCRIPTION
This is an external git diff driver shim. Normally, when using `git diff`
with an external diff driver, it will not see the contents of annexed
files, since git passes to it the git-annex symlinks or pointer files.
This command works around the problem, by running the
real external diff driver, and passing it the paths to the annexed content.
Normally, `git diff` when run on annexed files displays the changes that
are staged in git, eg annex symlinks and pointers. This command allows
`git diff` to diff the content of annexed files instead.
To use this, you will need to have installed some git external diff driver
command. This is not the regular diff command; it takes a git-specific
input. See git's documentation of `GIT_EXTERNAL_DIFF` and
This command can be used either as a simple text differ,
or as a shim that runs an external git diff driver.
If some of your annexed files are textual in form, and can be usefully
diffed with diff(1), you can configure git to use this command to diff
them, by configuring `.gitattributes` to contain eg `*.txt diff=annextextdiff`
and setting `git config diff.annextextdiff.command "git annex diffdriver --text"`
If your annexed files are not textual in form, you will need an external
diff driver program that is able to diff the file format(s) you use.
See git's documentation of `GIT_EXTERNAL_DIFF` and
gitattributes(5)'s documentation of external diff drivers.
Configure git to use "git-annex diffdriver -- cmd params --"
Normally, when using `git diff` with an external diff driver, it will not
see the contents of annexed files, since git passes to it the git-annex
symlinks or pointer files. This command works around the problem, by
running the real external diff driver, and passing it the paths to the
annexed content. Configure git to use "git-annex diffdriver -- cmd params --"
as the external diff driver, where cmd is the external diff
driver you want it to run, and params are any extra parameters to pass
to it. Note the trailing "--", which is required.
For example, set `GIT_EXTERNAL_DIFF="git-annex diffdriver -- j-c-diff --"`
For example, to use the j-c-diff program as the external diff driver,
set `GIT_EXTERNAL_DIFF="git-annex diffdriver -- j-c-diff --"`
# OPTIONS
Normally "--" followed by the diff driver command, its options,
and another "--"
To diff text files with diff(1), use the "--text" option.
To use an external diff driver command, the options must start with
"--" followed by the diff driver command, its options, and another "--"
Also the [[git-annex-common-options]](1) can be used.

View file

@ -731,8 +731,7 @@ content from the key-value store.
* `diffdriver`
This can be used to make `git diff` use an external diff driver with
annexed files.
This can be used to make `git diff` diff the content of annexed files.
See [[git-annex-diffdriver]](1) for details.

View file

@ -25,3 +25,5 @@ What do you think?
Cheers, Yann
PS: Thank you very much for git-annex, it's awesome! I'm giving a git-annex workshop next weekend [@Tuebix](https://cfp.tuebix.org/tuebix-2023/talk/review/GWRP3UKE3VFKVDG8RNQ8ZZPCZPNZYYWM), really looking forward to it.
> [[fixed|done]] --[[Joey]]

View file

@ -0,0 +1,28 @@
[[!comment format=mdwn
username="joey"
subject="""comment 1"""
date="2023-06-28T17:43:19Z"
content="""
This works:
echo * diff=text >>.gitattributes
git config diff.text.command "git-annex diffdriver -- sh -c 'echo diff a/\$0 b/\$0; echo \$0; diff -u \$1 \$4 || true' --"
Unfortunatly, the textconv approach will not work for locked files, because
git does not apply the textconv to symlinks. It could be made to work for
unlocked files but the above works for all annexed files.
diffing like this is only useful on text files, and most annexed files
are not text files. That's why the diffdriver command focuses on using some
external diff driver that knows how to diff whatever type of binary file is
being stored in git-annex. So this should certianly not be enabled by
default. It makes sense to enable it in .gitattributes when you have
annexed some textual files.
That command is a bit hard to come up with, with the complex shell quoting.
So rather than try to document it, I made an easier way to do the same
thing:
echo * diff=text >>.gitattributes
git config diff.text.command "git annex diffdriver --text"
"""]]