Skip to content

Commit

Permalink
Merge pull request #1214 from boogisha/safer-gitignore-support
Browse files Browse the repository at this point in the history
Safer gitignore support
  • Loading branch information
pmiossec committed Mar 13, 2020
2 parents 457efaf + 2f1ccf3 commit 73a7d1f
Show file tree
Hide file tree
Showing 18 changed files with 202 additions and 63 deletions.
1 change: 0 additions & 1 deletion src/GitTfs/Commands/Clone.cs
Expand Up @@ -129,7 +129,6 @@ public int Run(string tfsUrl, string tfsRepositoryPath, string gitRepositoryPath

retVal = _initBranch.Run();
}
_globals.Repository.SetConfig(GitTfsConstants.DisableGitignoreSupport, true);
}
catch (GitTfsException)
{
Expand Down
12 changes: 12 additions & 0 deletions src/GitTfs/Commands/Fetch.cs
Expand Up @@ -131,6 +131,8 @@ public int Run(params string[] args)

private int Run(bool stopOnFailMergeCommit, params string[] args)
{
UseTheGitIgnoreFile(_remoteOptions.GitIgnorePath);

if (!FetchAll && BranchStrategy == BranchStrategy.None)
_globals.Repository.SetConfig(GitTfsConstants.IgnoreBranches, true);

Expand All @@ -146,6 +148,16 @@ private int Run(bool stopOnFailMergeCommit, params string[] args)
return 0;
}

private void UseTheGitIgnoreFile(string pathToGitIgnoreFile)
{
if (string.IsNullOrWhiteSpace(pathToGitIgnoreFile))
{
Trace.WriteLine("No .gitignore file specified to use...");
return;
}
_globals.Repository.UseGitIgnore(pathToGitIgnoreFile);
}

private void FetchRemote(bool stopOnFailMergeCommit, IGitTfsRemote remote)
{
Trace.TraceInformation("Fetching from TFS remote '{0}'...", remote.Id);
Expand Down
15 changes: 13 additions & 2 deletions src/GitTfs/Commands/Init.cs
Expand Up @@ -51,7 +51,8 @@ public int Run(string tfsUrl, string tfsRepositoryPath)
DoGitInitDb();
VerifyGitUserConfig();
SaveAuthorFileInRepository();
CommitTheGitIgnoreFile(_initOptions.GitIgnorePath);
CommitTheGitIgnoreFile(_remoteOptions.GitIgnorePath);
UseTheGitIgnoreFile(_remoteOptions.GitIgnorePath);
GitTfsInit(tfsUrl, tfsRepositoryPath);
return 0;
}
Expand Down Expand Up @@ -80,12 +81,22 @@ private void CommitTheGitIgnoreFile(string pathToGitIgnoreFile)
{
if (string.IsNullOrWhiteSpace(pathToGitIgnoreFile))
{
Trace.WriteLine("No .gitignore file specified...");
Trace.WriteLine("No .gitignore file specified to commit...");
return;
}
_globals.Repository.CommitGitIgnore(pathToGitIgnoreFile);
}

private void UseTheGitIgnoreFile(string pathToGitIgnoreFile)
{
if (string.IsNullOrWhiteSpace(pathToGitIgnoreFile))
{
Trace.WriteLine("No .gitignore file specified to use...");
return;
}
_globals.Repository.UseGitIgnore(pathToGitIgnoreFile);
}

public int Run(string tfsUrl, string tfsRepositoryPath, string gitRepositoryPath)
{
tfsRepositoryPath.AssertValidTfsPathOrRoot();
Expand Down
2 changes: 0 additions & 2 deletions src/GitTfs/Commands/InitOptions.cs
Expand Up @@ -27,7 +27,6 @@ public OptionSet OptionSet
v => GitInitIgnoreCase = ValidateIgnoreCaseValue(v) },
{"bare", "Clone the TFS repository in a bare git repository", v => IsBare = v != null},
{"workspace=", "Set tfs workspace to a specific folder (a shorter path is better!)", v => WorkspacePath = v},
{"gitignore=", "Path toward the .gitignore file which be committed and used to ignore files", v => GitIgnorePath = v},
};
}
}
Expand Down Expand Up @@ -55,6 +54,5 @@ private string ValidateIgnoreCaseValue(string v)
public object GitInitShared { get; set; }
public string GitInitAutoCrlf { get; set; }
public string GitInitIgnoreCase { get; set; }
public string GitIgnorePath { get; set; }
}
}
7 changes: 7 additions & 0 deletions src/GitTfs/Commands/RemoteOptions.cs
Expand Up @@ -16,6 +16,10 @@ public OptionSet OptionSet
v => IgnoreRegex = v },
{ "except-regex=", "A regex of exceptions to '--ignore-regex'",
v => ExceptRegex = v},
{ "gitignore:", "Use .gitignore to ignore files on download from TFS. Alternatively, provide path toward the .gitignore file which will be used to ignore files",
v => { GitIgnorePath = v; UseGitIgnore = true; } },
{ "no-gitignore", "Do not use .gitignore to ignore files on download from TFS",
v => NoGitIgnore = v != null },
{ "u|username=", "TFS username",
v => Username = v },
{ "p|password=", "TFS password",
Expand All @@ -28,6 +32,9 @@ public OptionSet OptionSet

public string IgnoreRegex { get; set; }
public string ExceptRegex { get; set; }
public string GitIgnorePath { get; set; }
public bool UseGitIgnore { get; set; }
public bool NoGitIgnore { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public bool NoParallel { get; set; }
Expand Down
8 changes: 5 additions & 3 deletions src/GitTfs/Commands/Verify.cs
Expand Up @@ -15,28 +15,30 @@ namespace GitTfs.Commands
[Description("verify [options] [commitish]\n ex: git-tfs verify\n git-tfs verify 889ad74c162\n git-tfs verify tfs/mybranch\n git-tfs verify --all")]
public class Verify : GitTfsCommand
{
private readonly RemoteOptions _remoteOptions;
private readonly Help _helper;
private readonly Globals _globals;
private readonly TreeVerifier _verifier;

public Verify(Globals globals, TreeVerifier verifier, Help helper)
public Verify(Globals globals, TreeVerifier verifier, Help helper, RemoteOptions remoteOptions)
{
_globals = globals;
_verifier = verifier;
_helper = helper;
_remoteOptions = remoteOptions;
}

public OptionSet OptionSet
{
get
{
return new OptionSet()
{
{
{ "ignore-path-case-mismatch", "Ignore the case mismatch in the path when comparing the files.",
v => IgnorePathCaseMismatch = v != null },
{ "all", "Verify all the tfs remotes",
v => VerifyAllRemotes = v != null },
};
}.Merge(_remoteOptions.OptionSet);
}
}

Expand Down
16 changes: 16 additions & 0 deletions src/GitTfs/Core/DerivedGitTfsRemote.cs
Expand Up @@ -138,6 +138,12 @@ public string IgnoreExceptRegexExpression
set { throw DerivedRemoteException; }
}

public string GitIgnorePath
{
get { throw DerivedRemoteException; }
set { throw DerivedRemoteException; }
}

public IGitRepository Repository
{
get { throw DerivedRemoteException; }
Expand Down Expand Up @@ -205,6 +211,16 @@ public bool ShouldSkip(string path)
throw DerivedRemoteException;
}

public bool IsIgnored(string path)
{
throw DerivedRemoteException;
}

public bool IsInDotGit(string path)
{
throw DerivedRemoteException;
}

public IGitTfsRemote InitBranch(RemoteOptions remoteOptions, string tfsRepositoryPath, int shaRootChangesetId, bool fetchParentBranch, string gitBranchNameExpected = null, IRenameResult renameResult = null)
{
throw new NotImplementedException();
Expand Down
17 changes: 7 additions & 10 deletions src/GitTfs/Core/GitRepository.cs
Expand Up @@ -19,7 +19,6 @@ public class GitRepository : GitHelpers, IGitRepository
private IDictionary<string, IGitTfsRemote> _cachedRemotes;
private readonly Repository _repository;
private readonly RemoteConfigConverter _remoteConfigReader;
private readonly bool _disableGitignoreSupport;

public GitRepository(string gitDir, IContainer container, Globals globals, RemoteConfigConverter remoteConfigReader)
: base(container)
Expand All @@ -29,12 +28,6 @@ public GitRepository(string gitDir, IContainer container, Globals globals, Remot
GitDir = gitDir;
_repository = new Repository(GitDir);
_remoteConfigReader = remoteConfigReader;

var value = GetConfig<string>(GitTfsConstants.DisableGitignoreSupport, null);
bool disableGitignoreSupport;
if (value != null && bool.TryParse(value, out disableGitignoreSupport))
_disableGitignoreSupport = disableGitignoreSupport;

}

~GitRepository()
Expand Down Expand Up @@ -778,7 +771,7 @@ public IEnumerable<GitCommit> FindParentCommits(string @from, string to)

public bool IsPathIgnored(string relativePath)
{
return !_disableGitignoreSupport && _repository.Ignore.IsPathIgnored(relativePath);
return _repository.Ignore.IsPathIgnored(relativePath);
}

public string CommitGitIgnore(string pathToGitIgnoreFile)
Expand All @@ -796,11 +789,15 @@ public string CommitGitIgnore(string pathToGitIgnoreFile)

_repository.Refs.Add(ShortToTfsRemoteName("default"), new ObjectId(sha));
_repository.Refs.Add(ShortToLocalName("master"), new ObjectId(sha));

return sha;
}

public void UseGitIgnore(string pathToGitIgnoreFile)
{
//Should add ourself the rules to the temporary rules because committing directly to the git database
//prevent libgit2sharp to detect the new .gitignore file
_repository.Ignore.AddTemporaryRules(File.ReadLines(pathToGitIgnoreFile));

return sha;
}

public IDictionary<int, string> GetCommitChangeSetPairs()
Expand Down
32 changes: 24 additions & 8 deletions src/GitTfs/Core/GitTfsRemote.cs
Expand Up @@ -18,6 +18,7 @@ public class GitTfsRemote : IGitTfsRemote
private readonly Globals _globals;
private readonly RemoteOptions _remoteOptions;
private readonly ConfigProperties _properties;
private readonly bool _disableGitignoreSupport;
private int? firstChangesetId;
private int? maxChangesetId;
private string maxCommitHash;
Expand All @@ -42,12 +43,26 @@ public class GitTfsRemote : IGitTfsRemote
Aliases = (info.Aliases ?? Enumerable.Empty<string>()).ToArray();
IgnoreRegexExpression = info.IgnoreRegex;
IgnoreExceptRegexExpression = info.IgnoreExceptRegex;
GitIgnorePath = _remoteOptions.GitIgnorePath ?? info.GitIgnorePath;
UseGitIgnore = !_remoteOptions.NoGitIgnore && (_remoteOptions.UseGitIgnore || IsGitIgnoreSupportEnabled());

Autotag = info.Autotag;

IsSubtree = CheckSubtree();
}

private bool IsGitIgnoreSupportEnabled()
{
var isGitIgnoreSupportDisabled = true;

var value = Repository.GetConfig<string>(GitTfsConstants.DisableGitignoreSupport, null);
bool disableGitignoreSupport;
if (value != null && bool.TryParse(value, out disableGitignoreSupport))
isGitIgnoreSupportDisabled = disableGitignoreSupport;

return !isGitIgnoreSupportDisabled;
}

private bool CheckSubtree()
{
var m = GitTfsConstants.RemoteSubtreeRegex.Match(Id);
Expand Down Expand Up @@ -139,6 +154,8 @@ public string[] TfsSubtreePaths

public string IgnoreRegexExpression { get; set; }
public string IgnoreExceptRegexExpression { get; set; }
public string GitIgnorePath { get; set; }
public bool UseGitIgnore { get; set; }
public IGitRepository Repository { get; set; }
public ITfsHelper Tfs { get; set; }

Expand Down Expand Up @@ -248,15 +265,14 @@ public bool ShouldSkip(string path)
return IsInDotGit(path) || IsIgnored(path);
}

private bool IsIgnored(string path)
public bool IsIgnored(string path)
{
var isIgnored = Ignorance.IsIncluded(path) || Repository.IsPathIgnored(path);
if (isIgnored)
{
Trace.TraceWarning("The file '{0}' is ignored due to `.gitignore` or ignore regex defined.", path);
}
return Ignorance.IsIncluded(path) || IsPathIgnored(path);
}

return isIgnored;
private bool IsPathIgnored(string path)
{
return UseGitIgnore && Repository.IsPathIgnored(path);
}

private Bouncer _ignorance;
Expand All @@ -276,7 +292,7 @@ private Bouncer Ignorance
}
}

