2 * Copyright (C) 2009, Henon <meinrad.recheis@gmail.com>
6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the following
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
18 * - Neither the name of the Git Development Community nor the
19 * names of its contributors may be used to endorse or promote
20 * products derived from this software without specific prior
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
24 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
25 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
28 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 using System
.Collections
.Generic
;
43 using CoreCommit
= GitSharp
.Core
.Commit
;
49 /// Represents the index of a git repository which keeps track of changes that are about to be committed.
53 private Repository _repo
;
55 public Index(Repository repo
)
58 //GitIndex.FilenameEncoding = repo.PreferredEncoding;
59 //if (_repo.PreferredEncoding != Encoding.UTF8 && _repo.PreferredEncoding != Encoding.Default)
60 // GitIndex.FilenameEncoding = Encoding.Default;
65 PathEncoding
= Encoding
.UTF8
;
66 ContentEncoding
= Encoding
.UTF8
;
69 internal GitSharp
.Core
.GitIndex GitIndex
73 return _repo
._internal_repo
.Index
;
78 /// Add all untracked files to the index and stage all changes (like git add .)
82 Add(_repo
.WorkingDirectory
);
86 /// Adds untracked files or directories to the index and writes the index to the disk (like "git add").
87 /// For tracked files that were modified, it stages the modification. Is a no-op for tracked files that were
90 /// Note: Add as many files as possible by one call of this method for best performance.
92 /// <param name="paths">Paths to add to the index</param>
93 public void Add(params string[] paths
)
95 GitIndex
.RereadIfNecessary();
96 foreach (var absolute_or_relative_path
in paths
)
98 string path
= absolute_or_relative_path
;
99 if (!Path
.IsPathRooted(absolute_or_relative_path
))
100 path
= Path
.Combine(_repo
.WorkingDirectory
, path
);
101 if (new FileInfo(path
).Exists
)
102 AddFile(new FileInfo(path
));
103 else if (new DirectoryInfo(path
).Exists
)
104 AddDirectory(new DirectoryInfo(path
));
106 throw new ArgumentException("File or directory at <" + path
+ "> doesn't seem to exist.", "path");
112 /// Add a file to index (without relying on the working directory) by specifying the file's content as string.
113 /// The added file doesn't need to exist in the working directory.
115 /// <param name="path">Relative path in the working directory. Note: the path is encoded using PathEncoding</param>
116 /// <param name="content">The content as string. Note: the content is encoded using ContentEncoding</param>
117 public void AddContent(string path
, string content
)
119 AddContent(PathEncoding
.GetBytes(path
), ContentEncoding
.GetBytes(content
));
123 /// Add content to the index directly without the need for a file in the working directory.
125 /// <param name="encoded_relative_filepath">encoded file path (relative to working directory)</param>
126 /// <param name="encoded_content">encoded content</param>
127 public void AddContent(byte[] encoded_relative_filepath
, byte[] encoded_content
)
129 GitIndex
.RereadIfNecessary();
130 GitIndex
.add(encoded_relative_filepath
, encoded_content
);
134 private void AddFile(FileInfo path
)
136 GitIndex
.add(_repo
._internal_repo
.WorkingDirectory
, path
);
139 private GitSharp
.Core
.IgnoreHandler _ignoreHandler
;
140 public GitSharp
.Core
.IgnoreHandler IgnoreHandler
144 if (_ignoreHandler
== null)
145 _ignoreHandler
= new Core
.IgnoreHandler(_repo
);
146 return _ignoreHandler
;
150 private void AddDirectory(DirectoryInfo dir
)
152 foreach (var file
in dir
.GetFiles())
153 if (!IgnoreHandler
.IsIgnored(file
.FullName
))
155 foreach (var subdir
in dir
.GetDirectories())
156 if (subdir
.Name
!= GitSharp
.Core
.Constants
.DOT_GIT
&& !IgnoreHandler
.IsIgnored(subdir
.FullName
))
157 AddDirectory(subdir
);
161 /// Removes files or directories from the index which are no longer to be tracked.
162 /// Does not delete files from the working directory. Use <seealso cref="Delete"/> to remove and delete files.
164 /// <param name="paths"></param>
165 public void Remove(params string[] paths
)
167 GitIndex
.RereadIfNecessary();
168 foreach (var absolute_or_relative_path
in paths
)
170 string path
= absolute_or_relative_path
;
171 string relative_path
= absolute_or_relative_path
;
172 if (!Path
.IsPathRooted(absolute_or_relative_path
))
173 path
= Path
.Combine(_repo
.WorkingDirectory
, absolute_or_relative_path
);
175 relative_path
= Core
.Util
.PathUtil
.RelativePath(_repo
.WorkingDirectory
, absolute_or_relative_path
);
176 if (new FileInfo(path
).Exists
)
177 RemoveFile(new FileInfo(path
), false);
178 else if (new DirectoryInfo(path
).Exists
)
179 RemoveDirectory(new DirectoryInfo(path
), false);
181 GitIndex
.Remove(relative_path
);
187 /// Removes files or directories from the index and delete them from the working directory.
190 /// <param name="paths"></param>
191 public void Delete(params string[] paths
)
193 GitIndex
.RereadIfNecessary();
194 foreach (var absolute_or_relative_path
in paths
)
196 string path
= absolute_or_relative_path
;
197 if (!Path
.IsPathRooted(absolute_or_relative_path
))
198 path
= Path
.Combine(_repo
.WorkingDirectory
, path
);
199 if (new FileInfo(path
).Exists
)
200 RemoveFile(new FileInfo(path
), true);
201 else if (new DirectoryInfo(path
).Exists
)
202 RemoveDirectory(new DirectoryInfo(path
), true);
204 throw new ArgumentException("File or directory at <" + path
+ "> doesn't seem to exist.", "path");
209 private void RemoveFile(FileInfo path
, bool delete_file
)
211 GitIndex
.remove(_repo
._internal_repo
.WorkingDirectory
, path
); // Todo: change GitIndex.Remove to remove(DirectoryInfo , FileInfo) ??
216 private void RemoveDirectory(DirectoryInfo dir
, bool delete_dir
)
218 foreach (var file
in dir
.GetFiles())
219 RemoveFile(file
, delete_dir
);
220 foreach (var subdir
in dir
.GetDirectories())
221 RemoveDirectory(subdir
, delete_dir
);
227 /// Stages the given files. Untracked files are added. This is an alias for Add.
229 /// <param name="paths"></param>
230 public void Stage(params string[] paths
)
236 /// This is an alias for AddContent.
238 public void StageContent(string path
, string content
)
240 AddContent(path
, content
);
244 /// Unstage overwrites staged files in the index with their current version in HEAD. In case of newly added files they are removed from the index.
246 /// <param name="paths">Relative paths to files you want to unstage.</param>
247 public void Unstage(params string[] paths
)
249 GitIndex
.RereadIfNecessary();
250 foreach (var absolute_or_relative_path
in paths
)
252 string path
= absolute_or_relative_path
;
253 if (Path
.IsPathRooted(absolute_or_relative_path
))
254 path
= Core
.Util
.PathUtil
.RelativePath(_repo
.WorkingDirectory
, absolute_or_relative_path
);
255 if (this[path
] == null)
257 var blob
= _repo
.Get
<Leaf
>(path
); // <--- we wouldn't want to stage something that is not representing a file
259 GitIndex
.Remove(path
);
261 GitIndex
.add(Core
.Repository
.GitInternalSlash(PathEncoding
.GetBytes(path
)), blob
.RawData
);
267 /// Check out the index into the working directory. Any modified files will be overwritten.
269 /// <seealso cref="Branch.Checkout"/> to checkout from a commit.
271 public void Checkout()
273 Checkout(_repo
.WorkingDirectory
);
276 // [henon] we do not publicly expose checking out into a custom directory, as this is an unrealistic use case and conflicts with checking out paths.
277 // it is possible anyway by iterating over the Entries and writing the contents of each entry into a custom directory!
278 private void Checkout(string directory
)
280 GitIndex
.RereadIfNecessary();
281 GitIndex
.checkout(new FileInfo(directory
));
285 /// Check out given paths from the index overwriting files in the working directory. Modified files might be overwritten.
287 /// <param name="paths"></param>
288 public void Checkout(params string[] paths
)
290 GitIndex
.RereadIfNecessary();
291 foreach (var absolute_or_relative_path
in paths
)
293 string path
= absolute_or_relative_path
;
294 if (Path
.IsPathRooted(absolute_or_relative_path
))
295 path
= Core
.Util
.PathUtil
.RelativePath(_repo
.WorkingDirectory
, absolute_or_relative_path
);
296 var e
= GitIndex
.GetEntry(path
);
299 GitIndex
.checkoutEntry(new FileInfo(_repo
.WorkingDirectory
), e
);
304 /// Writes the index to the disk.
312 /// Reads the index from the disk
319 //public RepositoryStatus CompareAgainstWorkingDirectory(bool honor_ignore_rules)
321 public RepositoryStatus Status
330 /// Returns true if the index has been changed, which means there are changes to be committed. This
331 /// is not to be confused with the status of the working directory. If changes in the working directory have not been
332 /// staged then IsChanged is false.
334 public bool IsChanged
338 return GitIndex
.IsChanged
;
342 public Commit
CommitChanges(string message
, Author author
)
344 if (string.IsNullOrEmpty(message
))
345 throw new ArgumentException("Commit message must not be null or empty!", "message");
346 if (string.IsNullOrEmpty(author
.Name
))
347 throw new ArgumentException("Author name must not be null or empty!", "author");
348 GitIndex
.RereadIfNecessary();
349 var tree_id
= GitIndex
.writeTree();
350 // check if tree is different from current commit's tree
351 var parent
= _repo
.CurrentBranch
.CurrentCommit
;
352 if ((parent
== null && GitIndex
.Members
.Count
== 0) || (parent
!= null && parent
.Tree
._id
== tree_id
))
353 throw new InvalidOperationException("There are no changes to commit");
354 var commit
= Commit
.Create(message
, parent
, new Tree(_repo
, tree_id
), author
);
355 Ref
.Update("HEAD", commit
);
359 public override string ToString()
361 return "Index[" + Path
.Combine(_repo
.Directory
, "index") + "]";
365 /// The encoding to be used to convert file paths from string to byte arrays.
367 public static Encoding PathEncoding
374 /// The encoding to be used to convert file contents from string to byte arrays.
376 public static Encoding ContentEncoding
383 public string GetContent(string path
)
385 var blob
= this[path
];
388 return ContentEncoding
.GetString(blob
.RawData
);
391 public Blob
this[string path
]
395 var e
= GitIndex
.GetEntry(path
);
398 return new Blob(_repo
, e
.ObjectId
);
406 public IEnumerable
<string> Entries
410 GitIndex
.RereadIfNecessary();
411 return GitIndex
.Members
.Select(e
=> e
.Name
).ToArray();
416 /// The number of files tracked by the repository
420 get { return GitIndex.Members.Count; }