History

feat: Add `Repository::commit_as(committer, author, …)` convenience method.

That way it's, very much beyond convenience, possible to set the time of a commit. Many thanks to @epage for the suggestion.

Sebastian Thiel committed 3 years ago

Commit 8482f901 parent fd287533 files · +73 −18Browse files

Unified · Split

3 files
  1. etc/check-package-size.sh +1 −1
  2. git-repository/src/repository/object.rs +36 −17
  3. git-repository/tests/repository/object.rs +36 −0
etc/check-package-size.sh+1−1
@@ -53,7 +53,7 @@ echo "in root: gitoxide CLI" · 52 unmodified lines
53(enter git-prompt && indent cargo diet -n --package-size-limit 15KB)53(enter git-prompt && indent cargo diet -n --package-size-limit 15KB)
54(enter git-object && indent cargo diet -n --package-size-limit 25KB)54(enter git-object && indent cargo diet -n --package-size-limit 25KB)
55(enter git-commitgraph && indent cargo diet -n --package-size-limit 30KB)55(enter git-commitgraph && indent cargo diet -n --package-size-limit 30KB)
56(enter git-pack && indent cargo diet -n --package-size-limit 120KB)56(enter git-pack && indent cargo diet -n --package-size-limit 125KB)
57(enter git-odb && indent cargo diet -n --package-size-limit 130KB)57(enter git-odb && indent cargo diet -n --package-size-limit 130KB)
58(enter git-protocol && indent cargo diet -n --package-size-limit 60KB)58(enter git-protocol && indent cargo diet -n --package-size-limit 60KB)
59(enter git-packetline && indent cargo diet -n --package-size-limit 35KB)59(enter git-packetline && indent cargo diet -n --package-size-limit 35KB)
git-repository/src/repository/object.rs+36−17
@@ -124,21 +124,13 @@ impl crate::Repository { · 123 unmodified lines
124 self.tag_reference(name, tag_id, constraint).map_err(Into::into)124 self.tag_reference(name, tag_id, constraint).map_err(Into::into)
125 }125 }
126 126
127 /// Create a new commit object with `message` referring to `tree` with `parents`, and point `reference`127 /// Similar to [`commit(…)`][crate::Repository::commit()], but allows to create the commit with `committer` and `author` specified.
128 /// to it. The commit is written without message encoding field, which can be assumed to be UTF-8.
129 /// `author` and `committer` fields are pre-set from the configuration, which can be altered
130 /// [temporarily][crate::Repository::config_snapshot_mut()] before the call if required.
131 ///
132 /// `reference` will be created if it doesn't exist, and can be `"HEAD"` to automatically write-through to the symbolic reference
133 /// that `HEAD` points to if it is not detached. For this reason, detached head states cannot be created unless the `HEAD` is detached
134 /// already. The reflog will be written as canonical git would do, like `<operation> (<detail>): <summary>`.
135 ///
136 /// The first parent id in `parents` is expected to be the current target of `reference` and the operation will fail if it is not.
137 /// If there is no parent, the `reference` is expected to not exist yet.
138 ///128 ///
139 /// The method fails immediately if a `reference` lock can't be acquired.129 /// This forces setting the commit time and author time by hand. Note that typically, committer and author are the same.
140 pub fn commit<Name, E>(130 pub fn commit_as<'a, 'c, Name, E>(
141 &self,131 &self,
132 committer: impl Into<git_actor::SignatureRef<'c>>,
133 author: impl Into<git_actor::SignatureRef<'a>>,
142 reference: Name,134 reference: Name,
143 message: impl AsRef<str>,135 message: impl AsRef<str>,
144 tree: impl Into<ObjectId>,136 tree: impl Into<ObjectId>,
@@ -156,13 +148,11 @@ impl crate::Repository { · 11 unmodified lines
156 // TODO: possibly use CommitRef to save a few allocations (but will have to allocate for object ids anyway.148 // TODO: possibly use CommitRef to save a few allocations (but will have to allocate for object ids anyway.
157 // This can be made vastly more efficient though if we wanted to, so we lie in the API149 // This can be made vastly more efficient though if we wanted to, so we lie in the API
158 let reference = reference.try_into()?;150 let reference = reference.try_into()?;
159 let author = self.author_or_default();
160 let committer = self.committer_or_default();
161 let commit = git_object::Commit {151 let commit = git_object::Commit {
162 message: message.as_ref().into(),152 message: message.as_ref().into(),
163 tree: tree.into(),153 tree: tree.into(),
164 author: author.to_owned(),154 author: author.into().to_owned(),
165 committer: committer.to_owned(),155 committer: committer.into().to_owned(),
166 encoding: None,156 encoding: None,
167 parents: parents.into_iter().map(|id| id.into()).collect(),157 parents: parents.into_iter().map(|id| id.into()).collect(),
168 extra_headers: Default::default(),158 extra_headers: Default::default(),
@@ -194,6 +184,35 @@ impl crate::Repository { · 25 unmodified lines
194 Ok(commit_id)184 Ok(commit_id)
195 }185 }
196 186
187 /// Create a new commit object with `message` referring to `tree` with `parents`, and point `reference`
188 /// to it. The commit is written without message encoding field, which can be assumed to be UTF-8.
189 /// `author` and `committer` fields are pre-set from the configuration, which can be altered
190 /// [temporarily][crate::Repository::config_snapshot_mut()] before the call if required.
191 ///
192 /// `reference` will be created if it doesn't exist, and can be `"HEAD"` to automatically write-through to the symbolic reference
193 /// that `HEAD` points to if it is not detached. For this reason, detached head states cannot be created unless the `HEAD` is detached
194 /// already. The reflog will be written as canonical git would do, like `<operation> (<detail>): <summary>`.
195 ///
196 /// The first parent id in `parents` is expected to be the current target of `reference` and the operation will fail if it is not.
197 /// If there is no parent, the `reference` is expected to not exist yet.
198 ///
199 /// The method fails immediately if a `reference` lock can't be acquired.
200 pub fn commit<Name, E>(
201 &self,
202 reference: Name,
203 message: impl AsRef<str>,
204 tree: impl Into<ObjectId>,
205 parents: impl IntoIterator<Item = impl Into<ObjectId>>,
206 ) -> Result<Id<'_>, commit::Error>
207 where
208 Name: TryInto<FullName, Error = E>,
209 commit::Error: From<E>,
210 {
211 let author = self.author_or_default();
212 let committer = self.committer_or_default();
213 self.commit_as(committer, author, reference, message, tree, parents)
214 }
215
197 /// Return an empty tree object, suitable for [getting changes](crate::Tree::changes()).216 /// Return an empty tree object, suitable for [getting changes](crate::Tree::changes()).
198 ///217 ///
199 /// Note that it is special and doesn't physically exist in the object database even though it can be returned.218 /// Note that it is special and doesn't physically exist in the object database even though it can be returned.
git-repository/tests/repository/object.rs+36−0
@@ -147,6 +147,42 @@ mod tag { · 146 unmodified lines
147 }147 }
148}148}
149 149
150mod commit_as {
151 use git_repository as git;
152 use git_testtools::tempfile;
153
154 #[test]
155 fn specify_committer_and_author() -> crate::Result {
156 let tmp = tempfile::tempdir().unwrap();
157 let repo = git::init(&tmp).unwrap();
158 let empty_tree = repo.empty_tree();
159 let committer = git::actor::Signature {
160 name: "c".into(),
161 email: "c@example.com".into(),
162 time: git::actor::Time::new(1, 1800),
163 };
164 let author = git::actor::Signature {
165 name: "a".into(),
166 email: "a@example.com".into(),
167 time: git::actor::Time::new(3, 3600),
168 };
169
170 let commit_id = repo.commit_as(
171 &committer,
172 &author,
173 "HEAD",
174 "initial",
175 empty_tree.id,
176 git::commit::NO_PARENT_IDS,
177 )?;
178 let commit = commit_id.object()?.into_commit();
179
180 assert_eq!(commit.committer()?, committer.to_ref());
181 assert_eq!(commit.author()?, author.to_ref());
182 Ok(())
183 }
184}
185
150mod commit {186mod commit {
151 use git_repository as git;187 use git_repository as git;
152 use git_testtools::{hex_to_id, tempfile};188 use git_testtools::{hex_to_id, tempfile};