Update README.txt
[GitSharp.git] / GitSharp / Index.cs
blob3ed897afa4249da5e4b4091c22bbfbd828b58715
1 /*
2 * Copyright (C) 2009, Henon <meinrad.recheis@gmail.com>
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the following
8 * conditions are met:
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
21 * written permission.
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.
38 using System;
39 using System.Collections.Generic;
40 using System.Linq;
41 using System.Text;
42 using System.IO;
43 using CoreCommit = GitSharp.Core.Commit;
45 namespace GitSharp
48 /// <summary>
49 /// Represents the index of a git repository which keeps track of changes that are about to be committed.
50 /// </summary>
51 public class Index
53 private Repository _repo;
55 public Index(Repository repo)
57 _repo = repo;
58 //GitIndex.FilenameEncoding = repo.PreferredEncoding;
59 //if (_repo.PreferredEncoding != Encoding.UTF8 && _repo.PreferredEncoding != Encoding.Default)
60 // GitIndex.FilenameEncoding = Encoding.Default;
63 static Index()
65 PathEncoding = Encoding.UTF8;
66 ContentEncoding = Encoding.UTF8;
69 internal GitSharp.Core.GitIndex GitIndex
71 get
73 return _repo._internal_repo.Index;
77 /// <summary>
78 /// Add all untracked files to the index and stage all changes (like git add .)
79 /// </summary>
80 public void AddAll()
82 Add(_repo.WorkingDirectory);
85 /// <summary>
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
88 /// not modified.
89 ///
90 /// Note: Add as many files as possible by one call of this method for best performance.
91 /// </summary>
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));
105 else
106 throw new ArgumentException("File or directory at <" + path + "> doesn't seem to exist.", "path");
108 GitIndex.write();
111 /// <summary>
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.
114 /// </summary>
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));
122 /// <summary>
123 /// Add content to the index directly without the need for a file in the working directory.
124 /// </summary>
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);
131 GitIndex.write();
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))
154 AddFile(file);
155 foreach (var subdir in dir.GetDirectories())
156 if (subdir.Name != GitSharp.Core.Constants.DOT_GIT && !IgnoreHandler.IsIgnored(subdir.FullName))
157 AddDirectory(subdir);
160 /// <summary>
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.
163 /// </summary>
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);
174 else
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);
180 else
181 GitIndex.Remove(relative_path);
183 GitIndex.write();
186 /// <summary>
187 /// Removes files or directories from the index and delete them from the working directory.
188 ///
189 /// </summary>
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);
203 else
204 throw new ArgumentException("File or directory at <" + path + "> doesn't seem to exist.", "path");
206 GitIndex.write();
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) ??
212 if (delete_file)
213 path.Delete();
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);
222 if (delete_dir)
223 dir.Delete(true);
226 /// <summary>
227 /// Stages the given files. Untracked files are added. This is an alias for Add.
228 /// </summary>
229 /// <param name="paths"></param>
230 public void Stage(params string[] paths)
232 Add(paths);
235 /// <summary>
236 /// This is an alias for AddContent.
237 /// </summary>
238 public void StageContent(string path, string content)
240 AddContent(path, content);
243 /// <summary>
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.
245 /// </summary>
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)
256 return;
257 var blob = _repo.Get<Leaf>(path); // <--- we wouldn't want to stage something that is not representing a file
258 if (blob == null)
259 GitIndex.Remove(path);
260 else
261 GitIndex.add(Core.Repository.GitInternalSlash(PathEncoding.GetBytes(path)), blob.RawData);
263 GitIndex.write();
266 /// <summary>
267 /// Check out the index into the working directory. Any modified files will be overwritten.
268 /// <para/>
269 /// <seealso cref="Branch.Checkout"/> to checkout from a commit.
270 /// </summary>
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));
284 /// <summary>
285 /// Check out given paths from the index overwriting files in the working directory. Modified files might be overwritten.
286 /// </summary>
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);
297 if (e == null)
298 continue;
299 GitIndex.checkoutEntry(new FileInfo(_repo.WorkingDirectory), e);
303 /// <summary>
304 /// Writes the index to the disk.
305 /// </summary>
306 public void Write()
308 GitIndex.write();
311 /// <summary>
312 /// Reads the index from the disk
313 /// </summary>
314 public void Read()
316 GitIndex.Read();
319 //public RepositoryStatus CompareAgainstWorkingDirectory(bool honor_ignore_rules)
321 public RepositoryStatus Status
325 return _repo.Status;
329 /// <summary>
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.
333 /// </summary>
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);
356 return commit;
359 public override string ToString()
361 return "Index[" + Path.Combine(_repo.Directory, "index") + "]";
364 /// <summary>
365 /// The encoding to be used to convert file paths from string to byte arrays.
366 /// </summary>
367 public static Encoding PathEncoding
369 get;
370 set;
373 /// <summary>
374 /// The encoding to be used to convert file contents from string to byte arrays.
375 /// </summary>
376 public static Encoding ContentEncoding
378 get;
379 set;
383 public string GetContent(string path)
385 var blob = this[path];
386 if (blob == null)
387 return null;
388 return ContentEncoding.GetString(blob.RawData);
391 public Blob this[string path]
395 var e = GitIndex.GetEntry(path);
396 if (e == null)
397 return null;
398 return new Blob(_repo, e.ObjectId);
402 //todo
406 public IEnumerable<string> Entries
410 GitIndex.RereadIfNecessary();
411 return GitIndex.Members.Select(e => e.Name).ToArray();
415 /// <summary>
416 /// The number of files tracked by the repository
417 /// </summary>
418 public int Size
420 get { return GitIndex.Members.Count; }