private bool IsInDotGit(string path)
public bool IsInDotGit(string path)
{
return isInDotGit.IsMatch(path);
}
Expand Down
1 change: 1 addition & 0 deletions src/GitTfs/Core/IGitRepository.cs
Expand Up @@ -62,6 +62,7 @@ public interface IGitRepository : IGitHelpers
IEnumerable<GitCommit> FindParentCommits(string fromCommit, string toCommit);
bool IsPathIgnored(string relativePath);
string CommitGitIgnore(string pathToGitIgnoreFile);
void UseGitIgnore(string pathToGitIgnoreFile);
IDictionary<int, string> GetCommitChangeSetPairs();
}
}
3 changes: 3 additions & 0 deletions src/GitTfs/Core/IGitTfsRemote.cs
Expand Up @@ -40,6 +40,7 @@ public interface IGitTfsRemote
string[] TfsSubtreePaths { get; }
string IgnoreRegexExpression { get; set; }
string IgnoreExceptRegexExpression { get; set; }
string GitIgnorePath { get; set; }
bool Autotag { get; set; }
string TfsUsername { get; set; }
string TfsPassword { get; set; }
Expand All @@ -57,6 +58,8 @@ public interface IGitTfsRemote
int? GetFirstChangeset();
void SetFirstChangeset(int? changesetId);
bool ShouldSkip(string path);
bool IsIgnored(string path);
bool IsInDotGit(string path);
IGitTfsRemote InitBranch(RemoteOptions remoteOptions, string tfsRepositoryPath, int rootChangesetId = -1, bool fetchParentBranch = false, string gitBranchNameExpected = null, IRenameResult renameResult = null);
string GetPathInGitRepo(string tfsPath);
IFetchResult Fetch(bool stopOnFailMergeCommit = false, int lastChangesetIdToFetch = -1, IRenameResult renameResult = null);
Expand Down
3 changes: 3 additions & 0 deletions src/GitTfs/Core/RemoteConfigConverter.cs
Expand Up @@ -31,6 +31,8 @@ public IEnumerable<RemoteInfo> Load(IEnumerable<ConfigurationEntry<string>> conf
remote.IgnoreRegex = entry.Value;
else if (key == "ignore-except")
remote.IgnoreExceptRegex = entry.Value;
else if (key == "gitignore-path")
remote.GitIgnorePath = entry.Value;
else if (key == "legacy-urls")
remote.Aliases = entry.Value.Split(',');
else if (key == "autotag")
Expand All @@ -53,6 +55,7 @@ public IEnumerable<RemoteInfo> Load(IEnumerable<ConfigurationEntry<string>> conf
yield return c(prefix + "password", remote.Password);
yield return c(prefix + "ignore-paths", remote.IgnoreRegex);
yield return c(prefix + "ignore-except", remote.IgnoreExceptRegex);
yield return c(prefix + "gitignore-path", remote.GitIgnorePath);
yield return c(prefix + "legacy-urls", remote.Aliases == null ? null : string.Join(",", remote.Aliases));
yield return c(prefix + "autotag", remote.Autotag ? "true" : null);
yield return c(prefix + "noparallel", remote.NoParallel ? "true" : null);
Expand Down
5 changes: 3 additions & 2 deletions src/GitTfs/Core/RemoteInfo.cs
Expand Up @@ -12,14 +12,15 @@ public class RemoteInfo
public string Password { get; set; }
public string IgnoreRegex { get; set; }
public string IgnoreExceptRegex { get; set; }
public string GitIgnorePath { get; set; }
public IEnumerable<string> Aliases { get; set; }
public bool Autotag { get; set; }
public bool NoParallel { get; set; }

public RemoteOptions RemoteOptions
{
get { return new RemoteOptions { IgnoreRegex = IgnoreRegex, ExceptRegex = IgnoreExceptRegex, Username = Username, Password = Password, NoParallel = NoParallel }; }
set { IgnoreRegex = value.IgnoreRegex; IgnoreExceptRegex = value.ExceptRegex; Username = value.Username; Password = value.Password; NoParallel = value.NoParallel; }
get { return new RemoteOptions { IgnoreRegex = IgnoreRegex, ExceptRegex = IgnoreExceptRegex, GitIgnorePath = GitIgnorePath, Username = Username, Password = Password, NoParallel = NoParallel }; }
set { IgnoreRegex = value.IgnoreRegex; IgnoreExceptRegex = value.ExceptRegex; GitIgnorePath = value.GitIgnorePath; Username = value.Username; Password = value.Password; NoParallel = value.NoParallel; }
}
}
}
11 changes: 9 additions & 2 deletions src/GitTfs/Core/TfsChangeset.cs
Expand Up @@ -36,8 +36,7 @@ public LogEntry Apply(string lastCommit, IGitTreeModifier treeBuilder, ITfsWorks
IsRenameChangeset = true;
}
_changeset.Get(workspace, sieve.GetChangesToFetch(), ignorableErrorHandler);
var forceGetChanges = lastCommit == null;
foreach (var change in sieve.GetChangesToApply(forceGetChanges))
foreach (var change in sieve.GetChangesToApply())
{
ignorableErrorHandler.Catch(() =>
{
Expand All @@ -57,6 +56,9 @@ private void Apply(ApplicableChange change, IGitTreeModifier treeBuilder, ITfsWo
case ChangeType.Delete:
Delete(change.GitPath, treeBuilder, initialTree);
break;
case ChangeType.Ignore:
Ignore(change.GitPath);
break;
default:
throw new NotImplementedException("Unsupported change type: " + change.Type);
}
Expand All @@ -75,6 +77,11 @@ private void Update(ApplicableChange change, IGitTreeModifier treeBuilder, ITfsW
}
}

private void Ignore(string pathInGitRepo)
{
Trace.TraceInformation(string.Format("C{0} ! No changes applied to '{1}', file ignored", _changeset.ChangesetId, pathInGitRepo));
}

public IEnumerable<TfsTreeEntry> GetTree()
{
return GetFullTree().Where(item => item.Item.ItemType == TfsItemType.File && !Summary.Remote.ShouldSkip(item.FullName));
Expand Down

0 comments on commit 73a7d1f

Please sign in to comment.