Skip to main content

perforce_cli/cmd/
annotate.rs

1use std::ffi::OsStr;
2use std::path::{Path, PathBuf};
3use std::process::{Child, Command, Stdio};
4
5use super::{DiffOptions, ExclusiveOption, SubCommand, Unselected};
6
7use crate::global::GlobalOpts;
8use crate::spawn::ParameterizedSpawn;
9
10/// Internal representation of the `-T` / `--tab=N` tab stop setting.
11#[cfg(not(feature = "lt2016_1"))]
12#[derive(Debug, Clone, Copy)]
13enum Tab {
14    /// `-T`: align output to the default tab stop of 8.
15    Default,
16    /// `--tab=N`: align output to a tab stop of N.
17    Custom(u32),
18}
19
20/// Variants of the `[-i | -I]` mutually exclusive option group of
21/// `p4 annotate`.
22pub mod follow {
23    /// Follow file history across branches (`-i`).
24    #[derive(Debug, Clone, Copy, Default)]
25    pub struct Branches;
26
27    /// Follow integrations into the file (`-I`).
28    #[derive(Debug, Clone, Copy, Default)]
29    pub struct Integrations;
30}
31
32impl ExclusiveOption for follow::Branches {
33    fn inject_args(&self, command: &mut Command) {
34        command.arg("-i");
35    }
36}
37
38impl ExclusiveOption for follow::Integrations {
39    fn inject_args(&self, command: &mut Command) {
40        command.arg("-I");
41    }
42}
43
44#[cfg_attr(
45    feature = "lt2014_2",
46    doc = "`p4 [g-opts] annotate [-a -c -i -I -q -t -d flag] file[revRange] ...`: print file lines along with their revisions."
47)]
48#[cfg_attr(
49    all(feature = "lt2015_1", not(feature = "lt2014_2")),
50    doc = "`p4 [g-opts] annotate [-a -c -i -I -q -t -d options] file[revRange] ...`: print file lines along with their revisions."
51)]
52#[cfg_attr(
53    all(feature = "lt2015_2", not(feature = "lt2015_1")),
54    doc = "`p4 [g-opts] annotate [-a -c -i -I -q -t] [-d options] file[revRange] ...`: print file lines along with their revisions."
55)]
56#[cfg_attr(
57    all(feature = "lt2016_1", not(feature = "lt2015_2")),
58    doc = "`p4 [g-opts] annotate [-a -c -i -I -q -t -u] [-d options] file[revRange] ...`: print file lines along with their revisions."
59)]
60#[cfg_attr(
61    all(feature = "lt2018_1", not(feature = "lt2016_1")),
62    doc = "`p4 [g-opts] annotate [-a -c -i -I -q -t -u -T] [-d options] file[revRange] ...`: print file lines along with their revisions."
63)]
64#[cfg_attr(
65    all(feature = "lt2019_1", not(feature = "lt2018_1")),
66    doc = "`p4 [g-opts] annotate [-a -c -i -I -q -t -u -T] [-d options] File Spec[revSpec]`: print file lines along with their revisions."
67)]
68#[cfg_attr(
69    all(feature = "lt2022_2", not(feature = "lt2019_1")),
70    doc = "`p4 [g-opts] annotate [-a -c -i -I -q -t -T -u] [-d options] File Spec[revSpec]`: print file lines along with their revisions."
71)]
72#[cfg_attr(
73    not(feature = "lt2022_2"),
74    doc = "`p4 [g-opts] annotate [-a -c -i -I -q -t -T -u] [-d options] FileSpec[revSpec]`: print file lines along with their revisions."
75)]
76#[derive(Debug, Clone, Default)]
77pub struct Annotate<F = Unselected> {
78    bin: PathBuf,
79
80    global_opts: GlobalOpts,
81
82    all_lines: bool,
83
84    changelist_number: bool,
85
86    diff_options: Option<DiffOptions>,
87
88    follow: F,
89
90    quiet_mode: bool,
91
92    force_binary: bool,
93
94    #[cfg(not(feature = "lt2015_2"))]
95    user_date: bool,
96
97    #[cfg(not(feature = "lt2016_1"))]
98    tab: Option<Tab>,
99}
100
101impl Annotate<Unselected> {
102    /// Creates a new `p4 annotate` command.
103    ///
104    /// `bin` is the path to the Perforce command-line executable.
105    pub fn new(bin: impl Into<PathBuf>, global_opts: GlobalOpts) -> Self {
106        Self {
107            bin: bin.into(),
108            global_opts,
109            ..Default::default()
110        }
111    }
112
113    /// # Description
114    ///
115    /// -i
116    ///
117    /// Follow file history across branches. If a file was created by
118    /// branching,
119    #[cfg_attr(
120        feature = "lt2017_2",
121        doc = "Perforce includes revisions up to the branch point."
122    )]
123    #[cfg_attr(
124        not(feature = "lt2017_2"),
125        doc = "the output includes revisions up to the branch point."
126    )]
127    /// The use of the -i option implies the -c option. The -i option cannot be
128    /// combined with -I.
129    pub fn follow_branches(self) -> Annotate<follow::Branches> {
130        Annotate {
131            bin: self.bin,
132            global_opts: self.global_opts,
133            all_lines: self.all_lines,
134            changelist_number: self.changelist_number,
135            diff_options: self.diff_options,
136            follow: follow::Branches,
137            quiet_mode: self.quiet_mode,
138            force_binary: self.force_binary,
139            #[cfg(not(feature = "lt2015_2"))]
140            user_date: self.user_date,
141            #[cfg(not(feature = "lt2016_1"))]
142            tab: self.tab,
143        }
144    }
145
146    /// # Description
147    ///
148    /// -I
149    ///
150    /// Follow integrations into the file. If a line was introduced into the
151    /// file by a merge, the source of the merge is indicated as the changelist
152    /// that introduced the line.
153    #[cfg_attr(
154        feature = "lt2017_2",
155        doc = "If that source was itself the result of an integration, that",
156        doc = "source will be used instead, and so on. The use of the -I option",
157        doc = "implies the -c option. The -I option cannot be combined with -i."
158    )]
159    #[cfg_attr(
160        not(feature = "lt2017_2"),
161        doc = "If that source was itself the result of an integration, that",
162        doc = "source will be used instead. The use of the -I option implies the",
163        doc = "-c option. The -I option cannot be combined with -i."
164    )]
165    pub fn follow_integrations(self) -> Annotate<follow::Integrations> {
166        Annotate {
167            bin: self.bin,
168            global_opts: self.global_opts,
169            all_lines: self.all_lines,
170            changelist_number: self.changelist_number,
171            diff_options: self.diff_options,
172            follow: follow::Integrations,
173            quiet_mode: self.quiet_mode,
174            force_binary: self.force_binary,
175            #[cfg(not(feature = "lt2015_2"))]
176            user_date: self.user_date,
177            #[cfg(not(feature = "lt2016_1"))]
178            tab: self.tab,
179        }
180    }
181}
182
183impl<F: ExclusiveOption, S, I> ParameterizedSpawn<(S,)> for Annotate<F>
184where
185    S: IntoIterator<Item = I>,
186    I: AsRef<OsStr>,
187{
188    type Output = Child;
189    type Error = std::io::Error;
190
191    /// Spawns `p4 annotate` for the given files as a child process with piped
192    /// standard output and error streams; use the returned [`Child`] handle
193    /// to wait for it or interact with it.
194    fn spawn_with(&mut self, (files,): (S,)) -> Result<Self::Output, Self::Error> {
195        self.setup_command(&self.bin)
196            .args(files)
197            .stdout(Stdio::piped())
198            .stderr(Stdio::piped())
199            .spawn()
200    }
201}
202
203impl<F: ExclusiveOption> Annotate<F> {
204    /// Get the Perforce command path.
205    pub fn get_bin(&self) -> &Path {
206        &self.bin
207    }
208
209    /// Set the Perforce command path.
210    pub fn set_bin(&mut self, bin: impl Into<PathBuf>) -> &mut Self {
211        self.bin = bin.into();
212        self
213    }
214
215    /// Set the Perforce command path.
216    pub fn bin(mut self, bin: impl Into<PathBuf>) -> Self {
217        self.set_bin(bin);
218        self
219    }
220
221    /// # Description
222    ///
223    /// g-opts
224    ///
225    #[cfg_attr(
226        feature = "lt2014_2",
227        doc = "See the [Global Options](GlobalOpts) section."
228    )]
229    #[cfg_attr(
230        all(feature = "lt2015_1", not(feature = "lt2014_2")),
231        doc = "See the [“Global Options”](GlobalOpts) section."
232    )]
233    #[cfg_attr(
234        all(feature = "lt2017_1", not(feature = "lt2015_1")),
235        doc = "See [“Global Options”](GlobalOpts)."
236    )]
237    #[cfg_attr(
238        all(feature = "lt2018_2", not(feature = "lt2017_1")),
239        doc = "See [Global Options](GlobalOpts)."
240    )]
241    #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
242    pub fn get_global_opts(&self) -> &GlobalOpts {
243        &self.global_opts
244    }
245
246    /// # Description
247    ///
248    /// g-opts
249    ///
250    #[cfg_attr(
251        feature = "lt2014_2",
252        doc = "See the [Global Options](GlobalOpts) section."
253    )]
254    #[cfg_attr(
255        all(feature = "lt2015_1", not(feature = "lt2014_2")),
256        doc = "See the [“Global Options”](GlobalOpts) section."
257    )]
258    #[cfg_attr(
259        all(feature = "lt2017_1", not(feature = "lt2015_1")),
260        doc = "See [“Global Options”](GlobalOpts)."
261    )]
262    #[cfg_attr(
263        all(feature = "lt2018_2", not(feature = "lt2017_1")),
264        doc = "See [Global Options](GlobalOpts)."
265    )]
266    #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
267    pub fn set_global_opts(&mut self, v: GlobalOpts) -> &mut Self {
268        self.global_opts = v;
269        self
270    }
271
272    /// # Description
273    ///
274    /// g-opts
275    ///
276    #[cfg_attr(
277        feature = "lt2014_2",
278        doc = "See the [Global Options](GlobalOpts) section."
279    )]
280    #[cfg_attr(
281        all(feature = "lt2015_1", not(feature = "lt2014_2")),
282        doc = "See the [“Global Options”](GlobalOpts) section."
283    )]
284    #[cfg_attr(
285        all(feature = "lt2017_1", not(feature = "lt2015_1")),
286        doc = "See [“Global Options”](GlobalOpts)."
287    )]
288    #[cfg_attr(
289        all(feature = "lt2018_2", not(feature = "lt2017_1")),
290        doc = "See [Global Options](GlobalOpts)."
291    )]
292    #[cfg_attr(not(feature = "lt2018_2"), doc = "See [Global options](GlobalOpts).")]
293    pub fn global_opts(mut self, v: GlobalOpts) -> Self {
294        self.global_opts = v;
295        self
296    }
297
298    /// # Description
299    ///
300    /// -a
301    ///
302    /// All lines, including deleted lines and lines no longer present at the
303    /// head revision, are included. Each line includes a starting and ending
304    /// revision.
305    pub fn get_all_lines(&self) -> bool {
306        self.all_lines
307    }
308
309    /// # Description
310    ///
311    /// -a
312    ///
313    /// All lines, including deleted lines and lines no longer present at the
314    /// head revision, are included. Each line includes a starting and ending
315    /// revision.
316    pub fn set_all_lines(&mut self, v: bool) -> &mut Self {
317        self.all_lines = v;
318        self
319    }
320
321    /// # Description
322    ///
323    /// -a
324    ///
325    /// All lines, including deleted lines and lines no longer present at the
326    /// head revision, are included. Each line includes a starting and ending
327    /// revision.
328    pub fn all_lines(mut self, v: bool) -> Self {
329        self.all_lines = v;
330        self
331    }
332
333    /// # Description
334    ///
335    /// -c
336    ///
337    /// Display the changelist number, rather than the revision number,
338    /// associated with each line. If you use the -a option and the -c option
339    /// together, each line includes a starting and ending changelist number.
340    pub fn get_changelist_number(&self) -> bool {
341        self.changelist_number
342    }
343
344    /// # Description
345    ///
346    /// -c
347    ///
348    /// Display the changelist number, rather than the revision number,
349    /// associated with each line. If you use the -a option and the -c option
350    /// together, each line includes a starting and ending changelist number.
351    pub fn set_changelist_number(&mut self, v: bool) -> &mut Self {
352        self.changelist_number = v;
353        self
354    }
355
356    /// # Description
357    ///
358    /// -c
359    ///
360    /// Display the changelist number, rather than the revision number,
361    /// associated with each line. If you use the -a option and the -c option
362    /// together, each line includes a starting and ending changelist number.
363    pub fn changelist_number(mut self, v: bool) -> Self {
364        self.changelist_number = v;
365        self
366    }
367
368    /// # Description
369    ///
370    #[cfg_attr(feature = "lt2014_2", doc = "-d flag")]
371    #[cfg_attr(not(feature = "lt2014_2"), doc = "-d options")]
372    ///
373    /// Runs the diff routine with one of a subset of the standard UNIX diff
374    #[cfg_attr(
375        feature = "lt2014_2",
376        doc = "flags. See the Usage Notes below for a listing of these",
377        doc = "flags."
378    )]
379    #[cfg_attr(
380        all(feature = "lt2015_1", not(feature = "lt2014_2")),
381        doc = "options. See Usage Notes below for a listing of these",
382        doc = "options."
383    )]
384    #[cfg_attr(
385        all(feature = "lt2024_1", not(feature = "lt2015_1")),
386        doc = "options. See Usage Notes for a listing of these options."
387    )]
388    #[cfg_attr(
389        not(feature = "lt2024_1"),
390        doc = "options. See Usage notes for a listing of these options."
391    )]
392    pub fn get_diff_options(&self) -> Option<&DiffOptions> {
393        self.diff_options.as_ref()
394    }
395
396    /// # Description
397    ///
398    #[cfg_attr(feature = "lt2014_2", doc = "-d flag")]
399    #[cfg_attr(not(feature = "lt2014_2"), doc = "-d options")]
400    ///
401    /// Runs the diff routine with one of a subset of the standard UNIX diff
402    #[cfg_attr(
403        feature = "lt2014_2",
404        doc = "flags. See the Usage Notes below for a listing of these",
405        doc = "flags."
406    )]
407    #[cfg_attr(
408        all(feature = "lt2015_1", not(feature = "lt2014_2")),
409        doc = "options. See Usage Notes below for a listing of these",
410        doc = "options."
411    )]
412    #[cfg_attr(
413        all(feature = "lt2024_1", not(feature = "lt2015_1")),
414        doc = "options. See Usage Notes for a listing of these options."
415    )]
416    #[cfg_attr(
417        not(feature = "lt2024_1"),
418        doc = "options. See Usage notes for a listing of these options."
419    )]
420    pub fn set_diff_options(&mut self, v: impl Into<DiffOptions>) -> &mut Self {
421        self.diff_options = Some(v.into());
422        self
423    }
424
425    /// # Description
426    ///
427    #[cfg_attr(feature = "lt2014_2", doc = "-d flag")]
428    #[cfg_attr(not(feature = "lt2014_2"), doc = "-d options")]
429    ///
430    /// Runs the diff routine with one of a subset of the standard UNIX diff
431    #[cfg_attr(
432        feature = "lt2014_2",
433        doc = "flags. See the Usage Notes below for a listing of these",
434        doc = "flags."
435    )]
436    #[cfg_attr(
437        all(feature = "lt2015_1", not(feature = "lt2014_2")),
438        doc = "options. See Usage Notes below for a listing of these",
439        doc = "options."
440    )]
441    #[cfg_attr(
442        all(feature = "lt2024_1", not(feature = "lt2015_1")),
443        doc = "options. See Usage Notes for a listing of these options."
444    )]
445    #[cfg_attr(
446        not(feature = "lt2024_1"),
447        doc = "options. See Usage notes for a listing of these options."
448    )]
449    pub fn diff_options(mut self, v: impl Into<DiffOptions>) -> Self {
450        self.diff_options = Some(v.into());
451        self
452    }
453
454    /// # Description
455    ///
456    /// -q
457    ///
458    #[cfg_attr(
459        feature = "lt2017_2",
460        doc = "Quiet mode; suppress the one-line header for each file."
461    )]
462    #[cfg_attr(
463        not(feature = "lt2017_2"),
464        doc = "Quiet mode, which suppresses the one-line header for each file."
465    )]
466    pub fn get_quiet_mode(&self) -> bool {
467        self.quiet_mode
468    }
469
470    /// # Description
471    ///
472    /// -q
473    ///
474    #[cfg_attr(
475        feature = "lt2017_2",
476        doc = "Quiet mode; suppress the one-line header for each file."
477    )]
478    #[cfg_attr(
479        not(feature = "lt2017_2"),
480        doc = "Quiet mode, which suppresses the one-line header for each file."
481    )]
482    pub fn set_quiet_mode(&mut self, v: bool) -> &mut Self {
483        self.quiet_mode = v;
484        self
485    }
486
487    /// # Description
488    ///
489    /// -q
490    ///
491    #[cfg_attr(
492        feature = "lt2017_2",
493        doc = "Quiet mode; suppress the one-line header for each file."
494    )]
495    #[cfg_attr(
496        not(feature = "lt2017_2"),
497        doc = "Quiet mode, which suppresses the one-line header for each file."
498    )]
499    pub fn quiet_mode(mut self, v: bool) -> Self {
500        self.quiet_mode = v;
501        self
502    }
503
504    /// # Description
505    ///
506    /// -t
507    ///
508    /// Force `p4 annotate` to display non-text (binary) files.
509    pub fn get_force_binary(&self) -> bool {
510        self.force_binary
511    }
512
513    /// # Description
514    ///
515    /// -t
516    ///
517    /// Force `p4 annotate` to display non-text (binary) files.
518    pub fn set_force_binary(&mut self, v: bool) -> &mut Self {
519        self.force_binary = v;
520        self
521    }
522
523    /// # Description
524    ///
525    /// -t
526    ///
527    /// Force `p4 annotate` to display non-text (binary) files.
528    pub fn force_binary(mut self, v: bool) -> Self {
529        self.force_binary = v;
530        self
531    }
532
533    /// # Description
534    ///
535    /// -u
536    ///
537    /// Display the name of the user who modified the change and the date when
538    /// the modification occurred.
539    #[cfg(not(feature = "lt2015_2"))]
540    pub fn get_user_date(&self) -> bool {
541        self.user_date
542    }
543
544    /// # Description
545    ///
546    /// -u
547    ///
548    /// Display the name of the user who modified the change and the date when
549    /// the modification occurred.
550    #[cfg(not(feature = "lt2015_2"))]
551    pub fn set_user_date(&mut self, v: bool) -> &mut Self {
552        self.user_date = v;
553        self
554    }
555
556    /// # Description
557    ///
558    /// -u
559    ///
560    /// Display the name of the user who modified the change and the date when
561    /// the modification occurred.
562    #[cfg(not(feature = "lt2015_2"))]
563    pub fn user_date(mut self, v: bool) -> Self {
564        self.user_date = v;
565        self
566    }
567
568    /// # Description
569    ///
570    /// -T | `--tab=N`
571    ///
572    /// Align output to a tab stop of 8. You can specify a different tab value
573    /// using the `--tab` option and specifying the desired value for N.
574    ///
575    /// Returns the tab stop currently set, or `None` if no tab option is set.
576    #[cfg(not(feature = "lt2016_1"))]
577    pub fn get_tab(&self) -> Option<u32> {
578        match self.tab {
579            Some(Tab::Default) => Some(8),
580            Some(Tab::Custom(value)) => Some(value),
581            None => None,
582        }
583    }
584
585    /// # Description
586    ///
587    /// `--tab=N`
588    ///
589    /// Align output to a tab stop of N. Use [`Self::set_default_tab`] to
590    /// align output to the default tab stop of 8 with `-T`.
591    #[cfg(not(feature = "lt2016_1"))]
592    pub fn set_tab(&mut self, v: u32) -> &mut Self {
593        self.tab = Some(Tab::Custom(v));
594        self
595    }
596
597    /// # Description
598    ///
599    /// `--tab=N`
600    ///
601    /// Align output to a tab stop of N. Use [`Self::default_tab`] to align
602    /// output to the default tab stop of 8 with `-T`.
603    #[cfg(not(feature = "lt2016_1"))]
604    pub fn tab(mut self, v: u32) -> Self {
605        self.tab = Some(Tab::Custom(v));
606        self
607    }
608
609    /// # Description
610    ///
611    /// -T
612    ///
613    /// Align output to a tab stop of 8. Use [`Self::set_tab`] to align output
614    /// to a different tab stop with `--tab=N`.
615    #[cfg(not(feature = "lt2016_1"))]
616    pub fn set_default_tab(&mut self) -> &mut Self {
617        self.tab = Some(Tab::Default);
618        self
619    }
620
621    /// # Description
622    ///
623    /// -T
624    ///
625    /// Align output to a tab stop of 8. Use [`Self::tab`] to align output to
626    /// a different tab stop with `--tab=N`.
627    #[cfg(not(feature = "lt2016_1"))]
628    pub fn default_tab(mut self) -> Self {
629        self.tab = Some(Tab::Default);
630        self
631    }
632}
633
634impl<F: ExclusiveOption> SubCommand for Annotate<F> {
635    fn name(&self) -> &str {
636        "annotate"
637    }
638
639    fn inject_local_args(&self, command: &mut Command) {
640        if self.all_lines {
641            command.arg("-a");
642        }
643
644        if self.changelist_number {
645            command.arg("-c");
646        }
647
648        if let Some(diff_options) = &self.diff_options {
649            diff_options.inject_arg(command);
650        }
651
652        self.follow.inject_args(command);
653
654        if self.quiet_mode {
655            command.arg("-q");
656        }
657
658        if self.force_binary {
659            command.arg("-t");
660        }
661
662        #[cfg(not(feature = "lt2015_2"))]
663        {
664            if self.user_date {
665                command.arg("-u");
666            }
667        }
668
669        #[cfg(not(feature = "lt2016_1"))]
670        {
671            if let Some(tab) = self.tab {
672                match tab {
673                    Tab::Default => {
674                        command.arg("-T");
675                    }
676                    Tab::Custom(value) => {
677                        command.arg(format!("--tab={value}"));
678                    }
679                }
680            }
681        }
682    }
683
684    fn global_opts(&self) -> Option<&GlobalOpts> {
685        Some(&self.global_opts)
686    }
687}
688
689#[cfg(test)]
690mod tests {
691    use super::*;
692    use crate::cmd::DiffOptionsBuilder;
693    use crate::cmd::args_of;
694
695    #[test]
696    fn bin_accessors_update_executable_path() {
697        let mut command = Annotate::new("p4", GlobalOpts::new());
698        assert_eq!(command.get_bin(), Path::new("p4"));
699
700        command.set_bin("/usr/local/bin/p4");
701        assert_eq!(command.get_bin(), Path::new("/usr/local/bin/p4"));
702
703        let command = command.bin("p4.exe");
704        assert_eq!(command.get_bin(), Path::new("p4.exe"));
705    }
706
707    /// Dry-run checks of the assembled `p4 annotate` command line; no process
708    /// is spawned.
709    #[test]
710    fn without_options() {
711        let annotate = Annotate::new("p4", GlobalOpts::new());
712
713        assert_eq!(args_of(&annotate.setup_command("p4")), ["annotate"]);
714    }
715
716    #[test]
717    fn all_options() {
718        let mut annotate = Annotate::new("p4", GlobalOpts::new());
719        annotate
720            .set_all_lines(true)
721            .set_changelist_number(true)
722            .set_diff_options(DiffOptionsBuilder::raw("Su2"))
723            .set_quiet_mode(true)
724            .set_force_binary(true);
725        #[cfg(not(feature = "lt2015_2"))]
726        annotate.set_user_date(true);
727
728        // `-i` and `-I` are mutually exclusive; select the branch-following
729        // variant here.
730        #[cfg_attr(feature = "lt2016_1", allow(unused_mut))]
731        let mut annotate = annotate.follow_branches();
732
733        // `mut` is only needed when the tab block below is compiled in.
734        #[cfg_attr(feature = "lt2016_1", allow(unused_mut))]
735        let mut expected = vec!["annotate", "-a", "-c", "-dSu2", "-i", "-q", "-t"];
736        #[cfg(not(feature = "lt2015_2"))]
737        expected.push("-u");
738        #[cfg(not(feature = "lt2016_1"))]
739        {
740            annotate.set_default_tab();
741            expected.push("-T");
742        }
743
744        assert_eq!(args_of(&annotate.setup_command("p4")), expected);
745    }
746
747    #[cfg(not(feature = "lt2016_1"))]
748    #[test]
749    fn default_tab_uses_short_flag() {
750        let mut annotate = Annotate::new("p4", GlobalOpts::new());
751        annotate.set_default_tab();
752
753        assert_eq!(annotate.get_tab(), Some(8));
754        assert_eq!(args_of(&annotate.setup_command("p4")), ["annotate", "-T"]);
755    }
756
757    #[cfg(not(feature = "lt2016_1"))]
758    #[test]
759    fn custom_tab_uses_long_flag() {
760        let mut annotate = Annotate::new("p4", GlobalOpts::new());
761        annotate.set_tab(4);
762
763        assert_eq!(annotate.get_tab(), Some(4));
764        assert_eq!(
765            args_of(&annotate.setup_command("p4")),
766            ["annotate", "--tab=4"]
767        );
768    }
769}